mirror of
https://github.com/pjones/plasma-manager.git
synced 2024-10-03 18:57:24 +03:00
Merge branch 'trunk' into plasma-panel-spacer-extended
This commit is contained in:
commit
9075c576af
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -1,10 +1,16 @@
|
||||
name: "Check"
|
||||
name: "Nix Checks"
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**/*.nix'
|
||||
- 'flake.lock'
|
||||
- 'script/**'
|
||||
|
||||
# cancel previous runs when pushing new changes
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
checks:
|
||||
runs-on: ubuntu-latest
|
||||
|
12
.github/workflows/github_pages.yml
vendored
12
.github/workflows/github_pages.yml
vendored
@ -1,8 +1,14 @@
|
||||
name: GitHub Pages
|
||||
name: GitHub Pages Docs Generation
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- trunk
|
||||
- trunk
|
||||
paths:
|
||||
- 'flake.nix'
|
||||
- 'flake.lock'
|
||||
- 'modules/**'
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
strategy:
|
||||
@ -25,4 +31,4 @@ jobs:
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./public
|
||||
publish_dir: ./public
|
||||
|
@ -33,16 +33,19 @@ broken when used with plasma 5. If you want the best experience with
|
||||
At the moment `plasma-manager` supports configuring the following:
|
||||
- KDE configuration files (via the `files` module)
|
||||
- Global themes, colorschemes, icons, cursortheme, wallpaper (via the `workspace` module)
|
||||
- Desktop icons, widgets, and mouse actions (via the `desktop` module)
|
||||
- Configuration of spectacle shortcuts (via the `spectacle` module)
|
||||
- Shortcuts (via the `shortcuts` module)
|
||||
- Hotkeys (via the `hotkeys` module)
|
||||
- Panels and Extra Widgets (via the `panels` module)
|
||||
- Keyboards, Touchpads and Mice (via the `input` module)
|
||||
- KRunner (via the `krunner` module)
|
||||
- Screen locker (via the `kscreenlocker` module)
|
||||
- Fonts (via the `fonts` module)
|
||||
- Window Rules (via the `window-rules` module)
|
||||
- KDE apps (via the `apps` module). In particular the following kde apps have
|
||||
modules in `plasma-manager`:
|
||||
- ghostwriter
|
||||
- kate
|
||||
- konsole
|
||||
- okular
|
||||
@ -76,6 +79,10 @@ We provide some examples to help you get started. These are located in the
|
||||
With more to come! These should give you some idea how to get started with
|
||||
`plasma-manager`.
|
||||
|
||||
Additionally,
|
||||
[the manual section containing all the supported plasma-manager options](https://nix-community.github.io/plasma-manager/options.xhtml)
|
||||
may come in handy.
|
||||
|
||||
## Make your configuration more declarative with overrideConfig
|
||||
By default `plasma-manager` will simply write the specified configurations to
|
||||
various config-files and leave all other options alone. This way settings not
|
||||
|
@ -1,4 +1,6 @@
|
||||
{ pkgs ? import <nixpkgs> { } }:
|
||||
{
|
||||
pkgs ? import <nixpkgs> { },
|
||||
}:
|
||||
rec {
|
||||
docs = import ./docs {
|
||||
inherit pkgs;
|
||||
|
@ -1,7 +1,12 @@
|
||||
{ pkgs, lib, ... }:
|
||||
let
|
||||
dontCheckModules = { _module.check = false; };
|
||||
modules = [ ../modules dontCheckModules ];
|
||||
dontCheckModules = {
|
||||
_module.check = false;
|
||||
};
|
||||
modules = [
|
||||
../modules
|
||||
dontCheckModules
|
||||
];
|
||||
|
||||
githubDeclaration = user: repo: branch: subpath: {
|
||||
url = "https://github.com/${user}/${repo}/blob/${branch}/${subpath}";
|
||||
@ -10,30 +15,40 @@ let
|
||||
|
||||
pmPath = toString ./..;
|
||||
transformOptions =
|
||||
opt: opt // {
|
||||
declarations = (map
|
||||
(decl:
|
||||
if (lib.hasPrefix pmPath (toString decl)) then (githubDeclaration "nix-community" "plasma-manager" "trunk" (lib.removePrefix "/" (lib.removePrefix pmPath (toString decl)))) else decl)
|
||||
opt.declarations);
|
||||
opt:
|
||||
opt
|
||||
// {
|
||||
declarations = (
|
||||
map (
|
||||
decl:
|
||||
if (lib.hasPrefix pmPath (toString decl)) then
|
||||
(githubDeclaration "nix-community" "plasma-manager" "trunk" (
|
||||
lib.removePrefix "/" (lib.removePrefix pmPath (toString decl))
|
||||
))
|
||||
else
|
||||
decl
|
||||
) opt.declarations
|
||||
);
|
||||
};
|
||||
|
||||
buildOptionsDocs = (args@{ modules, ... }:
|
||||
buildOptionsDocs = (
|
||||
args@{ modules, ... }:
|
||||
let
|
||||
opts = (lib.evalModules {
|
||||
inherit modules;
|
||||
class = "homeManager";
|
||||
}).options;
|
||||
opts =
|
||||
(lib.evalModules {
|
||||
inherit modules;
|
||||
class = "homeManager";
|
||||
}).options;
|
||||
options = builtins.removeAttrs opts [ "_module" ];
|
||||
in
|
||||
pkgs.buildPackages.nixosOptionsDoc {
|
||||
inherit options;
|
||||
inherit transformOptions;
|
||||
warningsAreErrors = false;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
pmOptionsDoc = buildOptionsDocs {
|
||||
inherit modules;
|
||||
};
|
||||
pmOptionsDoc = buildOptionsDocs { inherit modules; };
|
||||
plasma-manager-options = pkgs.callPackage ./plasma-manager-options.nix {
|
||||
nixos-render-docs = pkgs.nixos-render-docs;
|
||||
plasma-manager-options = pmOptionsDoc.optionsJSON;
|
||||
@ -42,4 +57,5 @@ let
|
||||
in
|
||||
{
|
||||
html = plasma-manager-options;
|
||||
json = pmOptionsDoc.optionsJSON;
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
{ stdenv
|
||||
, nixos-render-docs
|
||||
, plasma-manager-options
|
||||
, revision
|
||||
, lib
|
||||
, documentation-highlighter
|
||||
{
|
||||
stdenv,
|
||||
nixos-render-docs,
|
||||
plasma-manager-options,
|
||||
revision,
|
||||
lib,
|
||||
documentation-highlighter,
|
||||
}:
|
||||
let
|
||||
outputPath = "share/doc/plasma-manager";
|
||||
|
@ -175,7 +175,7 @@
|
||||
icon = "view-media-track";
|
||||
};
|
||||
preferredSource = "spotify";
|
||||
showPlaybackControls = true;
|
||||
musicControls.showPlaybackControls = true;
|
||||
songText = {
|
||||
displayInSeparateLines = true;
|
||||
maximumWidth = 640;
|
||||
@ -213,29 +213,47 @@
|
||||
];
|
||||
|
||||
powerdevil = {
|
||||
powerButtonAction = "lockScreen";
|
||||
autoSuspend = {
|
||||
action = "shutDown";
|
||||
idleTimeout = 1000;
|
||||
AC = {
|
||||
powerButtonAction = "lockScreen";
|
||||
autoSuspend = {
|
||||
action = "shutDown";
|
||||
idleTimeout = 1000;
|
||||
};
|
||||
turnOffDisplay = {
|
||||
idleTimeout = 1000;
|
||||
idleTimeoutWhenLocked = "immediately";
|
||||
};
|
||||
};
|
||||
turnOffDisplay = {
|
||||
idleTimeout = 1000;
|
||||
idleTimeoutWhenLocked = "immediately";
|
||||
battery = {
|
||||
powerButtonAction = "sleep";
|
||||
whenSleepingEnter = "standbyThenHibernate";
|
||||
};
|
||||
lowBattery = {
|
||||
whenLaptopLidClosed = "hibernate";
|
||||
};
|
||||
};
|
||||
|
||||
kwin = {
|
||||
edgeBarrier = 0; # Disables the edge-barriers introduced in plasma 6.1
|
||||
cornerBarrier = false;
|
||||
|
||||
scripts.polonium.enable = true;
|
||||
};
|
||||
|
||||
kscreenlocker = {
|
||||
lockOnResume = true;
|
||||
timeout = 10;
|
||||
};
|
||||
|
||||
#
|
||||
# Some mid-level settings:
|
||||
#
|
||||
shortcuts = {
|
||||
ksmserver = {
|
||||
"Lock Session" = [ "Screensaver" "Meta+Ctrl+Alt+L" ];
|
||||
"Lock Session" = [
|
||||
"Screensaver"
|
||||
"Meta+Ctrl+Alt+L"
|
||||
];
|
||||
};
|
||||
|
||||
kwin = {
|
||||
@ -247,7 +265,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#
|
||||
# Some low-level settings:
|
||||
#
|
||||
|
@ -1,9 +1,7 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
<plasma-manager/modules>
|
||||
];
|
||||
imports = [ <plasma-manager/modules> ];
|
||||
|
||||
programs.plasma = {
|
||||
enable = true;
|
||||
@ -41,19 +39,19 @@
|
||||
{
|
||||
location = "top";
|
||||
height = 26;
|
||||
widgets = [
|
||||
"org.kde.plasma.appmenu"
|
||||
];
|
||||
widgets = [ "org.kde.plasma.appmenu" ];
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
#
|
||||
# Some mid-level settings:
|
||||
#
|
||||
shortcuts = {
|
||||
ksmserver = {
|
||||
"Lock Session" = [ "Screensaver" "Meta+Ctrl+Alt+L" ];
|
||||
"Lock Session" = [
|
||||
"Screensaver"
|
||||
"Meta+Ctrl+Alt+L"
|
||||
];
|
||||
};
|
||||
|
||||
kwin = {
|
||||
@ -65,7 +63,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#
|
||||
# Some low-level settings:
|
||||
#
|
||||
|
@ -14,7 +14,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs@ { nixpkgs, home-manager, plasma-manager, ... }:
|
||||
outputs =
|
||||
inputs@{
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
plasma-manager,
|
||||
...
|
||||
}:
|
||||
let
|
||||
# Replace with your username
|
||||
username = "jdoe";
|
||||
|
@ -14,7 +14,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs@ { nixpkgs, home-manager, plasma-manager, ... }:
|
||||
outputs =
|
||||
inputs@{
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
plasma-manager,
|
||||
...
|
||||
}:
|
||||
let
|
||||
# Replace with your username
|
||||
username = "jdoe";
|
||||
|
82
flake.nix
82
flake.nix
@ -8,7 +8,8 @@
|
||||
home-manager.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = inputs@{ self, ... }:
|
||||
outputs =
|
||||
inputs@{ self, ... }:
|
||||
let
|
||||
# Systems that can run tests:
|
||||
supportedSystems = [
|
||||
@ -21,36 +22,49 @@
|
||||
forAllSystems = inputs.nixpkgs.lib.genAttrs supportedSystems;
|
||||
|
||||
# Attribute set of nixpkgs for each system:
|
||||
nixpkgsFor = forAllSystems (system:
|
||||
import inputs.nixpkgs { inherit system; });
|
||||
nixpkgsFor = forAllSystems (system: import inputs.nixpkgs { inherit system; });
|
||||
in
|
||||
{
|
||||
homeManagerModules.plasma-manager = { ... }: {
|
||||
imports = [ ./modules ];
|
||||
};
|
||||
homeManagerModules.plasma-manager =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [ ./modules ];
|
||||
};
|
||||
|
||||
packages = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in
|
||||
packages = forAllSystems (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system};
|
||||
docs = import ./docs {
|
||||
inherit pkgs;
|
||||
lib = pkgs.lib;
|
||||
};
|
||||
in
|
||||
{
|
||||
default = self.packages.${system}.rc2nix;
|
||||
|
||||
demo = (inputs.nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
modules = [
|
||||
(import test/demo.nix {
|
||||
home-manager-module = inputs.home-manager.nixosModules.home-manager;
|
||||
plasma-module = self.homeManagerModules.plasma-manager;
|
||||
})
|
||||
(_: { environment.systemPackages = [ self.packages.${system}.rc2nix ]; })
|
||||
];
|
||||
}).config.system.build.vm;
|
||||
demo =
|
||||
(inputs.nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
modules = [
|
||||
(import test/demo.nix {
|
||||
home-manager-module = inputs.home-manager.nixosModules.home-manager;
|
||||
plasma-module = self.homeManagerModules.plasma-manager;
|
||||
})
|
||||
(_: { environment.systemPackages = [ self.packages.${system}.rc2nix ]; })
|
||||
];
|
||||
}).config.system.build.vm;
|
||||
|
||||
docs-html = docs.html;
|
||||
docs-json = docs.json;
|
||||
|
||||
rc2nix = pkgs.writeShellApplication {
|
||||
name = "rc2nix";
|
||||
runtimeInputs = with pkgs; [ python3 ];
|
||||
text = ''python3 ${script/rc2nix.py} "$@"'';
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
apps = forAllSystems (system: {
|
||||
default = self.apps.${system}.rc2nix;
|
||||
@ -66,28 +80,26 @@
|
||||
};
|
||||
});
|
||||
|
||||
checks = forAllSystems (system:
|
||||
{
|
||||
default = nixpkgsFor.${system}.callPackage ./test/basic.nix {
|
||||
home-manager-module = inputs.home-manager.nixosModules.home-manager;
|
||||
plasma-module = self.homeManagerModules.plasma-manager;
|
||||
};
|
||||
});
|
||||
checks = forAllSystems (system: {
|
||||
default = nixpkgsFor.${system}.callPackage ./test/basic.nix {
|
||||
home-manager-module = inputs.home-manager.nixosModules.home-manager;
|
||||
plasma-module = self.homeManagerModules.plasma-manager;
|
||||
};
|
||||
});
|
||||
|
||||
formatter = forAllSystems (system: nixpkgsFor.${system}.treefmt);
|
||||
|
||||
devShells = forAllSystems (system: {
|
||||
default = nixpkgsFor.${system}.mkShell {
|
||||
buildInputs = with nixpkgsFor.${system}; [
|
||||
nixfmt-rfc-style
|
||||
ruby
|
||||
ruby.devdoc
|
||||
(
|
||||
python3.withPackages (
|
||||
pyPkgs: [
|
||||
pyPkgs.python-lsp-server
|
||||
pyPkgs.black
|
||||
pyPkgs.isort
|
||||
]
|
||||
)
|
||||
)
|
||||
(python3.withPackages (pyPkgs: [
|
||||
pyPkgs.python-lsp-server
|
||||
pyPkgs.black
|
||||
pyPkgs.isort
|
||||
]))
|
||||
];
|
||||
};
|
||||
});
|
||||
|
@ -35,13 +35,10 @@ let
|
||||
"Colors:Window" = colorUIKeys;
|
||||
};
|
||||
in
|
||||
(lib.mkMerge
|
||||
(lib.mapAttrsToList
|
||||
(group: keys: {
|
||||
"kdeglobals"."${group}" = (lib.mkMerge
|
||||
(map
|
||||
(key:
|
||||
{ "${key}"."persistent" = (lib.mkDefault true); })
|
||||
keys));
|
||||
})
|
||||
ignoreKeys))
|
||||
(lib.mkMerge (
|
||||
lib.mapAttrsToList (group: keys: {
|
||||
"kdeglobals"."${group}" = (
|
||||
lib.mkMerge (map (key: { "${key}"."persistent" = (lib.mkDefault true); }) keys)
|
||||
);
|
||||
}) ignoreKeys
|
||||
))
|
||||
|
@ -1,7 +1,8 @@
|
||||
{ lib, config, ... }:
|
||||
let
|
||||
widgets = (import ../modules/widgets { inherit lib; });
|
||||
panelToLayout = panel:
|
||||
panelToLayout =
|
||||
panel:
|
||||
let
|
||||
inherit (widgets.lib) addWidgetStmts stringIfNotNull;
|
||||
inherit (lib) boolToString;
|
||||
@ -14,9 +15,14 @@ let
|
||||
'';
|
||||
in
|
||||
''
|
||||
${if (panel.screen == "all") then "for (screenID = 0; screenID < screenCount; screenID++)"
|
||||
else if (builtins.isList panel.screen) then "for (var screenID in [${builtins.concatStringsSep "," (map builtins.toString panel.screen)}])"
|
||||
else ""}
|
||||
${
|
||||
if (panel.screen == "all") then
|
||||
"for (screenID = 0; screenID < screenCount; screenID++)"
|
||||
else if (builtins.isList panel.screen) then
|
||||
"for (var screenID in [${builtins.concatStringsSep "," (map builtins.toString panel.screen)}])"
|
||||
else
|
||||
""
|
||||
}
|
||||
{
|
||||
const panel = new Panel();
|
||||
panel.height = ${toString panel.height};
|
||||
|
109
lib/qfont.nix
109
lib/qfont.nix
@ -1,4 +1,5 @@
|
||||
{lib, ...}: let
|
||||
{ lib, ... }:
|
||||
let
|
||||
#=== ENUMS ===
|
||||
enums = {
|
||||
# QFont::StyleHint
|
||||
@ -93,18 +94,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
inherit (builtins) attrNames mapAttrs removeAttrs isAttrs;
|
||||
inherit (builtins)
|
||||
attrNames
|
||||
mapAttrs
|
||||
removeAttrs
|
||||
isAttrs
|
||||
;
|
||||
inherit (lib) filterAttrs;
|
||||
|
||||
toEnums = v: lib.types.enum (attrNames v);
|
||||
in
|
||||
mapAttrs (_: toEnums) (removeAttrs enums ["styleStrategy"])
|
||||
// {
|
||||
styleStrategy = mapAttrs (_: toEnums) (filterAttrs (_: isAttrs) enums.styleStrategy);
|
||||
mapAttrs (_: toEnums) (removeAttrs enums [ "styleStrategy" ])
|
||||
// {
|
||||
styleStrategy = mapAttrs (_: toEnums) (filterAttrs (_: isAttrs) enums.styleStrategy);
|
||||
|
||||
# Converts a font specified by the given attrset to a string representation compatible with
|
||||
# QFont::fromString and QFont::toString.
|
||||
fontToString = {
|
||||
# Converts a font specified by the given attrset to a string representation compatible with
|
||||
# QFont::fromString and QFont::toString.
|
||||
fontToString =
|
||||
{
|
||||
family,
|
||||
pointSize ? null,
|
||||
pixelSize ? null,
|
||||
@ -119,18 +126,22 @@ in
|
||||
letterSpacing ? 0,
|
||||
wordSpacing ? 0,
|
||||
stretch ? "anyStretch",
|
||||
styleStrategy ? {},
|
||||
styleStrategy ? { },
|
||||
styleName ? null,
|
||||
}: let
|
||||
inherit (builtins) isString toString foldl' bitOr;
|
||||
}:
|
||||
let
|
||||
inherit (builtins)
|
||||
isString
|
||||
toString
|
||||
foldl'
|
||||
bitOr
|
||||
;
|
||||
|
||||
styleStrategy' = let
|
||||
match = s: enums.styleStrategy.${s}.${styleStrategy.${s} or "default"};
|
||||
ifSet = k:
|
||||
if styleStrategy.${k} or false
|
||||
then enums.styleStrategy.${k}
|
||||
else 0;
|
||||
in
|
||||
styleStrategy' =
|
||||
let
|
||||
match = s: enums.styleStrategy.${s}.${styleStrategy.${s} or "default"};
|
||||
ifSet = k: if styleStrategy.${k} or false then enums.styleStrategy.${k} else 0;
|
||||
in
|
||||
foldl' bitOr 0 [
|
||||
(match "prefer")
|
||||
(match "matchingPrefer")
|
||||
@ -140,40 +151,34 @@ in
|
||||
(ifSet "noFontMerging")
|
||||
];
|
||||
|
||||
sizeToString = s:
|
||||
if s == null
|
||||
then "-1"
|
||||
else toString s;
|
||||
sizeToString = s: if s == null then "-1" else toString s;
|
||||
|
||||
numOrEnum = attrs: s:
|
||||
if isString s
|
||||
then toString attrs.${s}
|
||||
else toString s;
|
||||
numOrEnum = attrs: s: if isString s then toString attrs.${s} else toString s;
|
||||
|
||||
zeroOrOne = b:
|
||||
if b
|
||||
then "1"
|
||||
else "0";
|
||||
zeroOrOne = b: if b then "1" else "0";
|
||||
in
|
||||
assert lib.assertMsg (lib.xor (pointSize != null) (pixelSize != null))
|
||||
"Exactly one of `pointSize` and `pixelSize` has to be set.";
|
||||
builtins.concatStringsSep "," ([
|
||||
family
|
||||
(sizeToString pointSize)
|
||||
(sizeToString pixelSize)
|
||||
(toString enums.styleHint.${styleHint})
|
||||
(numOrEnum enums.weight weight)
|
||||
(numOrEnum enums.style style)
|
||||
(zeroOrOne underline)
|
||||
(zeroOrOne strikeOut)
|
||||
(zeroOrOne fixedPitch)
|
||||
"0"
|
||||
(toString enums.capitalization.${capitalization})
|
||||
(toString enums.spacingType.${letterSpacingType})
|
||||
(toString letterSpacing)
|
||||
(toString wordSpacing)
|
||||
(numOrEnum enums.stretch stretch)
|
||||
(toString styleStrategy')
|
||||
]
|
||||
++ lib.optional (styleName != null) styleName);
|
||||
}
|
||||
assert lib.assertMsg (lib.xor (pointSize != null) (
|
||||
pixelSize != null
|
||||
)) "Exactly one of `pointSize` and `pixelSize` has to be set.";
|
||||
builtins.concatStringsSep "," (
|
||||
[
|
||||
family
|
||||
(sizeToString pointSize)
|
||||
(sizeToString pixelSize)
|
||||
(toString enums.styleHint.${styleHint})
|
||||
(numOrEnum enums.weight weight)
|
||||
(numOrEnum enums.style style)
|
||||
(zeroOrOne underline)
|
||||
(zeroOrOne strikeOut)
|
||||
(zeroOrOne fixedPitch)
|
||||
"0"
|
||||
(toString enums.capitalization.${capitalization})
|
||||
(toString enums.spacingType.${letterSpacingType})
|
||||
(toString letterSpacing)
|
||||
(toString wordSpacing)
|
||||
(numOrEnum enums.stretch stretch)
|
||||
(toString styleStrategy')
|
||||
]
|
||||
++ lib.optional (styleName != null) styleName
|
||||
);
|
||||
}
|
||||
|
@ -2,42 +2,62 @@
|
||||
let
|
||||
##############################################################################
|
||||
# Types for storing settings.
|
||||
basicSettingsType = (with lib.types;
|
||||
nullOr (oneOf [ bool float int str ]));
|
||||
advancedSettingsType = (with lib.types; submodule {
|
||||
options = {
|
||||
value = lib.mkOption {
|
||||
type = basicSettingsType;
|
||||
default = null;
|
||||
description = "The value for some key.";
|
||||
basicSettingsType = (
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
bool
|
||||
float
|
||||
int
|
||||
str
|
||||
])
|
||||
);
|
||||
advancedSettingsType = (
|
||||
with lib.types;
|
||||
submodule {
|
||||
options = {
|
||||
value = lib.mkOption {
|
||||
type = basicSettingsType;
|
||||
default = null;
|
||||
description = "The value for some key.";
|
||||
};
|
||||
immutable = lib.mkOption {
|
||||
type = bool;
|
||||
default = config.programs.plasma.immutableByDefault;
|
||||
description = ''
|
||||
Whether to make the key immutable. This corresponds to adding [$i] to
|
||||
the end of the key.
|
||||
'';
|
||||
};
|
||||
shellExpand = lib.mkOption {
|
||||
type = bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to mark the key for shell expansion. This corresponds to
|
||||
adding [$e] to the end of the key.
|
||||
'';
|
||||
};
|
||||
persistent = lib.mkOption {
|
||||
type = bool;
|
||||
default = false;
|
||||
description = ''
|
||||
When overrideConfig is enabled and the key is persistent,
|
||||
plasma-manager will leave it unchanged after activation.
|
||||
'';
|
||||
};
|
||||
escapeValue = lib.mkOption {
|
||||
type = bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to escape the value according to kde's escape-format. See:
|
||||
https://github.com/KDE/kconfig/blob/44f98ff5cb9008436ba5ba385cae03bbd0ab33e6/src/core/kconfigini.cpp#L882
|
||||
for info about this format.
|
||||
'';
|
||||
};
|
||||
};
|
||||
immutable = lib.mkOption {
|
||||
type = bool;
|
||||
default = config.programs.plasma.immutableByDefault;
|
||||
description = ''
|
||||
Whether to make the key immutable. This corresponds to adding [$i] to
|
||||
the end of the key.
|
||||
'';
|
||||
};
|
||||
shellExpand = lib.mkOption {
|
||||
type = bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to mark the key for shell expansion. This corresponds to
|
||||
adding [$e] to the end of the key.
|
||||
'';
|
||||
};
|
||||
persistent = lib.mkOption {
|
||||
type = bool;
|
||||
default = false;
|
||||
description = ''
|
||||
When overrideConfig is enabled and the key is persistent,
|
||||
plasma-manager will leave it unchanged after activation.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
coercedSettingsType = with lib.types;
|
||||
}
|
||||
);
|
||||
coercedSettingsType =
|
||||
with lib.types;
|
||||
coercedTo basicSettingsType (value: { inherit value; }) advancedSettingsType;
|
||||
in
|
||||
{
|
||||
|
@ -1,30 +1,55 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
wallpaperPictureOfTheDayType = with lib.types; submodule {
|
||||
options = {
|
||||
provider = lib.mkOption {
|
||||
type = nullOr (enum [ "apod" "bing" "flickr" "natgeo" "noaa" "wcpotd" "epod" "simonstalenhag" ]);
|
||||
description = "The provider for the Picture of the Day plugin.";
|
||||
};
|
||||
updateOverMeteredConnection = lib.mkOption {
|
||||
type = bool;
|
||||
default = false;
|
||||
description = "Whether to update the wallpaper on a metered connection.";
|
||||
wallpaperPictureOfTheDayType =
|
||||
with lib.types;
|
||||
submodule {
|
||||
options = {
|
||||
provider = lib.mkOption {
|
||||
type = nullOr (enum [
|
||||
"apod"
|
||||
"bing"
|
||||
"flickr"
|
||||
"natgeo"
|
||||
"noaa"
|
||||
"wcpotd"
|
||||
"epod"
|
||||
"simonstalenhag"
|
||||
]);
|
||||
description = "The provider for the Picture of the Day plugin.";
|
||||
};
|
||||
updateOverMeteredConnection = lib.mkOption {
|
||||
type = bool;
|
||||
default = false;
|
||||
description = "Whether to update the wallpaper on a metered connection.";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
wallpaperSlideShowType = with lib.types; submodule {
|
||||
options = {
|
||||
path = lib.mkOption {
|
||||
type = either path (listOf path);
|
||||
description = "The path(s) where the wallpapers are located.";
|
||||
};
|
||||
interval = lib.mkOption {
|
||||
type = int;
|
||||
default = 300;
|
||||
description = "The length between wallpaper switches.";
|
||||
wallpaperSlideShowType =
|
||||
with lib.types;
|
||||
submodule {
|
||||
options = {
|
||||
path = lib.mkOption {
|
||||
type = either path (listOf path);
|
||||
description = "The path(s) where the wallpapers are located.";
|
||||
};
|
||||
interval = lib.mkOption {
|
||||
type = int;
|
||||
default = 300;
|
||||
description = "The length between wallpaper switches.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Values are taken from
|
||||
# https://invent.kde.org/plasma/kdeplasma-addons/-/blob/bc53d651cf60709396c9229f8c582ec8a9d2ee53/applets/mediaframe/package/contents/ui/ConfigGeneral.qml#L148-170
|
||||
wallpaperFillModeTypes = {
|
||||
"stretch" = 0; # a.k.a. Scaled
|
||||
"preserveAspectFit" = 1; # a.k.a. Scaled Keep Proportions
|
||||
"preserveAspectCrop" = 2; # a.k.a. Scaled And Cropped
|
||||
"tile" = 3;
|
||||
"tileVertically" = 4;
|
||||
"tileHorizontally" = 5;
|
||||
"pad" = 6; # a.k.a. Centered
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
{ pkgs, lib, config }:
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
}:
|
||||
|
||||
let
|
||||
writeConfigScript = pkgs.writeShellApplication {
|
||||
@ -12,17 +16,19 @@ let
|
||||
# attribute-set as json. Here a is the attribute-set.
|
||||
#
|
||||
# Type: AttrSet -> string
|
||||
writeConfig = json: overrideConfig: resetFilesList:
|
||||
writeConfig =
|
||||
json: overrideConfig: resetFilesList:
|
||||
let
|
||||
jsonStr = builtins.toJSON json;
|
||||
# Writing to file handles special characters better than passing it in as
|
||||
# an argument to the script.
|
||||
jsonFile = pkgs.writeText "data.json" jsonStr;
|
||||
resetFilesStr = builtins.toString
|
||||
(if overrideConfig then
|
||||
resetFilesStr = builtins.toString (
|
||||
if overrideConfig then
|
||||
resetFilesList ++ [ "${config.xdg.dataHome}/plasma-manager/last_run_*" ]
|
||||
else
|
||||
resetFilesList);
|
||||
resetFilesList
|
||||
);
|
||||
immutableByDefault = (builtins.toString config.programs.plasma.immutableByDefault);
|
||||
in
|
||||
''
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
{
|
||||
imports = [
|
||||
./ghostwriter.nix
|
||||
./konsole.nix
|
||||
./kate
|
||||
./okular.nix
|
||||
|
737
modules/apps/ghostwriter.nix
Normal file
737
modules/apps/ghostwriter.nix
Normal file
@ -0,0 +1,737 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.programs.ghostwriter;
|
||||
|
||||
qfont = import ../../lib/qfont.nix { inherit lib; };
|
||||
|
||||
createThemes = lib.attrsets.mapAttrs' (
|
||||
name: value:
|
||||
lib.attrsets.nameValuePair ("ghostwriter/themes/${name}.json") ({
|
||||
enable = true;
|
||||
source = value;
|
||||
})
|
||||
);
|
||||
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (ghostwriter): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
getBoolFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else if (getIndexFromEnum enum value) == 0 then
|
||||
false
|
||||
else
|
||||
true;
|
||||
|
||||
styleStrategyType = lib.types.submodule {
|
||||
options = with qfont.styleStrategy; {
|
||||
prefer = lib.mkOption {
|
||||
type = prefer;
|
||||
default = "default";
|
||||
description = ''
|
||||
Which type of font is preferred by the font when finding an appropriate default family.
|
||||
|
||||
`default`, `bitmap`, `device`, `outline`, `forceOutline` correspond to the
|
||||
`PreferDefault`, `PreferBitmap`, `PreferDevice`, `PreferOutline`, `ForceOutline` enum flags
|
||||
respectively.
|
||||
'';
|
||||
};
|
||||
matchingPrefer = lib.mkOption {
|
||||
type = matchingPrefer;
|
||||
default = "default";
|
||||
description = ''
|
||||
Whether the font matching process prefers exact matches, of best quality matches.
|
||||
|
||||
`default` corresponds to not setting any enum flag, and `exact` and `quality`
|
||||
correspond to `PreferMatch` and `PreferQuality` enum flags respectively.
|
||||
'';
|
||||
};
|
||||
antialiasing = lib.mkOption {
|
||||
type = antialiasing;
|
||||
default = "default";
|
||||
description = ''
|
||||
Whether antialiasing is preferred for this font.
|
||||
|
||||
`default` corresponds to not setting any enum flag, and `prefer` and `disable`
|
||||
correspond to `PreferAntialias` and `NoAntialias` enum flags respectively.
|
||||
'';
|
||||
};
|
||||
noSubpixelAntialias = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If set to true, this font will try to avoid subpixel antialiasing.
|
||||
|
||||
Corresponds to the `NoSubpixelAntialias` enum flag.
|
||||
'';
|
||||
};
|
||||
noFontMerging = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If set to true, this font will not try to find a substitute font when encountering missing glyphs.
|
||||
|
||||
Corresponds to the `NoFontMerging` enum flag.
|
||||
'';
|
||||
};
|
||||
preferNoShaping = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If set to true, this font will not try to apply shaping rules that may be required for some scripts
|
||||
(e.g. Indic scripts), increasing performance if these rules are not required.
|
||||
|
||||
Corresponds to the `PreferNoShaping` enum flag.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fontType = lib.types.submodule {
|
||||
options = {
|
||||
family = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The font family of this font.";
|
||||
example = "Noto Sans";
|
||||
};
|
||||
pointSize = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.numbers.positive;
|
||||
default = null;
|
||||
description = ''
|
||||
The point size of this font.
|
||||
|
||||
Could be a decimal, but usually an integer. Mutually exclusive with pixel size.
|
||||
'';
|
||||
};
|
||||
pixelSize = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.ints.u16;
|
||||
default = null;
|
||||
description = ''
|
||||
The pixel size of this font.
|
||||
|
||||
Mutually exclusive with point size.
|
||||
'';
|
||||
};
|
||||
styleHint = lib.mkOption {
|
||||
type = qfont.styleHint;
|
||||
default = "anyStyle";
|
||||
description = ''
|
||||
The style hint of this font.
|
||||
|
||||
See https://doc.qt.io/qt-6/qfont.html#StyleHint-enum for more.
|
||||
'';
|
||||
};
|
||||
weight = lib.mkOption {
|
||||
type = lib.types.either (lib.types.ints.between 1 1000) qfont.weight;
|
||||
default = "normal";
|
||||
description = ''
|
||||
The weight of the font, either as a number between 1 to 1000 or as a pre-defined weight string.
|
||||
|
||||
See https://doc.qt.io/qt-6/qfont.html#Weight-enum for more.
|
||||
'';
|
||||
};
|
||||
style = lib.mkOption {
|
||||
type = qfont.style;
|
||||
default = "normal";
|
||||
description = "The style of the font.";
|
||||
};
|
||||
underline = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether the font is underlined.";
|
||||
};
|
||||
strikeOut = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether the font is struck out.";
|
||||
};
|
||||
fixedPitch = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether the font has a fixed pitch.";
|
||||
};
|
||||
capitalization = lib.mkOption {
|
||||
type = qfont.capitalization;
|
||||
default = "mixedCase";
|
||||
description = ''
|
||||
The capitalization settings for this font.
|
||||
|
||||
See https://doc.qt.io/qt-6/qfont.html#Capitalization-enum for more.
|
||||
'';
|
||||
};
|
||||
letterSpacingType = lib.mkOption {
|
||||
type = qfont.spacingType;
|
||||
default = "percentage";
|
||||
description = ''
|
||||
Whether to use percentage or absolute spacing for this font.
|
||||
|
||||
See https://doc.qt.io/qt-6/qfont.html#SpacingType-enum for more.
|
||||
'';
|
||||
};
|
||||
letterSpacing = lib.mkOption {
|
||||
type = lib.types.number;
|
||||
default = 0;
|
||||
description = ''
|
||||
The amount of letter spacing for this font.
|
||||
|
||||
Could be a percentage or an absolute spacing change (positive increases spacing, negative decreases spacing),
|
||||
based on the selected `letterSpacingType`.
|
||||
'';
|
||||
};
|
||||
wordSpacing = lib.mkOption {
|
||||
type = lib.types.number;
|
||||
default = 0;
|
||||
description = ''
|
||||
The amount of word spacing for this font, in pixels.
|
||||
|
||||
Positive values increase spacing while negative ones decrease spacing.
|
||||
'';
|
||||
};
|
||||
stretch = lib.mkOption {
|
||||
type = lib.types.either (lib.types.ints.between 1 4000) qfont.stretch;
|
||||
default = "anyStretch";
|
||||
description = ''
|
||||
The stretch factor for this font, as an integral percentage (i.e. 150 means a 150% stretch),
|
||||
or as a pre-defined stretch factor string.
|
||||
'';
|
||||
};
|
||||
styleStrategy = lib.mkOption {
|
||||
type = styleStrategyType;
|
||||
default = { };
|
||||
description = ''
|
||||
The strategy for matching similar fonts to this font.
|
||||
|
||||
See https://doc.qt.io/qt-6/qfont.html#StyleStrategy-enum for more.
|
||||
'';
|
||||
};
|
||||
styleName = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
The style name of this font, overriding the `style` and `weight` parameters when set.
|
||||
Used for special fonts that have styles beyond traditional settings.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options.programs.ghostwriter = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Enable configuration management for Ghostwriter.
|
||||
'';
|
||||
|
||||
font = lib.mkOption {
|
||||
type = lib.types.nullOr fontType;
|
||||
default = null;
|
||||
example = {
|
||||
family = "Noto Sans";
|
||||
pointSize = 12;
|
||||
};
|
||||
description = ''
|
||||
The font to use for Ghostwriter.
|
||||
'';
|
||||
apply = font: if font == null then null else ''"${qfont.fontToString font}"'';
|
||||
};
|
||||
|
||||
locale = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "en_US";
|
||||
description = ''
|
||||
The locale to use for Ghostwriter.
|
||||
'';
|
||||
};
|
||||
|
||||
package =
|
||||
lib.mkPackageOption pkgs
|
||||
[
|
||||
"kdePackages"
|
||||
"ghostwriter"
|
||||
]
|
||||
{
|
||||
example = "pkgs.kdePackages.ghostwriter";
|
||||
extraDescription = ''
|
||||
Use `pkgs.libsForQt5.ghostwriter` in Plasma5 and
|
||||
`pkgs.kdePackages.ghostwriter` in Plasma6.
|
||||
'';
|
||||
};
|
||||
|
||||
editor = {
|
||||
styling = {
|
||||
blockquoteStyle =
|
||||
let
|
||||
enumVals = [
|
||||
"simple"
|
||||
"italic"
|
||||
];
|
||||
in
|
||||
lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum enumVals);
|
||||
default = null;
|
||||
example = "simple";
|
||||
description = "The style of blockquotes.";
|
||||
apply = getBoolFromEnum enumVals;
|
||||
};
|
||||
editorWidth =
|
||||
let
|
||||
enumVals = [
|
||||
"narrow"
|
||||
"medium"
|
||||
"wide"
|
||||
"full"
|
||||
];
|
||||
in
|
||||
lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum enumVals);
|
||||
default = null;
|
||||
example = "medium";
|
||||
description = "The width of the editor.";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
emphasisStyle =
|
||||
let
|
||||
enumVals = [
|
||||
"italic"
|
||||
"underline"
|
||||
];
|
||||
in
|
||||
lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum enumVals);
|
||||
default = null;
|
||||
example = "bold";
|
||||
description = "The style of emphasis.";
|
||||
apply = getBoolFromEnum enumVals;
|
||||
};
|
||||
focusMode =
|
||||
let
|
||||
enumVals = [
|
||||
"sentence"
|
||||
"currentLine"
|
||||
"threeLines"
|
||||
"paragraph"
|
||||
"typewriter"
|
||||
];
|
||||
in
|
||||
lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum enumVals);
|
||||
default = null;
|
||||
example = "sentence";
|
||||
description = "The focus mode to use.";
|
||||
apply =
|
||||
focusMode:
|
||||
if focusMode == null then
|
||||
null
|
||||
else
|
||||
builtins.elemAt
|
||||
[
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
]
|
||||
(
|
||||
lib.lists.findFirstIndex (x: x == focusMode)
|
||||
(throw "editor.styling.focusMode: Value ${focusMode} isn't present in the enum. This is a bug")
|
||||
enumVals
|
||||
);
|
||||
};
|
||||
useLargeHeadings = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to use large headings.";
|
||||
};
|
||||
};
|
||||
tabulation = {
|
||||
insertSpacesForTabs = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
description = ''
|
||||
Whether to insert spaces for tabs.
|
||||
'';
|
||||
};
|
||||
tabWidth = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.ints.positive;
|
||||
default = null;
|
||||
description = ''
|
||||
The width of a tab.
|
||||
'';
|
||||
};
|
||||
};
|
||||
typing = {
|
||||
automaticallyMatchCharacters = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to automatically match characters.";
|
||||
};
|
||||
characters = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = ''\"'([{*_`<'';
|
||||
description = "The characters to automatically match.";
|
||||
apply = chars: if chars == null then null else ''"${chars}"'';
|
||||
};
|
||||
};
|
||||
bulletPointCycling = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to cycle through bullet points.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
general = {
|
||||
display = {
|
||||
hideMenubarInFullscreen = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to hide the menubar in fullscreen mode.";
|
||||
};
|
||||
interfaceStyle =
|
||||
let
|
||||
enumVals = [
|
||||
"rounded"
|
||||
"square"
|
||||
];
|
||||
in
|
||||
lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum enumVals);
|
||||
default = null;
|
||||
example = "rounded";
|
||||
description = "The interface style to use for Ghostwriter.";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
showCurrentTimeInFullscreen = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to show the current time in fullscreen mode.";
|
||||
};
|
||||
showUnbreakableSpace = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to show unbreakable space.";
|
||||
};
|
||||
};
|
||||
fileSaving = {
|
||||
autoSave = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to enable auto-save.";
|
||||
};
|
||||
backupFileOnSave = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to backup the file on save.";
|
||||
};
|
||||
backupLocation = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
example = "/home/user/.local/share/ghostwriter/backups";
|
||||
description = ''
|
||||
The location to store backups of the Ghostwriter configuration.
|
||||
'';
|
||||
};
|
||||
};
|
||||
session = {
|
||||
openLastFileOnStartup = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to open the last file on startup.";
|
||||
};
|
||||
rememberRecentFiles = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to remember recent files.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
preview = {
|
||||
codeFont = lib.mkOption {
|
||||
type = lib.types.nullOr fontType;
|
||||
default = null;
|
||||
example = {
|
||||
family = "Hack";
|
||||
pointSize = 12;
|
||||
};
|
||||
description = ''
|
||||
The code font to use for the preview.
|
||||
'';
|
||||
apply = font: if font == null then null else ''"${qfont.fontToString font}"'';
|
||||
};
|
||||
commandLineOptions = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Additional command line options to pass to the preview command.
|
||||
'';
|
||||
};
|
||||
markdownVariant = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
example = "cmark-gfm";
|
||||
description = ''
|
||||
The markdown variant to use for the preview.
|
||||
'';
|
||||
};
|
||||
openByDefault = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = ''
|
||||
Whether to open the preview by default.
|
||||
'';
|
||||
};
|
||||
textFont = lib.mkOption {
|
||||
type = lib.types.nullOr fontType;
|
||||
default = null;
|
||||
example = {
|
||||
family = "Inter";
|
||||
pointSize = 12;
|
||||
};
|
||||
description = ''
|
||||
The text font to use for the preview.
|
||||
'';
|
||||
apply = font: if font == null then null else ''"${qfont.fontToString font}"'';
|
||||
};
|
||||
};
|
||||
|
||||
spelling = {
|
||||
autoDetectLanguage = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to auto-detect the language.";
|
||||
};
|
||||
checkerEnabledByDefault = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether the checker is enabled by default.";
|
||||
};
|
||||
ignoreUppercase = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to ignore uppercase words.";
|
||||
};
|
||||
ignoredWords = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
||||
default = null;
|
||||
example = [
|
||||
"Amarok"
|
||||
"KHTML"
|
||||
"NixOS"
|
||||
];
|
||||
description = "Words to ignore in the spell checker.";
|
||||
apply =
|
||||
ignoredWords: if ignoredWords == null then null else builtins.concatStringsSep ", " ignoredWords;
|
||||
};
|
||||
liveSpellCheck = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to enable live spell checking.";
|
||||
};
|
||||
skipRunTogether = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to skip run-together words.";
|
||||
};
|
||||
};
|
||||
|
||||
theme = {
|
||||
name = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
example = "Ghostwriter";
|
||||
description = ''
|
||||
The name of the theme to use.
|
||||
'';
|
||||
};
|
||||
customThemes = lib.mkOption {
|
||||
type = with lib.types; attrsOf path;
|
||||
default = { };
|
||||
description = ''
|
||||
Custom themes to be added to the installation. The key is their name.
|
||||
Choose them in `programs.ghostwriter.theme.name`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
window.sidebarOpen = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether the sidebar is open by default.";
|
||||
};
|
||||
};
|
||||
|
||||
config = (
|
||||
lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.spelling.ignoredWords == null || cfg.locale != null;
|
||||
message = "ghostwriter: Ignored words can only be set if a locale is set.";
|
||||
}
|
||||
];
|
||||
|
||||
home.packages = [ cfg.package ];
|
||||
|
||||
programs.plasma.configFile = {
|
||||
"kde.org/ghostwriter.conf" = (
|
||||
lib.mkMerge [
|
||||
# Font
|
||||
(lib.mkIf (cfg.font != null) { Style.editorFont = cfg.font; })
|
||||
|
||||
# Locale
|
||||
(lib.mkIf (cfg.locale != null) { Application.locale = cfg.locale; })
|
||||
|
||||
# Editor > Styling
|
||||
(lib.mkIf (cfg.editor.styling.blockquoteStyle != null) {
|
||||
Style.blockquoteStyle = cfg.editor.styling.blockquoteStyle;
|
||||
})
|
||||
(lib.mkIf (cfg.editor.styling.emphasisStyle != null) {
|
||||
Style.underlineInsteadOfItalics = cfg.editor.styling.emphasisStyle;
|
||||
})
|
||||
(lib.mkIf (cfg.editor.styling.editorWidth != null) {
|
||||
Style.editorWidth = cfg.editor.styling.editorWidth;
|
||||
})
|
||||
(lib.mkIf (cfg.editor.styling.focusMode != null) {
|
||||
Style.focusMode = cfg.editor.styling.focusMode;
|
||||
})
|
||||
(lib.mkIf (cfg.editor.styling.useLargeHeadings != null) {
|
||||
Style.largeHeadings = cfg.editor.styling.useLargeHeadings;
|
||||
})
|
||||
|
||||
# Editor > Tabulation
|
||||
(lib.mkIf (cfg.editor.tabulation.insertSpacesForTabs != null) {
|
||||
Tabs.insertSpacesForTabs = cfg.editor.tabulation.insertSpacesForTabs;
|
||||
})
|
||||
(lib.mkIf (cfg.editor.tabulation.tabWidth != null) {
|
||||
Tabs.tabWidth = cfg.editor.tabulation.tabWidth;
|
||||
})
|
||||
|
||||
# Editor > Typing
|
||||
(lib.mkIf (cfg.editor.typing.automaticallyMatchCharacters.enable != null) {
|
||||
Typing.autoMatchEnabled = cfg.editor.typing.automaticallyMatchCharacters.enable;
|
||||
})
|
||||
(lib.mkIf (cfg.editor.typing.automaticallyMatchCharacters.characters != null) {
|
||||
Typing.autoMatchFilter = {
|
||||
value = cfg.editor.typing.automaticallyMatchCharacters.characters;
|
||||
escapeValue = false;
|
||||
};
|
||||
})
|
||||
(lib.mkIf (cfg.editor.typing.bulletPointCycling != null) {
|
||||
Typing.bulletPointCyclingEnabled = cfg.editor.typing.bulletPointCycling;
|
||||
})
|
||||
|
||||
# General > Display
|
||||
(lib.mkIf (cfg.general.display.hideMenubarInFullscreen != null) {
|
||||
Style.hideMenuBarInFullscreen = cfg.general.display.hideMenubarInFullscreen;
|
||||
})
|
||||
(lib.mkIf (cfg.general.display.interfaceStyle != null) {
|
||||
Style.interfaceStyle = cfg.general.display.interfaceStyle;
|
||||
})
|
||||
(lib.mkIf (cfg.general.display.showCurrentTimeInFullscreen != null) {
|
||||
Style.displayTimeInFullScreen = cfg.general.display.showCurrentTimeInFullscreen;
|
||||
})
|
||||
(lib.mkIf (cfg.general.display.showUnbreakableSpace != null) {
|
||||
style.showUnbreakableSpace = cfg.general.display.showUnbreakableSpace;
|
||||
})
|
||||
|
||||
# General > File Saving
|
||||
(lib.mkIf (cfg.general.fileSaving.autoSave != null) {
|
||||
Save.autoSave = cfg.general.fileSaving.autoSave;
|
||||
})
|
||||
(lib.mkIf (cfg.general.fileSaving.backupFileOnSave != null) {
|
||||
Save.backupFile = cfg.general.fileSaving.backupFileOnSave;
|
||||
})
|
||||
(lib.mkIf (cfg.general.fileSaving.backupLocation != null) {
|
||||
Backup.location = cfg.general.fileSaving.backupLocation;
|
||||
})
|
||||
|
||||
# General > Session
|
||||
(lib.mkIf (cfg.general.session.openLastFileOnStartup != null) {
|
||||
Session.restoreSession = cfg.general.session.openLastFileOnStartup;
|
||||
})
|
||||
(lib.mkIf (cfg.general.session.rememberRecentFiles != null) {
|
||||
Session.rememberFileHistory = cfg.general.session.rememberRecentFiles;
|
||||
})
|
||||
|
||||
# Spelling
|
||||
(lib.mkIf (cfg.spelling.liveSpellCheck != null) {
|
||||
Spelling = {
|
||||
liveSpellCheck = cfg.spelling.liveSpellCheck;
|
||||
};
|
||||
})
|
||||
|
||||
# Preview options
|
||||
(lib.mkIf (cfg.preview.codeFont != null) { Preview.codeFont = cfg.preview.codeFont; })
|
||||
(lib.mkIf (cfg.preview.commandLineOptions != null) {
|
||||
Preview.lastUsedExporterParams = cfg.preview.commandLineOptions;
|
||||
})
|
||||
(lib.mkIf (cfg.preview.markdownVariant != null) {
|
||||
Preview.lastUsedExporter = cfg.preview.markdownVariant;
|
||||
})
|
||||
(lib.mkIf (cfg.preview.openByDefault != null) {
|
||||
Preview.htmlPreviewOpen = cfg.preview.openByDefault;
|
||||
})
|
||||
(lib.mkIf (cfg.preview.textFont != null) { Preview.textFont = cfg.preview.textFont; })
|
||||
|
||||
# Theme
|
||||
(lib.mkIf (cfg.theme.name != null) { Style.theme = cfg.theme.name; })
|
||||
|
||||
# Window
|
||||
(lib.mkIf (cfg.window.sidebarOpen != null) { Window.sidebarOpen = cfg.window.sidebarOpen; })
|
||||
]
|
||||
);
|
||||
|
||||
"KDE/Sonnet.conf".General = (
|
||||
lib.mkMerge [
|
||||
(lib.mkIf (cfg.spelling.autoDetectLanguage != null) {
|
||||
autodetectLanguage = cfg.spelling.autoDetectLanguage;
|
||||
})
|
||||
(lib.mkIf (cfg.spelling.checkerEnabledByDefault != null) {
|
||||
checkerEnabledByDefault = cfg.spelling.checkerEnabledByDefault;
|
||||
})
|
||||
(lib.mkIf (cfg.spelling.ignoreUppercase != null) {
|
||||
checkUppercase = !cfg.spelling.ignoreUppercase;
|
||||
})
|
||||
(lib.mkIf (cfg.spelling.ignoredWords != null && cfg.locale != null) {
|
||||
"ignore_${cfg.locale}" = cfg.spelling.ignoredWords;
|
||||
})
|
||||
(lib.mkIf (cfg.locale != null) { defaultLanguage = cfg.locale; })
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
xdg.dataFile = (createThemes cfg.theme.customThemes);
|
||||
}
|
||||
);
|
||||
}
|
@ -1,4 +1,9 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.kate;
|
||||
@ -7,12 +12,12 @@ let
|
||||
# 0 is not tab & not undoByShiftTab
|
||||
# 1 is tab & undoByShiftTab
|
||||
# 2 is not tab & undoByShiftTab
|
||||
tabHandlingMode = indentSettings:
|
||||
if (!indentSettings.undoByShiftTab && !indentSettings.tabFromEverywhere) then 0 else
|
||||
(
|
||||
if (indentSettings.undoByShiftTab && indentSettings.tabFromEverywhere) then 1 else
|
||||
2
|
||||
);
|
||||
tabHandlingMode =
|
||||
indentSettings:
|
||||
if (!indentSettings.undoByShiftTab && !indentSettings.tabFromEverywhere) then
|
||||
0
|
||||
else
|
||||
(if (indentSettings.undoByShiftTab && indentSettings.tabFromEverywhere) then 1 else 2);
|
||||
|
||||
checkThemeNameScript = pkgs.writeShellApplication {
|
||||
name = "checkThemeName";
|
||||
@ -20,22 +25,20 @@ let
|
||||
text = builtins.readFile ./check-theme-name-free.sh;
|
||||
};
|
||||
|
||||
checkThemeName = name:
|
||||
''
|
||||
${checkThemeNameScript}/bin/checkThemeName ${name}
|
||||
'';
|
||||
checkThemeName = name: ''
|
||||
${checkThemeNameScript}/bin/checkThemeName ${name}
|
||||
'';
|
||||
|
||||
script = pkgs.writeScript "kate-check" (checkThemeName cfg.editor.theme.name);
|
||||
|
||||
|
||||
getIndexFromEnum = enum: value:
|
||||
if value == null
|
||||
then null
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex
|
||||
(x: x == value)
|
||||
(throw "getIndexFromEnum (kate): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
lib.lists.findFirstIndex (
|
||||
x: x == value
|
||||
) (throw "getIndexFromEnum (kate): Value ${value} isn't present in the enum. This is a bug") enum;
|
||||
|
||||
qfont = import ../../../lib/qfont.nix { inherit lib; };
|
||||
|
||||
@ -237,15 +240,21 @@ in
|
||||
Enable configuration management for kate.
|
||||
'';
|
||||
|
||||
package = lib.mkPackageOption pkgs [ "kdePackages" "kate" ] {
|
||||
example = "pkgs.libsForQt5.kate";
|
||||
extraDescription = ''
|
||||
Which kate package to install. Use `pkgs.libsForQt5.kate` in Plasma5 and
|
||||
`pkgs.kdePackages.kate` in Plasma6. Use `null` if home-manager should not install kate
|
||||
(use this if you want to manage the settings of this user of a system-wide kate
|
||||
installation).
|
||||
'';
|
||||
};
|
||||
package =
|
||||
lib.mkPackageOption pkgs
|
||||
[
|
||||
"kdePackages"
|
||||
"kate"
|
||||
]
|
||||
{
|
||||
example = "pkgs.libsForQt5.kate";
|
||||
extraDescription = ''
|
||||
Which kate package to install. Use `pkgs.libsForQt5.kate` in Plasma5 and
|
||||
`pkgs.kdePackages.kate` in Plasma6. Use `null` if home-manager should not install kate
|
||||
(use this if you want to manage the settings of this user of a system-wide kate
|
||||
installation).
|
||||
'';
|
||||
};
|
||||
|
||||
# ==================================
|
||||
# INDENTATION
|
||||
@ -306,15 +315,20 @@ in
|
||||
type = lib.types.bool;
|
||||
};
|
||||
|
||||
inputMode = let
|
||||
enumVals = [ "normal" "vi" ];
|
||||
in lib.mkOption {
|
||||
type = lib.types.enum enumVals;
|
||||
description = "The input mode for the editor.";
|
||||
default = "normal";
|
||||
example = "vi";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
inputMode =
|
||||
let
|
||||
enumVals = [
|
||||
"normal"
|
||||
"vi"
|
||||
];
|
||||
in
|
||||
lib.mkOption {
|
||||
type = lib.types.enum enumVals;
|
||||
description = "The input mode for the editor.";
|
||||
default = "normal";
|
||||
example = "vi";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
|
||||
font = lib.mkOption {
|
||||
type = fontType;
|
||||
@ -367,25 +381,30 @@ in
|
||||
config.programs.kate.editor.theme = {
|
||||
# kate's naming scheme is ${themename}.theme
|
||||
# which is why we use the same naming scheme here
|
||||
name = lib.mkIf (cfg.enable && null != cfg.editor.theme.src) (lib.mkForce (builtins.fromJSON (builtins.readFile cfg.editor.theme.src))."metadata"."name");
|
||||
name = lib.mkIf (cfg.enable && null != cfg.editor.theme.src) (
|
||||
lib.mkForce (builtins.fromJSON (builtins.readFile cfg.editor.theme.src))."metadata"."name"
|
||||
);
|
||||
};
|
||||
|
||||
# This won't override existing files since the home-manager activation fails in that case
|
||||
config.xdg.dataFile."${cfg.editor.theme.name}.theme" = lib.mkIf (cfg.enable && null != cfg.editor.theme.src)
|
||||
{
|
||||
source = cfg.editor.theme.src;
|
||||
target = "org.kde.syntax-highlighting/themes/${cfg.editor.theme.name}.theme";
|
||||
};
|
||||
config.xdg.dataFile."${cfg.editor.theme.name}.theme" =
|
||||
lib.mkIf (cfg.enable && null != cfg.editor.theme.src)
|
||||
{
|
||||
source = cfg.editor.theme.src;
|
||||
target = "org.kde.syntax-highlighting/themes/${cfg.editor.theme.name}.theme";
|
||||
};
|
||||
|
||||
config = {
|
||||
home.packages = lib.mkIf (cfg.enable && cfg.package != null) [ cfg.package ];
|
||||
|
||||
# In case of using a custom theme, check that there is no name collision
|
||||
home.activation.checkKateTheme = lib.mkIf (cfg.enable && cfg.editor.theme.src != null) (lib.hm.dag.entryBefore [ "writeBoundary" ]
|
||||
# No `$DRY_RUN_CMD`, since even a dryrun should fail if checks fail
|
||||
''
|
||||
${script}
|
||||
'');
|
||||
home.activation.checkKateTheme = lib.mkIf (cfg.enable && cfg.editor.theme.src != null) (
|
||||
lib.hm.dag.entryBefore [ "writeBoundary" ]
|
||||
# No `$DRY_RUN_CMD`, since even a dryrun should fail if checks fail
|
||||
''
|
||||
${script}
|
||||
''
|
||||
);
|
||||
|
||||
# In case of using a system theme, there should be a check that there exists such a theme
|
||||
# but I could not figure out where to find them
|
||||
@ -393,12 +412,11 @@ in
|
||||
# See also [the original PR](https://github.com/nix-community/plasma-manager/pull/95#issue-2206192839)
|
||||
};
|
||||
|
||||
|
||||
# ==================================
|
||||
# LSP Servers
|
||||
options.programs.kate.lsp.customServers = lib.mkOption {
|
||||
default = { };
|
||||
type = lib.types.attrs;
|
||||
default = null;
|
||||
type = lib.types.nullOr lib.types.attrs;
|
||||
description = ''
|
||||
Add more lsp server settings here. Check out the format on the
|
||||
[KDE page](https://docs.kde.org/stable5/en/kate/kate/kate-application-plugin-lspclient.html).
|
||||
@ -406,7 +424,7 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
config.xdg.configFile."kate/lspclient/settings.json" = {
|
||||
config.xdg.configFile."kate/lspclient/settings.json" = lib.mkIf (cfg.lsp.customServers != null) {
|
||||
text = builtins.toJSON { servers = cfg.lsp.customServers; };
|
||||
};
|
||||
|
||||
@ -476,7 +494,10 @@ in
|
||||
};
|
||||
|
||||
"KTextEditor View" = {
|
||||
"Chars To Enclose Selection" = cfg.editor.brackets.characters;
|
||||
"Chars To Enclose Selection" = {
|
||||
value = cfg.editor.brackets.characters;
|
||||
escapeValue = false;
|
||||
};
|
||||
"Bracket Match Preview" = cfg.editor.brackets.highlightMatching;
|
||||
"Auto Brackets" = cfg.editor.brackets.automaticallyAddClosing;
|
||||
"Input Mode" = cfg.editor.inputMode;
|
||||
|
@ -1,12 +1,26 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (import ../../lib/types.nix { inherit lib; inherit config; }) basicSettingsType;
|
||||
inherit
|
||||
(import ../../lib/types.nix {
|
||||
inherit lib;
|
||||
inherit config;
|
||||
})
|
||||
basicSettingsType
|
||||
;
|
||||
|
||||
# used as shown in the example in the library docs:
|
||||
# https://ryantm.github.io/nixpkgs/functions/library/attrsets/#function-library-lib.attrsets.mapAttrs-prime
|
||||
createColorSchemes = lib.attrsets.mapAttrs' (name: value: lib.attrsets.nameValuePair
|
||||
("konsole/${name}.colorscheme")
|
||||
({ enable = true; source = value; })
|
||||
createColorSchemes = lib.attrsets.mapAttrs' (
|
||||
name: value:
|
||||
lib.attrsets.nameValuePair ("konsole/${name}.colorscheme") ({
|
||||
enable = true;
|
||||
source = value;
|
||||
})
|
||||
);
|
||||
|
||||
cfg = config.programs.konsole;
|
||||
@ -42,11 +56,11 @@ let
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
/*
|
||||
TODO: Set default to null after adding an assertion
|
||||
Konsole needs to have a font set to be able to change font size
|
||||
Since I couldn't get that to work I'll just set a default font
|
||||
Not ideal since IMO we should only write things that are set explicitly
|
||||
by the user but ehh it is what it is
|
||||
TODO: Set default to null after adding an assertion
|
||||
Konsole needs to have a font set to be able to change font size
|
||||
Since I couldn't get that to work I'll just set a default font
|
||||
Not ideal since IMO we should only write things that are set explicitly
|
||||
by the user but ehh it is what it is
|
||||
*/
|
||||
default = "Hack";
|
||||
example = "Hack";
|
||||
@ -131,17 +145,14 @@ in
|
||||
|
||||
config = lib.mkIf (cfg.enable) {
|
||||
programs.plasma.configFile."konsolerc" = lib.mkMerge [
|
||||
(
|
||||
lib.mkIf (cfg.defaultProfile != null) {
|
||||
"Desktop Entry"."DefaultProfile" = "${cfg.defaultProfile}.profile";
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.extraConfig != null) (lib.mapAttrs
|
||||
(groupName: groupAttrs:
|
||||
(lib.mapAttrs (keyName: keyAttrs: { value = keyAttrs; }) groupAttrs))
|
||||
cfg.extraConfig)
|
||||
)
|
||||
(lib.mkIf (cfg.defaultProfile != null) {
|
||||
"Desktop Entry"."DefaultProfile" = "${cfg.defaultProfile}.profile";
|
||||
})
|
||||
(lib.mkIf (cfg.extraConfig != null) (
|
||||
lib.mapAttrs (
|
||||
groupName: groupAttrs: (lib.mapAttrs (keyName: keyAttrs: { value = keyAttrs; }) groupAttrs)
|
||||
) cfg.extraConfig
|
||||
))
|
||||
{
|
||||
"UiSettings"."ColorScheme" = lib.mkIf (cfg.ui.colorScheme != null) {
|
||||
value = cfg.ui.colorScheme;
|
||||
@ -153,50 +164,44 @@ in
|
||||
];
|
||||
|
||||
xdg.dataFile = lib.mkMerge [
|
||||
(lib.mkIf (cfg.profiles != { })
|
||||
(
|
||||
lib.mkMerge ([
|
||||
(
|
||||
lib.mkMerge (
|
||||
lib.mapAttrsToList
|
||||
(
|
||||
attrName: profile:
|
||||
let
|
||||
# Use the name from the name option if it's set
|
||||
profileName = if builtins.isString profile.name then profile.name else attrName;
|
||||
fontString = lib.mkIf (profile.font.name != null) "${profile.font.name},${builtins.toString profile.font.size}";
|
||||
in
|
||||
(lib.mkIf (cfg.profiles != { }) (
|
||||
lib.mkMerge ([
|
||||
(lib.mkMerge (
|
||||
lib.mapAttrsToList (
|
||||
attrName: profile:
|
||||
let
|
||||
# Use the name from the name option if it's set
|
||||
profileName = if builtins.isString profile.name then profile.name else attrName;
|
||||
fontString = lib.mkIf (
|
||||
profile.font.name != null
|
||||
) "${profile.font.name},${builtins.toString profile.font.size}";
|
||||
in
|
||||
{
|
||||
"konsole/${profileName}.profile".text = lib.generators.toINI { } (
|
||||
lib.recursiveUpdate {
|
||||
"General" = (
|
||||
{
|
||||
"konsole/${profileName}.profile".text = lib.generators.toINI { }
|
||||
(lib.recursiveUpdate
|
||||
{
|
||||
"General" = (
|
||||
{
|
||||
"Name" = profileName;
|
||||
# Konsole generated profiles seem to always have this
|
||||
"Parent" = "FALLBACK/";
|
||||
} //
|
||||
(lib.optionalAttrs (profile.command != null) { "Command" = profile.command; })
|
||||
);
|
||||
"Appearance" = (
|
||||
{
|
||||
# If the font size is not set we leave a comma at the end after the name
|
||||
# We should fix this probs but konsole doesn't seem to care ¯\_(ツ)_/¯
|
||||
"Font" = fontString.content;
|
||||
} //
|
||||
(lib.optionalAttrs (profile.colorScheme != null) { "ColorScheme" = profile.colorScheme; })
|
||||
);
|
||||
}
|
||||
profile.extraConfig
|
||||
);
|
||||
"Name" = profileName;
|
||||
# Konsole generated profiles seem to always have this
|
||||
"Parent" = "FALLBACK/";
|
||||
}
|
||||
)
|
||||
cfg.profiles
|
||||
)
|
||||
)
|
||||
])
|
||||
)
|
||||
)
|
||||
// (lib.optionalAttrs (profile.command != null) { "Command" = profile.command; })
|
||||
);
|
||||
"Appearance" = (
|
||||
{
|
||||
# If the font size is not set we leave a comma at the end after the name
|
||||
# We should fix this probs but konsole doesn't seem to care ¯\_(ツ)_/¯
|
||||
"Font" = fontString.content;
|
||||
}
|
||||
// (lib.optionalAttrs (profile.colorScheme != null) { "ColorScheme" = profile.colorScheme; })
|
||||
);
|
||||
} profile.extraConfig
|
||||
);
|
||||
}
|
||||
) cfg.profiles
|
||||
))
|
||||
])
|
||||
))
|
||||
(createColorSchemes cfg.customColorSchemes)
|
||||
];
|
||||
};
|
||||
|
@ -1,7 +1,8 @@
|
||||
{ config
|
||||
, lib
|
||||
, pkgs
|
||||
, ...
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
@ -3,10 +3,12 @@
|
||||
{
|
||||
imports = [
|
||||
./apps
|
||||
./desktop.nix
|
||||
./files.nix
|
||||
./fonts.nix
|
||||
./hotkeys.nix
|
||||
./input.nix
|
||||
./krunner.nix
|
||||
./kscreenlocker.nix
|
||||
./kwin.nix
|
||||
./panels.nix
|
||||
|
538
modules/desktop.nix
Normal file
538
modules/desktop.nix
Normal file
@ -0,0 +1,538 @@
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.programs.plasma;
|
||||
|
||||
widgets = import ./widgets { inherit lib; };
|
||||
|
||||
desktopIconSortingModeId = {
|
||||
manual = -1;
|
||||
name = 0;
|
||||
size = 1;
|
||||
date = 2;
|
||||
type = 6;
|
||||
};
|
||||
|
||||
mouseActions = {
|
||||
applicationLauncher = "org.kde.applauncher";
|
||||
contextMenu = "org.kde.contextmenu";
|
||||
paste = "org.kde.paste";
|
||||
switchActivity = "switchactivity";
|
||||
switchVirtualDesktop = "org.kde.switchdesktop";
|
||||
switchWindow = "switchwindow";
|
||||
};
|
||||
|
||||
mouseActionNamesEnum = lib.types.enum (builtins.attrNames mouseActions);
|
||||
|
||||
# Becomes true if any option under "cfg.desktop.icons" is set to something other than null.
|
||||
anyDesktopFolderSettingsSet =
|
||||
let
|
||||
recurse =
|
||||
l: lib.any (v: if builtins.isAttrs v then recurse v else v != null) (builtins.attrValues l);
|
||||
in
|
||||
recurse cfg.desktop.icons;
|
||||
|
||||
# Becomes true if any option under "cfg.desktop.mouseActions" is set to something other than null.
|
||||
anyDesktopMouseActionsSet = lib.any (v: v != null) (builtins.attrValues cfg.desktop.mouseActions);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"arrangement"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"arrangement"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"alignment"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"alignment"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"lockInPlace"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"lockInPlace"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"sorting"
|
||||
"mode"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"sorting"
|
||||
"mode"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"sorting"
|
||||
"descending"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"sorting"
|
||||
"descending"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"sorting"
|
||||
"foldersFirst"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"sorting"
|
||||
"foldersFirst"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"size"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"size"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"folderPreviewPopups"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"folderPreviewPopups"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"icons"
|
||||
"previewPlugins"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"icons"
|
||||
"previewPlugins"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"mouseActions"
|
||||
"leftClick"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"mouseActions"
|
||||
"leftClick"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"mouseActions"
|
||||
"middleClick"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"mouseActions"
|
||||
"middleClick"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"mouseActions"
|
||||
"rightClick"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"mouseActions"
|
||||
"rightClick"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"desktop"
|
||||
"mouseActions"
|
||||
"verticalScroll"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"desktop"
|
||||
"mouseActions"
|
||||
"verticalScroll"
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
options.programs.plasma.desktop = {
|
||||
icons = {
|
||||
arrangement = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"leftToRight"
|
||||
"topToBottom"
|
||||
]);
|
||||
default = null;
|
||||
example = "topToBottom";
|
||||
description = ''
|
||||
The direction, in which desktop icons are to be arranged.
|
||||
'';
|
||||
};
|
||||
|
||||
alignment = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"left"
|
||||
"right"
|
||||
]);
|
||||
default = null;
|
||||
example = "right";
|
||||
description = ''
|
||||
Whether to align the icons on the left (the default) or right
|
||||
side of the screen.
|
||||
'';
|
||||
};
|
||||
|
||||
lockInPlace = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = ''
|
||||
Locks the position of all desktop icons to the order and placement
|
||||
defined by `arrangement`, `alignment` and the `sorting` options
|
||||
so they can’t be manually moved.
|
||||
'';
|
||||
};
|
||||
|
||||
sorting = {
|
||||
mode = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum (builtins.attrNames desktopIconSortingModeId));
|
||||
default = null;
|
||||
example = "type";
|
||||
description = ''
|
||||
Specifies the sort mode for the desktop icons. By default they are
|
||||
sorted by name.
|
||||
'';
|
||||
apply = sortMode: if (sortMode == null) then null else desktopIconSortingModeId.${sortMode};
|
||||
};
|
||||
|
||||
descending = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = ''
|
||||
Reverses the sorting order if enabled. Sorting is ascending by default.
|
||||
'';
|
||||
};
|
||||
|
||||
foldersFirst = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = false;
|
||||
description = ''
|
||||
Folders are sorted separately from files by default. This means
|
||||
folders appear first, sorted for example ascending by name,
|
||||
followed by files, also sorted ascending by name.
|
||||
If this option is disabled, all items are sorted irrespective
|
||||
of their type.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
size = lib.mkOption {
|
||||
type = with lib.types; nullOr (ints.between 0 6);
|
||||
default = null;
|
||||
example = 2;
|
||||
description = ''
|
||||
The desktop icon size, which is normally configured via a slider
|
||||
with seven possible values ranging from small (0) to large (6).
|
||||
The fourth position (3) is the default.
|
||||
'';
|
||||
};
|
||||
|
||||
folderPreviewPopups = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = false;
|
||||
description = ''
|
||||
Enables the arrow button when hovering over a folder on the desktop
|
||||
which shows a preview popup of the folder’s contents.
|
||||
Is enabled by default.
|
||||
'';
|
||||
};
|
||||
|
||||
previewPlugins = lib.mkOption {
|
||||
type = with lib.types; nullOr (listOf str);
|
||||
default = null;
|
||||
example = [
|
||||
"audiothumbnail"
|
||||
"fontthumbnail"
|
||||
];
|
||||
description = ''
|
||||
Configures the preview plugins used to preview desktop files and folders.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
mouseActions = {
|
||||
leftClick = lib.mkOption {
|
||||
type = lib.types.nullOr mouseActionNamesEnum;
|
||||
default = null;
|
||||
example = "appLauncher";
|
||||
description = "Action for a left click on the desktop.";
|
||||
apply = value: if (value == null) then null else mouseActions.${value};
|
||||
};
|
||||
|
||||
middleClick = lib.mkOption {
|
||||
type = lib.types.nullOr mouseActionNamesEnum;
|
||||
default = null;
|
||||
example = "switchWindow";
|
||||
description = "Action for a click on the desktop with the middle mouse button.";
|
||||
apply = value: if (value == null) then null else mouseActions.${value};
|
||||
};
|
||||
|
||||
rightClick = lib.mkOption {
|
||||
type = lib.types.nullOr mouseActionNamesEnum;
|
||||
default = null;
|
||||
example = "contextMenu";
|
||||
description = "Action for a right click on the desktop.";
|
||||
apply = value: if (value == null) then null else mouseActions.${value};
|
||||
};
|
||||
|
||||
verticalScroll = lib.mkOption {
|
||||
type = lib.types.nullOr mouseActionNamesEnum;
|
||||
default = null;
|
||||
example = "switchVirtualDesktop";
|
||||
description = "Action for scrolling (vertically) while hovering over the desktop.";
|
||||
apply = value: if (value == null) then null else mouseActions.${value};
|
||||
};
|
||||
};
|
||||
|
||||
widgets = lib.mkOption {
|
||||
type = with lib.types; nullOr (listOf widgets.desktopType);
|
||||
default = null;
|
||||
example = [
|
||||
{
|
||||
name = "org.kde.plasma.digitalclock";
|
||||
position = {
|
||||
horizontal = 51;
|
||||
vertical = 100;
|
||||
};
|
||||
size = {
|
||||
width = 250;
|
||||
height = 250;
|
||||
};
|
||||
config.Appearance.showDate = false;
|
||||
}
|
||||
{
|
||||
plasmusicToolbar = {
|
||||
position = {
|
||||
horizontal = 51;
|
||||
vertical = 300;
|
||||
};
|
||||
size = {
|
||||
width = 250;
|
||||
height = 400;
|
||||
};
|
||||
background = "transparentShadow";
|
||||
};
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
A list of widgets to be added to the desktop.
|
||||
'';
|
||||
apply = option: if option == null then null else (map widgets.desktopConvert option);
|
||||
};
|
||||
};
|
||||
|
||||
config = (
|
||||
lib.mkIf cfg.enable {
|
||||
programs.plasma.startup = {
|
||||
desktopScript."set_desktop_folder_settings" = (
|
||||
lib.mkIf anyDesktopFolderSettingsSet {
|
||||
text = ''
|
||||
// Desktop folder settings
|
||||
let allDesktops = desktops();
|
||||
for (const desktop of allDesktops) {
|
||||
desktop.currentConfigGroup = ["General"];
|
||||
${
|
||||
lib.optionalString (
|
||||
cfg.desktop.icons.arrangement == "topToBottom"
|
||||
) ''desktop.writeConfig("arrangement", 1);''
|
||||
}
|
||||
${
|
||||
lib.optionalString (cfg.desktop.icons.alignment == "right") ''desktop.writeConfig("alignment", 1);''
|
||||
}
|
||||
${
|
||||
lib.optionalString (cfg.desktop.icons.lockInPlace == true) ''desktop.writeConfig("locked", true);''
|
||||
}
|
||||
${widgets.lib.stringIfNotNull cfg.desktop.icons.size ''desktop.writeConfig("iconSize", ${builtins.toString cfg.desktop.icons.size});''}
|
||||
${
|
||||
lib.optionalString (
|
||||
cfg.desktop.icons.folderPreviewPopups == false
|
||||
) ''desktop.writeConfig("popups", false);''
|
||||
}
|
||||
${widgets.lib.stringIfNotNull cfg.desktop.icons.previewPlugins ''desktop.writeConfig("previewPlugins", "${lib.strings.concatStringsSep "," cfg.desktop.icons.previewPlugins}");''}
|
||||
${widgets.lib.stringIfNotNull cfg.desktop.icons.sorting.mode ''desktop.writeConfig("sortMode", ${builtins.toString cfg.desktop.icons.sorting.mode});''}
|
||||
${
|
||||
lib.optionalString (
|
||||
cfg.desktop.icons.sorting.descending == true
|
||||
) ''desktop.writeConfig("sortDesc", true);''
|
||||
}
|
||||
${
|
||||
lib.optionalString (
|
||||
cfg.desktop.icons.sorting.foldersFirst == false
|
||||
) ''desktop.writeConfig("sortDirsFirst", false);''
|
||||
}
|
||||
}
|
||||
'';
|
||||
priority = 3;
|
||||
}
|
||||
);
|
||||
|
||||
desktopScript."set_desktop_mouse_actions" = (
|
||||
lib.mkIf anyDesktopMouseActionsSet {
|
||||
text = ''
|
||||
// Mouse actions
|
||||
let configFile = ConfigFile('plasma-org.kde.plasma.desktop-appletsrc');
|
||||
configFile.group = 'ActionPlugins';
|
||||
// References the section [ActionPlugins][0].
|
||||
let actionPluginSubSection = ConfigFile(configFile, 0)
|
||||
${widgets.lib.stringIfNotNull cfg.desktop.mouseActions.leftClick ''actionPluginSubSection.writeEntry("LeftButton;NoModifier", "${cfg.desktop.mouseActions.leftClick}");''}
|
||||
${widgets.lib.stringIfNotNull cfg.desktop.mouseActions.middleClick ''actionPluginSubSection.writeEntry("MiddleButton;NoModifier", "${cfg.desktop.mouseActions.middleClick}");''}
|
||||
${widgets.lib.stringIfNotNull cfg.desktop.mouseActions.rightClick ''actionPluginSubSection.writeEntry("RightButton;NoModifier", "${cfg.desktop.mouseActions.rightClick}");''}
|
||||
${widgets.lib.stringIfNotNull cfg.desktop.mouseActions.verticalScroll ''actionPluginSubSection.writeEntry("wheel:Vertical;NoModifier", "${cfg.desktop.mouseActions.verticalScroll}");''}
|
||||
'';
|
||||
priority = 3;
|
||||
restartServices = [ "plasma-plasmashell" ];
|
||||
}
|
||||
);
|
||||
|
||||
desktopScript."set_desktop_widgets" = (
|
||||
lib.mkIf (cfg.desktop.widgets != null) {
|
||||
text = ''
|
||||
// Desktop widgets
|
||||
let allDesktops = desktops();
|
||||
|
||||
// Remove all desktop widgets
|
||||
allDesktops.forEach((desktop) => {
|
||||
desktop.widgets().forEach((widget) => {
|
||||
widget.remove();
|
||||
});
|
||||
});
|
||||
|
||||
for (let i = 0; i < allDesktops.length; i++) {
|
||||
const desktop = allDesktops[i];
|
||||
${widgets.lib.addDesktopWidgetStmts "desktop" "desktopWidgets" cfg.desktop.widgets}
|
||||
}
|
||||
'';
|
||||
priority = 2;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
@ -1,65 +1,93 @@
|
||||
# Low-level access to changing Plasma settings.
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (import ../lib/writeconfig.nix { inherit lib pkgs config; }) writeConfig;
|
||||
inherit (import ../lib/types.nix { inherit lib; inherit config; }) coercedSettingsType;
|
||||
inherit
|
||||
(import ../lib/types.nix {
|
||||
inherit lib;
|
||||
inherit config;
|
||||
})
|
||||
coercedSettingsType
|
||||
;
|
||||
|
||||
# Helper function to prepend the appropriate path prefix (e.g. XDG_CONFIG_HOME) to file
|
||||
prependPath = prefix: attrset:
|
||||
lib.mapAttrs'
|
||||
(path: config: { name = "${prefix}/${path}"; value = config; })
|
||||
attrset;
|
||||
prependPath =
|
||||
prefix: attrset:
|
||||
lib.mapAttrs' (path: config: {
|
||||
name = "${prefix}/${path}";
|
||||
value = config;
|
||||
}) attrset;
|
||||
plasmaCfg = config.programs.plasma;
|
||||
cfg =
|
||||
(prependPath config.home.homeDirectory plasmaCfg.file) //
|
||||
(prependPath config.xdg.configHome plasmaCfg.configFile) //
|
||||
(prependPath config.xdg.dataHome plasmaCfg.dataFile);
|
||||
(prependPath config.home.homeDirectory plasmaCfg.file)
|
||||
// (prependPath config.xdg.configHome plasmaCfg.configFile)
|
||||
// (prependPath config.xdg.dataHome plasmaCfg.dataFile);
|
||||
|
||||
fileSettingsType = with lib.types; attrsOf (attrsOf (attrsOf coercedSettingsType));
|
||||
|
||||
##############################################################################
|
||||
# Generate a script that will use write_config.py to update all
|
||||
# settings.
|
||||
resetFilesList = (map (f: "${config.xdg.configHome}/${f}") (lib.lists.subtractLists plasmaCfg.resetFilesExclude plasmaCfg.resetFiles));
|
||||
resetFilesList = (
|
||||
map (f: "${config.xdg.configHome}/${f}") (
|
||||
lib.lists.subtractLists plasmaCfg.resetFilesExclude plasmaCfg.resetFiles
|
||||
)
|
||||
);
|
||||
script = pkgs.writeScript "plasma-config" (writeConfig cfg plasmaCfg.overrideConfig resetFilesList);
|
||||
|
||||
##############################################################################
|
||||
# Generate a script that will remove all the current config files.
|
||||
defaultResetFiles = (if plasmaCfg.overrideConfig then [
|
||||
"baloofilerc"
|
||||
"dolphinrc"
|
||||
"ffmpegthumbsrc"
|
||||
"kactivitymanagerdrc"
|
||||
"katerc"
|
||||
"kcminputrc"
|
||||
"kded5rc"
|
||||
"kded6rc"
|
||||
"kdeglobals"
|
||||
"kgammarc"
|
||||
"kglobalshortcutsrc"
|
||||
"khotkeysrc"
|
||||
"kiorc"
|
||||
"klaunchrc"
|
||||
"klipperrc"
|
||||
"kmixrc"
|
||||
"krunnerrc"
|
||||
"kscreenlockerrc"
|
||||
"kservicemenurc"
|
||||
"ksmserverrc"
|
||||
"ksplashrc"
|
||||
"kwalletrc"
|
||||
"kwin_rules_dialogrc"
|
||||
"kwinrc"
|
||||
"kwinrulesrc"
|
||||
"kxkbrc"
|
||||
"plasma-localerc"
|
||||
"plasmanotifyrc"
|
||||
"plasmarc"
|
||||
"plasmashellrc"
|
||||
"powerdevilrc"
|
||||
"systemsettingsrc"
|
||||
] else lib.optional (builtins.length plasmaCfg.window-rules > 0) "kwinrulesrc");
|
||||
defaultResetFiles = (
|
||||
if plasmaCfg.overrideConfig then
|
||||
[
|
||||
"auroraerc"
|
||||
"baloofilerc"
|
||||
"dolphinrc"
|
||||
"ffmpegthumbsrc"
|
||||
"kactivitymanagerdrc"
|
||||
"katerc"
|
||||
"kcminputrc"
|
||||
"KDE/Sonnet.conf"
|
||||
"kde.org/ghostwriter.conf"
|
||||
"kded5rc"
|
||||
"kded6rc"
|
||||
"kdeglobals"
|
||||
"kgammarc"
|
||||
"kglobalshortcutsrc"
|
||||
"khotkeysrc"
|
||||
"kiorc"
|
||||
"klaunchrc"
|
||||
"klipperrc"
|
||||
"kmixrc"
|
||||
"krunnerrc"
|
||||
"kscreenlockerrc"
|
||||
"kservicemenurc"
|
||||
"ksmserverrc"
|
||||
"ksplashrc"
|
||||
"kwalletrc"
|
||||
"kwin_rules_dialogrc"
|
||||
"kwinrc"
|
||||
"kwinrulesrc"
|
||||
"kxkbrc"
|
||||
"plasma_calendar_alternatecalendar"
|
||||
"plasma_calendar_astronomicalevents"
|
||||
"plasma_calendar_holiday_regions"
|
||||
"plasma-localerc"
|
||||
"plasmanotifyrc"
|
||||
"plasmarc"
|
||||
"plasmashellrc"
|
||||
"powerdevilrc"
|
||||
"systemsettingsrc"
|
||||
]
|
||||
else
|
||||
lib.optional (builtins.length plasmaCfg.window-rules > 0) "kwinrulesrc"
|
||||
);
|
||||
in
|
||||
{
|
||||
options.programs.plasma = {
|
||||
@ -119,16 +147,49 @@ in
|
||||
};
|
||||
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule [ "programs" "plasma" "files" ] [ "programs" "plasma" "configFile" ])
|
||||
(lib.mkRenamedOptionModule [ "programs" "plasma" "overrideConfigFiles" ] [ "programs" "plasma" "resetFiles" ])
|
||||
(lib.mkRenamedOptionModule [ "programs" "plasma" "overrideConfigExclude" ] [ "programs" "plasma" "resetFilesExclude" ])
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"files"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"configFile"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"overrideConfigFiles"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"resetFiles"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"overrideConfigExclude"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"resetFilesExclude"
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
config.home.activation = lib.mkIf plasmaCfg.enable {
|
||||
configure-plasma = (lib.hm.dag.entryAfter [ "writeBoundary" ]
|
||||
''
|
||||
configure-plasma = (
|
||||
lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
$DRY_RUN_CMD ${script}
|
||||
'');
|
||||
''
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
{ lib
|
||||
, config
|
||||
, ...
|
||||
}:
|
||||
{ lib, config, ... }:
|
||||
let
|
||||
inherit (lib) mkIf mkOption types;
|
||||
qfont = import ../lib/qfont.nix { inherit lib; };
|
||||
|
@ -1,5 +1,10 @@
|
||||
# Global hotkeys (user-defined keyboard shortcuts):
|
||||
{ pkgs, config, lib, ... }:
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.programs.plasma;
|
||||
|
||||
@ -11,60 +16,62 @@ let
|
||||
description = "Plasma Manager";
|
||||
};
|
||||
|
||||
commandType = { name, ... }: {
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = name;
|
||||
description = "Command hotkey name.";
|
||||
};
|
||||
commandType =
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = name;
|
||||
description = "Command hotkey name.";
|
||||
};
|
||||
|
||||
comment = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = name;
|
||||
description = "Optional comment to display in the KDE settings UI.";
|
||||
};
|
||||
comment = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = name;
|
||||
description = "Optional comment to display in the KDE settings UI.";
|
||||
};
|
||||
|
||||
key = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The key combination that triggers the action.";
|
||||
default = "";
|
||||
};
|
||||
key = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The key combination that triggers the action.";
|
||||
default = "";
|
||||
};
|
||||
|
||||
keys = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
description = "The key combinations that trigger the action.";
|
||||
default = [ ];
|
||||
};
|
||||
keys = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
description = "The key combinations that trigger the action.";
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
command = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The command to execute.";
|
||||
};
|
||||
command = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The command to execute.";
|
||||
};
|
||||
|
||||
logs.enabled = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Connect command's stdin and stdout to systemd journal with systemd-cat.";
|
||||
};
|
||||
logs.enabled = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Connect command's stdin and stdout to systemd journal with systemd-cat.";
|
||||
};
|
||||
|
||||
logs.identifier = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = lib.trivial.pipe name [
|
||||
lib.strings.toLower
|
||||
(builtins.replaceStrings [ " " ] [ "-" ])
|
||||
(n: "${group.name}-${n}")
|
||||
];
|
||||
description = "Identifier passed down to systemd-cat.";
|
||||
};
|
||||
logs.identifier = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = lib.trivial.pipe name [
|
||||
lib.strings.toLower
|
||||
(builtins.replaceStrings [ " " ] [ "-" ])
|
||||
(n: "${group.name}-${n}")
|
||||
];
|
||||
description = "Identifier passed down to systemd-cat.";
|
||||
};
|
||||
|
||||
logs.extraArgs = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Additional arguments provided to systemd-cat.";
|
||||
logs.extraArgs = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Additional arguments provided to systemd-cat.";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options.programs.plasma.hotkeys = {
|
||||
@ -75,36 +82,35 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf
|
||||
(cfg.enable && builtins.length (builtins.attrNames cfg.hotkeys.commands) != 0)
|
||||
{
|
||||
xdg.desktopEntries."${group.name}" = {
|
||||
name = group.description;
|
||||
noDisplay = true;
|
||||
type = "Application";
|
||||
actions = lib.mapAttrs
|
||||
(_: command: {
|
||||
name = command.name;
|
||||
exec =
|
||||
if command.logs.enabled then
|
||||
"${pkgs.systemd}/bin/systemd-cat --identifier=${command.logs.identifier} ${command.logs.extraArgs} ${commandString command.command}"
|
||||
else (commandString command.command);
|
||||
})
|
||||
cfg.hotkeys.commands;
|
||||
};
|
||||
|
||||
programs.plasma.configFile."kglobalshortcutsrc"."${group.desktop}" = {
|
||||
_k_friendly_name = group.description;
|
||||
} // lib.mapAttrs
|
||||
(_: command:
|
||||
let
|
||||
keys = command.keys ++ lib.optionals (command.key != "") [ command.key ];
|
||||
in
|
||||
lib.concatStringsSep "," [
|
||||
(lib.concatStringsSep "\t" (map (lib.escape [ "," ]) keys))
|
||||
"" # List of default keys, not needed.
|
||||
command.comment
|
||||
])
|
||||
cfg.hotkeys.commands;
|
||||
config = lib.mkIf (cfg.enable && builtins.length (builtins.attrNames cfg.hotkeys.commands) != 0) {
|
||||
xdg.desktopEntries."${group.name}" = {
|
||||
name = group.description;
|
||||
noDisplay = true;
|
||||
type = "Application";
|
||||
actions = lib.mapAttrs (_: command: {
|
||||
name = command.name;
|
||||
exec =
|
||||
if command.logs.enabled then
|
||||
"${pkgs.systemd}/bin/systemd-cat --identifier=${command.logs.identifier} ${command.logs.extraArgs} ${commandString command.command}"
|
||||
else
|
||||
(commandString command.command);
|
||||
}) cfg.hotkeys.commands;
|
||||
};
|
||||
|
||||
programs.plasma.configFile."kglobalshortcutsrc"."${group.desktop}" =
|
||||
{
|
||||
_k_friendly_name = group.description;
|
||||
}
|
||||
// lib.mapAttrs (
|
||||
_: command:
|
||||
let
|
||||
keys = command.keys ++ lib.optionals (command.key != "") [ command.key ];
|
||||
in
|
||||
lib.concatStringsSep "," [
|
||||
(lib.concatStringsSep "\t" (map (lib.escape [ "," ]) keys))
|
||||
"" # List of default keys, not needed.
|
||||
command.comment
|
||||
]
|
||||
) cfg.hotkeys.commands;
|
||||
};
|
||||
}
|
||||
|
@ -4,7 +4,17 @@ with lib;
|
||||
|
||||
let
|
||||
cfg = config.programs.plasma;
|
||||
numlockSettings = [ "on" "off" "unchanged" ];
|
||||
numlockSettings = [
|
||||
"on"
|
||||
"off"
|
||||
"unchanged"
|
||||
];
|
||||
switchModes = [
|
||||
"global"
|
||||
"desktop"
|
||||
"winClass"
|
||||
"window"
|
||||
];
|
||||
|
||||
scrollMethods = {
|
||||
twoFingers = 1;
|
||||
@ -15,6 +25,44 @@ let
|
||||
twoFingers = 2;
|
||||
};
|
||||
|
||||
capitalizeWord =
|
||||
word:
|
||||
let
|
||||
firstLetter = builtins.substring 0 1 word;
|
||||
rest = builtins.substring 1 (builtins.stringLength word - 1) word;
|
||||
in
|
||||
"${toUpper firstLetter}${rest}";
|
||||
|
||||
layoutType = types.submodule {
|
||||
options = {
|
||||
layout = mkOption {
|
||||
type = types.str;
|
||||
example = "us";
|
||||
description = ''
|
||||
Keyboard layout.
|
||||
'';
|
||||
};
|
||||
variant = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
example = "eng";
|
||||
description = ''
|
||||
Keyboard layout variant.
|
||||
'';
|
||||
apply = builtins.toString;
|
||||
};
|
||||
displayName = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
example = "us";
|
||||
description = ''
|
||||
Keyboard layout display name.
|
||||
'';
|
||||
apply = builtins.toString;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
touchPadType = types.submodule {
|
||||
options = {
|
||||
enable = mkOption {
|
||||
@ -142,7 +190,12 @@ let
|
||||
apply = method: if (method == null) then null else rightClickMethods."${method}";
|
||||
};
|
||||
twoFingerTap = mkOption {
|
||||
type = with types; nullOr (enum [ "rightClick" "middleClick" ]);
|
||||
type =
|
||||
with types;
|
||||
nullOr (enum [
|
||||
"rightClick"
|
||||
"middleClick"
|
||||
]);
|
||||
default = null;
|
||||
example = "twoFingers";
|
||||
description = ''
|
||||
@ -152,14 +205,15 @@ let
|
||||
};
|
||||
};
|
||||
};
|
||||
touchPadToConfig = touchpad:
|
||||
touchPadToConfig =
|
||||
touchpad:
|
||||
let
|
||||
touchName = touchpad.name;
|
||||
touchVendor = touchpad.vendorId;
|
||||
touchProduct = touchpad.productId;
|
||||
touchVendor = builtins.toString (lib.fromHexString touchpad.vendorId);
|
||||
touchProduct = builtins.toString (lib.fromHexString touchpad.productId);
|
||||
in
|
||||
{
|
||||
"Libinput/${touchVendor}/${touchProduct}/${lib.escape ["/"] touchName}" = {
|
||||
"Libinput/${touchVendor}/${touchProduct}/${lib.escape [ "/" ] touchName}" = {
|
||||
Enabled = touchpad.enable;
|
||||
DisableWhileTyping = touchpad.disableWhileTyping;
|
||||
LeftHanded = touchpad.leftHanded;
|
||||
@ -244,11 +298,23 @@ let
|
||||
'';
|
||||
};
|
||||
accelerationProfile = mkOption {
|
||||
type = with types; nullOr (enum [ "none" "default" ]);
|
||||
type =
|
||||
with types;
|
||||
nullOr (enum [
|
||||
"none"
|
||||
"default"
|
||||
]);
|
||||
default = null;
|
||||
example = "none";
|
||||
description = "Mouse acceleration profile.";
|
||||
apply = profile: if profile == "none" then 1 else if profile == "default" then 2 else null;
|
||||
apply =
|
||||
profile:
|
||||
if profile == "none" then
|
||||
1
|
||||
else if profile == "default" then
|
||||
2
|
||||
else
|
||||
null;
|
||||
};
|
||||
naturalScroll = mkOption {
|
||||
type = with types; nullOr bool;
|
||||
@ -269,11 +335,12 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
mouseToConfig = mouse:
|
||||
mouseToConfig =
|
||||
mouse:
|
||||
let
|
||||
mouseName = mouse.name;
|
||||
mouseVendor = mouse.vendorId;
|
||||
mouseProduct = mouse.productId;
|
||||
mouseVendor = builtins.toString (lib.fromHexString mouse.vendorId);
|
||||
mouseProduct = builtins.toString (lib.fromHexString mouse.productId);
|
||||
in
|
||||
{
|
||||
"Libinput/${mouseVendor}/${mouseProduct}/${mouseName}" = {
|
||||
@ -288,12 +355,80 @@ let
|
||||
};
|
||||
in
|
||||
{
|
||||
config.assertions = [
|
||||
(
|
||||
let
|
||||
validChars = [
|
||||
"0"
|
||||
"1"
|
||||
"2"
|
||||
"3"
|
||||
"4"
|
||||
"5"
|
||||
"6"
|
||||
"7"
|
||||
"8"
|
||||
"9"
|
||||
"a"
|
||||
"b"
|
||||
"c"
|
||||
"d"
|
||||
"e"
|
||||
"f"
|
||||
];
|
||||
hexChars = hexStr: builtins.tail (lib.reverseList (builtins.tail (lib.splitString "" hexStr)));
|
||||
hexCodeInvalid =
|
||||
hex:
|
||||
!(lib.all (c: builtins.elem (lib.toLower c) validChars) (hexChars hex))
|
||||
&& (builtins.stringLength hex) > 0;
|
||||
allHexCodes = lib.flatten (
|
||||
(map (t: [
|
||||
t.vendorId
|
||||
t.productId
|
||||
]) (cfg.input.touchpads ++ cfg.input.mice))
|
||||
);
|
||||
invalidHexCodes = builtins.filter hexCodeInvalid allHexCodes;
|
||||
in
|
||||
{
|
||||
assertion = (builtins.length invalidHexCodes) == 0;
|
||||
message = "Invalid hex-code for product or vendor-ID in the input module in plasma-manager: ${builtins.head invalidHexCodes}";
|
||||
}
|
||||
)
|
||||
];
|
||||
# Keyboard options
|
||||
options.programs.plasma.input.keyboard = {
|
||||
layouts = mkOption {
|
||||
type = with types; nullOr (listOf str);
|
||||
model = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
example = [ "es" "us" ];
|
||||
example = "pc104";
|
||||
description = ''
|
||||
Keyboard model.
|
||||
'';
|
||||
};
|
||||
switchingPolicy = mkOption {
|
||||
type = with types; nullOr (enum switchModes);
|
||||
default = null;
|
||||
example = "global";
|
||||
description = ''
|
||||
Switching policy for keyboard layouts.
|
||||
'';
|
||||
apply = policy: if policy == null then null else capitalizeWord policy;
|
||||
};
|
||||
layouts = mkOption {
|
||||
type = with types; nullOr (listOf layoutType);
|
||||
default = null;
|
||||
example = [
|
||||
{ layout = "us"; }
|
||||
{
|
||||
layout = "ca";
|
||||
variant = "eng";
|
||||
}
|
||||
{
|
||||
layout = "us";
|
||||
variant = "intl";
|
||||
displayName = "usi";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
Keyboard layouts to use.
|
||||
'';
|
||||
@ -316,27 +451,53 @@ in
|
||||
'';
|
||||
};
|
||||
repeatRate = mkOption {
|
||||
type = with types; nullOr (numbers.between 0.20 100.0);
|
||||
type = with types; nullOr (numbers.between 0.2 100.0);
|
||||
default = null;
|
||||
example = 50.0;
|
||||
description = ''
|
||||
How quick the inputs should be repeated when holding down a key.
|
||||
'';
|
||||
};
|
||||
options = mkOption {
|
||||
type = with types; nullOr (listOf str);
|
||||
default = null;
|
||||
example = [
|
||||
"altwin:meta_alt"
|
||||
"caps:shift"
|
||||
"custom:types"
|
||||
];
|
||||
description = ''
|
||||
Keyboard options.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config.programs.plasma.configFile."kxkbrc" = mkIf (cfg.enable) (
|
||||
mkMerge [
|
||||
(
|
||||
mkIf (cfg.input.keyboard.layouts != null) {
|
||||
Layout = {
|
||||
Use.value = true;
|
||||
LayoutList.value = strings.concatStringsSep "," cfg.input.keyboard.layouts;
|
||||
};
|
||||
}
|
||||
)
|
||||
]
|
||||
);
|
||||
config.programs.plasma.configFile."kxkbrc" = mkIf (cfg.enable) (mkMerge [
|
||||
(mkIf (cfg.input.keyboard.layouts != null) {
|
||||
Layout = {
|
||||
Use.value = true;
|
||||
LayoutList.value = strings.concatStringsSep "," (map (l: l.layout) cfg.input.keyboard.layouts);
|
||||
VariantList.value = strings.concatStringsSep "," (map (l: l.variant) cfg.input.keyboard.layouts);
|
||||
DisplayNames.value = strings.concatStringsSep "," (map (l: l.displayName) cfg.input.keyboard.layouts);
|
||||
};
|
||||
})
|
||||
(mkIf (cfg.input.keyboard.options != null) {
|
||||
Layout = {
|
||||
ResetOldOptions.value = true;
|
||||
Options.value = strings.concatStringsSep "," cfg.input.keyboard.options;
|
||||
};
|
||||
})
|
||||
(mkIf (cfg.input.keyboard.model != null) {
|
||||
Layout = {
|
||||
Model.value = cfg.input.keyboard.model;
|
||||
};
|
||||
})
|
||||
(mkIf (cfg.input.keyboard.switchingPolicy != null) {
|
||||
Layout = {
|
||||
SwitchMode.value = cfg.input.keyboard.switchingPolicy;
|
||||
};
|
||||
})
|
||||
]);
|
||||
|
||||
# Touchpads options
|
||||
options.programs.plasma.input.touchpads = mkOption {
|
||||
@ -385,14 +546,15 @@ in
|
||||
|
||||
config.programs.plasma.configFile."kcminputrc" = mkIf (cfg.enable) (mkMerge [
|
||||
{
|
||||
Keyboard = (lib.filterAttrs (k: v: v != null) {
|
||||
NumLock = (lists.findFirstIndex (x: x == cfg.input.keyboard.numlockOnStartup) null numlockSettings);
|
||||
RepeatDelay = cfg.input.keyboard.repeatDelay;
|
||||
RepeatRate = cfg.input.keyboard.repeatRate;
|
||||
});
|
||||
Keyboard = (
|
||||
lib.filterAttrs (k: v: v != null) {
|
||||
NumLock = (lists.findFirstIndex (x: x == cfg.input.keyboard.numlockOnStartup) null numlockSettings);
|
||||
RepeatDelay = cfg.input.keyboard.repeatDelay;
|
||||
RepeatRate = cfg.input.keyboard.repeatRate;
|
||||
}
|
||||
);
|
||||
}
|
||||
(mkMerge (map touchPadToConfig cfg.input.touchpads))
|
||||
(mkMerge (map mouseToConfig cfg.input.mice))
|
||||
]
|
||||
);
|
||||
]);
|
||||
}
|
||||
|
58
modules/krunner.nix
Normal file
58
modules/krunner.nix
Normal file
@ -0,0 +1,58 @@
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.programs.plasma;
|
||||
in
|
||||
{
|
||||
options.programs.plasma.krunner = {
|
||||
position = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"top"
|
||||
"center"
|
||||
]);
|
||||
default = null;
|
||||
example = "center";
|
||||
description = "Position of KRunner on screen.";
|
||||
};
|
||||
activateWhenTypingOnDesktop = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Activate KRunner when typing on the desktop.";
|
||||
};
|
||||
historyBehavior = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"disabled"
|
||||
"enableSuggestions"
|
||||
"enableAutoComplete"
|
||||
]);
|
||||
default = null;
|
||||
example = "disabled";
|
||||
description = "Behavior of KRunner’s history.";
|
||||
};
|
||||
};
|
||||
|
||||
config.programs.plasma.configFile."krunnerrc" = (
|
||||
lib.mkMerge [
|
||||
(lib.mkIf (cfg.krunner.position != null) {
|
||||
General.FreeFloating = cfg.krunner.position == "center";
|
||||
})
|
||||
(lib.mkIf (cfg.krunner.activateWhenTypingOnDesktop != null) {
|
||||
General.ActivateWhenTypingOnDesktop = cfg.krunner.activateWhenTypingOnDesktop;
|
||||
})
|
||||
(lib.mkIf (cfg.krunner.historyBehavior != null) {
|
||||
General.historyBehavior = (
|
||||
if cfg.krunner.historyBehavior == "enableSuggestions" then
|
||||
"CompletionSuggestion"
|
||||
else if cfg.krunner.historyBehavior == "enableAutoComplete" then
|
||||
"ImmediateCompletion"
|
||||
else
|
||||
"Disabled"
|
||||
);
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
@ -1,83 +1,271 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.programs.plasma;
|
||||
inherit (import ../lib/wallpapers.nix { inherit lib; }) wallpaperPictureOfTheDayType wallpaperSlideShowType;
|
||||
inherit (import ../lib/wallpapers.nix { inherit lib; })
|
||||
wallpaperPictureOfTheDayType
|
||||
wallpaperSlideShowType
|
||||
;
|
||||
in
|
||||
{
|
||||
options.programs.plasma.kscreenlocker = {
|
||||
wallpaper = lib.mkOption {
|
||||
type = with lib.types; nullOr path;
|
||||
autoLock = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = lib.literalExpression ''"''${pkgs.kdePackages.plasma-workspace-wallpapers}/share/wallpapers/Kay/contents/images/1080x1920.png"'';
|
||||
example = true;
|
||||
description = ''
|
||||
The wallpaper for the lockscreen. Can be either be the path to an image file or a kpackage.
|
||||
Sets whether the screen will be locked after the specified time.
|
||||
'';
|
||||
};
|
||||
wallpaperPictureOfTheDay = lib.mkOption {
|
||||
type = lib.types.nullOr wallpaperPictureOfTheDayType;
|
||||
lockOnResume = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = { provider = "apod"; };
|
||||
example = false;
|
||||
description = ''
|
||||
Allows you to set wallpaper using the picture of the day plugin. Needs the provider.
|
||||
Whether to lock the screen when the system resumes from sleep.
|
||||
'';
|
||||
};
|
||||
wallpaperSlideShow = lib.mkOption {
|
||||
type = lib.types.nullOr wallpaperSlideShowType;
|
||||
|
||||
timeout = lib.mkOption {
|
||||
type = with lib.types; nullOr ints.unsigned;
|
||||
default = null;
|
||||
example = lib.literalExpression ''{ path = "''${pkgs.kdePackages.plasma-workspace-wallpapers}/share/wallpapers/"; }'';
|
||||
example = 5;
|
||||
description = ''
|
||||
Allows you to set wallpaper using the slideshow plugin. Needs the path
|
||||
to at least one directory.
|
||||
Sets the minutes after which the screen is locked.
|
||||
'';
|
||||
};
|
||||
wallpaperPlainColor = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
|
||||
passwordRequired = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = "0,64,174,256";
|
||||
example = true;
|
||||
description = ''
|
||||
Allows you to set wallpaper using a plain color. Color is a comma-seperated R,G,B,A string. Alpha optional (default is 256).
|
||||
Whether the password is required to unlock the screen.
|
||||
'';
|
||||
};
|
||||
|
||||
passwordRequiredDelay = lib.mkOption {
|
||||
type = with lib.types; nullOr ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = ''
|
||||
The time it takes in seconds for the password to be required after the screen is locked.
|
||||
'';
|
||||
};
|
||||
|
||||
lockOnStartup = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = false;
|
||||
description = ''
|
||||
Whether to lock the screen on startup.
|
||||
|
||||
NOTE: This option is not provided in the system settings.
|
||||
'';
|
||||
};
|
||||
|
||||
appearance = {
|
||||
alwaysShowClock = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = false;
|
||||
description = ''
|
||||
Whether to always show the clock on the lockscreen, even if the unlock dialog is not shown.
|
||||
'';
|
||||
};
|
||||
showMediaControls = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = false;
|
||||
description = ''
|
||||
Whether to show media controls on the lockscreen.
|
||||
'';
|
||||
};
|
||||
|
||||
wallpaper = lib.mkOption {
|
||||
type = with lib.types; nullOr path;
|
||||
default = null;
|
||||
example = lib.literalExpression ''"''${pkgs.kdePackages.plasma-workspace-wallpapers}/share/wallpapers/Kay/contents/images/1080x1920.png"'';
|
||||
description = ''
|
||||
The wallpaper for the lockscreen. Can be either be the path to an image file or a kpackage.
|
||||
'';
|
||||
};
|
||||
wallpaperPictureOfTheDay = lib.mkOption {
|
||||
type = lib.types.nullOr wallpaperPictureOfTheDayType;
|
||||
default = null;
|
||||
example = {
|
||||
provider = "apod";
|
||||
};
|
||||
description = ''
|
||||
Allows you to set wallpaper using the picture of the day plugin. Needs the provider.
|
||||
'';
|
||||
};
|
||||
wallpaperSlideShow = lib.mkOption {
|
||||
type = lib.types.nullOr wallpaperSlideShowType;
|
||||
default = null;
|
||||
example = lib.literalExpression ''{ path = "''${pkgs.kdePackages.plasma-workspace-wallpapers}/share/wallpapers/"; }'';
|
||||
description = ''
|
||||
Allows you to set wallpaper using the slideshow plugin. Needs the path
|
||||
to at least one directory.
|
||||
'';
|
||||
};
|
||||
wallpaperPlainColor = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "0,64,174,256";
|
||||
description = ''
|
||||
Allows you to set wallpaper using a plain color. Color is a comma-seperated R,G,B,A string. Alpha optional (default is 256).
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kscreenlocker"
|
||||
"wallpaper"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kscreenlocker"
|
||||
"appearance"
|
||||
"wallpaper"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kscreenlocker"
|
||||
"wallpaperPictureOfTheDay"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kscreenlocker"
|
||||
"appearance"
|
||||
"wallpaperPictureOfTheDay"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kscreenlocker"
|
||||
"wallpaperSlideShow"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kscreenlocker"
|
||||
"appearance"
|
||||
"wallpaperSlideShow"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kscreenlocker"
|
||||
"wallpaperPlainColor"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kscreenlocker"
|
||||
"appearance"
|
||||
"wallpaperPlainColor"
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
config = {
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
let
|
||||
wallpapers = with cfg.workspace; [ wallpaperSlideShow wallpaper wallpaperPictureOfTheDay wallpaperPlainColor ];
|
||||
wallpapers = with cfg.kscreenlocker.appearance; [
|
||||
wallpaperSlideShow
|
||||
wallpaper
|
||||
wallpaperPictureOfTheDay
|
||||
wallpaperPlainColor
|
||||
];
|
||||
in
|
||||
lib.count (x: x != null) wallpapers <= 1;
|
||||
message = "Can set only one of wallpaper, wallpaperSlideShow, wallpaperPictureOfTheDay, and wallpaperPlainColor for kscreenlocker.";
|
||||
}
|
||||
];
|
||||
programs.plasma.configFile.kscreenlockerrc = (lib.mkMerge [
|
||||
(lib.mkIf (cfg.kscreenlocker.wallpaper != null) {
|
||||
Greeter.WallpaperPlugin = "org.kde.image";
|
||||
"Greeter/Wallpaper/org.kde.image/General".Image = (builtins.toString cfg.kscreenlocker.wallpaper);
|
||||
})
|
||||
(lib.mkIf (cfg.kscreenlocker.wallpaperPictureOfTheDay != null) {
|
||||
Greeter.WallpaperPlugin = "org.kde.potd";
|
||||
"Greeter/Wallpaper/org.kde.potd/General" = {
|
||||
Provider = cfg.kscreenlocker.wallpaperPictureOfTheDay.provider;
|
||||
UpdateOverMeteredConnection = with cfg.kscreenlocker.wallpaperPictureOfTheDay;
|
||||
(lib.mkIf (updateOverMeteredConnection != null) (if updateOverMeteredConnection then 1 else 0));
|
||||
};
|
||||
})
|
||||
(lib.mkIf (cfg.kscreenlocker.wallpaperSlideShow != null) {
|
||||
Greeter.WallpaperPlugin = "org.kde.slideshow";
|
||||
"Greeter/Wallpaper/org.kde.slideshow/General" = {
|
||||
SlidePaths = with cfg.kscreenlocker.wallpaperSlideShow;
|
||||
(if ((builtins.isPath path) || (builtins.isString path)) then
|
||||
(builtins.toString cfg.kscreenlocker.wallpaperSlideShow.path) else
|
||||
(builtins.concatStringsSep "," cfg.kscreenlocker.wallpaperSlideShow.path));
|
||||
SlideInterval = cfg.kscreenlocker.wallpaperSlideShow.interval;
|
||||
};
|
||||
})
|
||||
(lib.mkIf (cfg.kscreenlocker.wallpaperPlainColor != null) {
|
||||
Greeter.WallpaperPlugin = "org.kde.color";
|
||||
"Greeter/Wallpaper/org.kde.color/General".Color = cfg.kscreenlocker.wallpaperPlainColor;
|
||||
})
|
||||
]);
|
||||
programs.plasma.configFile.kscreenlockerrc = (
|
||||
lib.mkMerge [
|
||||
(lib.mkIf (cfg.kscreenlocker.appearance.wallpaper != null) {
|
||||
Greeter.WallpaperPlugin = "org.kde.image";
|
||||
"Greeter/Wallpaper/org.kde.image/General".Image = (
|
||||
builtins.toString cfg.kscreenlocker.appearance.wallpaper
|
||||
);
|
||||
})
|
||||
(lib.mkIf (cfg.kscreenlocker.appearance.wallpaperPictureOfTheDay != null) {
|
||||
Greeter.WallpaperPlugin = "org.kde.potd";
|
||||
"Greeter/Wallpaper/org.kde.potd/General" = {
|
||||
Provider = cfg.kscreenlocker.appearance.wallpaperPictureOfTheDay.provider;
|
||||
UpdateOverMeteredConnection =
|
||||
with cfg.kscreenlocker.appearance.wallpaperPictureOfTheDay;
|
||||
(lib.mkIf (updateOverMeteredConnection != null) (if updateOverMeteredConnection then 1 else 0));
|
||||
};
|
||||
})
|
||||
(lib.mkIf (cfg.kscreenlocker.appearance.wallpaperSlideShow != null) {
|
||||
Greeter.WallpaperPlugin = "org.kde.slideshow";
|
||||
"Greeter/Wallpaper/org.kde.slideshow/General" = {
|
||||
SlidePaths =
|
||||
with cfg.kscreenlocker.appearance.wallpaperSlideShow;
|
||||
(
|
||||
if ((builtins.isPath path) || (builtins.isString path)) then
|
||||
(builtins.toString cfg.kscreenlocker.appearance.wallpaperSlideShow.path)
|
||||
else
|
||||
(builtins.concatStringsSep "," cfg.kscreenlocker.appearance.wallpaperSlideShow.path)
|
||||
);
|
||||
SlideInterval = cfg.kscreenlocker.appearance.wallpaperSlideShow.interval;
|
||||
};
|
||||
})
|
||||
(lib.mkIf (cfg.kscreenlocker.appearance.wallpaperPlainColor != null) {
|
||||
Greeter.WallpaperPlugin = "org.kde.color";
|
||||
"Greeter/Wallpaper/org.kde.color/General".Color = cfg.kscreenlocker.appearance.wallpaperPlainColor;
|
||||
})
|
||||
|
||||
(lib.mkIf (cfg.kscreenlocker.appearance.alwaysShowClock != null) {
|
||||
"Greeter/LnF/General".alwaysShowClock = cfg.kscreenlocker.appearance.alwaysShowClock;
|
||||
})
|
||||
(lib.mkIf (cfg.kscreenlocker.appearance.showMediaControls != null) {
|
||||
"Greeter/LnF/General".showMediaControls = cfg.kscreenlocker.appearance.showMediaControls;
|
||||
})
|
||||
|
||||
(lib.mkIf (cfg.kscreenlocker.autoLock != null) { Daemon.Autolock = cfg.kscreenlocker.autoLock; })
|
||||
|
||||
(lib.mkIf (cfg.kscreenlocker.lockOnResume != null) {
|
||||
Daemon.LockOnResume = cfg.kscreenlocker.lockOnResume;
|
||||
})
|
||||
|
||||
(lib.mkIf (cfg.kscreenlocker.timeout != null) { Daemon.Timeout = cfg.kscreenlocker.timeout; })
|
||||
|
||||
(lib.mkIf (cfg.kscreenlocker.passwordRequiredDelay != null) {
|
||||
Daemon.LockGrace = cfg.kscreenlocker.passwordRequiredDelay;
|
||||
})
|
||||
|
||||
(lib.mkIf (cfg.kscreenlocker.passwordRequired != null) {
|
||||
Daemon.RequirePassword = cfg.kscreenlocker.passwordRequired;
|
||||
})
|
||||
|
||||
(lib.mkIf (cfg.kscreenlocker.lockOnStartup != null) {
|
||||
Daemon.LockOnStart = cfg.kscreenlocker.lockOnStartup;
|
||||
})
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
658
modules/kwin.nix
658
modules/kwin.nix
@ -1,4 +1,9 @@
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
@ -32,31 +37,28 @@ let
|
||||
};
|
||||
|
||||
# Gets a list with long names and turns it into short names
|
||||
getShortNames = wantedButtons:
|
||||
lists.forEach
|
||||
(
|
||||
lists.flatten (
|
||||
lists.forEach wantedButtons (currentButton:
|
||||
lists.remove null (
|
||||
lists.imap0
|
||||
(index: value:
|
||||
if value == currentButton then "${toString index}" else null
|
||||
)
|
||||
validTitlebarButtons.longNames
|
||||
)
|
||||
)
|
||||
getShortNames =
|
||||
wantedButtons:
|
||||
lists.forEach (lists.flatten (
|
||||
lists.forEach wantedButtons (
|
||||
currentButton:
|
||||
lists.remove null (
|
||||
lists.imap0 (
|
||||
index: value: if value == currentButton then "${toString index}" else null
|
||||
) validTitlebarButtons.longNames
|
||||
)
|
||||
)
|
||||
getShortNameFromIndex;
|
||||
)) getShortNameFromIndex;
|
||||
|
||||
# Gets the index and returns the short name in that position
|
||||
getShortNameFromIndex = position: builtins.elemAt validTitlebarButtons.shortNames (strings.toInt position);
|
||||
getShortNameFromIndex =
|
||||
position: builtins.elemAt validTitlebarButtons.shortNames (strings.toInt position);
|
||||
|
||||
virtualDesktopNameAttrs = names:
|
||||
builtins.listToAttrs
|
||||
(imap1 (i: v: (nameValuePair "Name_${builtins.toString i}" v)) names);
|
||||
virtualDesktopNameAttrs =
|
||||
names: builtins.listToAttrs (imap1 (i: v: (nameValuePair "Name_${builtins.toString i}" v)) names);
|
||||
|
||||
capitalizeWord = word:
|
||||
capitalizeWord =
|
||||
word:
|
||||
let
|
||||
firstLetter = builtins.substring 0 1 word;
|
||||
rest = builtins.substring 1 (builtins.stringLength word - 1) word;
|
||||
@ -64,19 +66,77 @@ let
|
||||
"${toUpper firstLetter}${rest}";
|
||||
|
||||
removeColon = string: builtins.replaceStrings [ ":" ] [ "" ] string;
|
||||
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex (
|
||||
x: x == value
|
||||
) (throw "getIndexFromEnum (kwin): Value ${value} isn't present in the enum. This is a bug") enum;
|
||||
|
||||
convertPoloniumFilter = list: if list == null then null else builtins.concatStringsSep ", " list;
|
||||
|
||||
tilingLayoutType = types.submodule {
|
||||
options = {
|
||||
id = mkOption {
|
||||
type = types.str;
|
||||
description = "The id of the layout.";
|
||||
example = "cf5c25c2-4217-4193-add6-b5971cb543f2";
|
||||
};
|
||||
tiles = mkOption {
|
||||
type = with types; attrsOf anything;
|
||||
example = {
|
||||
layoutDirection = "horizontal";
|
||||
tiles = [
|
||||
{ width = 0.5; }
|
||||
{
|
||||
layoutDirection = "vertical";
|
||||
tiles = [
|
||||
{ height = 0.5; }
|
||||
{ height = 0.5; }
|
||||
];
|
||||
width = 0.5;
|
||||
}
|
||||
];
|
||||
};
|
||||
apply = builtins.toJSON;
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "programs" "plasma" "kwin" "virtualDesktops" "animation" ]
|
||||
[ "programs" "plasma" "kwin" "effects" "desktopSwitching" "animation" ])
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kwin"
|
||||
"virtualDesktops"
|
||||
"animation"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"kwin"
|
||||
"effects"
|
||||
"desktopSwitching"
|
||||
"animation"
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
options.programs.plasma.kwin = {
|
||||
titlebarButtons.right = mkOption {
|
||||
type = with types; nullOr (listOf (enum validTitlebarButtons.longNames));
|
||||
default = null;
|
||||
example = [ "help" "minimize" "maximize" "close" ];
|
||||
example = [
|
||||
"help"
|
||||
"minimize"
|
||||
"maximize"
|
||||
"close"
|
||||
];
|
||||
description = ''
|
||||
Title bar buttons to be placed on the right.
|
||||
'';
|
||||
@ -84,7 +144,10 @@ in
|
||||
titlebarButtons.left = mkOption {
|
||||
type = with types; nullOr (listOf (enum validTitlebarButtons.longNames));
|
||||
default = null;
|
||||
example = [ "on-all-desktops" "keep-above-windows" ];
|
||||
example = [
|
||||
"on-all-desktops"
|
||||
"keep-above-windows"
|
||||
];
|
||||
description = ''
|
||||
Title bar buttons to be placed on the left.
|
||||
'';
|
||||
@ -103,7 +166,13 @@ in
|
||||
};
|
||||
minimization = {
|
||||
animation = mkOption {
|
||||
type = with types; nullOr (enum [ "squash" "magiclamp" ]);
|
||||
type =
|
||||
with types;
|
||||
nullOr (enum [
|
||||
"squash"
|
||||
"magiclamp"
|
||||
"off"
|
||||
]);
|
||||
default = null;
|
||||
example = "magiclamp";
|
||||
description = "The effect when windows are minimized.";
|
||||
@ -134,14 +203,27 @@ in
|
||||
description = "Arrange desktops in a virtual cube.";
|
||||
};
|
||||
desktopSwitching.animation = mkOption {
|
||||
type = with types; nullOr (enum [ "fade" "slide" ]);
|
||||
type =
|
||||
with types;
|
||||
nullOr (enum [
|
||||
"fade"
|
||||
"slide"
|
||||
"off"
|
||||
]);
|
||||
default = null;
|
||||
example = "fade";
|
||||
description = "The animation used when switching virtual desktop.";
|
||||
};
|
||||
windowOpenClose = {
|
||||
animation = mkOption {
|
||||
type = with types; nullOr (enum [ "fade" "glide" "scale" ]);
|
||||
type =
|
||||
with types;
|
||||
nullOr (enum [
|
||||
"fade"
|
||||
"glide"
|
||||
"scale"
|
||||
"off"
|
||||
]);
|
||||
default = null;
|
||||
example = "glide";
|
||||
description = "The animation used when opening/closing windows.";
|
||||
@ -191,7 +273,12 @@ in
|
||||
names = mkOption {
|
||||
type = with types; nullOr (listOf str);
|
||||
default = null;
|
||||
example = [ "Desktop 1" "Desktop 2" "Desktop 3" "Desktop 4" ];
|
||||
example = [
|
||||
"Desktop 1"
|
||||
"Desktop 2"
|
||||
"Desktop 3"
|
||||
"Desktop 4"
|
||||
];
|
||||
description = ''
|
||||
The names of your virtual desktops. When set, the number of virtual
|
||||
desktops is automatically detected and doesn't need to be specified.
|
||||
@ -224,7 +311,13 @@ in
|
||||
description = "Enable the night light effect.";
|
||||
};
|
||||
mode = mkOption {
|
||||
type = with types; nullOr (enum [ "constant" "location" "times" ]);
|
||||
type =
|
||||
with types;
|
||||
nullOr (enum [
|
||||
"constant"
|
||||
"location"
|
||||
"times"
|
||||
]);
|
||||
default = null;
|
||||
example = "times";
|
||||
description = "The mode of the night light effect.";
|
||||
@ -298,162 +391,371 @@ in
|
||||
example = false;
|
||||
description = "When enabled, prevents the cursor from crossing at screen-corners.";
|
||||
};
|
||||
|
||||
tiling = {
|
||||
padding = mkOption {
|
||||
type = with types; nullOr ints.positive;
|
||||
default = null;
|
||||
example = 10;
|
||||
description = "The padding between windows in tiling.";
|
||||
};
|
||||
layout = mkOption {
|
||||
type = with types; nullOr tilingLayoutType;
|
||||
default = null;
|
||||
example = {
|
||||
id = "cf5c25c2-4217-4193-add6-b5971cb543f2";
|
||||
tiles = {
|
||||
layoutDirection = "horizontal";
|
||||
tiles = [
|
||||
{ width = 0.5; }
|
||||
{
|
||||
layoutDirection = "vertical";
|
||||
tiles = [
|
||||
{ height = 0.5; }
|
||||
{ height = 0.5; }
|
||||
];
|
||||
width = 0.5;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
scripts = {
|
||||
polonium = {
|
||||
enable = mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to enable Polonium";
|
||||
};
|
||||
settings = {
|
||||
borderVisibility =
|
||||
let
|
||||
enumVals = [
|
||||
"noBorderAll"
|
||||
"noBorderTiled"
|
||||
"borderSelected"
|
||||
"borderAll"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "noBorderAll";
|
||||
description = "The border visibility setting for Polonium";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
callbackDelay = mkOption {
|
||||
type = with types; nullOr (ints.between 1 200);
|
||||
default = null;
|
||||
example = 100;
|
||||
description = "The callback delay setting for Polonium";
|
||||
};
|
||||
enableDebug = mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to enable debug for Polonium";
|
||||
};
|
||||
filter = {
|
||||
processes = mkOption {
|
||||
type = with types; nullOr (listOf str);
|
||||
default = null;
|
||||
example = [
|
||||
"firefox"
|
||||
"chromium"
|
||||
];
|
||||
description = "The processes to filter for Polonium";
|
||||
apply = convertPoloniumFilter;
|
||||
};
|
||||
windowTitles = mkOption {
|
||||
type = with types; nullOr (listOf str);
|
||||
default = null;
|
||||
example = [
|
||||
"Discord"
|
||||
"Telegram"
|
||||
];
|
||||
description = "The window titles to filter for Polonium";
|
||||
apply = convertPoloniumFilter;
|
||||
};
|
||||
};
|
||||
layout = {
|
||||
engine =
|
||||
let
|
||||
enumVals = [
|
||||
"binaryTree"
|
||||
"half"
|
||||
"threeColumn"
|
||||
"monocle"
|
||||
"kwin"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "binaryTree";
|
||||
description = "The layout engine setting for Polonium";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
insertionPoint =
|
||||
let
|
||||
enumVals = [
|
||||
"left"
|
||||
"right"
|
||||
"activeWindow"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "top";
|
||||
description = "The insertion point setting for Polonium";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
rotate = mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to rotate layout for Polonium";
|
||||
};
|
||||
};
|
||||
maximizeSingleWindow = mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to maximize single window for Polonium";
|
||||
};
|
||||
resizeAmount = mkOption {
|
||||
type = with types; nullOr (ints.between 1 450);
|
||||
default = null;
|
||||
example = 100;
|
||||
description = "The resize amount setting for Polonium";
|
||||
};
|
||||
saveOnTileEdit = mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to save on tile edit for Polonium";
|
||||
};
|
||||
tilePopups = mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to tile popups for Polonium";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config.assertions = [
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.virtualDesktops.number == null ||
|
||||
cfg.kwin.virtualDesktops.names == null ||
|
||||
cfg.kwin.virtualDesktops.number == (builtins.length cfg.kwin.virtualDesktops.names);
|
||||
message = "programs.plasma.virtualDesktops.number doesn't match the length of programs.plasma.virtualDesktops.names.";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.virtualDesktops.rows == null ||
|
||||
(cfg.kwin.virtualDesktops.names == null && cfg.kwin.virtualDesktops.number == null) ||
|
||||
(cfg.kwin.virtualDesktops.number != null && cfg.kwin.virtualDesktops.number >= cfg.kwin.virtualDesktops.rows) ||
|
||||
(cfg.kwin.virtualDesktops.names != null && (builtins.length cfg.kwin.virtualDesktops.names) >= cfg.kwin.virtualDesktops.rows);
|
||||
message = "KWin cannot have more rows virtual desktops.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.kwin.effects.minimization.duration == null || cfg.kwin.effects.minimization.animation == "magiclamp";
|
||||
message = "programs.plasma.kwin.effects.minimization.duration is only supported for the magic lamp effect";
|
||||
}
|
||||
{
|
||||
assertion = (cfg.kwin.nightLight.enable == null || cfg.kwin.nightLight.enable == false) || cfg.kwin.nightLight.mode != null;
|
||||
message = "programs.plasma.kwin.nightLight.mode must be set when programs.plasma.kwin.nightLight.enable is true.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.kwin.nightLight.mode != "Times" || (cfg.kwin.nightLight.time.morning != null && cfg.kwin.nightLight.time.evening != null);
|
||||
message = "programs.plasma.kwin.nightLight.time.morning and programs.plasma.kwin.nightLight.time.evening must be set when programs.plasma.kwin.nightLight.mode is set to times.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.kwin.nightLight.mode != "Location" || (cfg.kwin.nightLight.location.latitude != null && cfg.kwin.nightLight.location.longitude != null);
|
||||
message = "programs.plasma.kwin.nightLight.location.latitude and programs.plasma.kwin.nightLight.location.longitude must be set when programs.plasma.kwin.nightLight.mode is set to location.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.kwin.nightLight.time.morning == null || builtins.stringLength cfg.kwin.nightLight.time.morning == 4;
|
||||
message = "programs.plasma.kwin.nightLight.time.morning must have the exact length of 4. If it doesn't have, it means that it doesn't have this time format: HH:MM";
|
||||
}
|
||||
{
|
||||
assertion = cfg.kwin.nightLight.time.evening == null || builtins.stringLength cfg.kwin.nightLight.time.evening == 4;
|
||||
message = "programs.plasma.kwin.nightLight.time.evening must have the exact length of 4. If it doesn't have, it means that it doesn't have this time format: HH:MM";
|
||||
}
|
||||
];
|
||||
|
||||
config.programs.plasma.configFile."kwinrc" = mkIf (cfg.enable)
|
||||
(mkMerge [
|
||||
# Titlebar buttons
|
||||
(
|
||||
mkIf (cfg.kwin.titlebarButtons.left != null) {
|
||||
"org.kde.kdecoration2".ButtonsOnLeft = strings.concatStrings (getShortNames cfg.kwin.titlebarButtons.left);
|
||||
config = (
|
||||
mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.virtualDesktops.number == null
|
||||
|| cfg.kwin.virtualDesktops.names == null
|
||||
|| cfg.kwin.virtualDesktops.number == (builtins.length cfg.kwin.virtualDesktops.names);
|
||||
message = "programs.plasma.virtualDesktops.number doesn't match the length of programs.plasma.virtualDesktops.names.";
|
||||
}
|
||||
)
|
||||
(
|
||||
mkIf (cfg.kwin.titlebarButtons.right != null) {
|
||||
"org.kde.kdecoration2".ButtonsOnRight = strings.concatStrings (getShortNames cfg.kwin.titlebarButtons.right);
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.virtualDesktops.rows == null
|
||||
|| (cfg.kwin.virtualDesktops.names == null && cfg.kwin.virtualDesktops.number == null)
|
||||
|| (
|
||||
cfg.kwin.virtualDesktops.number != null
|
||||
&& cfg.kwin.virtualDesktops.number >= cfg.kwin.virtualDesktops.rows
|
||||
)
|
||||
|| (
|
||||
cfg.kwin.virtualDesktops.names != null
|
||||
&& (builtins.length cfg.kwin.virtualDesktops.names) >= cfg.kwin.virtualDesktops.rows
|
||||
);
|
||||
message = "KWin cannot have more rows virtual desktops.";
|
||||
}
|
||||
)
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.effects.minimization.duration == null
|
||||
|| cfg.kwin.effects.minimization.animation == "magiclamp";
|
||||
message = "programs.plasma.kwin.effects.minimization.duration is only supported for the magic lamp effect";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
(cfg.kwin.nightLight.enable == null || cfg.kwin.nightLight.enable == false)
|
||||
|| cfg.kwin.nightLight.mode != null;
|
||||
message = "programs.plasma.kwin.nightLight.mode must be set when programs.plasma.kwin.nightLight.enable is true.";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.nightLight.mode != "Times"
|
||||
|| (cfg.kwin.nightLight.time.morning != null && cfg.kwin.nightLight.time.evening != null);
|
||||
message = "programs.plasma.kwin.nightLight.time.morning and programs.plasma.kwin.nightLight.time.evening must be set when programs.plasma.kwin.nightLight.mode is set to times.";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.nightLight.mode != "Location"
|
||||
|| (
|
||||
cfg.kwin.nightLight.location.latitude != null && cfg.kwin.nightLight.location.longitude != null
|
||||
);
|
||||
message = "programs.plasma.kwin.nightLight.location.latitude and programs.plasma.kwin.nightLight.location.longitude must be set when programs.plasma.kwin.nightLight.mode is set to location.";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.nightLight.time.morning == null
|
||||
|| builtins.stringLength cfg.kwin.nightLight.time.morning == 4;
|
||||
message = "programs.plasma.kwin.nightLight.time.morning must have the exact length of 4. If it doesn't have, it means that it doesn't have this time format: HH:MM";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
cfg.kwin.nightLight.time.evening == null
|
||||
|| builtins.stringLength cfg.kwin.nightLight.time.evening == 4;
|
||||
message = "programs.plasma.kwin.nightLight.time.evening must have the exact length of 4. If it doesn't have, it means that it doesn't have this time format: HH:MM";
|
||||
}
|
||||
];
|
||||
|
||||
# Effects
|
||||
(mkIf (cfg.kwin.effects.shakeCursor.enable != null) {
|
||||
Plugins.shakecursorEnabled = cfg.kwin.effects.shakeCursor.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.minimization.animation != null) {
|
||||
Plugins = {
|
||||
magiclampEnabled = cfg.kwin.effects.minimization.animation == "magiclamp";
|
||||
squashEnabled = cfg.kwin.effects.minimization.animation == "squash";
|
||||
};
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.minimization.duration != null) {
|
||||
Effect-magiclamp.AnimationDuration = cfg.kwin.effects.minimization.duration;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.wobblyWindows.enable != null) {
|
||||
Plugins.wobblywindowsEnabled = cfg.kwin.effects.wobblyWindows.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.translucency.enable != null) {
|
||||
Plugins.translucencyEnabled = cfg.kwin.effects.translucency.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.windowOpenClose.animation != null) {
|
||||
Plugins = {
|
||||
glideEnabled = cfg.kwin.effects.windowOpenClose.animation == "glide";
|
||||
fadeEnabled = cfg.kwin.effects.windowOpenClose.animation == "fade";
|
||||
scaleEnabled = cfg.kwin.effects.windowOpenClose.animation == "scale";
|
||||
};
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.fps.enable != null) {
|
||||
Plugins.showfpsEnabled = cfg.kwin.effects.fps.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.cube.enable != null) {
|
||||
Plugins.cubeEnabled = cfg.kwin.effects.cube.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.desktopSwitching.animation != null) {
|
||||
Plugins.slideEnabled = cfg.kwin.effects.desktopSwitching.animation == "slide";
|
||||
Plugins.fadedesktopEnabled = cfg.kwin.effects.desktopSwitching.animation == "fade";
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.fallApart.enable != null) {
|
||||
Plugins.fallapartEnabled = cfg.kwin.effects.fallApart.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.snapHelper.enable != null) {
|
||||
Plugins.snaphelperEnabled = cfg.kwin.effects.snapHelper.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.blur.enable != null) {
|
||||
Plugins.blurEnabled = cfg.kwin.effects.blur.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.dimInactive.enable != null) {
|
||||
Plugins.diminactiveEnabled = cfg.kwin.effects.dimInactive.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.dimAdminMode.enable != null) {
|
||||
Plugins.dimscreenEnabled = cfg.kwin.effects.dimAdminMode.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.slideBack.enable != null) {
|
||||
Plugins.slidebackEnabled = cfg.kwin.effects.slideBack.enable;
|
||||
})
|
||||
home.packages = with pkgs; [ ] ++ optionals (cfg.kwin.scripts.polonium.enable == true) [ polonium ];
|
||||
|
||||
# Virtual Desktops
|
||||
(mkIf (cfg.kwin.virtualDesktops.number != null) {
|
||||
Desktops.Number = cfg.kwin.virtualDesktops.number;
|
||||
})
|
||||
(mkIf (cfg.kwin.virtualDesktops.rows != null) {
|
||||
Desktops.Rows = cfg.kwin.virtualDesktops.rows;
|
||||
})
|
||||
(mkIf (cfg.kwin.virtualDesktops.names != null) {
|
||||
Desktops = mkMerge [
|
||||
{
|
||||
Number = builtins.length cfg.kwin.virtualDesktops.names;
|
||||
}
|
||||
(virtualDesktopNameAttrs cfg.kwin.virtualDesktops.names)
|
||||
];
|
||||
})
|
||||
programs.plasma.configFile."kwinrc" = (
|
||||
mkMerge [
|
||||
# Titlebar buttons
|
||||
(mkIf (cfg.kwin.titlebarButtons.left != null) {
|
||||
"org.kde.kdecoration2".ButtonsOnLeft = strings.concatStrings (
|
||||
getShortNames cfg.kwin.titlebarButtons.left
|
||||
);
|
||||
})
|
||||
(mkIf (cfg.kwin.titlebarButtons.right != null) {
|
||||
"org.kde.kdecoration2".ButtonsOnRight = strings.concatStrings (
|
||||
getShortNames cfg.kwin.titlebarButtons.right
|
||||
);
|
||||
})
|
||||
|
||||
# Borderless maximized windows
|
||||
(mkIf (cfg.kwin.borderlessMaximizedWindows != null) {
|
||||
Windows = {
|
||||
BorderlessMaximizedWindows = cfg.kwin.borderlessMaximizedWindows;
|
||||
};
|
||||
})
|
||||
# Effects
|
||||
(mkIf (cfg.kwin.effects.shakeCursor.enable != null) {
|
||||
Plugins.shakecursorEnabled = cfg.kwin.effects.shakeCursor.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.minimization.animation != null) {
|
||||
Plugins = {
|
||||
magiclampEnabled = cfg.kwin.effects.minimization.animation == "magiclamp";
|
||||
squashEnabled = cfg.kwin.effects.minimization.animation == "squash";
|
||||
};
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.minimization.duration != null) {
|
||||
Effect-magiclamp.AnimationDuration = cfg.kwin.effects.minimization.duration;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.wobblyWindows.enable != null) {
|
||||
Plugins.wobblywindowsEnabled = cfg.kwin.effects.wobblyWindows.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.translucency.enable != null) {
|
||||
Plugins.translucencyEnabled = cfg.kwin.effects.translucency.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.windowOpenClose.animation != null) {
|
||||
Plugins = {
|
||||
glideEnabled = cfg.kwin.effects.windowOpenClose.animation == "glide";
|
||||
fadeEnabled = cfg.kwin.effects.windowOpenClose.animation == "fade";
|
||||
scaleEnabled = cfg.kwin.effects.windowOpenClose.animation == "scale";
|
||||
};
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.fps.enable != null) {
|
||||
Plugins.showfpsEnabled = cfg.kwin.effects.fps.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.cube.enable != null) {
|
||||
Plugins.cubeEnabled = cfg.kwin.effects.cube.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.desktopSwitching.animation != null) {
|
||||
Plugins.slideEnabled = cfg.kwin.effects.desktopSwitching.animation == "slide";
|
||||
Plugins.fadedesktopEnabled = cfg.kwin.effects.desktopSwitching.animation == "fade";
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.fallApart.enable != null) {
|
||||
Plugins.fallapartEnabled = cfg.kwin.effects.fallApart.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.snapHelper.enable != null) {
|
||||
Plugins.snaphelperEnabled = cfg.kwin.effects.snapHelper.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.blur.enable != null) {
|
||||
Plugins.blurEnabled = cfg.kwin.effects.blur.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.dimInactive.enable != null) {
|
||||
Plugins.diminactiveEnabled = cfg.kwin.effects.dimInactive.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.dimAdminMode.enable != null) {
|
||||
Plugins.dimscreenEnabled = cfg.kwin.effects.dimAdminMode.enable;
|
||||
})
|
||||
(mkIf (cfg.kwin.effects.slideBack.enable != null) {
|
||||
Plugins.slidebackEnabled = cfg.kwin.effects.slideBack.enable;
|
||||
})
|
||||
|
||||
# Night Light
|
||||
(mkIf (cfg.kwin.nightLight.enable != null) {
|
||||
NightColor = {
|
||||
Active = cfg.kwin.nightLight.enable;
|
||||
DayTemperature = cfg.kwin.nightLight.temperature.day;
|
||||
EveningBeginFixed = cfg.kwin.nightLight.time.evening;
|
||||
LatitudeFixed = cfg.kwin.nightLight.location.latitude;
|
||||
LongitudeFixed = cfg.kwin.nightLight.location.longitude;
|
||||
Mode = cfg.kwin.nightLight.mode;
|
||||
MorningBeginFixed = cfg.kwin.nightLight.time.morning;
|
||||
NightTemperature = cfg.kwin.nightLight.temperature.night;
|
||||
TransitionTime = cfg.kwin.nightLight.transitionTime;
|
||||
};
|
||||
})
|
||||
# Virtual Desktops
|
||||
(mkIf (cfg.kwin.virtualDesktops.number != null) {
|
||||
Desktops.Number = cfg.kwin.virtualDesktops.number;
|
||||
})
|
||||
(mkIf (cfg.kwin.virtualDesktops.rows != null) { Desktops.Rows = cfg.kwin.virtualDesktops.rows; })
|
||||
(mkIf (cfg.kwin.virtualDesktops.names != null) {
|
||||
Desktops = mkMerge [
|
||||
{ Number = builtins.length cfg.kwin.virtualDesktops.names; }
|
||||
(virtualDesktopNameAttrs cfg.kwin.virtualDesktops.names)
|
||||
];
|
||||
})
|
||||
|
||||
(mkIf (cfg.kwin.cornerBarrier != null) {
|
||||
EdgeBarrier.CornerBarrier = cfg.kwin.cornerBarrier;
|
||||
})
|
||||
(mkIf (cfg.kwin.edgeBarrier != null) {
|
||||
EdgeBarrier.EdgeBarrier = cfg.kwin.edgeBarrier;
|
||||
})
|
||||
]);
|
||||
# Borderless maximized windows
|
||||
(mkIf (cfg.kwin.borderlessMaximizedWindows != null) {
|
||||
Windows = {
|
||||
BorderlessMaximizedWindows = cfg.kwin.borderlessMaximizedWindows;
|
||||
};
|
||||
})
|
||||
|
||||
# Night Light
|
||||
(mkIf (cfg.kwin.nightLight.enable != null) {
|
||||
NightColor = {
|
||||
Active = cfg.kwin.nightLight.enable;
|
||||
DayTemperature = cfg.kwin.nightLight.temperature.day;
|
||||
EveningBeginFixed = cfg.kwin.nightLight.time.evening;
|
||||
LatitudeFixed = cfg.kwin.nightLight.location.latitude;
|
||||
LongitudeFixed = cfg.kwin.nightLight.location.longitude;
|
||||
Mode = cfg.kwin.nightLight.mode;
|
||||
MorningBeginFixed = cfg.kwin.nightLight.time.morning;
|
||||
NightTemperature = cfg.kwin.nightLight.temperature.night;
|
||||
TransitionTime = cfg.kwin.nightLight.transitionTime;
|
||||
};
|
||||
})
|
||||
|
||||
(mkIf (cfg.kwin.cornerBarrier != null) { EdgeBarrier.CornerBarrier = cfg.kwin.cornerBarrier; })
|
||||
(mkIf (cfg.kwin.edgeBarrier != null) { EdgeBarrier.EdgeBarrier = cfg.kwin.edgeBarrier; })
|
||||
|
||||
(mkIf (cfg.kwin.scripts.polonium.enable != null) {
|
||||
Plugins.poloniumEnabled = cfg.kwin.scripts.polonium.enable;
|
||||
Script-polonium = {
|
||||
Borders = cfg.kwin.scripts.polonium.settings.borderVisibility;
|
||||
Debug = cfg.kwin.scripts.polonium.settings.enableDebug;
|
||||
EngineType = cfg.kwin.scripts.polonium.settings.layout.engine;
|
||||
FilterCaption = cfg.kwin.scripts.polonium.settings.filter.windowTitles;
|
||||
FilterProcess = cfg.kwin.scripts.polonium.settings.filter.processes;
|
||||
InsertionPoint = cfg.kwin.scripts.polonium.settings.layout.insertionPoint;
|
||||
MaximizeSingle = cfg.kwin.scripts.polonium.settings.maximizeSingleWindow;
|
||||
ResizeAmount = cfg.kwin.scripts.polonium.settings.resizeAmount;
|
||||
RotateLayout = cfg.kwin.scripts.polonium.settings.layout.rotate;
|
||||
SaveOnTileEdit = cfg.kwin.scripts.polonium.settings.saveOnTileEdit;
|
||||
TilePopups = cfg.kwin.scripts.polonium.settings.tilePopups;
|
||||
TimerDelay = cfg.kwin.scripts.polonium.settings.callbackDelay;
|
||||
};
|
||||
})
|
||||
|
||||
(mkIf (cfg.kwin.tiling.padding != null) {
|
||||
Tiling = {
|
||||
padding = cfg.kwin.tiling.padding;
|
||||
};
|
||||
})
|
||||
|
||||
(mkIf (cfg.kwin.tiling.layout != null) {
|
||||
"Tiling/${cfg.kwin.tiling.layout.id}" = {
|
||||
tiles = {
|
||||
escapeValue = false;
|
||||
value = cfg.kwin.tiling.layout.tiles;
|
||||
};
|
||||
};
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,25 @@
|
||||
{ lib
|
||||
, config
|
||||
, pkgs
|
||||
, ...
|
||||
} @ args:
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}@args:
|
||||
let
|
||||
cfg = config.programs.plasma;
|
||||
hasWidget = widgetName: builtins.any (panel: builtins.any (widget: widget.name == widgetName) panel.widgets) cfg.panels;
|
||||
desktopWidgets = if cfg.desktop.widgets != null then cfg.desktop.widgets else [ ];
|
||||
hasWidget =
|
||||
widgetName:
|
||||
builtins.any (panel: builtins.any (widget: widget.name == widgetName) panel.widgets) cfg.panels
|
||||
|| builtins.any (widget: widget.name == widgetName) desktopWidgets;
|
||||
|
||||
# An attrset keeping track of the packages which should be added when a
|
||||
# widget is present in the config.
|
||||
additionalWidgetPackages = with pkgs; {
|
||||
"com.github.antroids.application-title-bar" = [ application-title-bar ];
|
||||
plasmusic-toolbar = [ plasmusic-toolbar ];
|
||||
"luisbocanegra.panel.colorizer" = [ plasma-panel-colorizer ];
|
||||
"org.kde.windowbuttons" = [ kdePackages.applet-window-buttons6 ];
|
||||
"org.dhruv8sh.kara" = [ kara ];
|
||||
"luisbocanegra.panelspacer.extended" = [ plasma-panel-spacer-extended ];
|
||||
};
|
||||
# An attrset of service-names and widgets/conditions. If any of the
|
||||
@ -23,143 +30,179 @@ let
|
||||
"plasma-plasmashell" = [
|
||||
{
|
||||
widget = "org.kde.plasma.systemmonitor";
|
||||
cond = widget: ((builtins.hasAttr "org.kde.ksysguard.piechart/General" widget.config) && (builtins.hasAttr "showLegend" widget.config."org.kde.ksysguard.piechart/General"));
|
||||
cond =
|
||||
widget:
|
||||
(
|
||||
(builtins.hasAttr "org.kde.ksysguard.piechart/General" widget.config)
|
||||
&& (builtins.hasAttr "showLegend" widget.config."org.kde.ksysguard.piechart/General")
|
||||
);
|
||||
}
|
||||
];
|
||||
};
|
||||
widgetsOfName = name: (lib.filter (w: w.name == name) (lib.flatten (map (panel: panel.widgets) cfg.panels)));
|
||||
shouldRestart = service:
|
||||
widgetsOfName =
|
||||
name: (lib.filter (w: w.name == name) (lib.flatten (map (panel: panel.widgets) cfg.panels)));
|
||||
shouldRestart =
|
||||
service:
|
||||
(
|
||||
let candidates = serviceRestarts."${service}";
|
||||
in (builtins.any (x: x) (map (v: (builtins.any v.cond (widgetsOfName v.widget))) candidates))
|
||||
let
|
||||
candidates = serviceRestarts."${service}";
|
||||
in
|
||||
(builtins.any (x: x) (map (v: (builtins.any v.cond (widgetsOfName v.widget))) candidates))
|
||||
);
|
||||
|
||||
widgets = import ./widgets args;
|
||||
|
||||
panelType = lib.types.submodule ({ config, ... }: {
|
||||
options = {
|
||||
height = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 44;
|
||||
description = "The height of the panel.";
|
||||
panelType = lib.types.submodule (
|
||||
{ config, ... }:
|
||||
{
|
||||
options = {
|
||||
height = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 44;
|
||||
description = "The height of the panel.";
|
||||
};
|
||||
offset = lib.mkOption {
|
||||
type = with lib.types; nullOr int;
|
||||
default = null;
|
||||
example = 100;
|
||||
description = "The offset of the panel from the anchor-point.";
|
||||
};
|
||||
minLength = lib.mkOption {
|
||||
type = with lib.types; nullOr int;
|
||||
default = null;
|
||||
example = 1000;
|
||||
description = "The minimum required length/width of the panel.";
|
||||
};
|
||||
maxLength = lib.mkOption {
|
||||
type = with lib.types; nullOr int;
|
||||
default = null;
|
||||
example = 1600;
|
||||
description = "The maximum allowed length/width of the panel.";
|
||||
};
|
||||
lengthMode = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"fit"
|
||||
"fill"
|
||||
"custom"
|
||||
]);
|
||||
default = if config.minLength != null || config.maxLength != null then "custom" else null;
|
||||
example = "fit";
|
||||
description = "The length mode of the panel. Defaults to `custom` if either `minLength` or `maxLength` is set.";
|
||||
};
|
||||
location = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"top"
|
||||
"bottom"
|
||||
"left"
|
||||
"right"
|
||||
"floating"
|
||||
]);
|
||||
default = "bottom";
|
||||
example = "left";
|
||||
description = "The location of the panel.";
|
||||
};
|
||||
alignment = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"left"
|
||||
"center"
|
||||
"right"
|
||||
]);
|
||||
default = "center";
|
||||
example = "right";
|
||||
description = "The alignment of the panel.";
|
||||
};
|
||||
hiding = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"none"
|
||||
"autohide"
|
||||
# Plasma 5 only
|
||||
"windowscover"
|
||||
"windowsbelow"
|
||||
# Plasma 6 only
|
||||
"dodgewindows"
|
||||
"normalpanel"
|
||||
"windowsgobelow"
|
||||
]);
|
||||
default = null;
|
||||
example = "autohide";
|
||||
description = ''
|
||||
The hiding mode of the panel. Here windowscover and windowsbelow are
|
||||
plasma 5 only, while dodgewindows, windowsgobelow and normalpanel are
|
||||
plasma 6 only.
|
||||
'';
|
||||
};
|
||||
floating = lib.mkEnableOption "Enable or disable floating style.";
|
||||
widgets = lib.mkOption {
|
||||
type = lib.types.listOf widgets.type;
|
||||
default = [
|
||||
"org.kde.plasma.kickoff"
|
||||
"org.kde.plasma.pager"
|
||||
"org.kde.plasma.icontasks"
|
||||
"org.kde.plasma.marginsseparator"
|
||||
"org.kde.plasma.systemtray"
|
||||
"org.kde.plasma.digitalclock"
|
||||
"org.kde.plasma.showdesktop"
|
||||
];
|
||||
example = [
|
||||
"org.kde.plasma.kickoff"
|
||||
"org.kde.plasma.icontasks"
|
||||
"org.kde.plasma.marginsseparator"
|
||||
"org.kde.plasma.digitalclock"
|
||||
];
|
||||
description = ''
|
||||
The widgets to use in the panel. To get the names, it may be useful
|
||||
to look in the share/plasma/plasmoids folder of the nix-package the
|
||||
widget/plasmoid is from. Some packages which include some
|
||||
widgets/plasmoids are for example plasma-desktop and
|
||||
plasma-workspace.
|
||||
'';
|
||||
apply = map widgets.convert;
|
||||
};
|
||||
screen = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
ints.unsigned
|
||||
(listOf ints.unsigned)
|
||||
(enum [ "all" ])
|
||||
]);
|
||||
default = null;
|
||||
description = ''
|
||||
The screen the panel should appear on. Can be an int, or a list of ints,
|
||||
starting from 0, representing the ID of the screen the panel should
|
||||
appear on. Alternatively it can be set to "all" if the panel should
|
||||
appear on all the screens.
|
||||
'';
|
||||
};
|
||||
extraSettings = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Extra lines to add to the layout.js. See
|
||||
https://develop.kde.org/docs/plasma/scripting/ for inspiration.
|
||||
'';
|
||||
};
|
||||
};
|
||||
offset = lib.mkOption {
|
||||
type = with lib.types; nullOr int;
|
||||
default = null;
|
||||
example = 100;
|
||||
description = "The offset of the panel from the anchor-point.";
|
||||
};
|
||||
minLength = lib.mkOption {
|
||||
type = with lib.types; nullOr int;
|
||||
default = null;
|
||||
example = 1000;
|
||||
description = "The minimum required length/width of the panel.";
|
||||
};
|
||||
maxLength = lib.mkOption {
|
||||
type = with lib.types; nullOr int;
|
||||
default = null;
|
||||
example = 1600;
|
||||
description = "The maximum allowed length/width of the panel.";
|
||||
};
|
||||
lengthMode = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum [ "fit" "fill" "custom" ]);
|
||||
default =
|
||||
if config.minLength != null || config.maxLength != null then
|
||||
"custom"
|
||||
else
|
||||
null;
|
||||
example = "fit";
|
||||
description = "The length mode of the panel. Defaults to `custom` if either `minLength` or `maxLength` is set.";
|
||||
};
|
||||
location = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum [ "top" "bottom" "left" "right" "floating" ]);
|
||||
default = "bottom";
|
||||
example = "left";
|
||||
description = "The location of the panel.";
|
||||
};
|
||||
alignment = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum [ "left" "center" "right" ]);
|
||||
default = "center";
|
||||
example = "right";
|
||||
description = "The alignment of the panel.";
|
||||
};
|
||||
hiding = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum [
|
||||
"none"
|
||||
"autohide"
|
||||
# Plasma 5 only
|
||||
"windowscover"
|
||||
"windowsbelow"
|
||||
# Plasma 6 only
|
||||
"dodgewindows"
|
||||
"normalpanel"
|
||||
"windowsgobelow"
|
||||
]);
|
||||
default = null;
|
||||
example = "autohide";
|
||||
description = ''
|
||||
The hiding mode of the panel. Here windowscover and windowsbelow are
|
||||
plasma 5 only, while dodgewindows, windowsgobelow and normalpanel are
|
||||
plasma 6 only.
|
||||
'';
|
||||
};
|
||||
floating = lib.mkEnableOption "Enable or disable floating style.";
|
||||
widgets = lib.mkOption {
|
||||
type = lib.types.listOf widgets.type;
|
||||
default = [
|
||||
"org.kde.plasma.kickoff"
|
||||
"org.kde.plasma.pager"
|
||||
"org.kde.plasma.icontasks"
|
||||
"org.kde.plasma.marginsseparator"
|
||||
"org.kde.plasma.systemtray"
|
||||
"org.kde.plasma.digitalclock"
|
||||
"org.kde.plasma.showdesktop"
|
||||
];
|
||||
example = [
|
||||
"org.kde.plasma.kickoff"
|
||||
"org.kde.plasma.icontasks"
|
||||
"org.kde.plasma.marginsseparator"
|
||||
"org.kde.plasma.digitalclock"
|
||||
];
|
||||
description = ''
|
||||
The widgets to use in the panel. To get the names, it may be useful
|
||||
to look in the share/plasma/plasmoids folder of the nix-package the
|
||||
widget/plasmoid is from. Some packages which include some
|
||||
widgets/plasmoids are for example plasma-desktop and
|
||||
plasma-workspace.
|
||||
'';
|
||||
apply = map widgets.convert;
|
||||
};
|
||||
screen = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ ints.unsigned (listOf ints.unsigned) (enum [ "all" ]) ]);
|
||||
default = null;
|
||||
description = ''
|
||||
The screen the panel should appear on. Can be an int, or a list of ints,
|
||||
starting from 0, representing the ID of the screen the panel should
|
||||
appear on. Alternatively it can be set to "any" if the panel should
|
||||
appear on all the screens.
|
||||
'';
|
||||
};
|
||||
extraSettings = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Extra lines to add to the layout.js. See
|
||||
https://develop.kde.org/docs/plasma/scripting/ for inspiration.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
anyPanelOrWallpaperSet = ((cfg.workspace.wallpaper != null) ||
|
||||
(cfg.workspace.wallpaperSlideShow != null) ||
|
||||
(cfg.workspace.wallpaperPictureOfTheDay != null) ||
|
||||
(cfg.workspace.wallpaperPlainColor != null) ||
|
||||
((builtins.length cfg.panels) > 0));
|
||||
anyPanelSet = (builtins.length cfg.panels) > 0;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "programs" "plasma" "extraWidgets" ] "Place the widget packages in home.packages or environment.systemPackages instead.")
|
||||
(lib.mkRemovedOptionModule [
|
||||
"programs"
|
||||
"plasma"
|
||||
"extraWidgets"
|
||||
] "Place the widget packages in home.packages or environment.systemPackages instead.")
|
||||
];
|
||||
|
||||
options.programs.plasma.panels = lib.mkOption {
|
||||
@ -167,83 +210,55 @@ in
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
# Wallpaper and panels are in the same script since the resetting of the
|
||||
# panels in the panels-script also has a tendency to reset the wallpaper, so
|
||||
# these should run at the same time.
|
||||
config = (lib.mkIf cfg.enable {
|
||||
home.packages = (lib.flatten (lib.filter (x: x != null)
|
||||
(lib.mapAttrsToList
|
||||
(widgetName: packages: if (hasWidget widgetName) then packages else null)
|
||||
additionalWidgetPackages)));
|
||||
config = (
|
||||
lib.mkIf cfg.enable {
|
||||
home.packages = (
|
||||
lib.flatten (
|
||||
lib.filter (x: x != null) (
|
||||
lib.mapAttrsToList (
|
||||
widgetName: packages: if (hasWidget widgetName) then packages else null
|
||||
) additionalWidgetPackages
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
programs.plasma.startup.desktopScript."panels_and_wallpaper" = (lib.mkIf anyPanelOrWallpaperSet
|
||||
(
|
||||
let
|
||||
anyPanels = ((builtins.length cfg.panels) > 0);
|
||||
anyNonDefaultScreens = ((builtins.any (panel: panel.screen != null)) cfg.panels);
|
||||
panelPreCMD = (if anyPanels then ''
|
||||
# We delete plasma-org.kde.plasma.desktop-appletsrc to hinder it
|
||||
# growing indefinitely. See:
|
||||
# https://github.com/nix-community/plasma-manager/issues/76
|
||||
[ -f ${config.xdg.configHome}/plasma-org.kde.plasma.desktop-appletsrc ] && rm ${config.xdg.configHome}/plasma-org.kde.plasma.desktop-appletsrc
|
||||
'' else "");
|
||||
panelLayoutStr = (if anyPanels then (import ../lib/panel.nix { inherit lib; inherit config; }) else "");
|
||||
panelPostCMD = (if anyNonDefaultScreens then ''
|
||||
sed -i 's/^lastScreen\\x5b$i\\x5d=/lastScreen[$i]=/' ${config.xdg.configHome}/plasma-org.kde.plasma.desktop-appletsrc
|
||||
'' else "");
|
||||
# This meaningless comment inserts the URL into the desktop-script
|
||||
# which means that when the wallpaper is updated, the sha256 hash
|
||||
# changes and the script will be re-run.
|
||||
wallpaperDesktopScript = (if (cfg.workspace.wallpaper != null) then ''
|
||||
// Wallpaper to set later: ${cfg.workspace.wallpaper}
|
||||
'' else "");
|
||||
wallpaperPostCMD = (if (cfg.workspace.wallpaper != null) then ''
|
||||
plasma-apply-wallpaperimage ${cfg.workspace.wallpaper}
|
||||
'' else "");
|
||||
wallpaperSlideShow = (if (cfg.workspace.wallpaperSlideShow != null) then ''
|
||||
// Wallpaper slideshow
|
||||
let allDesktops = desktops();
|
||||
for (var desktopIndex = 0; desktopIndex < allDesktops.length; desktopIndex++) {
|
||||
var desktop = allDesktops[desktopIndex];
|
||||
desktop.wallpaperPlugin = "org.kde.slideshow";
|
||||
desktop.currentConfigGroup = Array("Wallpaper", "org.kde.slideshow", "General");
|
||||
desktop.writeConfig("SlidePaths", ${with cfg.workspace.wallpaperSlideShow; if ((builtins.isPath path) || (builtins.isString path)) then
|
||||
"\"" + (builtins.toString path) + "\"" else
|
||||
"[" + (builtins.concatStringsSep "," (map (s: "\"" + s + "\"") path)) + "]"});
|
||||
desktop.writeConfig("SlideInterval", "${builtins.toString cfg.workspace.wallpaperSlideShow.interval}");
|
||||
}
|
||||
'' else "");
|
||||
wallpaperPOTD = (if (cfg.workspace.wallpaperPictureOfTheDay != null) then ''
|
||||
// Wallpaper POTD
|
||||
let allDesktops = desktops();
|
||||
for (const desktop of allDesktops) {
|
||||
desktop.wallpaperPlugin = "org.kde.potd";
|
||||
desktop.currentConfigGroup = ["Wallpaper", "org.kde.potd", "General"];
|
||||
desktop.writeConfig("Provider", "${cfg.workspace.wallpaperPictureOfTheDay.provider}");
|
||||
desktop.writeConfig("UpdateOverMeteredConnection", "${if (cfg.workspace.wallpaperPictureOfTheDay.updateOverMeteredConnection) then "1" else "0"}");
|
||||
programs.plasma.startup.desktopScript."panels" = (
|
||||
lib.mkIf anyPanelSet (
|
||||
let
|
||||
anyNonDefaultScreens = ((builtins.any (panel: panel.screen != null)) cfg.panels);
|
||||
panelPreCMD = ''
|
||||
# We delete plasma-org.kde.plasma.desktop-appletsrc to hinder it
|
||||
# growing indefinitely. See:
|
||||
# https://github.com/nix-community/plasma-manager/issues/76
|
||||
[ -f ${config.xdg.configHome}/plasma-org.kde.plasma.desktop-appletsrc ] && rm ${config.xdg.configHome}/plasma-org.kde.plasma.desktop-appletsrc
|
||||
'';
|
||||
panelLayoutStr = (
|
||||
import ../lib/panel.nix {
|
||||
inherit lib;
|
||||
inherit config;
|
||||
}
|
||||
'' else "");
|
||||
wallpaperPlainColor = (if (cfg.workspace.wallpaperPlainColor != null) then ''
|
||||
// Wallpaper plain color
|
||||
let allDesktops = desktops();
|
||||
for (var desktopIndex = 0; desktopIndex < allDesktops.length; desktopIndex++) {
|
||||
var desktop = allDesktops[desktopIndex];
|
||||
desktop.wallpaperPlugin = "org.kde.color";
|
||||
desktop.currentConfigGroup = Array("Wallpaper", "org.kde.color", "General");
|
||||
desktop.writeConfig("Color", "${cfg.workspace.wallpaperPlainColor}");
|
||||
}
|
||||
'' else ""
|
||||
);
|
||||
in
|
||||
{
|
||||
preCommands = panelPreCMD;
|
||||
text = panelLayoutStr + wallpaperDesktopScript + wallpaperSlideShow + wallpaperPOTD + wallpaperPlainColor;
|
||||
postCommands = panelPostCMD + wallpaperPostCMD;
|
||||
restartServices =
|
||||
(lib.unique (if anyNonDefaultScreens then [ "plasma-plasmashell" ] else [ ])
|
||||
++ (lib.filter (service: shouldRestart service) (builtins.attrNames serviceRestarts)));
|
||||
priority = 2;
|
||||
}
|
||||
));
|
||||
});
|
||||
);
|
||||
panelPostCMD = (
|
||||
if anyNonDefaultScreens then
|
||||
''
|
||||
sed -i 's/^lastScreen\\x5b$i\\x5d=/lastScreen[$i]=/' ${config.xdg.configHome}/plasma-org.kde.plasma.desktop-appletsrc
|
||||
''
|
||||
else
|
||||
""
|
||||
);
|
||||
in
|
||||
{
|
||||
preCommands = panelPreCMD;
|
||||
text = panelLayoutStr;
|
||||
postCommands = panelPostCMD;
|
||||
restartServices = (
|
||||
lib.unique (if anyNonDefaultScreens then [ "plasma-plasmashell" ] else [ ])
|
||||
++ (lib.filter (service: shouldRestart service) (builtins.attrNames serviceRestarts))
|
||||
);
|
||||
priority = 2;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -2,107 +2,297 @@
|
||||
let
|
||||
cfg = config.programs.plasma;
|
||||
|
||||
# Values can be found at:
|
||||
# https://github.com/KDE/powerdevil/blob/master/daemon/powerdevilenums.h
|
||||
powerButtonActions = {
|
||||
nothing = 0;
|
||||
sleep = 1;
|
||||
hibernate = 2;
|
||||
shutDown = 8;
|
||||
lockScreen = 32;
|
||||
showLogoutScreen = null;
|
||||
showLogoutScreen = 16;
|
||||
turnOffScreen = 64;
|
||||
};
|
||||
|
||||
autoSuspendActions = {
|
||||
nothing = 0;
|
||||
sleep = null;
|
||||
hibernate = 2;
|
||||
sleep = 1;
|
||||
shutDown = 8;
|
||||
};
|
||||
in
|
||||
{
|
||||
config.assertions = [
|
||||
{
|
||||
assertion = (cfg.powerdevil.autoSuspend.action != autoSuspendActions.nothing || cfg.powerdevil.autoSuspend.idleTimeout == null);
|
||||
message = "Setting programs.plasma.powerdevil.autoSuspend.idleTimeout for autosuspend-action \"nothing\" is not supported.";
|
||||
}
|
||||
{
|
||||
assertion = (cfg.powerdevil.turnOffDisplay.idleTimeout != -1 || cfg.powerdevil.turnOffDisplay.idleTimeoutWhenLocked == null);
|
||||
message = "Setting programs.plasma.powerdevil.turnOffDisplay.idleTimeoutWhenLocked for when idleTimeout is \"never\" is not supported.";
|
||||
}
|
||||
];
|
||||
|
||||
options = {
|
||||
programs.plasma.powerdevil = {
|
||||
powerButtonAction = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum (builtins.attrNames powerButtonActions));
|
||||
whenSleepingEnterActions = {
|
||||
standby = 1;
|
||||
hybridSleep = 2;
|
||||
standbyThenHibernate = 3;
|
||||
};
|
||||
|
||||
whenLaptopLidClosedActions = {
|
||||
doNothing = 0;
|
||||
sleep = 1;
|
||||
hibernate = 2;
|
||||
shutdown = 8;
|
||||
lockScreen = 32;
|
||||
turnOffScreen = 64;
|
||||
};
|
||||
|
||||
# Since AC and battery allows the same options we create a function here which
|
||||
# can generate the options by just specifying the type (i.e. "AC" or
|
||||
# "battery").
|
||||
createPowerDevilOptions = type: {
|
||||
powerButtonAction = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum (builtins.attrNames powerButtonActions));
|
||||
default = null;
|
||||
example = "nothing";
|
||||
description = ''
|
||||
The action, when on ${type}, to perform when the power button is pressed.
|
||||
'';
|
||||
apply = action: if (action == null) then null else powerButtonActions."${action}";
|
||||
};
|
||||
autoSuspend = {
|
||||
action = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum (builtins.attrNames autoSuspendActions));
|
||||
default = null;
|
||||
example = "nothing";
|
||||
description = ''
|
||||
The action to perform when the power button is pressed.
|
||||
The action, when on ${type}, to perform after a certain period of inactivity.
|
||||
'';
|
||||
apply = action: if (action == null) then null else powerButtonActions."${action}";
|
||||
apply = action: if (action == null) then null else autoSuspendActions."${action}";
|
||||
};
|
||||
autoSuspend = {
|
||||
action = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum (builtins.attrNames autoSuspendActions));
|
||||
default = null;
|
||||
example = "nothing";
|
||||
description = ''
|
||||
The action to perform after a certain period of inactivity.
|
||||
'';
|
||||
apply = action: if (action == null) then null else autoSuspendActions."${action}";
|
||||
};
|
||||
idleTimeout = lib.mkOption {
|
||||
type = with lib.types; nullOr (ints.between 60 600000);
|
||||
default = null;
|
||||
example = 600;
|
||||
description = ''
|
||||
The duration (in seconds) the computer must be idle until the
|
||||
auto-suspend action is executed.
|
||||
'';
|
||||
};
|
||||
idleTimeout = lib.mkOption {
|
||||
type = with lib.types; nullOr (ints.between 60 600000);
|
||||
default = null;
|
||||
example = 600;
|
||||
description = ''
|
||||
The duration (in seconds), when on ${type}, the computer must be idle
|
||||
until the auto-suspend action is executed.
|
||||
'';
|
||||
};
|
||||
turnOffDisplay = {
|
||||
idleTimeout = lib.mkOption {
|
||||
type = with lib.types; nullOr (either (enum [ "never" ]) (ints.between 30 600000));
|
||||
};
|
||||
whenSleepingEnter = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum (builtins.attrNames whenSleepingEnterActions));
|
||||
default = null;
|
||||
example = "standbyThenHibernate";
|
||||
description = ''
|
||||
The state, when on ${type}, to enter when sleeping.
|
||||
'';
|
||||
apply = action: if (action == null) then null else whenSleepingEnterActions."${action}";
|
||||
};
|
||||
whenLaptopLidClosed = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum (builtins.attrNames whenLaptopLidClosedActions));
|
||||
default = null;
|
||||
example = "shutdown";
|
||||
description = ''
|
||||
The action, when on ${type}, to perform when the laptop lid is closed.
|
||||
'';
|
||||
apply = action: if (action == null) then null else whenLaptopLidClosedActions."${action}";
|
||||
};
|
||||
inhibitLidActionWhenExternalMonitorConnected = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = ''
|
||||
If enabled, the lid action will be inhibited when an external monitor is connected.
|
||||
'';
|
||||
};
|
||||
turnOffDisplay = {
|
||||
idleTimeout = lib.mkOption {
|
||||
type = with lib.types; nullOr (either (enum [ "never" ]) (ints.between 30 600000));
|
||||
default = null;
|
||||
example = 300;
|
||||
description = ''
|
||||
The duration (in seconds), when on ${type}, the computer must be idle
|
||||
(when unlocked) until the display turns off.
|
||||
'';
|
||||
apply =
|
||||
timeout:
|
||||
if (timeout == null) then
|
||||
null
|
||||
else if (timeout == "never") then
|
||||
-1
|
||||
else
|
||||
timeout;
|
||||
};
|
||||
idleTimeoutWhenLocked = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (
|
||||
either (enum [
|
||||
"whenLockedAndUnlocked"
|
||||
"immediately"
|
||||
]) (ints.between 20 600000)
|
||||
);
|
||||
default = null;
|
||||
example = 60;
|
||||
description = ''
|
||||
The duration (in seconds), when on ${type}, the computer must be idle
|
||||
(when locked) until the display turns off.
|
||||
'';
|
||||
apply =
|
||||
timeout:
|
||||
if (timeout == null) then
|
||||
null
|
||||
else if (timeout == "whenLockedAndUnlocked") then
|
||||
-2
|
||||
else if (timeout == "immediately") then
|
||||
0
|
||||
else
|
||||
timeout;
|
||||
};
|
||||
};
|
||||
dimDisplay = {
|
||||
enable = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = false;
|
||||
description = "Enable or disable screen dimming.";
|
||||
};
|
||||
idleTimeout = lib.mkOption {
|
||||
type = with lib.types; nullOr (ints.between 20 600000);
|
||||
default = null;
|
||||
example = 300;
|
||||
description = ''
|
||||
The duration (in seconds), when on ${type}, the computer must be idle
|
||||
until the display starts dimming.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# By the same logic as createPowerDevilOptions, we can generate the
|
||||
# configuration. cfgSectName is here the name of the section in powerdevilrc,
|
||||
# while optionsName is the name of the "namespace" where we should draw the
|
||||
# options from (i.e. powerdevil.AC or powerdevil.battery).
|
||||
createPowerDevilConfig = cfgSectName: optionsName: {
|
||||
"${cfgSectName}/SuspendAndShutdown" = {
|
||||
PowerButtonAction = cfg.powerdevil.${optionsName}.powerButtonAction;
|
||||
AutoSuspendAction = cfg.powerdevil.${optionsName}.autoSuspend.action;
|
||||
AutoSuspendIdleTimeoutSec = cfg.powerdevil.${optionsName}.autoSuspend.idleTimeout;
|
||||
SleepMode = cfg.powerdevil.${optionsName}.whenSleepingEnter;
|
||||
LidAction = cfg.powerdevil.${optionsName}.whenLaptopLidClosed;
|
||||
InhibitLidActionWhenExternalMonitorPresent =
|
||||
cfg.powerdevil.${optionsName}.inhibitLidActionWhenExternalMonitorConnected;
|
||||
};
|
||||
"${cfgSectName}/Display" = {
|
||||
TurnOffDisplayIdleTimeoutSec = cfg.powerdevil.${optionsName}.turnOffDisplay.idleTimeout;
|
||||
TurnOffDisplayIdleTimeoutWhenLockedSec =
|
||||
cfg.powerdevil.${optionsName}.turnOffDisplay.idleTimeoutWhenLocked;
|
||||
DimDisplayWhenIdle =
|
||||
if (cfg.powerdevil.${optionsName}.dimDisplay.enable != null) then
|
||||
cfg.powerdevil.${optionsName}.dimDisplay.enable
|
||||
else if (cfg.powerdevil.${optionsName}.dimDisplay.idleTimeout != null) then
|
||||
true
|
||||
else
|
||||
null;
|
||||
DimDisplayIdleTimeoutSec = cfg.powerdevil.${optionsName}.dimDisplay.idleTimeout;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"powerdevil"
|
||||
"powerButtonAction"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"powerdevil"
|
||||
"AC"
|
||||
"powerButtonAction"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"powerdevil"
|
||||
"autoSuspend"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"powerdevil"
|
||||
"AC"
|
||||
"autoSuspend"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"powerdevil"
|
||||
"turnOffDisplay"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"powerdevil"
|
||||
"AC"
|
||||
"turnOffDisplay"
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
config.assertions =
|
||||
let
|
||||
createAssertions = type: [
|
||||
{
|
||||
assertion = (
|
||||
cfg.powerdevil.${type}.autoSuspend.action != autoSuspendActions.nothing
|
||||
|| cfg.powerdevil.${type}.autoSuspend.idleTimeout == null
|
||||
);
|
||||
message = "Setting programs.plasma.powerdevil.${type}.autoSuspend.idleTimeout for autosuspend-action \"nothing\" is not supported.";
|
||||
}
|
||||
{
|
||||
assertion = (
|
||||
cfg.powerdevil.${type}.turnOffDisplay.idleTimeout != -1
|
||||
|| cfg.powerdevil.${type}.turnOffDisplay.idleTimeoutWhenLocked == null
|
||||
);
|
||||
message = "Setting programs.plasma.powerdevil.${type}.turnOffDisplay.idleTimeoutWhenLocked for idleTimeout \"never\" is not supported.";
|
||||
}
|
||||
{
|
||||
assertion = (
|
||||
cfg.powerdevil.${type}.dimDisplay.enable != false
|
||||
|| cfg.powerdevil.${type}.dimDisplay.idleTimeout == null
|
||||
);
|
||||
message = "Cannot set programs.plasma.powerdevil.${type}.dimDisplay.idleTimeout when programs.plasma.powerdevil.${type}.dimDisplay.enable is disabled.";
|
||||
}
|
||||
];
|
||||
in
|
||||
(createAssertions "AC") ++ (createAssertions "battery") ++ (createAssertions "lowBattery");
|
||||
|
||||
options = {
|
||||
programs.plasma.powerdevil = {
|
||||
AC = (createPowerDevilOptions "AC");
|
||||
battery = (createPowerDevilOptions "battery");
|
||||
lowBattery = (createPowerDevilOptions "lowBattery");
|
||||
general = {
|
||||
pausePlayersOnSuspend = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = 300;
|
||||
example = false;
|
||||
description = ''
|
||||
The duration (in seconds) the computer must be idle (when unlocked)
|
||||
until the display turns off.
|
||||
If enabled, pause media players when the system is suspended.
|
||||
'';
|
||||
apply = timeout:
|
||||
if (timeout == null) then null else
|
||||
if (timeout == "never") then -1
|
||||
else timeout;
|
||||
};
|
||||
idleTimeoutWhenLocked = lib.mkOption {
|
||||
type = with lib.types; nullOr (either (enum [ "whenLockedAndUnlocked" "immediately" ]) (ints.between 20 600000));
|
||||
default = null;
|
||||
example = 60;
|
||||
description = ''
|
||||
The duration (in seconds) the computer must be idle (when locked)
|
||||
until the display turns off.
|
||||
'';
|
||||
apply = timeout:
|
||||
if (timeout == null) then null else
|
||||
if (timeout == "whenLockedAndUnlocked") then -2 else
|
||||
if (timeout == "immediately") then 0
|
||||
else timeout;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config.programs.plasma.configFile = lib.mkIf cfg.enable {
|
||||
powerdevilrc = lib.filterAttrs (k: v: v != null) {
|
||||
"AC/SuspendAndShutdown" = {
|
||||
PowerButtonAction = cfg.powerdevil.powerButtonAction;
|
||||
AutoSuspendAction = cfg.powerdevil.autoSuspend.action;
|
||||
AutoSuspendIdleTimeoutSec = cfg.powerdevil.autoSuspend.idleTimeout;
|
||||
};
|
||||
"AC/Display" = {
|
||||
TurnOffDisplayIdleTimeoutSec = cfg.powerdevil.turnOffDisplay.idleTimeout;
|
||||
TurnOffDisplayIdleTimeoutWhenLockedSec = cfg.powerdevil.turnOffDisplay.idleTimeoutWhenLocked;
|
||||
};
|
||||
};
|
||||
powerdevilrc = lib.filterAttrsRecursive (k: v: v != null) (
|
||||
(createPowerDevilConfig "AC" "AC")
|
||||
// (createPowerDevilConfig "Battery" "battery")
|
||||
// (createPowerDevilConfig "LowBattery" "lowBattery")
|
||||
// {
|
||||
General = {
|
||||
pausePlayersOnSuspend = cfg.powerdevil.general.pausePlayersOnSuspend;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -6,43 +6,57 @@ let
|
||||
|
||||
# Checks if the shortcut is in the "service" group, in which case we need to
|
||||
# write the values a little differently.
|
||||
isService = group:
|
||||
isService =
|
||||
group:
|
||||
let
|
||||
startString = "services/";
|
||||
in
|
||||
(builtins.substring 0 (builtins.stringLength startString) group) == startString;
|
||||
|
||||
# Convert one shortcut into a settings attribute set.
|
||||
shortcutToConfigValue = group: _action: skey:
|
||||
shortcutToConfigValue =
|
||||
group: _action: skey:
|
||||
let
|
||||
# Keys are expected to be a list:
|
||||
keys =
|
||||
if builtins.isList skey
|
||||
then
|
||||
if builtins.isList skey then
|
||||
(if ((builtins.length skey) == 0) then [ "none" ] else skey)
|
||||
else [ skey ];
|
||||
else
|
||||
[ skey ];
|
||||
|
||||
# Don't allow un-escaped commas:
|
||||
escape = lib.escape [ "," ];
|
||||
keysStr = (if ((builtins.length keys) == 1) then (escape (builtins.head keys)) else "\t" + (lib.concatStringsSep "\t" (map escape keys)));
|
||||
keysStr = (
|
||||
if ((builtins.length keys) == 1) then
|
||||
(escape (builtins.head keys))
|
||||
else
|
||||
"\t" + (lib.concatStringsSep "\t" (map escape keys))
|
||||
);
|
||||
in
|
||||
(if (isService group) then
|
||||
keysStr
|
||||
else
|
||||
(lib.concatStringsSep "," [
|
||||
(
|
||||
if (isService group) then
|
||||
keysStr
|
||||
"" # List of default keys, not needed.
|
||||
"" # Display string, not needed.
|
||||
]));
|
||||
else
|
||||
(lib.concatStringsSep "," [
|
||||
keysStr
|
||||
"" # List of default keys, not needed.
|
||||
"" # Display string, not needed.
|
||||
])
|
||||
);
|
||||
|
||||
shortcutsToSettings = groups:
|
||||
lib.mapAttrs
|
||||
(group: attrs: (lib.mapAttrs (shortcutToConfigValue group) attrs))
|
||||
groups;
|
||||
shortcutsToSettings =
|
||||
groups: lib.mapAttrs (group: attrs: (lib.mapAttrs (shortcutToConfigValue group) attrs)) groups;
|
||||
in
|
||||
{
|
||||
options.programs.plasma.shortcuts = lib.mkOption {
|
||||
type = with lib.types; attrsOf (attrsOf (oneOf [ (listOf str) str ]));
|
||||
type =
|
||||
with lib.types;
|
||||
attrsOf (
|
||||
attrsOf (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
])
|
||||
);
|
||||
default = { };
|
||||
description = ''
|
||||
An attribute set where the keys are application groups and the
|
||||
@ -51,7 +65,6 @@ in
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
programs.plasma.configFile."kglobalshortcutsrc" =
|
||||
shortcutsToSettings cfg.shortcuts;
|
||||
programs.plasma.configFile."kglobalshortcutsrc" = shortcutsToSettings cfg.shortcuts;
|
||||
};
|
||||
}
|
||||
|
@ -6,7 +6,12 @@ in
|
||||
{
|
||||
options.programs.plasma.spectacle.shortcuts = {
|
||||
captureActiveWindow = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Meta+Print";
|
||||
description = ''
|
||||
@ -15,7 +20,12 @@ in
|
||||
};
|
||||
|
||||
captureCurrentMonitor = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Print";
|
||||
description = ''
|
||||
@ -24,7 +34,12 @@ in
|
||||
};
|
||||
|
||||
captureEntireDesktop = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Shift+Print";
|
||||
description = ''
|
||||
@ -33,7 +48,12 @@ in
|
||||
};
|
||||
|
||||
captureRectangularRegion = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Meta+Shift+S";
|
||||
description = ''
|
||||
@ -42,7 +62,12 @@ in
|
||||
};
|
||||
|
||||
captureWindowUnderCursor = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Meta+Ctrl+Print";
|
||||
description = ''
|
||||
@ -51,7 +76,12 @@ in
|
||||
};
|
||||
|
||||
launch = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Meta+S";
|
||||
description = ''
|
||||
@ -60,7 +90,12 @@ in
|
||||
};
|
||||
|
||||
launchWithoutCapturing = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Meta+Alt+S";
|
||||
description = ''
|
||||
@ -69,7 +104,12 @@ in
|
||||
};
|
||||
|
||||
recordRegion = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Meta+Shift+R";
|
||||
description = ''
|
||||
@ -78,7 +118,12 @@ in
|
||||
};
|
||||
|
||||
recordScreen = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Meta+Alt+R";
|
||||
description = ''
|
||||
@ -87,7 +132,12 @@ in
|
||||
};
|
||||
|
||||
recordWindow = lib.mkOption {
|
||||
type = with lib.types; nullOr (oneOf [ (listOf str) str ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
(listOf str)
|
||||
str
|
||||
]);
|
||||
default = null;
|
||||
example = "Meta+Ctrl+R";
|
||||
description = ''
|
||||
@ -98,56 +148,34 @@ in
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
programs.plasma.shortcuts."org.kde.spectacle.desktop" = lib.mkMerge [
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.captureActiveWindow != null) {
|
||||
ActiveWindowScreenShot = cfg.spectacle.shortcuts.captureActiveWindow;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.captureCurrentMonitor != null) {
|
||||
CurrentMonitorScreenShot = cfg.spectacle.shortcuts.captureCurrentMonitor;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.captureEntireDesktop != null) {
|
||||
FullScreenScreenShot = cfg.spectacle.shortcuts.captureEntireDesktop;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.captureRectangularRegion != null) {
|
||||
RectangularRegionScreenShot = cfg.spectacle.shortcuts.captureRectangularRegion;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.captureWindowUnderCursor != null) {
|
||||
WindowUnderCursorScreenShot = cfg.spectacle.shortcuts.captureWindowUnderCursor;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.launch != null) {
|
||||
_launch = cfg.spectacle.shortcuts.launch;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.launchWithoutCapturing != null) {
|
||||
OpenWithoutScreenshot = cfg.spectacle.shortcuts.launchWithoutCapturing;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.recordRegion != null) {
|
||||
RecordRegion = cfg.spectacle.shortcuts.recordRegion;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.recordScreen != null) {
|
||||
RecordScreen = cfg.spectacle.shortcuts.recordScreen;
|
||||
}
|
||||
)
|
||||
(
|
||||
lib.mkIf (cfg.spectacle.shortcuts.recordWindow != null) {
|
||||
RecordWindow = cfg.spectacle.shortcuts.recordWindow;
|
||||
}
|
||||
)
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.captureActiveWindow != null) {
|
||||
ActiveWindowScreenShot = cfg.spectacle.shortcuts.captureActiveWindow;
|
||||
})
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.captureCurrentMonitor != null) {
|
||||
CurrentMonitorScreenShot = cfg.spectacle.shortcuts.captureCurrentMonitor;
|
||||
})
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.captureEntireDesktop != null) {
|
||||
FullScreenScreenShot = cfg.spectacle.shortcuts.captureEntireDesktop;
|
||||
})
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.captureRectangularRegion != null) {
|
||||
RectangularRegionScreenShot = cfg.spectacle.shortcuts.captureRectangularRegion;
|
||||
})
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.captureWindowUnderCursor != null) {
|
||||
WindowUnderCursorScreenShot = cfg.spectacle.shortcuts.captureWindowUnderCursor;
|
||||
})
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.launch != null) { _launch = cfg.spectacle.shortcuts.launch; })
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.launchWithoutCapturing != null) {
|
||||
OpenWithoutScreenshot = cfg.spectacle.shortcuts.launchWithoutCapturing;
|
||||
})
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.recordRegion != null) {
|
||||
RecordRegion = cfg.spectacle.shortcuts.recordRegion;
|
||||
})
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.recordScreen != null) {
|
||||
RecordScreen = cfg.spectacle.shortcuts.recordScreen;
|
||||
})
|
||||
(lib.mkIf (cfg.spectacle.shortcuts.recordWindow != null) {
|
||||
RecordWindow = cfg.spectacle.shortcuts.recordWindow;
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
|
@ -19,12 +19,22 @@ let
|
||||
default = [ ];
|
||||
description = "Services to restart after the script has been run.";
|
||||
};
|
||||
runAlwaysOption = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
example = true;
|
||||
description = ''
|
||||
When enabled the script will run even if no changes have been made
|
||||
since last successful run.
|
||||
'';
|
||||
};
|
||||
|
||||
startupScriptType = lib.types.submodule {
|
||||
options = {
|
||||
text = textOption;
|
||||
priority = priorityOption;
|
||||
restartServices = restartServicesOption;
|
||||
runAlways = runAlwaysOption;
|
||||
};
|
||||
};
|
||||
desktopScriptType = lib.types.submodule {
|
||||
@ -32,6 +42,7 @@ let
|
||||
text = textOption;
|
||||
priority = priorityOption;
|
||||
restartServices = restartServicesOption;
|
||||
runAlways = runAlwaysOption;
|
||||
preCommands = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Commands to run before the desktop script lines.";
|
||||
@ -45,25 +56,46 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
createScriptContentRunOnce = name: sha256sumFile: script: text: ''
|
||||
last_update="$(sha256sum ${sha256sumFile})"
|
||||
last_update_file=${config.xdg.dataHome}/plasma-manager/last_run_${name}
|
||||
if [ -f "$last_update_file" ]; then
|
||||
stored_last_update=$(cat "$last_update_file")
|
||||
fi
|
||||
|
||||
if ! [ "$last_update" = "$stored_last_update" ]; then
|
||||
echo "Running script: ${name}"
|
||||
success=1
|
||||
trap 'success=0' ERR
|
||||
${text}
|
||||
if [ $success -eq 1 ]; then
|
||||
echo "$last_update" > "$last_update_file"
|
||||
${
|
||||
builtins.concatStringsSep "\n" (
|
||||
map (
|
||||
s: "echo ${s} >> ${config.xdg.dataHome}/plasma-manager/services_to_restart"
|
||||
) script.restartServices
|
||||
)
|
||||
}
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
|
||||
createScriptContentRunAlways = name: text: ''
|
||||
echo "Running script: ${name}"
|
||||
${text}
|
||||
'';
|
||||
|
||||
createScriptContent = name: sha256sumFile: script: text: {
|
||||
"plasma-manager/${cfg.startup.scriptsDir}/${builtins.toString script.priority}_${name}.sh" = {
|
||||
text = ''
|
||||
#!/bin/sh
|
||||
last_update="$(sha256sum ${sha256sumFile})"
|
||||
last_update_file=${config.xdg.dataHome}/plasma-manager/last_run_${name}
|
||||
if [ -f "$last_update_file" ]; then
|
||||
stored_last_update=$(cat "$last_update_file")
|
||||
fi
|
||||
|
||||
if ! [ "$last_update" = "$stored_last_update" ]; then
|
||||
success=1
|
||||
trap 'success=0' ERR
|
||||
${text}
|
||||
if [ $success -eq 1 ]; then
|
||||
echo "$last_update" > "$last_update_file"
|
||||
${builtins.concatStringsSep "\n" (map (s: "echo ${s} >> ${config.xdg.dataHome}/plasma-manager/services_to_restart") script.restartServices)}
|
||||
fi
|
||||
fi
|
||||
${
|
||||
if script.runAlways then
|
||||
(createScriptContentRunAlways name text)
|
||||
else
|
||||
(createScriptContentRunOnce name sha256sumFile script text)
|
||||
}
|
||||
'';
|
||||
executable = true;
|
||||
};
|
||||
@ -102,81 +134,114 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config.xdg = lib.mkIf
|
||||
(cfg.enable &&
|
||||
(builtins.length (builtins.attrNames cfg.startup.startupScript) != 0 ||
|
||||
(builtins.length (builtins.attrNames cfg.startup.desktopScript)) != 0))
|
||||
{
|
||||
dataFile = lib.mkMerge [
|
||||
# Autostart scripts
|
||||
(lib.mkMerge
|
||||
(lib.mapAttrsToList
|
||||
(name: script: createScriptContent name "$0" script script.text)
|
||||
cfg.startup.startupScript))
|
||||
# Desktop scripts
|
||||
(lib.mkMerge
|
||||
((lib.mapAttrsToList
|
||||
(name: script:
|
||||
let layoutScriptPath = "${config.xdg.dataHome}/plasma-manager/${cfg.startup.dataDir}/desktop_script_${name}.js";
|
||||
in createScriptContent "desktop_script_${name}" layoutScriptPath script
|
||||
''
|
||||
${script.preCommands}
|
||||
qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "$(cat ${layoutScriptPath})"
|
||||
${script.postCommands}
|
||||
'')
|
||||
cfg.startup.desktopScript) ++
|
||||
(lib.mapAttrsToList
|
||||
(name: content: {
|
||||
config.xdg =
|
||||
lib.mkIf
|
||||
(
|
||||
cfg.enable
|
||||
&& (
|
||||
builtins.length (builtins.attrNames cfg.startup.startupScript) != 0
|
||||
|| (builtins.length (builtins.attrNames cfg.startup.desktopScript)) != 0
|
||||
)
|
||||
)
|
||||
{
|
||||
dataFile = lib.mkMerge [
|
||||
# Autostart scripts
|
||||
(lib.mkMerge (
|
||||
lib.mapAttrsToList (
|
||||
name: script: createScriptContent "script_${name}" "$0" script script.text
|
||||
) cfg.startup.startupScript
|
||||
))
|
||||
# Desktop scripts
|
||||
(lib.mkMerge (
|
||||
(lib.mapAttrsToList (
|
||||
name: script:
|
||||
let
|
||||
layoutScriptPath = "${config.xdg.dataHome}/plasma-manager/${cfg.startup.dataDir}/desktop_script_${name}.js";
|
||||
in
|
||||
createScriptContent "desktop_script_${name}" layoutScriptPath script ''
|
||||
${script.preCommands}
|
||||
qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "$(cat ${layoutScriptPath})"
|
||||
${script.postCommands}
|
||||
''
|
||||
) cfg.startup.desktopScript)
|
||||
++ (lib.mapAttrsToList (name: content: {
|
||||
"plasma-manager/${cfg.startup.dataDir}/desktop_script_${name}.js" = {
|
||||
text = content.text;
|
||||
};
|
||||
})
|
||||
cfg.startup.desktopScript)))
|
||||
# Datafiles
|
||||
(lib.mkMerge
|
||||
(lib.mapAttrsToList
|
||||
(name: content: {
|
||||
}) cfg.startup.desktopScript)
|
||||
))
|
||||
# Datafiles
|
||||
(lib.mkMerge (
|
||||
lib.mapAttrsToList (name: content: {
|
||||
"plasma-manager/${cfg.startup.dataDir}/${name}" = {
|
||||
text = content;
|
||||
};
|
||||
})
|
||||
cfg.startup.dataFile)
|
||||
)
|
||||
# Autostart script runner
|
||||
{
|
||||
"plasma-manager/${topScriptName}" = {
|
||||
text = ''
|
||||
#!/bin/sh
|
||||
}) cfg.startup.dataFile
|
||||
))
|
||||
# Autostart script runner
|
||||
{
|
||||
"plasma-manager/${topScriptName}" = {
|
||||
text = ''
|
||||
#!/bin/sh
|
||||
|
||||
services_restart_file="${config.xdg.dataHome}/plasma-manager/services_to_restart"
|
||||
services_restart_file="${config.xdg.dataHome}/plasma-manager/services_to_restart"
|
||||
|
||||
# Reset the file keeping track of which scripts to restart.
|
||||
# Technically can be put at the end as well (maybe better, at
|
||||
# least assuming the file hasn't been tampered with of some sort).
|
||||
if [ -f $services_restart_file ]; then rm $services_restart_file; fi
|
||||
# Reset the file keeping track of which scripts to restart.
|
||||
# Technically can be put at the end as well (maybe better, at
|
||||
# least assuming the file hasn't been tampered with of some sort).
|
||||
if [ -f $services_restart_file ]; then rm $services_restart_file; fi
|
||||
|
||||
for script in ${config.xdg.dataHome}/plasma-manager/${cfg.startup.scriptsDir}/*.sh; do
|
||||
for script in ${config.xdg.dataHome}/plasma-manager/${cfg.startup.scriptsDir}/*.sh; do
|
||||
[ -x "$script" ] && $script
|
||||
done
|
||||
|
||||
# Restart the services
|
||||
if [ -f $services_restart_file ]; then
|
||||
for service in $(sort $services_restart_file | uniq); do
|
||||
systemctl --user restart $service
|
||||
done
|
||||
fi
|
||||
'';
|
||||
executable = true;
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
configFile."autostart/plasma-manager-autostart.desktop".text = ''
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Plasma Manager theme application
|
||||
Exec=${config.xdg.dataHome}/plasma-manager/${topScriptName}
|
||||
X-KDE-autostart-condition=ksmserver
|
||||
'';
|
||||
};
|
||||
# Restart the services
|
||||
if [ -f $services_restart_file ]; then
|
||||
for service in $(sort $services_restart_file | uniq); do
|
||||
systemctl --user restart $service
|
||||
done
|
||||
fi
|
||||
'';
|
||||
executable = true;
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
configFile."autostart/plasma-manager-autostart.desktop".text = ''
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Plasma Manager theme application
|
||||
Exec=${config.xdg.dataHome}/plasma-manager/${topScriptName}
|
||||
X-KDE-autostart-condition=ksmserver
|
||||
'';
|
||||
};
|
||||
|
||||
# Due to the fact that running certain desktop-scripts can reset what has
|
||||
# been applied by other desktop-script (for example running the panel
|
||||
# desktop-script will reset the wallpaper), we make it so that if any of the
|
||||
# desktop-scripts have been modified, that we must re-run all the
|
||||
# desktop-scripts, not just the ones who have been changed.
|
||||
config.programs.plasma.startup.startupScript."reset_lastrun_desktopscripts" =
|
||||
lib.mkIf (cfg.startup.desktopScript != { })
|
||||
{
|
||||
text = ''
|
||||
should_reset=0
|
||||
for ds in ${config.xdg.dataHome}/plasma-manager/data/desktop_script_*.js; do
|
||||
ds_name="$(basename $ds)"
|
||||
ds_name="''${ds_name%.js}"
|
||||
ds_shafile="${config.xdg.dataHome}/plasma-manager/last_run_"$ds_name
|
||||
|
||||
if ! [ -f "$ds_shafile" ]; then
|
||||
echo "Resetting desktop-script last_run-files since $ds_name is a new desktop-script"
|
||||
should_reset=1
|
||||
elif ! [ "$(cat $ds_shafile)" = "$(sha256sum $ds)" ]; then
|
||||
echo "Resetting desktop-script last_run-files since $ds_name has changed content"
|
||||
should_reset=1
|
||||
fi
|
||||
done
|
||||
|
||||
[ $should_reset = 1 ] && rm ${config.xdg.dataHome}/plasma-manager/last_run_desktop_script_*
|
||||
'';
|
||||
runAlways = true;
|
||||
};
|
||||
}
|
||||
|
64
modules/widgets/app-menu.nix
Normal file
64
modules/widgets/app-menu.nix
Normal file
@ -0,0 +1,64 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption =
|
||||
description:
|
||||
lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
inherit description;
|
||||
};
|
||||
in
|
||||
{
|
||||
appMenu = {
|
||||
opts = {
|
||||
position = lib.mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 100;
|
||||
vertical = 300;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = lib.mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 50;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
compactView = mkBoolOption "Whether to show the app menu in a compact view";
|
||||
settings = lib.mkOption {
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
Appearance = {
|
||||
compactView = true;
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
Extra configuration for the widget
|
||||
'';
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
|
||||
convert =
|
||||
{
|
||||
position,
|
||||
size,
|
||||
compactView,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.appmenu";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) { inherit compactView; };
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
}
|
@ -1,14 +1,19 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption = description: lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
mkBoolOption =
|
||||
description:
|
||||
lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
|
||||
convertHorizontalAlignment = horizontalAlignment:
|
||||
convertHorizontalAlignment =
|
||||
horizontalAlignment:
|
||||
let
|
||||
mappings = {
|
||||
left = 1;
|
||||
@ -17,9 +22,13 @@ let
|
||||
justify = 8;
|
||||
};
|
||||
in
|
||||
if horizontalAlignment == null then
|
||||
null
|
||||
else
|
||||
mappings.${horizontalAlignment} or (throw "Invalid enum value: ${horizontalAlignment}");
|
||||
|
||||
convertVerticalAlignment = verticalAlignment:
|
||||
convertVerticalAlignment =
|
||||
verticalAlignment:
|
||||
let
|
||||
mappings = {
|
||||
top = 1;
|
||||
@ -28,14 +37,17 @@ let
|
||||
baseline = 256;
|
||||
};
|
||||
in
|
||||
if verticalAlignment == null then
|
||||
null
|
||||
else
|
||||
mappings.${verticalAlignment} or (throw "Invalid enum value: ${verticalAlignment}");
|
||||
|
||||
getIndexFromEnum = enum: value:
|
||||
if value == null
|
||||
then null
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex
|
||||
(x: x == value)
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (application-title-bar widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
@ -43,8 +55,15 @@ let
|
||||
options = {
|
||||
bold = mkBoolOption "Enable bold text.";
|
||||
fit =
|
||||
let enumVals = [ "fixedSize" "horizontalFit" "verticalFit" "fit" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"fixedSize"
|
||||
"horizontalFit"
|
||||
"verticalFit"
|
||||
"fit"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "fixedSize";
|
||||
@ -86,9 +105,14 @@ let
|
||||
|
||||
titleReplacementType = types.submodule {
|
||||
options = {
|
||||
type =
|
||||
let enumVals = [ "string" "regexp" ];
|
||||
in mkOption {
|
||||
type =
|
||||
let
|
||||
enumVals = [
|
||||
"string"
|
||||
"regexp"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.enum enumVals;
|
||||
default = null;
|
||||
example = "string";
|
||||
@ -113,6 +137,22 @@ in
|
||||
description = "KDE plasmoid with window title and buttons";
|
||||
|
||||
opts = {
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 100;
|
||||
vertical = 300;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 50;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
layout = {
|
||||
widgetMargins = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
@ -125,20 +165,42 @@ in
|
||||
description = "The spacing between elements.";
|
||||
};
|
||||
horizontalAlignment = mkOption {
|
||||
type = types.enum [ "left" "right" "center" "justify" ];
|
||||
default = "left";
|
||||
type = types.nullOr (
|
||||
types.enum [
|
||||
"left"
|
||||
"right"
|
||||
"center"
|
||||
"justify"
|
||||
]
|
||||
);
|
||||
default = null;
|
||||
example = "left";
|
||||
description = "The horizontal alignment of the widget.";
|
||||
apply = convertHorizontalAlignment;
|
||||
};
|
||||
verticalAlignment = mkOption {
|
||||
type = types.enum [ "top" "center" "bottom" "baseline" ];
|
||||
default = "center";
|
||||
type = types.nullOr (
|
||||
types.enum [
|
||||
"top"
|
||||
"center"
|
||||
"bottom"
|
||||
"baseline"
|
||||
]
|
||||
);
|
||||
default = null;
|
||||
example = "center";
|
||||
description = "The vertical alignment of the widget.";
|
||||
apply = convertVerticalAlignment;
|
||||
};
|
||||
showDisabledElements =
|
||||
let enumVals = [ "deactivated" "hideKeepSpace" "hide" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"deactivated"
|
||||
"hideKeepSpace"
|
||||
"hide"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "deactivated";
|
||||
@ -147,17 +209,21 @@ in
|
||||
};
|
||||
fillFreeSpace = mkBoolOption "Whether the widget should fill the free space on the panel.";
|
||||
elements = mkOption {
|
||||
type = types.nullOr (types.listOf (types.enum [
|
||||
"windowCloseButton"
|
||||
"windowMinimizeButton"
|
||||
"windowMaximizeButton"
|
||||
"windowKeepAboveButton"
|
||||
"windowKeepBelowButton"
|
||||
"windowShadeButton"
|
||||
"windowTitle"
|
||||
"windowIcon"
|
||||
"spacer"
|
||||
]));
|
||||
type = types.nullOr (
|
||||
types.listOf (
|
||||
types.enum [
|
||||
"windowCloseButton"
|
||||
"windowMinimizeButton"
|
||||
"windowMaximizeButton"
|
||||
"windowKeepAboveButton"
|
||||
"windowKeepBelowButton"
|
||||
"windowShadeButton"
|
||||
"windowTitle"
|
||||
"windowIcon"
|
||||
"spacer"
|
||||
]
|
||||
)
|
||||
);
|
||||
default = null;
|
||||
example = [ "windowTitle" ];
|
||||
description = ''
|
||||
@ -167,8 +233,15 @@ in
|
||||
};
|
||||
windowControlButtons = {
|
||||
iconSource =
|
||||
let enumVals = [ "plasma" "breeze" "aurorae" "oxygen" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"plasma"
|
||||
"breeze"
|
||||
"aurorae"
|
||||
"oxygen"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "plasma";
|
||||
@ -223,11 +296,13 @@ in
|
||||
size = 11;
|
||||
};
|
||||
description = "The font settings of the window title.";
|
||||
apply = font: lib.optionalAttrs (font != null) {
|
||||
windowTitleFontBold = font.bold;
|
||||
windowTitleFontSize = font.size;
|
||||
windowTitleFontSizeMode = font.fit;
|
||||
};
|
||||
apply =
|
||||
font:
|
||||
lib.optionalAttrs (font != null) {
|
||||
windowTitleFontBold = font.bold;
|
||||
windowTitleFontSize = font.size;
|
||||
windowTitleFontSizeMode = font.fit;
|
||||
};
|
||||
};
|
||||
hideEmptyTitle = mkBoolOption "Whether to hide the window title when it's empty.";
|
||||
undefinedWindowTitle = mkOption {
|
||||
@ -236,8 +311,15 @@ in
|
||||
description = "The text to show when the window title is undefined.";
|
||||
};
|
||||
source =
|
||||
let enumVals = [ "appName" "decoration" "genericAppName" "alwaysUndefined" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"appName"
|
||||
"decoration"
|
||||
"genericAppName"
|
||||
"alwaysUndefined"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "appName";
|
||||
@ -261,28 +343,34 @@ in
|
||||
bottom = 0;
|
||||
};
|
||||
description = "The margins around the window title.";
|
||||
apply = margins: lib.optionalAttrs (margins != null) {
|
||||
windowTitleMarginsLeft = margins.left;
|
||||
windowTitleMarginsRight = margins.right;
|
||||
windowTitleMarginsTop = margins.top;
|
||||
windowTitleMarginsBottom = margins.bottom;
|
||||
};
|
||||
apply =
|
||||
margins:
|
||||
lib.optionalAttrs (margins != null) {
|
||||
windowTitleMarginsLeft = margins.left;
|
||||
windowTitleMarginsRight = margins.right;
|
||||
windowTitleMarginsTop = margins.top;
|
||||
windowTitleMarginsBottom = margins.bottom;
|
||||
};
|
||||
};
|
||||
};
|
||||
overrideForMaximized = {
|
||||
enable = mkBoolOption "Whether to override the settings for maximized windows.";
|
||||
elements = mkOption {
|
||||
type = types.nullOr (types.listOf (types.enum [
|
||||
"windowCloseButton"
|
||||
"windowMinimizeButton"
|
||||
"windowMaximizeButton"
|
||||
"windowKeepAboveButton"
|
||||
"windowKeepBelowButton"
|
||||
"windowShadeButton"
|
||||
"windowTitle"
|
||||
"windowIcon"
|
||||
"spacer"
|
||||
]));
|
||||
type = types.nullOr (
|
||||
types.listOf (
|
||||
types.enum [
|
||||
"windowCloseButton"
|
||||
"windowMinimizeButton"
|
||||
"windowMaximizeButton"
|
||||
"windowKeepAboveButton"
|
||||
"windowKeepBelowButton"
|
||||
"windowShadeButton"
|
||||
"windowTitle"
|
||||
"windowIcon"
|
||||
"spacer"
|
||||
]
|
||||
)
|
||||
);
|
||||
default = null;
|
||||
example = [ "windowTitle" ];
|
||||
description = ''
|
||||
@ -291,7 +379,12 @@ in
|
||||
};
|
||||
source =
|
||||
let
|
||||
enumVals = [ "appName" "decoration" "genericAppName" "alwaysUndefined" ];
|
||||
enumVals = [
|
||||
"appName"
|
||||
"decoration"
|
||||
"genericAppName"
|
||||
"alwaysUndefined"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
@ -310,8 +403,14 @@ in
|
||||
};
|
||||
behavior = {
|
||||
activeTaskSource =
|
||||
let enumVals = [ "activeTask" "lastActiveTask" "lastActiveMaximized" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"activeTask"
|
||||
"lastActiveTask"
|
||||
"lastActiveMaximized"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "activeTask";
|
||||
@ -431,14 +530,16 @@ in
|
||||
}
|
||||
];
|
||||
description = "The replacements for the window title.";
|
||||
apply = replacements: lib.optionalAttrs (replacements != null) {
|
||||
titleReplacementsPatterns = map (r: r.originalTitle) replacements;
|
||||
titleReplacementsTemplates = map (r: r.newTitle) replacements;
|
||||
titleReplacementsTypes = map (r: r.type) replacements;
|
||||
};
|
||||
apply =
|
||||
replacements:
|
||||
lib.optionalAttrs (replacements != null) {
|
||||
titleReplacementsPatterns = map (r: r.originalTitle) replacements;
|
||||
titleReplacementsTemplates = map (r: r.newTitle) replacements;
|
||||
titleReplacementsTypes = map (r: r.type) replacements;
|
||||
};
|
||||
};
|
||||
settings = mkOption {
|
||||
type = with types; nullOr (attrsOf (attrsOf (either (oneOf [ bool float int str ]) (listOf (oneOf [ bool float int str ])))));
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
Appearance = {
|
||||
@ -450,21 +551,25 @@ in
|
||||
|
||||
See available options at https://github.com/antroids/application-title-bar/blob/main/package/contents/config/main.xml
|
||||
'';
|
||||
apply = settings: if settings == null then {} else settings;
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
convert =
|
||||
{ layout
|
||||
, windowControlButtons
|
||||
, windowTitle
|
||||
, overrideForMaximized
|
||||
, behavior
|
||||
, mouseAreaDrag
|
||||
, mouseAreaClick
|
||||
, mouseAreaWheel
|
||||
, titleReplacements
|
||||
, settings
|
||||
}: {
|
||||
{
|
||||
position,
|
||||
size,
|
||||
layout,
|
||||
windowControlButtons,
|
||||
windowTitle,
|
||||
overrideForMaximized,
|
||||
behavior,
|
||||
mouseAreaDrag,
|
||||
mouseAreaClick,
|
||||
mouseAreaWheel,
|
||||
titleReplacements,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "com.github.antroids.application-title-bar";
|
||||
config = lib.recursiveUpdate {
|
||||
Appearance = lib.filterAttrs (_: v: v != null) (
|
||||
@ -500,42 +605,40 @@ in
|
||||
// windowTitle.font
|
||||
// windowTitle.margins
|
||||
);
|
||||
Behavior = lib.filterAttrs (_: v: v != null) (
|
||||
{
|
||||
# Behavior
|
||||
widgetActiveTaskSource = behavior.activeTaskSource;
|
||||
widgetActiveTaskFilterByActivity = behavior.filterByActivity;
|
||||
widgetActiveTaskFilterByScreen = behavior.filterByScreen;
|
||||
widgetActiveTaskFilterByVirtualDesktop = behavior.filterByVirtualDesktop;
|
||||
widgetActiveTaskFilterNotMaximized = behavior.disableForNotMaximized;
|
||||
disableButtonsForNotHoveredWidget = behavior.disableButtonsForNotHovered;
|
||||
Behavior = lib.filterAttrs (_: v: v != null) ({
|
||||
# Behavior
|
||||
widgetActiveTaskSource = behavior.activeTaskSource;
|
||||
widgetActiveTaskFilterByActivity = behavior.filterByActivity;
|
||||
widgetActiveTaskFilterByScreen = behavior.filterByScreen;
|
||||
widgetActiveTaskFilterByVirtualDesktop = behavior.filterByVirtualDesktop;
|
||||
widgetActiveTaskFilterNotMaximized = behavior.disableForNotMaximized;
|
||||
disableButtonsForNotHoveredWidget = behavior.disableButtonsForNotHovered;
|
||||
|
||||
# Mouse area drag
|
||||
windowTitleDragEnabled = mouseAreaDrag.enable;
|
||||
windowTitleDragOnlyMaximized = mouseAreaDrag.onlyMaximized;
|
||||
windowTitleDragThreshold = mouseAreaDrag.threshold;
|
||||
widgetMouseAreaLeftDragAction = mouseAreaDrag.leftDragAction;
|
||||
widgetMouseAreaMiddleDragAction = mouseAreaDrag.middleDragAction;
|
||||
# Mouse area drag
|
||||
windowTitleDragEnabled = mouseAreaDrag.enable;
|
||||
windowTitleDragOnlyMaximized = mouseAreaDrag.onlyMaximized;
|
||||
windowTitleDragThreshold = mouseAreaDrag.threshold;
|
||||
widgetMouseAreaLeftDragAction = mouseAreaDrag.leftDragAction;
|
||||
widgetMouseAreaMiddleDragAction = mouseAreaDrag.middleDragAction;
|
||||
|
||||
# Mouse area click
|
||||
widgetMouseAreaClickEnabled = mouseAreaClick.enable;
|
||||
widgetMouseAreaLeftClickAction = mouseAreaClick.leftButtonClick;
|
||||
widgetMouseAreaLeftDoubleClickAction = mouseAreaClick.leftButtonDoubleClick;
|
||||
widgetMouseAreaLeftLongPressAction = mouseAreaClick.leftButtonLongClick;
|
||||
widgetMouseAreaMiddleClickAction = mouseAreaClick.middleButtonClick;
|
||||
widgetMouseAreaMiddleDoubleClickAction = mouseAreaClick.middleButtonDoubleClick;
|
||||
widgetMouseAreaMiddleLongPressAction = mouseAreaClick.middleButtonLongClick;
|
||||
# Mouse area click
|
||||
widgetMouseAreaClickEnabled = mouseAreaClick.enable;
|
||||
widgetMouseAreaLeftClickAction = mouseAreaClick.leftButtonClick;
|
||||
widgetMouseAreaLeftDoubleClickAction = mouseAreaClick.leftButtonDoubleClick;
|
||||
widgetMouseAreaLeftLongPressAction = mouseAreaClick.leftButtonLongClick;
|
||||
widgetMouseAreaMiddleClickAction = mouseAreaClick.middleButtonClick;
|
||||
widgetMouseAreaMiddleDoubleClickAction = mouseAreaClick.middleButtonDoubleClick;
|
||||
widgetMouseAreaMiddleLongPressAction = mouseAreaClick.middleButtonLongClick;
|
||||
|
||||
# Mouse area wheel
|
||||
widgetMouseAreaWheelEnabled = mouseAreaWheel.enable;
|
||||
widgetMouseAreaWheelFirstEventDistance = mouseAreaWheel.firstEventDistance;
|
||||
widgetMouseAreaWheelNextEventDistance = mouseAreaWheel.nextEventDistance;
|
||||
widgetMouseAreaWheelUpAction = mouseAreaWheel.wheelUp;
|
||||
widgetMouseAreaWheelDownAction = mouseAreaWheel.wheelDown;
|
||||
widgetMouseAreaWheelLeftAction = mouseAreaWheel.wheelLeft;
|
||||
widgetMouseAreaWheelRightAction = mouseAreaWheel.wheelRight;
|
||||
}
|
||||
);
|
||||
# Mouse area wheel
|
||||
widgetMouseAreaWheelEnabled = mouseAreaWheel.enable;
|
||||
widgetMouseAreaWheelFirstEventDistance = mouseAreaWheel.firstEventDistance;
|
||||
widgetMouseAreaWheelNextEventDistance = mouseAreaWheel.nextEventDistance;
|
||||
widgetMouseAreaWheelUpAction = mouseAreaWheel.wheelUp;
|
||||
widgetMouseAreaWheelDownAction = mouseAreaWheel.wheelDown;
|
||||
widgetMouseAreaWheelLeftAction = mouseAreaWheel.wheelLeft;
|
||||
widgetMouseAreaWheelRightAction = mouseAreaWheel.wheelRight;
|
||||
});
|
||||
TitleReplacements = titleReplacements;
|
||||
} settings;
|
||||
};
|
||||
|
@ -1,9 +1,30 @@
|
||||
{ lib, ... }: {
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
in
|
||||
{
|
||||
battery = {
|
||||
description = "The battery indicator widget.";
|
||||
|
||||
# See https://invent.kde.org/plasma/plasma-workspace/-/blob/master/applets/batterymonitor/package/contents/config/main.xml for the accepted raw options
|
||||
opts = {
|
||||
position = lib.mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = lib.mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
showPercentage = lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
@ -11,24 +32,29 @@
|
||||
description = "Enable to show the battery percentage as a small label over the battery icon.";
|
||||
};
|
||||
settings = lib.mkOption {
|
||||
type = with lib.types; nullOr (attrsOf (attrsOf (either (oneOf [ bool float int str ]) (listOf (oneOf [ bool float int str ])))));
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
showPercentage = true;
|
||||
};
|
||||
};
|
||||
apply = settings: if settings == null then {} else settings;
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
|
||||
convert = { showPercentage, settings }: {
|
||||
name = "org.kde.plasma.battery";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) {
|
||||
inherit showPercentage;
|
||||
};
|
||||
} settings;
|
||||
};
|
||||
convert =
|
||||
{
|
||||
position,
|
||||
size,
|
||||
showPercentage,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.battery";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) { inherit showPercentage; };
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,28 +1,68 @@
|
||||
{ lib, ... } @ args:
|
||||
{ lib, ... }@args:
|
||||
let
|
||||
args' = args // {
|
||||
widgets = self;
|
||||
};
|
||||
|
||||
sources = lib.attrsets.mergeAttrsList (map (s: import s args') [
|
||||
./application-title-bar.nix
|
||||
./battery.nix
|
||||
./digital-clock.nix
|
||||
./icon-tasks.nix
|
||||
./kickoff.nix
|
||||
./plasmusic-toolbar.nix
|
||||
./system-monitor.nix
|
||||
./system-tray.nix
|
||||
]);
|
||||
sources = lib.attrsets.mergeAttrsList (
|
||||
map (s: import s args') [
|
||||
./app-menu.nix
|
||||
./application-title-bar.nix
|
||||
./battery.nix
|
||||
./digital-clock.nix
|
||||
./icon-tasks.nix
|
||||
./keyboard-layout.nix
|
||||
./kicker.nix
|
||||
./kickerdash.nix
|
||||
./kickoff.nix
|
||||
./panel-spacer.nix
|
||||
./plasma-panel-colorizer.nix
|
||||
./plasmusic-toolbar.nix
|
||||
./system-monitor.nix
|
||||
./system-tray.nix
|
||||
]
|
||||
);
|
||||
|
||||
positionType = lib.types.submodule {
|
||||
options = {
|
||||
horizontal = lib.mkOption {
|
||||
type = lib.types.ints.unsigned;
|
||||
example = 500;
|
||||
description = "The horizontal position of the widget.";
|
||||
};
|
||||
vertical = lib.mkOption {
|
||||
type = lib.types.ints.unsigned;
|
||||
example = 500;
|
||||
description = "The vertical position of the widget.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sizeType = lib.types.submodule {
|
||||
options = {
|
||||
width = lib.mkOption {
|
||||
type = lib.types.ints.unsigned;
|
||||
example = 500;
|
||||
description = "The width of the widget.";
|
||||
};
|
||||
height = lib.mkOption {
|
||||
type = lib.types.ints.unsigned;
|
||||
example = 500;
|
||||
description = "The height of the widget.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
compositeWidgetType = lib.pipe sources [
|
||||
(builtins.mapAttrs
|
||||
(_: s: lib.mkOption {
|
||||
(builtins.mapAttrs (
|
||||
_: s:
|
||||
lib.mkOption {
|
||||
inherit (s) description;
|
||||
type = lib.types.submodule (submoduleArgs: {
|
||||
options = if builtins.isFunction s.opts then s.opts submoduleArgs else s.opts;
|
||||
});
|
||||
}))
|
||||
}
|
||||
))
|
||||
lib.types.attrTag
|
||||
];
|
||||
|
||||
@ -39,7 +79,62 @@ let
|
||||
example = {
|
||||
General.icon = "nix-snowflake-white";
|
||||
};
|
||||
description = ''
|
||||
description = ''
|
||||
Configuration options for the widget.
|
||||
|
||||
See https://develop.kde.org/docs/plasma/scripting/keys/ for an (incomplete) list of options
|
||||
that can be set here.
|
||||
'';
|
||||
};
|
||||
extraConfig = lib.mkOption {
|
||||
type = lib.types.lines;
|
||||
default = "";
|
||||
example = ''
|
||||
(widget) => {
|
||||
widget.currentConfigGroup = ["General"];
|
||||
widget.writeConfig("title", "My widget");
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Extra configuration for the widget in JavaScript.
|
||||
|
||||
Should be a lambda/anonymous function that takes the widget as its sole argument,
|
||||
which can then be called by the script.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
desktopSimpleWidgetType = lib.types.submodule {
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "org.kde.plasma.kickoff";
|
||||
description = "The name of the widget to add.";
|
||||
};
|
||||
position = lib.mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 500;
|
||||
vertical = 500;
|
||||
};
|
||||
description = "The position of the widget.";
|
||||
};
|
||||
size = lib.mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget.";
|
||||
};
|
||||
config = lib.mkOption {
|
||||
type = (import ./lib.nix (args // { widgets = self; })).configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General.icon = "nix-snowflake-white";
|
||||
};
|
||||
description = ''
|
||||
Configuration options for the widget.
|
||||
|
||||
See https://develop.kde.org/docs/plasma/scripting/keys/ for an (incomplete) list of options
|
||||
@ -68,15 +163,72 @@ let
|
||||
isKnownWidget = lib.flip builtins.hasAttr sources;
|
||||
|
||||
self = {
|
||||
inherit isKnownWidget;
|
||||
inherit isKnownWidget positionType sizeType;
|
||||
|
||||
type = lib.types.oneOf [ lib.types.str compositeWidgetType simpleWidgetType ];
|
||||
type = lib.types.oneOf [
|
||||
lib.types.str
|
||||
compositeWidgetType
|
||||
simpleWidgetType
|
||||
];
|
||||
desktopType = lib.types.oneOf [
|
||||
compositeWidgetType
|
||||
desktopSimpleWidgetType
|
||||
];
|
||||
|
||||
lib = import ./lib.nix (args // { widgets = self; });
|
||||
|
||||
convert = widget:
|
||||
desktopConvert =
|
||||
widget:
|
||||
let
|
||||
inherit (builtins) length head attrNames mapAttrs isAttrs isString;
|
||||
inherit (builtins)
|
||||
length
|
||||
head
|
||||
attrNames
|
||||
mapAttrs
|
||||
isAttrs
|
||||
isString
|
||||
;
|
||||
keys = attrNames widget;
|
||||
type = head keys;
|
||||
|
||||
base = {
|
||||
config = null;
|
||||
extraConfig = "";
|
||||
};
|
||||
converters = mapAttrs (_: s: s.convert) sources;
|
||||
in
|
||||
if isAttrs widget && length keys == 1 && isKnownWidget type then
|
||||
let
|
||||
convertedWidget = converters.${type} widget.${type};
|
||||
in
|
||||
base
|
||||
// convertedWidget
|
||||
// {
|
||||
position =
|
||||
if isAttrs widget.${type}.position then
|
||||
widget.${type}.position
|
||||
else
|
||||
(throw "Desktop widget requires a position");
|
||||
size =
|
||||
if isAttrs widget.${type}.size then
|
||||
widget.${type}.size
|
||||
else
|
||||
(throw "Desktop widget requires a size");
|
||||
}
|
||||
else
|
||||
widget; # not a known composite type
|
||||
|
||||
convert =
|
||||
widget:
|
||||
let
|
||||
inherit (builtins)
|
||||
length
|
||||
head
|
||||
attrNames
|
||||
mapAttrs
|
||||
isAttrs
|
||||
isString
|
||||
;
|
||||
keys = attrNames widget;
|
||||
type = head keys;
|
||||
|
||||
@ -90,7 +242,8 @@ let
|
||||
base // { name = widget; }
|
||||
else if isAttrs widget && length keys == 1 && isKnownWidget type then
|
||||
base // converters.${type} widget.${type}
|
||||
else widget; # not a known composite type
|
||||
else
|
||||
widget; # not a known composite type
|
||||
};
|
||||
in
|
||||
self
|
||||
|
@ -1,19 +1,23 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption = description: mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
mkBoolOption =
|
||||
description:
|
||||
mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
|
||||
getIndexFromEnum = enum: value:
|
||||
if value == null
|
||||
then null
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex
|
||||
(x: x == value)
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (digital-clock widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
@ -51,30 +55,57 @@ in
|
||||
opts = {
|
||||
# See https://invent.kde.org/plasma/plasma-workspace/-/blob/master/applets/digital-clock/package/contents/config/main.xml for the accepted raw options
|
||||
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
date = {
|
||||
enable = mkBoolOption "Enable showing the current date.";
|
||||
|
||||
format =
|
||||
let
|
||||
enumVals = [ "shortDate" "longDate" "isoDate" ];
|
||||
enumVals = [
|
||||
"shortDate"
|
||||
"longDate"
|
||||
"isoDate"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (either (enum enumVals) (submodule {
|
||||
options.custom = mkOption {
|
||||
type = str;
|
||||
example = "ddd d";
|
||||
description = "The custom date format to use.";
|
||||
};
|
||||
}));
|
||||
type =
|
||||
with types;
|
||||
nullOr (
|
||||
either (enum enumVals) (submodule {
|
||||
options.custom = mkOption {
|
||||
type = str;
|
||||
example = "ddd d";
|
||||
description = "The custom date format to use.";
|
||||
};
|
||||
})
|
||||
);
|
||||
default = null;
|
||||
example = { custom = "d.MM.yyyy"; };
|
||||
example = {
|
||||
custom = "d.MM.yyyy";
|
||||
};
|
||||
description = ''
|
||||
The date format used for this clock.
|
||||
|
||||
Could be as a short date, long date, a ISO 8601 date (yyyy-mm-dd), or a custom date format.
|
||||
Short and long date formats are locale-dependent.
|
||||
'';
|
||||
apply = f:
|
||||
apply =
|
||||
f:
|
||||
if f == null then
|
||||
{ }
|
||||
else if f ? custom then
|
||||
@ -84,12 +115,17 @@ in
|
||||
}
|
||||
else
|
||||
{ dateFormat = f; };
|
||||
|
||||
};
|
||||
|
||||
position =
|
||||
let enumVals = [ "adaptive" "besideTime" "belowTime" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"adaptive"
|
||||
"besideTime"
|
||||
"belowTime"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "belowTime";
|
||||
@ -104,8 +140,14 @@ in
|
||||
|
||||
time = {
|
||||
showSeconds =
|
||||
let enumVals = [ "never" "onlyInTooltip" "always" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"never"
|
||||
"onlyInTooltip"
|
||||
"always"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "always";
|
||||
@ -117,8 +159,14 @@ in
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
format =
|
||||
let enumVals = [ "12h" "default" "24h" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"12h"
|
||||
"default"
|
||||
"24h"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "24h";
|
||||
@ -135,7 +183,10 @@ in
|
||||
selected = mkOption {
|
||||
type = with types; nullOr (listOf str);
|
||||
default = null;
|
||||
example = [ "Europe/Berlin" "Asia/Shanghai" ];
|
||||
example = [
|
||||
"Europe/Berlin"
|
||||
"Asia/Shanghai"
|
||||
];
|
||||
description = ''
|
||||
The timezones that are configured for this clock.
|
||||
|
||||
@ -153,8 +204,14 @@ in
|
||||
};
|
||||
changeOnScroll = mkBoolOption "Allow changing the displayed timezone by scrolling on the widget with the mouse wheel.";
|
||||
format =
|
||||
let enumVals = [ "code" "city" "offset" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"code"
|
||||
"city"
|
||||
"offset"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "code";
|
||||
@ -173,8 +230,18 @@ in
|
||||
|
||||
calendar = {
|
||||
firstDayOfWeek =
|
||||
let enumVals = [ "sunday" "monday" "tuesday" "wednesday" "thursday" "friday" "saturday" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"sunday"
|
||||
"monday"
|
||||
"tuesday"
|
||||
"wednesday"
|
||||
"thursday"
|
||||
"friday"
|
||||
"saturday"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "monday";
|
||||
@ -206,7 +273,8 @@ in
|
||||
|
||||
If null, then it will use the system font and automatically expand to fill available space.
|
||||
'';
|
||||
apply = font:
|
||||
apply =
|
||||
font:
|
||||
{
|
||||
autoFontAndSize = (font == null);
|
||||
}
|
||||
@ -220,7 +288,7 @@ in
|
||||
};
|
||||
};
|
||||
settings = mkOption {
|
||||
type = with types; nullOr (attrsOf (attrsOf (either (oneOf [ bool float int str ]) (listOf (oneOf [ bool float int str ])))));
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
Appearance = {
|
||||
@ -232,18 +300,22 @@ in
|
||||
|
||||
See https://develop.kde.org/docs/plasma/scripting/keys/ for an list of options
|
||||
'';
|
||||
apply = settings: if settings == null then {} else settings;
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
|
||||
convert =
|
||||
{ date
|
||||
, time
|
||||
, timeZone
|
||||
, calendar
|
||||
, font
|
||||
, settings
|
||||
}: {
|
||||
{
|
||||
position,
|
||||
size,
|
||||
date,
|
||||
time,
|
||||
timeZone,
|
||||
calendar,
|
||||
font,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.digitalclock";
|
||||
config = lib.recursiveUpdate {
|
||||
Appearance = lib.filterAttrs (_: v: v != null) (
|
||||
|
@ -1,14 +1,19 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption = description: mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
mkBoolOption =
|
||||
description:
|
||||
mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
|
||||
convertSpacing = spacing:
|
||||
convertSpacing =
|
||||
spacing:
|
||||
let
|
||||
mappings = {
|
||||
small = 0;
|
||||
@ -16,33 +21,60 @@ let
|
||||
large = 3;
|
||||
};
|
||||
in
|
||||
mappings.${spacing} or (throw "Invalid spacing: ${spacing}");
|
||||
|
||||
|
||||
getIndexFromEnum = enum: value:
|
||||
if value == null
|
||||
then null
|
||||
if spacing == null then
|
||||
null
|
||||
else if builtins.isString spacing then
|
||||
mappings.${spacing} or (throw "Invalid spacing: ${spacing}")
|
||||
else
|
||||
lib.lists.findFirstIndex
|
||||
(x: x == value)
|
||||
spacing;
|
||||
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (icon-tasks widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
positionToReverse = position:
|
||||
positionToReverse =
|
||||
position:
|
||||
let
|
||||
mappings = { left = true; right = false; };
|
||||
mappings = {
|
||||
left = true;
|
||||
right = false;
|
||||
};
|
||||
in
|
||||
mappings.${position} or (throw "Invalid position: ${position}");
|
||||
if position == null then null else mappings.${position} or (throw "Invalid position: ${position}");
|
||||
in
|
||||
{
|
||||
iconTasks = {
|
||||
description = "Icons Only Task Manager shows tasks only by their icon and not by icon and title of the window opened.";
|
||||
|
||||
opts = {
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
launchers = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [ "applications:org.kde.dolphin.desktop" "applications:org.kde.konsole.desktop" ];
|
||||
example = [
|
||||
"applications:org.kde.dolphin.desktop"
|
||||
"applications:org.kde.konsole.desktop"
|
||||
];
|
||||
description = "The list of launcher tasks on the widget. Usually .desktop file or executable URLs. Special URLs such as preferred://browser that expand to default applications are supported.";
|
||||
};
|
||||
appearance = {
|
||||
@ -52,11 +84,17 @@ in
|
||||
fill = mkBoolOption "Whether task manager should occupy all available space.";
|
||||
rows = {
|
||||
multirowView = mkOption {
|
||||
type = types.enum [ "never" "lowSpace" "always" ];
|
||||
type = types.enum [
|
||||
"never"
|
||||
"lowSpace"
|
||||
"always"
|
||||
];
|
||||
default = "never";
|
||||
example = "lowSpace";
|
||||
description = "When to use multi-row view.";
|
||||
apply = multirowView: if multirowView == "never" then false else (if multirowView == "always" then true else null);
|
||||
apply =
|
||||
multirowView:
|
||||
if multirowView == "never" then false else (if multirowView == "always" then true else null);
|
||||
};
|
||||
maximum = mkOption {
|
||||
type = types.nullOr types.ints.positive;
|
||||
@ -66,8 +104,17 @@ in
|
||||
};
|
||||
};
|
||||
iconSpacing = mkOption {
|
||||
type = types.enum [ "small" "medium" "large" ];
|
||||
default = "medium";
|
||||
type = types.nullOr (
|
||||
types.oneOf [
|
||||
(types.enum [
|
||||
"small"
|
||||
"medium"
|
||||
"large"
|
||||
])
|
||||
types.ints.positive
|
||||
]
|
||||
);
|
||||
default = null;
|
||||
example = "small";
|
||||
description = "The spacing between icons.";
|
||||
apply = convertSpacing;
|
||||
@ -76,8 +123,13 @@ in
|
||||
behavior = {
|
||||
grouping = {
|
||||
method =
|
||||
let enumVals = [ "none" "byProgramName" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"none"
|
||||
"byProgramName"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "none";
|
||||
@ -85,8 +137,15 @@ in
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
clickAction =
|
||||
let enumVals = [ "cycle" "showTooltips" "showPresentWindowsEffect" "showTextualList" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"cycle"
|
||||
"showTooltips"
|
||||
"showPresentWindowsEffect"
|
||||
"showTextualList"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "cycle";
|
||||
@ -95,8 +154,16 @@ in
|
||||
};
|
||||
};
|
||||
sortingMethod =
|
||||
let enumVals = [ "none" "manually" "alphabetically" "byDesktop" "byActivity" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"none"
|
||||
"manually"
|
||||
"alphabetically"
|
||||
"byDesktop"
|
||||
"byActivity"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "manually";
|
||||
@ -105,8 +172,17 @@ in
|
||||
};
|
||||
minimizeActiveTaskOnClick = mkBoolOption "Whether to minimize the currently-active task when clicked. If false, clicking on the currently-active task will do nothing.";
|
||||
middleClickAction =
|
||||
let enumVals = [ "none" "close" "newInstance" "toggleMinimized" "toggleGrouping" "bringToCurrentDesktop" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"none"
|
||||
"close"
|
||||
"newInstance"
|
||||
"toggleMinimized"
|
||||
"toggleGrouping"
|
||||
"bringToCurrentDesktop"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "bringToCurrentDesktop";
|
||||
@ -121,71 +197,94 @@ in
|
||||
onlyInCurrentScreen = mkBoolOption "Whether to show only window tasks that are on the same screen as the widget.";
|
||||
onlyInCurrentDesktop = mkBoolOption "Whether to only show tasks that are on the current virtual desktop.";
|
||||
onlyInCurrentActivity = mkBoolOption "Whether to show only tasks that are on the current activity.";
|
||||
onlyMinimized = mkBoolOption "Whether to show only window tasks that are minimized.";
|
||||
onlyMinimized = mkOption {
|
||||
type = types.nullOr types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = "Whether to show only window tasks that are minimized.";
|
||||
apply =
|
||||
onlyMinimized:
|
||||
if onlyMinimized == null then
|
||||
null
|
||||
else if onlyMinimized == true then
|
||||
1
|
||||
else
|
||||
0;
|
||||
};
|
||||
};
|
||||
unhideOnAttentionNeeded = mkBoolOption "Whether to unhide if a window wants attention.";
|
||||
newTasksAppearOn = mkOption {
|
||||
type = types.enum [ "left" "right" ];
|
||||
default = "right";
|
||||
example = "left";
|
||||
type = types.nullOr (
|
||||
types.enum [
|
||||
"left"
|
||||
"right"
|
||||
]
|
||||
);
|
||||
default = null;
|
||||
example = "right";
|
||||
description = "Whether new tasks should appear in the left or right.";
|
||||
apply = positionToReverse;
|
||||
};
|
||||
};
|
||||
settings = mkOption {
|
||||
type = with types; nullOr (attrsOf (attrsOf (either (oneOf [ bool float int str ]) (listOf (oneOf [ bool float int str ])))));
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
launchers = [ "applications:org.kde.dolphin.desktop" "applications:org.kde.konsole.desktop" ];
|
||||
launchers = [
|
||||
"applications:org.kde.dolphin.desktop"
|
||||
"applications:org.kde.konsole.desktop"
|
||||
];
|
||||
};
|
||||
};
|
||||
description = "Extra configuration options for the widget.";
|
||||
apply = settings: if settings == null then {} else settings;
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
convert =
|
||||
{ appearance
|
||||
, behavior
|
||||
, launchers
|
||||
, settings
|
||||
}: {
|
||||
{
|
||||
position,
|
||||
size,
|
||||
appearance,
|
||||
behavior,
|
||||
launchers,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.icontasks";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) (
|
||||
{
|
||||
launchers = launchers;
|
||||
General = lib.filterAttrs (_: v: v != null) ({
|
||||
launchers = launchers;
|
||||
|
||||
# Appearance
|
||||
showToolTips = appearance.showTooltips;
|
||||
highlightWindows = appearance.highlightWindows;
|
||||
indicateAudioStreams = appearance.indicateAudioStreams;
|
||||
fill = appearance.fill;
|
||||
# Appearance
|
||||
showToolTips = appearance.showTooltips;
|
||||
highlightWindows = appearance.highlightWindows;
|
||||
indicateAudioStreams = appearance.indicateAudioStreams;
|
||||
fill = appearance.fill;
|
||||
|
||||
forceStripes = appearance.rows.multirowView;
|
||||
maxStripes = appearance.rows.maximum;
|
||||
forceStripes = appearance.rows.multirowView;
|
||||
maxStripes = appearance.rows.maximum;
|
||||
|
||||
iconSpacing = appearance.iconSpacing;
|
||||
iconSpacing = appearance.iconSpacing;
|
||||
|
||||
# Behavior
|
||||
groupingStrategy = behavior.grouping.method;
|
||||
groupedTaskVisualization = behavior.grouping.clickAction;
|
||||
sortingStrategy = behavior.sortingMethod;
|
||||
minimizeActiveTaskOnClick = behavior.minimizeActiveTaskOnClick;
|
||||
middleClickAction = behavior.middleClickAction;
|
||||
# Behavior
|
||||
groupingStrategy = behavior.grouping.method;
|
||||
groupedTaskVisualization = behavior.grouping.clickAction;
|
||||
sortingStrategy = behavior.sortingMethod;
|
||||
minimizeActiveTaskOnClick = behavior.minimizeActiveTaskOnClick;
|
||||
middleClickAction = behavior.middleClickAction;
|
||||
|
||||
wheelEnabled = behavior.wheel.switchBetweenTasks;
|
||||
wheelSkipMinimized = behavior.wheel.ignoreMinimizedTasks;
|
||||
wheelEnabled = behavior.wheel.switchBetweenTasks;
|
||||
wheelSkipMinimized = behavior.wheel.ignoreMinimizedTasks;
|
||||
|
||||
showOnlyCurrentScreen = behavior.showTasks.onlyInCurrentScreen;
|
||||
showOnlyCurrentDesktop = behavior.showTasks.onlyInCurrentDesktop;
|
||||
showOnlyCurrentActivity = behavior.showTasks.onlyInCurrentActivity;
|
||||
showOnlyMinimized = behavior.showTasks.onlyMinimized;
|
||||
showOnlyCurrentScreen = behavior.showTasks.onlyInCurrentScreen;
|
||||
showOnlyCurrentDesktop = behavior.showTasks.onlyInCurrentDesktop;
|
||||
showOnlyCurrentActivity = behavior.showTasks.onlyInCurrentActivity;
|
||||
showOnlyMinimized = behavior.showTasks.onlyMinimized;
|
||||
|
||||
unhideOnAttention = behavior.unhideOnAttentionNeeded;
|
||||
reverseMode = behavior.newTasksAppearOn;
|
||||
}
|
||||
);
|
||||
unhideOnAttention = behavior.unhideOnAttentionNeeded;
|
||||
reverseMode = behavior.newTasksAppearOn;
|
||||
});
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
|
77
modules/widgets/keyboard-layout.nix
Normal file
77
modules/widgets/keyboard-layout.nix
Normal file
@ -0,0 +1,77 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (keyboard-layout widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
in
|
||||
{
|
||||
keyboardLayout = {
|
||||
description = "The keyboard layout indicator widget.";
|
||||
|
||||
opts = {
|
||||
position = lib.mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = lib.mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
displayStyle =
|
||||
let
|
||||
enumVals = [
|
||||
"label"
|
||||
"flag"
|
||||
"labelOverFlag"
|
||||
];
|
||||
in
|
||||
lib.mkOption {
|
||||
type = with lib.types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "labelOverFlag";
|
||||
description = "Keyboard layout indicator display style.";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
settings = lib.mkOption {
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
displayStyle = 1;
|
||||
};
|
||||
};
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
|
||||
convert =
|
||||
{
|
||||
position,
|
||||
size,
|
||||
displayStyle,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.keyboardlayout";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) { inherit displayStyle; };
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
}
|
165
modules/widgets/kicker.nix
Normal file
165
modules/widgets/kicker.nix
Normal file
@ -0,0 +1,165 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption =
|
||||
description:
|
||||
mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
inherit description;
|
||||
};
|
||||
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (kicker widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
checkPath =
|
||||
path:
|
||||
if path == null then
|
||||
null
|
||||
else if lib.strings.hasPrefix "/" path then
|
||||
path
|
||||
else
|
||||
throw "checkPath (kicker widget): Path ${path} is not an absolute path.";
|
||||
in
|
||||
{
|
||||
kicker = {
|
||||
description = ''
|
||||
Kicker is a launcher, which is also known as Application Menu.
|
||||
Kicker does not have fancy features, like the other launchers,
|
||||
but provides a tightly arranged interface.
|
||||
'';
|
||||
|
||||
opts = {
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
icon = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "start-here-kde-symbolic";
|
||||
description = "The icon to use for the kickoff button.";
|
||||
};
|
||||
customButtonImage = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "/home/user/pictures/custom-button.png";
|
||||
description = "The absolute path image to use for the custom button.";
|
||||
apply = checkPath;
|
||||
};
|
||||
applicationNameFormat =
|
||||
let
|
||||
enumVals = [
|
||||
"nameOnly"
|
||||
"genericNameOnly"
|
||||
"nameAndGenericName"
|
||||
"genericNameAndName"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "nameOnly";
|
||||
description = "The format of the application name to display.";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
behavior = {
|
||||
sortAlphabetically = mkBoolOption "Whether to sort the applications alphabetically.";
|
||||
flattenCategories = mkBoolOption "Whether to flatten top-level menu categories to a single level instead of displaying sub-categories.";
|
||||
showIconsOnRootLevel = mkBoolOption "Whether to show icons on the root level of the menu.";
|
||||
};
|
||||
categories = {
|
||||
show = {
|
||||
recentApplications = mkBoolOption "Whether to show recent applications.";
|
||||
recentFiles = mkBoolOption "Whether to show recent files.";
|
||||
};
|
||||
order =
|
||||
let
|
||||
enumVals = [
|
||||
"recentFirst"
|
||||
"popularFirst"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "recentFirst";
|
||||
description = "The order in which to show the categories.";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
};
|
||||
search = {
|
||||
alignResultsToBottom = mkBoolOption "Whether to align the search results to the bottom of the screen.";
|
||||
expandSearchResults = mkBoolOption "Whether to expand the search results to bookmarks, files and emails.";
|
||||
};
|
||||
settings = mkOption {
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
icon = "nix-snowflake-white";
|
||||
};
|
||||
};
|
||||
description = "Extra configuration options for the widget.";
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
convert =
|
||||
{
|
||||
position,
|
||||
size,
|
||||
icon,
|
||||
customButtonImage,
|
||||
applicationNameFormat,
|
||||
behavior,
|
||||
categories,
|
||||
search,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.kicker";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) ({
|
||||
inherit icon customButtonImage;
|
||||
inherit (search) alignResultsToBottom;
|
||||
|
||||
useCustomButtonImage = (customButtonImage != null);
|
||||
|
||||
appNameFormat = applicationNameFormat;
|
||||
|
||||
alphaSort = behavior.sortAlphabetically;
|
||||
limitDepth = behavior.flattenCategories;
|
||||
showIconsRootLevel = behavior.showIconsOnRootLevel;
|
||||
|
||||
showRecentApps = categories.show.recentApplications;
|
||||
showRecentDocs = categories.show.recentFiles;
|
||||
recentOrdering = categories.order;
|
||||
|
||||
useExtraRunners = search.expandSearchResults;
|
||||
});
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
}
|
154
modules/widgets/kickerdash.nix
Normal file
154
modules/widgets/kickerdash.nix
Normal file
@ -0,0 +1,154 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption =
|
||||
description:
|
||||
mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
inherit description;
|
||||
};
|
||||
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (kickerdash widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
checkPath =
|
||||
path:
|
||||
if path == null then
|
||||
null
|
||||
else if lib.strings.hasPrefix "/" path then
|
||||
path
|
||||
else
|
||||
throw "checkPath (kickerdash widget): Path ${path} is not an absolute path.";
|
||||
in
|
||||
{
|
||||
kickerdash = {
|
||||
description = "Application Dashboard (kickerdash) is an alternative launcher which fills the whole desktop.";
|
||||
|
||||
opts = {
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
icon = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "start-here-kde-symbolic";
|
||||
description = "The icon to use for the kickoff button.";
|
||||
};
|
||||
customButtonImage = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "/home/user/pictures/custom-button.png";
|
||||
description = "The absolute path image to use for the custom button.";
|
||||
apply = checkPath;
|
||||
};
|
||||
applicationNameFormat =
|
||||
let
|
||||
enumVals = [
|
||||
"nameOnly"
|
||||
"genericNameOnly"
|
||||
"nameAndGenericName"
|
||||
"genericNameAndName"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "nameOnly";
|
||||
description = "The format of the application name to display.";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
behavior = {
|
||||
sortAlphabetically = mkBoolOption "Whether to sort the applications alphabetically.";
|
||||
};
|
||||
categories = {
|
||||
show = {
|
||||
recentApplications = mkBoolOption "Whether to show recent applications.";
|
||||
recentFiles = mkBoolOption "Whether to show recent files.";
|
||||
};
|
||||
order =
|
||||
let
|
||||
enumVals = [
|
||||
"recentFirst"
|
||||
"popularFirst"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "recentFirst";
|
||||
description = "The order in which to show the categories.";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
};
|
||||
search = {
|
||||
expandSearchResults = mkBoolOption "Whether to expand the search results to bookmarks, files and emails.";
|
||||
};
|
||||
settings = mkOption {
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
icon = "nix-snowflake-white";
|
||||
};
|
||||
};
|
||||
description = "Extra configuration options for the widget.";
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
convert =
|
||||
{
|
||||
position,
|
||||
size,
|
||||
icon,
|
||||
customButtonImage,
|
||||
applicationNameFormat,
|
||||
behavior,
|
||||
categories,
|
||||
search,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.kickerdash";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) ({
|
||||
inherit icon customButtonImage;
|
||||
useCustomButtonImage = (customButtonImage != null);
|
||||
|
||||
appNameFormat = applicationNameFormat;
|
||||
|
||||
alphaSort = behavior.sortAlphabetically;
|
||||
|
||||
showRecentApps = categories.show.recentApplications;
|
||||
showRecentDocs = categories.show.recentFiles;
|
||||
recentOrdering = categories.order;
|
||||
|
||||
useExtraRunners = search.expandSearchResults;
|
||||
});
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
}
|
@ -1,26 +1,37 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption = description: mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
mkBoolOption =
|
||||
description:
|
||||
mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
|
||||
getIndexFromEnum = enum: value:
|
||||
if value == null
|
||||
then null
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex
|
||||
(x: x == value)
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (kickoff widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
convertSidebarPosition = sidebarPosition:
|
||||
convertSidebarPosition =
|
||||
sidebarPosition:
|
||||
let
|
||||
mappings = { left = false; right = true; };
|
||||
mappings = {
|
||||
left = false;
|
||||
right = true;
|
||||
};
|
||||
in
|
||||
if sidebarPosition == null then
|
||||
null
|
||||
else
|
||||
mappings.${sidebarPosition} or (throw "Invalid sidebar position: ${sidebarPosition}");
|
||||
in
|
||||
{
|
||||
@ -28,11 +39,32 @@ in
|
||||
description = "Kickoff is the default application launcher of the Plasma desktop.";
|
||||
|
||||
opts = {
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
icon = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "start-here-kde-symbolic";
|
||||
description = "The icon to use for the kickoff button.";
|
||||
description = ''
|
||||
The icon to use for the kickoff button.
|
||||
|
||||
This can also be used to specify a custom image for the kickoff button.
|
||||
To do this, set the value to a absolute path to the image file.
|
||||
'';
|
||||
};
|
||||
label = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
@ -43,15 +75,25 @@ in
|
||||
sortAlphabetically = mkBoolOption "Whether to sort menu contents alphabetically or use manual/system sort order.";
|
||||
compactDisplayStyle = mkBoolOption "Whether to use a compact display style for list items.";
|
||||
sidebarPosition = mkOption {
|
||||
type = types.enum [ "left" "right" ];
|
||||
default = "left";
|
||||
example = "right";
|
||||
type = types.nullOr (
|
||||
types.enum [
|
||||
"left"
|
||||
"right"
|
||||
]
|
||||
);
|
||||
default = null;
|
||||
example = "left";
|
||||
description = "The position of the sidebar.";
|
||||
apply = convertSidebarPosition;
|
||||
};
|
||||
favoritesDisplayMode =
|
||||
let enumVals = [ "grid" "list" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"grid"
|
||||
"list"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "list";
|
||||
@ -59,8 +101,13 @@ in
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
applicationsDisplayMode =
|
||||
let enumVals = [ "grid" "list" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"grid"
|
||||
"list"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "grid";
|
||||
@ -68,8 +115,15 @@ in
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
showButtonsFor =
|
||||
let enumVals = [ "power" "session" "custom" "powerAndSession" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"power"
|
||||
"session"
|
||||
"custom"
|
||||
"powerAndSession"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "powerAndSession";
|
||||
@ -78,50 +132,66 @@ in
|
||||
};
|
||||
showActionButtonCaptions = mkBoolOption "Whether to display captions ('shut down', 'log out', etc.) for the footer action buttons";
|
||||
pin = mkBoolOption "Whether the popup should remain open when another window is activated.";
|
||||
popupHeight = mkOption {
|
||||
type = with types; nullOr ints.positive;
|
||||
default = null;
|
||||
example = 500;
|
||||
};
|
||||
popupWidth = mkOption {
|
||||
type = with types; nullOr ints.positive;
|
||||
default = null;
|
||||
example = 700;
|
||||
};
|
||||
settings = mkOption {
|
||||
type = with types; nullOr (attrsOf (attrsOf (either (oneOf [ bool float int str ]) (listOf (oneOf [ bool float int str ])))));
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
icon = "nix-snowflake-white";
|
||||
};
|
||||
popupHeight = 500;
|
||||
};
|
||||
description = "Extra configuration options for the widget.";
|
||||
apply = settings: if settings == null then {} else settings;
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
convert =
|
||||
{ icon
|
||||
, label
|
||||
, sortAlphabetically
|
||||
, compactDisplayStyle
|
||||
, sidebarPosition
|
||||
, favoritesDisplayMode
|
||||
, applicationsDisplayMode
|
||||
, showButtonsFor
|
||||
, showActionButtonCaptions
|
||||
, pin
|
||||
, settings
|
||||
}: {
|
||||
{
|
||||
position,
|
||||
size,
|
||||
icon,
|
||||
label,
|
||||
sortAlphabetically,
|
||||
compactDisplayStyle,
|
||||
sidebarPosition,
|
||||
favoritesDisplayMode,
|
||||
applicationsDisplayMode,
|
||||
showButtonsFor,
|
||||
showActionButtonCaptions,
|
||||
pin,
|
||||
popupHeight,
|
||||
popupWidth,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.kickoff";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) (
|
||||
{
|
||||
icon = icon;
|
||||
menuLabel = label;
|
||||
alphaSort = sortAlphabetically;
|
||||
compactMode = compactDisplayStyle;
|
||||
paneSwap = sidebarPosition;
|
||||
favoritesDisplay = favoritesDisplayMode;
|
||||
applicationsDisplay = applicationsDisplayMode;
|
||||
primaryActions = showButtonsFor;
|
||||
showActionButtonCaptions = showActionButtonCaptions;
|
||||
config = lib.recursiveUpdate (lib.filterAttrsRecursive (_: v: v != null) {
|
||||
popupHeight = popupHeight;
|
||||
popupWidth = popupWidth;
|
||||
|
||||
# Other useful options
|
||||
pin = pin;
|
||||
}
|
||||
);
|
||||
} settings;
|
||||
General = {
|
||||
inherit icon pin;
|
||||
|
||||
menuLabel = label;
|
||||
alphaSort = sortAlphabetically;
|
||||
compactMode = compactDisplayStyle;
|
||||
paneSwap = sidebarPosition;
|
||||
favoritesDisplay = favoritesDisplayMode;
|
||||
applicationsDisplay = applicationsDisplayMode;
|
||||
primaryActions = showButtonsFor;
|
||||
showActionButtonCaptions = showActionButtonCaptions;
|
||||
};
|
||||
}) settings;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -5,50 +5,119 @@ let
|
||||
concatMapStringsSep
|
||||
concatStringsSep
|
||||
mapAttrsToList
|
||||
splitString;
|
||||
filterAttrs
|
||||
splitString
|
||||
;
|
||||
|
||||
configValueTypes = with lib.types; oneOf [ bool float int str ];
|
||||
configValueType = with lib.types; nullOr (attrsOf (attrsOf (either configValueTypes (listOf configValueTypes))));
|
||||
configValueTypes =
|
||||
with lib.types;
|
||||
oneOf [
|
||||
bool
|
||||
float
|
||||
int
|
||||
str
|
||||
];
|
||||
configValueTypeInner = with lib.types; either configValueTypes (listOf configValueTypes);
|
||||
configValueType =
|
||||
with lib.types;
|
||||
nullOr (attrsOf (either configValueTypeInner (attrsOf configValueTypeInner)));
|
||||
|
||||
# any value or null -> string -> string
|
||||
# If value is null, returns the empty string, otherwise returns the provided string
|
||||
stringIfNotNull = v: optionalString (v != null);
|
||||
|
||||
# Converts each datatype into an expression which can be parsed in JavaScript
|
||||
valToJS = v: if (builtins.isString v) then ''"${v}"'' else if (builtins.isBool v) then (lib.boolToString v) else (builtins.toString v);
|
||||
valToJS =
|
||||
v:
|
||||
if (builtins.isString v) then
|
||||
''"${v}"''
|
||||
else if (builtins.isBool v) then
|
||||
(lib.boolToString v)
|
||||
else
|
||||
(builtins.toString v);
|
||||
|
||||
# Converts a list of to a single string, that can be parsed as a string list in JavaScript
|
||||
toJSList = values: ''[${concatMapStringsSep ", " valToJS values}]'';
|
||||
|
||||
setWidgetSettings = var: settings:
|
||||
setWidgetSettings =
|
||||
var: settings:
|
||||
let
|
||||
perConfig = group: key: value: ''${var}.writeConfig("${key}", ${
|
||||
if builtins.isList value
|
||||
then toJSList value
|
||||
else valToJS value
|
||||
});'';
|
||||
perConfig =
|
||||
key: value:
|
||||
''${var}.writeConfig("${key}", ${
|
||||
if builtins.isList value then toJSList value else valToJS value
|
||||
});'';
|
||||
|
||||
perGroup = group: configs: ''
|
||||
${var}.currentConfigGroup = ${toJSList (splitString "/" group)};
|
||||
${concatStringsSep "\n" (mapAttrsToList (perConfig group) configs)}
|
||||
${concatStringsSep "\n" (mapAttrsToList perConfig configs)}
|
||||
'';
|
||||
|
||||
groups = (filterAttrs (_: value: builtins.isAttrs value) settings);
|
||||
topLevel = (filterAttrs (_: value: !builtins.isAttrs value) settings);
|
||||
in
|
||||
concatStringsSep "\n" (mapAttrsToList perGroup settings);
|
||||
concatStringsSep "\n" (
|
||||
(lib.optional (topLevel != { }) "${var}.currentConfigGroup = [];")
|
||||
++ (mapAttrsToList perConfig topLevel)
|
||||
++
|
||||
|
||||
addWidgetStmts = containment: var: ws:
|
||||
(mapAttrsToList perGroup groups)
|
||||
);
|
||||
|
||||
addWidgetStmts =
|
||||
containment: var: ws:
|
||||
let
|
||||
widgetConfigsToStmts = { name, config, ... }: ''
|
||||
var w = ${var}["${name}"];
|
||||
${setWidgetSettings "w" config}
|
||||
'';
|
||||
widgetConfigsToStmts =
|
||||
{ name, config, ... }:
|
||||
''
|
||||
var w = ${var}["${name}"];
|
||||
${setWidgetSettings "w" config}
|
||||
'';
|
||||
|
||||
addStmt = { name, config, extraConfig }@widget: ''
|
||||
${var}["${name}"] = ${containment}.addWidget("${name}");
|
||||
${stringIfNotNull config (widgetConfigsToStmts widget)}
|
||||
${lib.optionalString (extraConfig != "") ''
|
||||
(${extraConfig})(${var}["${name}"]);
|
||||
''}
|
||||
'';
|
||||
addStmt =
|
||||
{
|
||||
name,
|
||||
config,
|
||||
extraConfig,
|
||||
}@widget:
|
||||
''
|
||||
${var}["${name}"] = ${containment}.addWidget("${name}");
|
||||
${stringIfNotNull config (widgetConfigsToStmts widget)}
|
||||
${lib.optionalString (extraConfig != "") ''
|
||||
(${extraConfig})(${var}["${name}"]);
|
||||
''}
|
||||
'';
|
||||
in
|
||||
''
|
||||
const ${var} = {};
|
||||
${lib.concatMapStringsSep "\n" addStmt ws}
|
||||
'';
|
||||
|
||||
addDesktopWidgetStmts =
|
||||
containment: var: ws:
|
||||
let
|
||||
widgetConfigsToStmts =
|
||||
{ name, config, ... }:
|
||||
''
|
||||
var w = ${var}["${name}"];
|
||||
${setWidgetSettings "w" config}
|
||||
'';
|
||||
|
||||
addStmt =
|
||||
{
|
||||
name,
|
||||
position,
|
||||
size,
|
||||
config,
|
||||
extraConfig,
|
||||
}@widget:
|
||||
''
|
||||
${var}["${name}"] = ${containment}.addWidget("${name}", ${toString position.horizontal}, ${toString position.vertical}, ${toString size.width}, ${toString size.height});
|
||||
${stringIfNotNull config (widgetConfigsToStmts widget)}
|
||||
${lib.optionalString (extraConfig != "") ''
|
||||
(${extraConfig})(${var}["${name}"]);
|
||||
''}
|
||||
'';
|
||||
in
|
||||
''
|
||||
const ${var} = {};
|
||||
@ -60,5 +129,7 @@ in
|
||||
stringIfNotNull
|
||||
setWidgetSettings
|
||||
addWidgetStmts
|
||||
configValueType;
|
||||
addDesktopWidgetStmts
|
||||
configValueType
|
||||
;
|
||||
}
|
||||
|
74
modules/widgets/panel-spacer.nix
Normal file
74
modules/widgets/panel-spacer.nix
Normal file
@ -0,0 +1,74 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption =
|
||||
description:
|
||||
lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
example = true;
|
||||
inherit description;
|
||||
};
|
||||
in
|
||||
{
|
||||
panelSpacer = {
|
||||
opts = {
|
||||
position = lib.mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 100;
|
||||
vertical = 300;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = lib.mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 50;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
expanding = mkBoolOption "Whether the spacer should expand to fill the available space.";
|
||||
length = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.int;
|
||||
default = null;
|
||||
example = 50;
|
||||
description = ''
|
||||
The length of the spacer.
|
||||
If expanding is set to true, this value is ignored.
|
||||
'';
|
||||
};
|
||||
settings = lib.mkOption {
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
expanding = true;
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
Extra configuration for the widget
|
||||
'';
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
|
||||
convert =
|
||||
{
|
||||
position,
|
||||
size,
|
||||
expanding,
|
||||
length,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.panelspacer";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) { inherit expanding length; };
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
}
|
925
modules/widgets/plasma-panel-colorizer.nix
Normal file
925
modules/widgets/plasma-panel-colorizer.nix
Normal file
@ -0,0 +1,925 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption =
|
||||
description:
|
||||
mkOption {
|
||||
type = types.nullOr types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
inherit description;
|
||||
};
|
||||
|
||||
systemColors = [
|
||||
"text"
|
||||
"disabledText"
|
||||
"highlightedText"
|
||||
"activeText"
|
||||
"link"
|
||||
"visitedLink"
|
||||
"negativeText"
|
||||
"neutralText"
|
||||
"positiveText"
|
||||
"background"
|
||||
"highlight"
|
||||
"activeBackground"
|
||||
"linkBackground"
|
||||
"visitedLinkBackground"
|
||||
"negativeBackground"
|
||||
"neutralBackground"
|
||||
"positiveBackground"
|
||||
"alternateBackground"
|
||||
"focus"
|
||||
"hover"
|
||||
];
|
||||
|
||||
systemColorSets = [
|
||||
"view"
|
||||
"window"
|
||||
"button"
|
||||
"selection"
|
||||
"tooltip"
|
||||
"complementary"
|
||||
"header"
|
||||
];
|
||||
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (plasma-panel-colorizer widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
convertColorList = colors: if colors == null then null else builtins.concatStringsSep " " colors;
|
||||
|
||||
convertWidgets =
|
||||
widgets: if widgets == null then null else "|" + builtins.concatStringsSep "|" widgets;
|
||||
|
||||
convertWidgetMarginRules =
|
||||
rules:
|
||||
if rules == null then
|
||||
null
|
||||
else
|
||||
let
|
||||
widgetToString =
|
||||
widget:
|
||||
"${widget.widgetId},${toString widget.margin.vertical},${toString widget.margin.horizontal}";
|
||||
in
|
||||
builtins.concatStringsSep "|" (map widgetToString rules);
|
||||
|
||||
widgetMarginRuleType = types.submodule {
|
||||
options = {
|
||||
widgetId = mkOption {
|
||||
type = types.str;
|
||||
example = "org.kde.plasma.kickoff";
|
||||
description = "Widget id";
|
||||
};
|
||||
margin = {
|
||||
vertical = mkOption {
|
||||
type = types.int;
|
||||
example = 5;
|
||||
description = "Vertical margin value";
|
||||
};
|
||||
horizontal = mkOption {
|
||||
type = types.int;
|
||||
example = 5;
|
||||
description = "Horizontal margin value";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
plasmaPanelColorizer = {
|
||||
description = "Fully-featured widget to bring Latte-Dock and WM status bar customization features to the default KDE Plasma panel";
|
||||
|
||||
opts = {
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
general = {
|
||||
enable = mkBoolOption "Whether to enable the widget";
|
||||
hideWidget = mkBoolOption "Whether to hide the widget";
|
||||
};
|
||||
presetAutoLoading = {
|
||||
normal = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "Normal preset";
|
||||
description = "Preset to load when panel is on 'normal' state";
|
||||
};
|
||||
floating = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "Floating preset";
|
||||
description = "Preset to load when panel is on 'floating' state";
|
||||
};
|
||||
touchingWindow = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "Touching window preset";
|
||||
description = "Preset to load when panel is on 'touching window' state";
|
||||
};
|
||||
maximized = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "Maximized preset";
|
||||
description = "Preset to load when panel is on 'maximized' state";
|
||||
};
|
||||
};
|
||||
widgetBackground = {
|
||||
enable = mkBoolOption "Whether to enable the widget background configuration";
|
||||
colorMode = {
|
||||
mode =
|
||||
let
|
||||
enumVals = [
|
||||
"static"
|
||||
"animated"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "static";
|
||||
description = "The color mode to use for the widget background";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
animationInterval = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 3000;
|
||||
description = "The interval in milliseconds between each color change";
|
||||
};
|
||||
animationSmoothing = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 800;
|
||||
description = "The time in milliseconds it takes to transition between colors";
|
||||
};
|
||||
};
|
||||
colors = {
|
||||
source =
|
||||
let
|
||||
enumVals = [
|
||||
"custom"
|
||||
"system"
|
||||
"customList"
|
||||
"random"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "custom";
|
||||
description = "The source of the colors to use for the widget background";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
customColor = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#ff0000";
|
||||
description = "The custom color to use for the widget background";
|
||||
};
|
||||
system = {
|
||||
color = mkOption {
|
||||
type = types.nullOr (types.enum systemColors);
|
||||
default = null;
|
||||
example = "text";
|
||||
description = "The system color to use for the widget background";
|
||||
apply = getIndexFromEnum systemColors;
|
||||
};
|
||||
colorSet = mkOption {
|
||||
type = types.nullOr (types.enum systemColorSets);
|
||||
default = null;
|
||||
example = "view";
|
||||
description = "The system color variant to use for the widget background";
|
||||
apply = getIndexFromEnum systemColorSets;
|
||||
};
|
||||
};
|
||||
customColorList = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [
|
||||
"#ff0000"
|
||||
"#00ff00"
|
||||
"#0000ff"
|
||||
];
|
||||
description = "The list of custom colors to use for the widget background";
|
||||
apply = convertColorList;
|
||||
};
|
||||
contrastCorrection = {
|
||||
enable = mkBoolOption "Whether to enable contrast correction for the widget background";
|
||||
saturation = {
|
||||
enable = mkBoolOption "Whether to enable saturation correction for the widget background";
|
||||
value = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The value to use for the saturation correction";
|
||||
};
|
||||
};
|
||||
lightness = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The value to use for the lightness correction";
|
||||
};
|
||||
};
|
||||
};
|
||||
shape = {
|
||||
opacity = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The opacity to use for the widget background";
|
||||
};
|
||||
radius = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The radius to use for the widget background";
|
||||
};
|
||||
line = {
|
||||
enable = mkBoolOption "Whether to enable the line for the widget background";
|
||||
position =
|
||||
let
|
||||
enumVals = [
|
||||
"top"
|
||||
"bottom"
|
||||
"left"
|
||||
"right"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "top";
|
||||
description = "The position to use for the line of the widget background";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
width = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The width to use for the line of the widget background";
|
||||
};
|
||||
horizontalOffset = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The X offset to use for the line of the widget background";
|
||||
};
|
||||
verticalOffset = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The Y offset to use for the line of the widget background";
|
||||
};
|
||||
};
|
||||
outline = {
|
||||
colorSource =
|
||||
let
|
||||
enumVals = [
|
||||
"custom"
|
||||
"system"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "custom";
|
||||
description = "The source of the color to use for the outline of the widget background";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
customColor = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#ff0000";
|
||||
description = "The custom color to use for the outline of the widget background";
|
||||
};
|
||||
system = {
|
||||
color = mkOption {
|
||||
type = types.nullOr (types.enum systemColors);
|
||||
default = null;
|
||||
example = "text";
|
||||
description = "The system color to use for the outline of the widget background";
|
||||
apply = getIndexFromEnum systemColors;
|
||||
};
|
||||
colorSet = mkOption {
|
||||
type = types.nullOr (types.enum systemColorSets);
|
||||
default = null;
|
||||
example = "view";
|
||||
description = "The system color variant to use for the outline of the widget background";
|
||||
apply = getIndexFromEnum systemColorSets;
|
||||
};
|
||||
};
|
||||
opacity = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The opacity to use for the outline of the widget background";
|
||||
};
|
||||
width = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The width to use for the outline of the widget background";
|
||||
};
|
||||
};
|
||||
shadow = {
|
||||
color = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#7f000000";
|
||||
description = "The color to use for the shadow of the widget background";
|
||||
};
|
||||
size = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The size to use for the shadow of the widget background";
|
||||
};
|
||||
horizontalOffset = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The X offset to use for the shadow of the widget background";
|
||||
};
|
||||
verticalOffset = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The Y offset to use for the shadow of the widget background";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
textAndIcons = {
|
||||
enable = mkBoolOption "Whether to enable the text and icons configuration";
|
||||
colorMode = {
|
||||
mode =
|
||||
let
|
||||
enumVals = [
|
||||
"static"
|
||||
"interval"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "static";
|
||||
description = "The color mode to use for the text and icons";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
interval = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 3000;
|
||||
description = "The interval in milliseconds between each color change";
|
||||
};
|
||||
};
|
||||
colors = {
|
||||
source =
|
||||
let
|
||||
enumVals = [
|
||||
"custom"
|
||||
"system"
|
||||
"widgetBackground"
|
||||
"customList"
|
||||
"random"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "custom";
|
||||
description = "The source of the colors to use for the text and icons";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
customColor = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#ff0000";
|
||||
description = "The custom color to use for the text and icons";
|
||||
};
|
||||
system = {
|
||||
color = mkOption {
|
||||
type = types.nullOr (types.enum systemColors);
|
||||
default = null;
|
||||
example = "text";
|
||||
description = "The system color to use for the text and icons";
|
||||
apply = getIndexFromEnum systemColors;
|
||||
};
|
||||
colorSet = mkOption {
|
||||
type = types.nullOr (types.enum systemColorSets);
|
||||
default = null;
|
||||
example = "view";
|
||||
description = "The system color variant to use for the text and icons";
|
||||
apply = getIndexFromEnum systemColorSets;
|
||||
};
|
||||
};
|
||||
customColorList = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [
|
||||
"#ff0000"
|
||||
"#00ff00"
|
||||
"#0000ff"
|
||||
];
|
||||
description = "The list of custom colors to use for the text and icons";
|
||||
apply = convertColorList;
|
||||
};
|
||||
opacity = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The opacity to use for the text and icons";
|
||||
};
|
||||
contrastCorrection = {
|
||||
enable = mkBoolOption "Whether to enable contrast correction for the text and icons";
|
||||
saturation = {
|
||||
enable = mkBoolOption "Whether to enable saturation correction for the text and icons";
|
||||
value = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The value to use for the saturation correction";
|
||||
};
|
||||
};
|
||||
lightness = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The value to use for the lightness correction";
|
||||
};
|
||||
};
|
||||
};
|
||||
shadow = {
|
||||
enable = mkBoolOption "Whether to enable the shadow for the text and icons";
|
||||
color = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#7f000000";
|
||||
description = "The color to use for the shadow of the text and icons";
|
||||
};
|
||||
strength = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The strength to use for the shadow of the text and icons";
|
||||
};
|
||||
horizontalOffset = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The X offset to use for the shadow of the text and icons";
|
||||
};
|
||||
verticalOffset = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The Y offset to use for the shadow of the text and icons";
|
||||
};
|
||||
};
|
||||
customBadges = {
|
||||
fixCustomBadges = mkBoolOption "Whether to fix custom badges";
|
||||
};
|
||||
forceIconColor = {
|
||||
widgets = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [ "org.kde.plasma.digitalclock" ];
|
||||
description = "List of widgets to force icon color";
|
||||
apply = convertWidgets;
|
||||
};
|
||||
};
|
||||
};
|
||||
panelBackground = {
|
||||
originalBackground = {
|
||||
hide = mkBoolOption "Whether to hide the original panel background";
|
||||
opacity = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The opacity to use for the original panel background";
|
||||
};
|
||||
fixedSizePadding = {
|
||||
enable = mkBoolOption "Whether to enable fixed size padding";
|
||||
value = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The value to use for the fixed size padding in pixels";
|
||||
};
|
||||
};
|
||||
};
|
||||
customBackground = {
|
||||
enable = mkBoolOption "Whether to enable the custom panel background";
|
||||
colorSource =
|
||||
let
|
||||
enumVals = [
|
||||
"custom"
|
||||
"system"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "custom";
|
||||
description = "The source of the color to use for the custom panel background";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
customColor = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#ff0000";
|
||||
description = "The custom color to use for the custom panel background";
|
||||
};
|
||||
system = {
|
||||
color = mkOption {
|
||||
type = types.nullOr (types.enum systemColors);
|
||||
default = null;
|
||||
example = "text";
|
||||
description = "The system color to use for the custom panel background";
|
||||
apply = getIndexFromEnum systemColors;
|
||||
};
|
||||
colorSet = mkOption {
|
||||
type = types.nullOr (types.enum systemColorSets);
|
||||
default = null;
|
||||
example = "view";
|
||||
description = "The system color variant to use for the custom panel background";
|
||||
apply = getIndexFromEnum systemColorSets;
|
||||
};
|
||||
};
|
||||
opacity = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The opacity to use for the custom panel background";
|
||||
};
|
||||
radius = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The radius to use for the custom panel background";
|
||||
};
|
||||
outline = {
|
||||
colorSource =
|
||||
let
|
||||
enumVals = [
|
||||
"custom"
|
||||
"system"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "custom";
|
||||
description = "The source of the color to use for the outline of the custom panel background";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
customColor = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#ff0000";
|
||||
description = "The custom color to use for the outline of the custom panel background";
|
||||
};
|
||||
system = {
|
||||
color = mkOption {
|
||||
type = types.nullOr (types.enum systemColors);
|
||||
default = null;
|
||||
example = "text";
|
||||
description = "The system color to use for the outline of the custom panel background";
|
||||
apply = getIndexFromEnum systemColors;
|
||||
};
|
||||
colorSet = mkOption {
|
||||
type = types.nullOr (types.enum systemColorSets);
|
||||
default = null;
|
||||
example = "view";
|
||||
description = "The system color variant to use for the outline of the custom panel background";
|
||||
apply = getIndexFromEnum systemColorSets;
|
||||
};
|
||||
};
|
||||
opacity = mkOption {
|
||||
type = types.nullOr (types.numbers.between 0 1);
|
||||
default = null;
|
||||
example = 0.5;
|
||||
description = "The opacity to use for the outline of the custom panel background";
|
||||
};
|
||||
width = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The width to use for the outline of the custom panel background";
|
||||
};
|
||||
};
|
||||
shadow = {
|
||||
color = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#7f000000";
|
||||
description = "The color to use for the shadow of the custom panel background";
|
||||
};
|
||||
size = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The size to use for the shadow of the custom panel background";
|
||||
};
|
||||
horizontalOffset = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The X offset to use for the shadow of the custom panel background";
|
||||
};
|
||||
verticalOffset = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The Y offset to use for the shadow of the custom panel background";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
blacklist = {
|
||||
enable = mkBoolOption "Whether to enable the blacklist";
|
||||
colorSource =
|
||||
let
|
||||
enumVals = [
|
||||
"custom"
|
||||
"system"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.enum enumVals);
|
||||
default = null;
|
||||
example = "custom";
|
||||
description = "The source of the color to use for the blacklisted text and icons";
|
||||
apply = getIndexFromEnum enumVals;
|
||||
};
|
||||
customColor = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "#ff0000";
|
||||
description = "The custom color to use for the blacklisted text and icons";
|
||||
};
|
||||
system = {
|
||||
color = mkOption {
|
||||
type = types.nullOr (types.enum systemColors);
|
||||
default = null;
|
||||
example = "text";
|
||||
description = "The system color to use for the blacklisted text and icons";
|
||||
apply = getIndexFromEnum systemColors;
|
||||
};
|
||||
colorSet = mkOption {
|
||||
type = types.nullOr (types.enum systemColorSets);
|
||||
default = null;
|
||||
example = "view";
|
||||
description = "The system color variant to use for the blacklisted text and icons";
|
||||
apply = getIndexFromEnum systemColorSets;
|
||||
};
|
||||
};
|
||||
widgets = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [ "org.kde.plasma.digitalclock" ];
|
||||
description = "List of widgets to blacklist, blacklisted widgets will not be colorized";
|
||||
apply = convertWidgets;
|
||||
};
|
||||
};
|
||||
layout = {
|
||||
enable = mkBoolOption "Whether to enable the layout configuration";
|
||||
backgroundMargin = {
|
||||
spacing = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The spacing to use for the background margin";
|
||||
};
|
||||
vertical = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The vertical margin to use for the background margin";
|
||||
};
|
||||
horizontal = mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "The horizontal margin to use for the background margin";
|
||||
};
|
||||
};
|
||||
widgetMarginRules = mkOption {
|
||||
type = types.nullOr (types.listOf widgetMarginRuleType);
|
||||
default = null;
|
||||
example = [
|
||||
{
|
||||
widgetId = "org.kde.plasma.kickoff";
|
||||
margin = {
|
||||
vertical = 1;
|
||||
horizontal = 2;
|
||||
};
|
||||
}
|
||||
{
|
||||
widgetId = "org.kde.plasma.digitalclock";
|
||||
margin = {
|
||||
vertical = 2;
|
||||
horizontal = 1;
|
||||
};
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
List of rules to apply margins to specific widgets
|
||||
|
||||
Define every widget from the panel here.
|
||||
'';
|
||||
apply = convertWidgetMarginRules;
|
||||
};
|
||||
};
|
||||
settings = mkOption {
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
isEnabled = true;
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
Extra configuration for the widget options.
|
||||
|
||||
See available options at https://github.com/luisbocanegra/plasma-panel-colorizer/blob/main/package/contents/config/main.xml
|
||||
'';
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
convert =
|
||||
{
|
||||
position,
|
||||
size,
|
||||
general,
|
||||
presetAutoLoading,
|
||||
widgetBackground,
|
||||
textAndIcons,
|
||||
panelBackground,
|
||||
blacklist,
|
||||
layout,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "luisbocanegra.panel.colorizer";
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) {
|
||||
# General options
|
||||
isEnabled = general.enable;
|
||||
hideWidget = general.hideWidget;
|
||||
|
||||
# Preset autoloading
|
||||
normalPreset = presetAutoLoading.normal;
|
||||
floatingPreset = presetAutoLoading.floating;
|
||||
touchingWindowPreset = presetAutoLoading.touchingWindow;
|
||||
maximizedPreset = presetAutoLoading.maximized;
|
||||
|
||||
# Widget background options
|
||||
widgetBgEnabled = widgetBackground.enable;
|
||||
|
||||
# Widget background options > Color mode
|
||||
mode = widgetBackground.colorMode.mode; # Color mode
|
||||
rainbowInterval = widgetBackground.colorMode.animationInterval;
|
||||
rainbowTransition = widgetBackground.colorMode.animationSmoothing;
|
||||
|
||||
# Widget background options > Colors
|
||||
colorMode = widgetBackground.colors.source;
|
||||
singleColor = widgetBackground.colors.customColor; # Custom
|
||||
colorModeTheme = widgetBackground.colors.system.color; # System
|
||||
colorModeThemeVariant = widgetBackground.colors.system.colorSet; # System variant
|
||||
customColors = widgetBackground.colors.customColorList; # Custom list
|
||||
bgContrastFixEnabled = widgetBackground.colors.contrastCorrection.enable;
|
||||
bgSaturationEnabled = widgetBackground.colors.contrastCorrection.saturation.enable;
|
||||
bgSaturation = widgetBackground.colors.contrastCorrection.saturation.value;
|
||||
bgLightness = widgetBackground.colors.contrastCorrection.lightness;
|
||||
|
||||
# Widget background options > Shape
|
||||
opacity = widgetBackground.shape.opacity;
|
||||
radius = widgetBackground.shape.radius;
|
||||
bgLineModeEnabled = widgetBackground.shape.line.enable;
|
||||
bgLinePosition = widgetBackground.shape.line.position;
|
||||
bgLineWidth = widgetBackground.shape.line.width;
|
||||
bgLineXOffset = widgetBackground.shape.line.horizontalOffset;
|
||||
bgLineYOffset = widgetBackground.shape.line.verticalOffset;
|
||||
|
||||
# Widget background options > Shape > Outline
|
||||
widgetOutlineColorMode = widgetBackground.shape.outline.colorSource;
|
||||
widgetOutlineColor = widgetBackground.shape.outline.customColor;
|
||||
widgetOutlineColorModeTheme = widgetBackground.shape.outline.system.color;
|
||||
widgetOutlineColorModeThemeVariant = widgetBackground.shape.outline.system.colorSet;
|
||||
widgetOutlineOpacity = widgetBackground.shape.outline.opacity;
|
||||
widgetOutlineWidth = widgetBackground.shape.outline.width;
|
||||
|
||||
# Widget background options > Shape > Shadow
|
||||
widgetShadowColor = widgetBackground.shape.shadow.color;
|
||||
widgetShadowSize = widgetBackground.shape.shadow.size;
|
||||
widgetShadowX = widgetBackground.shape.shadow.horizontalOffset;
|
||||
widgetShadowY = widgetBackground.shape.shadow.verticalOffset;
|
||||
|
||||
# Text and icons options
|
||||
fgColorEnabled = textAndIcons.enable;
|
||||
|
||||
# Text and icons options > Color mode
|
||||
fgMode = textAndIcons.colorMode.mode;
|
||||
fgRainbowInterval = textAndIcons.colorMode.interval;
|
||||
|
||||
# Text and icons options > Colors
|
||||
fgColorMode = textAndIcons.colors.source;
|
||||
fgSingleColor = textAndIcons.colors.customColor;
|
||||
fgColorModeTheme = textAndIcons.colors.system.color;
|
||||
fgColorModeThemeVariant = textAndIcons.colors.system.colorSet;
|
||||
fgCustomColors = textAndIcons.colors.customColorList;
|
||||
fgOpacity = textAndIcons.colors.opacity;
|
||||
fgContrastFixEnabled = textAndIcons.colors.contrastCorrection.enable;
|
||||
fgSaturationEnabled = textAndIcons.colors.contrastCorrection.saturation.enable;
|
||||
fgSaturation = textAndIcons.colors.contrastCorrection.saturation.value;
|
||||
fgLightness = textAndIcons.colors.contrastCorrection.lightness;
|
||||
|
||||
# Text and icons options > Shadow
|
||||
fgShadowEnabled = textAndIcons.shadow.enable;
|
||||
fgShadowColor = textAndIcons.shadow.color;
|
||||
fgShadowRadius = textAndIcons.shadow.strength;
|
||||
fgShadowX = textAndIcons.shadow.horizontalOffset;
|
||||
fgShadowY = textAndIcons.shadow.verticalOffset;
|
||||
|
||||
# Text and icons options > Custom badges
|
||||
fixCustomBadges = textAndIcons.customBadges.fixCustomBadges;
|
||||
|
||||
# Text and icons options > Force icon color
|
||||
forceRecolor = textAndIcons.forceIconColor.widgets;
|
||||
|
||||
# Panel background options > Original background
|
||||
hideRealPanelBg = panelBackground.originalBackground.hide;
|
||||
panelRealBgOpacity = panelBackground.originalBackground.opacity;
|
||||
enableCustomPadding = panelBackground.originalBackground.fixedSizePadding.enable;
|
||||
panelPadding = panelBackground.originalBackground.fixedSizePadding.value;
|
||||
|
||||
# Panel background options > Custom background
|
||||
panelBgEnabled = panelBackground.customBackground.enable;
|
||||
panelBgColorMode = panelBackground.customBackground.colorSource;
|
||||
panelBgColor = panelBackground.customBackground.customColor;
|
||||
panelBgColorModeTheme = panelBackground.customBackground.system.color;
|
||||
panelBgColorModeThemeVariant = panelBackground.customBackground.system.colorSet;
|
||||
panelBgOpacity = panelBackground.customBackground.opacity;
|
||||
panelBgRadius = panelBackground.customBackground.radius;
|
||||
|
||||
# Panel background options > Custom background > Outline
|
||||
panelOutlineColorMode = panelBackground.customBackground.outline.colorSource;
|
||||
panelOutlineColor = panelBackground.customBackground.outline.customColor;
|
||||
panelOutlineColorModeTheme = panelBackground.customBackground.outline.system.color;
|
||||
panelOutlineColorModeThemeVariant = panelBackground.customBackground.outline.system.colorSet;
|
||||
panelOutlineOpacity = panelBackground.customBackground.outline.opacity;
|
||||
panelOutlineWidth = panelBackground.customBackground.outline.width;
|
||||
|
||||
# Panel background options > Custom background > Shadow
|
||||
panelShadowColor = panelBackground.customBackground.shadow.color;
|
||||
panelShadowSize = panelBackground.customBackground.shadow.size;
|
||||
panelShadowX = panelBackground.customBackground.shadow.horizontalOffset;
|
||||
panelShadowY = panelBackground.customBackground.shadow.verticalOffset;
|
||||
|
||||
# Blacklist options
|
||||
fgBlacklistedColorEnabled = blacklist.enable;
|
||||
fgBlacklistedColorMode = blacklist.colorSource;
|
||||
blacklistedFgColor = blacklist.customColor;
|
||||
fgBlacklistedColorModeTheme = blacklist.system.color;
|
||||
fgBlacklistedColorModeThemeVariant = blacklist.system.colorSet;
|
||||
blacklist = blacklist.widgets;
|
||||
|
||||
# Layout options
|
||||
layoutEnabled = layout.enable;
|
||||
|
||||
# Layout options > Background margin
|
||||
panelSpacing = layout.backgroundMargin.spacing;
|
||||
widgetBgHMargin = layout.backgroundMargin.horizontal;
|
||||
widgetBgVMargin = layout.backgroundMargin.vertical;
|
||||
|
||||
# Layout options > Widget margin rules
|
||||
marginRules = layout.widgetMarginRules;
|
||||
};
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
}
|
@ -1,20 +1,25 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
qfont = import ../../lib/qfont.nix { inherit lib; };
|
||||
|
||||
mkBoolOption = description: lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
mkBoolOption =
|
||||
description:
|
||||
lib.mkOption {
|
||||
type = with lib.types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
|
||||
getIndexFromEnum = enum: value:
|
||||
if value == null
|
||||
then null
|
||||
getIndexFromEnum =
|
||||
enum: value:
|
||||
if value == null then
|
||||
null
|
||||
else
|
||||
lib.lists.findFirstIndex
|
||||
(x: x == value)
|
||||
lib.lists.findFirstIndex (x: x == value)
|
||||
(throw "getIndexFromEnum (plasmusic-toolbar widget): Value ${value} isn't present in the enum. This is a bug")
|
||||
enum;
|
||||
|
||||
@ -215,6 +220,22 @@ in
|
||||
description = "KDE Plasma widget that shows currently playing song information and provide playback controls.";
|
||||
|
||||
opts = {
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 100;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 100;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
panelIcon = {
|
||||
icon = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
@ -223,6 +244,7 @@ in
|
||||
description = "Icon to show in the panel.";
|
||||
};
|
||||
albumCover = {
|
||||
fallbackToIcon = mkBoolOption "Whether to fallback to icon if cover is not available.";
|
||||
useAsIcon = mkBoolOption "Whether to use album cover as icon or not.";
|
||||
radius = mkOption {
|
||||
type = types.nullOr (types.ints.between 0 25);
|
||||
@ -233,8 +255,14 @@ in
|
||||
};
|
||||
};
|
||||
preferredSource =
|
||||
let enumVals = [ "any" "spotify" "vlc" ];
|
||||
in mkOption {
|
||||
let
|
||||
enumVals = [
|
||||
"any"
|
||||
"spotify"
|
||||
"vlc"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "any";
|
||||
@ -249,9 +277,14 @@ in
|
||||
description = "Maximum width of the song text.";
|
||||
};
|
||||
scrolling = {
|
||||
enable = mkBoolOption "Whether to enable scrolling text or not.";
|
||||
behavior =
|
||||
let
|
||||
enumVals = [ "alwaysScroll" "scrollOnHover" "alwaysScrollExceptOnHover" ];
|
||||
enumVals = [
|
||||
"alwaysScroll"
|
||||
"scrollOnHover"
|
||||
"alwaysScrollExceptOnHover"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
@ -266,10 +299,19 @@ in
|
||||
example = 3;
|
||||
description = "Speed of the scrolling text.";
|
||||
};
|
||||
resetOnPause = mkBoolOption "Whether to reset the scrolling text when the song is paused or not.";
|
||||
};
|
||||
displayInSeparateLines = mkBoolOption "Whether to display song information (title and artist) in separate lines or not.";
|
||||
};
|
||||
showPlaybackControls = mkBoolOption "Whether to show playback controls or not.";
|
||||
musicControls = {
|
||||
showPlaybackControls = mkBoolOption "Whether to show playback controls or not.";
|
||||
volumeStep = mkOption {
|
||||
type = types.nullOr (types.ints.between 1 100);
|
||||
default = null;
|
||||
example = 5;
|
||||
description = "Step size for volume control.";
|
||||
};
|
||||
};
|
||||
font = mkOption {
|
||||
type = types.nullOr fontType;
|
||||
default = null;
|
||||
@ -280,8 +322,46 @@ in
|
||||
description = "Custom font to use for the widget.";
|
||||
apply = font: if font == null then null else qfont.fontToString font;
|
||||
};
|
||||
background =
|
||||
let
|
||||
enumVals = [
|
||||
"standard"
|
||||
"transparent"
|
||||
"transparentShadow"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = with types; nullOr (enum enumVals);
|
||||
default = null;
|
||||
example = "transparent";
|
||||
description = "Widget background type (only for desktop widget)";
|
||||
apply =
|
||||
background:
|
||||
if background == null then
|
||||
null
|
||||
else
|
||||
builtins.elemAt
|
||||
[
|
||||
1
|
||||
0
|
||||
4
|
||||
]
|
||||
(
|
||||
lib.lists.findFirstIndex (
|
||||
x: x == background
|
||||
) (throw "plasmusic-toolbar: non-existent background ${background}. This is a bug!") enumVals
|
||||
);
|
||||
};
|
||||
albumCover = {
|
||||
albumPlaceholder = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "file:///home/user/placeholder.png";
|
||||
description = "Path to the album placeholder image.";
|
||||
};
|
||||
};
|
||||
settings = mkOption {
|
||||
type = with types; nullOr (attrsOf (attrsOf (either (oneOf [ bool float int str ]) (listOf (oneOf [ bool float int str ])))));
|
||||
type = configValueType;
|
||||
default = null;
|
||||
example = {
|
||||
General = {
|
||||
@ -290,41 +370,55 @@ in
|
||||
};
|
||||
description = ''
|
||||
Extra configuration for the widget options.
|
||||
|
||||
|
||||
See available options at https://github.com/ccatterina/plasmusic-toolbar/blob/main/src/contents/config/main.xml
|
||||
'';
|
||||
apply = settings: if settings == null then {} else settings;
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
convert =
|
||||
{ panelIcon
|
||||
, preferredSource
|
||||
, songText
|
||||
, showPlaybackControls
|
||||
, font
|
||||
, settings
|
||||
}: {
|
||||
{
|
||||
position,
|
||||
size,
|
||||
panelIcon,
|
||||
preferredSource,
|
||||
songText,
|
||||
musicControls,
|
||||
font,
|
||||
background,
|
||||
albumCover,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "plasmusic-toolbar";
|
||||
|
||||
config = lib.recursiveUpdate {
|
||||
General = lib.filterAttrs (_: v: v != null) (
|
||||
{
|
||||
panelIcon = panelIcon.icon;
|
||||
useAlbumCoverAsPanelIcon = panelIcon.albumCover.useAsIcon;
|
||||
albumCoverRadius = panelIcon.albumCover.radius;
|
||||
General = lib.filterAttrs (_: v: v != null) {
|
||||
panelIcon = panelIcon.icon;
|
||||
useAlbumCoverAsPanelIcon = panelIcon.albumCover.useAsIcon;
|
||||
albumCoverRadius = panelIcon.albumCover.radius;
|
||||
fallbackToIconWhenArtNotAvailable = panelIcon.albumCover.fallbackToIcon;
|
||||
|
||||
sourceIndex = preferredSource;
|
||||
sourceIndex = preferredSource;
|
||||
|
||||
maxSongWidthInPanel = songText.maximumWidth;
|
||||
textScrollingSpeed = songText.scrolling.speed;
|
||||
separateText = songText.displayInSeparateLines;
|
||||
textScrollingBehaviour = songText.scrolling.behavior;
|
||||
maxSongWidthInPanel = songText.maximumWidth;
|
||||
separateText = songText.displayInSeparateLines;
|
||||
|
||||
commandsInPanel = showPlaybackControls;
|
||||
|
||||
useCustomFont = (font != null);
|
||||
customFont = font;
|
||||
}
|
||||
);
|
||||
textScrollingEnabled = songText.scrolling.enable;
|
||||
textScrollingBehaviour = songText.scrolling.behavior;
|
||||
textScrollingSpeed = songText.scrolling.speed;
|
||||
textScrollingResetOnPause = songText.scrolling.resetOnPause;
|
||||
|
||||
commandsInPanel = musicControls.showPlaybackControls;
|
||||
volumeStep = musicControls.volumeStep;
|
||||
|
||||
useCustomFont = (font != null);
|
||||
customFont = font;
|
||||
|
||||
desktopWidgetBg = background;
|
||||
|
||||
albumPlaceholder = albumCover.albumPlaceholder;
|
||||
};
|
||||
} settings;
|
||||
};
|
||||
};
|
||||
|
@ -1,6 +1,8 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
# KDE expects a key/value pair like this:
|
||||
# ```ini
|
||||
@ -15,10 +17,8 @@ let
|
||||
# So, to satisfy the expected format we must quote the ENTIRE string as a valid JS string,
|
||||
# which means constructing a string that looks like this in the source code:
|
||||
# "[\"cpu/all/usage\", \"cpu/all/averageTemperature\"]"
|
||||
toEscapedList = ids:
|
||||
if ids != null
|
||||
then "[${lib.concatMapStringsSep ", " (x: ''\"${x}\"'') ids}]"
|
||||
else null;
|
||||
toEscapedList =
|
||||
ids: if ids != null then "[${lib.concatMapStringsSep ", " (x: ''\"${x}\"'') ids}]" else null;
|
||||
|
||||
mkListOption = mkOption {
|
||||
type = with types; nullOr (listOf str);
|
||||
@ -29,20 +29,22 @@ let
|
||||
# {name, color} -> {name, value}
|
||||
# Convert the sensor attrset into a name-value pair expected by listToAttrs
|
||||
toColorKV =
|
||||
{ name
|
||||
, color
|
||||
, label
|
||||
,
|
||||
}: {
|
||||
{
|
||||
name,
|
||||
color,
|
||||
label,
|
||||
}:
|
||||
{
|
||||
inherit name;
|
||||
value = color;
|
||||
};
|
||||
toLabelKV =
|
||||
{ name
|
||||
, color
|
||||
, label
|
||||
,
|
||||
}: {
|
||||
{
|
||||
name,
|
||||
color,
|
||||
label,
|
||||
}:
|
||||
{
|
||||
inherit name;
|
||||
value = label;
|
||||
};
|
||||
@ -54,6 +56,22 @@ in
|
||||
opts = {
|
||||
# See https://invent.kde.org/plasma/plasma-workspace/-/blob/master/applets/systemmonitor/systemmonitor/package/contents/config/main.xml for the accepted raw options
|
||||
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
title = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
@ -76,40 +94,47 @@ in
|
||||
description = "The display style of the chart. Uses the internal plugin name.";
|
||||
};
|
||||
sensors = mkOption {
|
||||
type = with types;
|
||||
nullOr (listOf (submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = str;
|
||||
example = "cpu/all/usage";
|
||||
description = "The name of the sensor.";
|
||||
type =
|
||||
with types;
|
||||
nullOr (
|
||||
listOf (submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = str;
|
||||
example = "cpu/all/usage";
|
||||
description = "The name of the sensor.";
|
||||
};
|
||||
color = mkOption {
|
||||
type = str; # TODO maybe use a better type
|
||||
example = "255,255,255";
|
||||
description = "The color of the sensor, as a string containing 8-bit integral RGB values separated by commas";
|
||||
};
|
||||
label = mkOption {
|
||||
type = str;
|
||||
example = "CPU %";
|
||||
description = "The label of the sensor.";
|
||||
};
|
||||
};
|
||||
color = mkOption {
|
||||
type = str; # TODO maybe use a better type
|
||||
example = "255,255,255";
|
||||
description = "The color of the sensor, as a string containing 8-bit integral RGB values separated by commas";
|
||||
};
|
||||
label = mkOption {
|
||||
type = str;
|
||||
example = "CPU %";
|
||||
description = "The label of the sensor.";
|
||||
};
|
||||
};
|
||||
}));
|
||||
})
|
||||
);
|
||||
default = null;
|
||||
example = [{
|
||||
name = "gpu/gpu1/usage";
|
||||
color = "180,190,254";
|
||||
label = "GPU %";
|
||||
}];
|
||||
example = [
|
||||
{
|
||||
name = "gpu/gpu1/usage";
|
||||
color = "180,190,254";
|
||||
label = "GPU %";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
The list of sensors displayed as a part of the graph/chart.
|
||||
'';
|
||||
apply = sensors: lib.optionalAttrs (sensors != null) {
|
||||
SensorColors = builtins.listToAttrs (map toColorKV sensors);
|
||||
SensorLabels = builtins.listToAttrs (map toLabelKV sensors);
|
||||
Sensors.highPrioritySensorIds = toEscapedList (map (s: s.name) sensors);
|
||||
};
|
||||
apply =
|
||||
sensors:
|
||||
lib.optionalAttrs (sensors != null) {
|
||||
SensorColors = builtins.listToAttrs (map toColorKV sensors);
|
||||
SensorLabels = builtins.listToAttrs (map toLabelKV sensors);
|
||||
Sensors.highPrioritySensorIds = toEscapedList (map (s: s.name) sensors);
|
||||
};
|
||||
};
|
||||
|
||||
totalSensors = mkListOption // {
|
||||
@ -119,7 +144,10 @@ in
|
||||
'';
|
||||
};
|
||||
textOnlySensors = mkListOption // {
|
||||
example = [ "cpu/all/averageTemperature" "cpu/all/averageFrequency" ];
|
||||
example = [
|
||||
"cpu/all/averageTemperature"
|
||||
"cpu/all/averageFrequency"
|
||||
];
|
||||
description = ''
|
||||
The list of text-only sensors, displayed in the pop-up upon clicking the widget.
|
||||
'';
|
||||
@ -137,45 +165,48 @@ in
|
||||
};
|
||||
};
|
||||
settings = mkOption {
|
||||
type = with types; nullOr (attrsOf (attrsOf (either (oneOf [ bool float int str ]) (listOf (oneOf [ bool float int str ])))));
|
||||
type = configValueType;
|
||||
default = null;
|
||||
description = "Extra configuration options for the widget.";
|
||||
apply = settings: if settings == null then {} else settings;
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
};
|
||||
|
||||
convert =
|
||||
{ title
|
||||
, showTitle
|
||||
, showLegend
|
||||
, displayStyle
|
||||
, totalSensors
|
||||
, sensors
|
||||
, textOnlySensors
|
||||
, range
|
||||
, settings
|
||||
}: {
|
||||
{
|
||||
position,
|
||||
size,
|
||||
title,
|
||||
showTitle,
|
||||
showLegend,
|
||||
displayStyle,
|
||||
totalSensors,
|
||||
sensors,
|
||||
textOnlySensors,
|
||||
range,
|
||||
settings,
|
||||
}:
|
||||
{
|
||||
name = "org.kde.plasma.systemmonitor";
|
||||
config = lib.filterAttrsRecursive (_: v: v != null)
|
||||
(lib.recursiveUpdate
|
||||
({
|
||||
Appearance = {
|
||||
inherit title;
|
||||
inherit showTitle;
|
||||
chartFace = displayStyle;
|
||||
};
|
||||
Sensors = {
|
||||
lowPrioritySensorIds = textOnlySensors;
|
||||
totalSensors = totalSensors;
|
||||
};
|
||||
"org.kde.ksysguard.piechart/General" = {
|
||||
inherit showLegend;
|
||||
rangeAuto = (range.from == null && range.to == null);
|
||||
rangeFrom = range.from;
|
||||
rangeTo = range.to;
|
||||
};
|
||||
})
|
||||
(lib.recursiveUpdate sensors settings));
|
||||
config = lib.filterAttrsRecursive (_: v: v != null) (
|
||||
lib.recursiveUpdate ({
|
||||
Appearance = {
|
||||
inherit title;
|
||||
inherit showTitle;
|
||||
chartFace = displayStyle;
|
||||
};
|
||||
Sensors = {
|
||||
lowPrioritySensorIds = textOnlySensors;
|
||||
totalSensors = totalSensors;
|
||||
};
|
||||
"org.kde.ksysguard.piechart/General" = {
|
||||
inherit showLegend;
|
||||
rangeAuto = (range.from == null && range.to == null);
|
||||
rangeFrom = range.from;
|
||||
rangeTo = range.to;
|
||||
};
|
||||
}) (lib.recursiveUpdate sensors settings)
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,169 +1,206 @@
|
||||
{ lib, widgets, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (import ./lib.nix { inherit lib; }) configValueType;
|
||||
inherit (import ./default.nix { inherit lib; }) positionType sizeType;
|
||||
|
||||
mkBoolOption = description: mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
mkBoolOption =
|
||||
description:
|
||||
mkOption {
|
||||
type = with types; nullOr bool;
|
||||
default = null;
|
||||
inherit description;
|
||||
};
|
||||
in
|
||||
{
|
||||
systemTray = {
|
||||
description = "A system tray of other widgets/plasmoids";
|
||||
|
||||
opts = ({ options, ... }: {
|
||||
# See https://invent.kde.org/plasma/plasma-workspace/-/blob/master/applets/systemtray/package/contents/config/main.xml for the accepted raw options.
|
||||
opts = (
|
||||
{ options, ... }:
|
||||
{
|
||||
# See https://invent.kde.org/plasma/plasma-workspace/-/blob/master/applets/systemtray/package/contents/config/main.xml for the accepted raw options.
|
||||
|
||||
pin = mkBoolOption "Whether the popup should remain open when another window is activated.";
|
||||
|
||||
icons = {
|
||||
spacing =
|
||||
let
|
||||
enum = [ "small" "medium" "large" ];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.either (types.enum enum) types.ints.positive);
|
||||
default = null;
|
||||
description = ''
|
||||
The spacing between icons.
|
||||
|
||||
Could be an integer unit, or "small" (1 unit), "medium" (2 units) or "large" (6 units).
|
||||
'';
|
||||
apply = spacing:
|
||||
(if (spacing == null) then null
|
||||
else
|
||||
(if builtins.isInt spacing then
|
||||
spacing
|
||||
else
|
||||
builtins.elemAt [ 1 2 6 ] (
|
||||
lib.lists.findFirstIndex
|
||||
(x: x == spacing)
|
||||
(throw "systemTray: nonexistent spacing ${spacing}! This is a bug!")
|
||||
enum
|
||||
)));
|
||||
};
|
||||
scaleToFit = mkBoolOption ''
|
||||
Whether to automatically scale System Tray icons to fix the available thickness of the panel.
|
||||
|
||||
If false, tray icons will be capped at the smallMedium size (22px) and become a two-row/column
|
||||
layout when the panel is thick.
|
||||
'';
|
||||
};
|
||||
|
||||
items = {
|
||||
showAll = mkBoolOption "If true, all system tray entries will always be in the main bar, outside the popup.";
|
||||
|
||||
hidden = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [
|
||||
# Plasmoid plugin example
|
||||
"org.kde.plasma.brightness"
|
||||
|
||||
# StatusNotifier example
|
||||
"org.kde.plasma.addons.katesessions"
|
||||
];
|
||||
description = ''
|
||||
List of widgets that should be hidden from the main bar, only visible in the popup.
|
||||
|
||||
Expects a list of plasmoid plugin IDs or StatusNotifier IDs.
|
||||
'';
|
||||
};
|
||||
|
||||
shown = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [
|
||||
# Plasmoid plugin example
|
||||
"org.kde.plasma.battery"
|
||||
|
||||
# StatusNotifier example
|
||||
"org.kde.plasma.addons.katesessions"
|
||||
];
|
||||
description = ''
|
||||
List of widgets that should be shown in the main bar.
|
||||
|
||||
Expects a list of plasmoid plugin IDs or StatusNotifier IDs.
|
||||
'';
|
||||
};
|
||||
|
||||
extra = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [ "org.kde.plasma.battery" ];
|
||||
description = ''
|
||||
List of extra widgets that are explicitly enabled in the system tray.
|
||||
|
||||
Expects a list of plasmoid plugin IDs.
|
||||
'';
|
||||
};
|
||||
|
||||
configs = mkOption {
|
||||
# The type here is deliberately NOT modelled exactly correctly,
|
||||
# to allow the apply function to provide better errors with the richer option and type system.
|
||||
type = types.attrsOf (types.attrsOf types.anything);
|
||||
default = { };
|
||||
position = mkOption {
|
||||
type = positionType;
|
||||
example = {
|
||||
# Example of a widget-specific config
|
||||
battery.showPercentage = true;
|
||||
|
||||
# Example of raw config for an untyped widget
|
||||
"org.kde.plasma.devicenotifier".config = {
|
||||
removableDevices = false;
|
||||
nonRemovableDevices = true;
|
||||
};
|
||||
horizontal = 250;
|
||||
vertical = 50;
|
||||
};
|
||||
description = ''
|
||||
Configurations for each widget in the tray.
|
||||
description = "The position of the widget. (Only for desktop widget)";
|
||||
};
|
||||
size = mkOption {
|
||||
type = sizeType;
|
||||
example = {
|
||||
width = 500;
|
||||
height = 500;
|
||||
};
|
||||
description = "The size of the widget. (Only for desktop widget)";
|
||||
};
|
||||
pin = mkBoolOption "Whether the popup should remain open when another window is activated.";
|
||||
|
||||
Uses widget-specific configs if the key is a known widget type,
|
||||
otherwise uses raw configs that's not specifically checked to be valid,
|
||||
or even idiomatic in Nix!
|
||||
icons = {
|
||||
spacing =
|
||||
let
|
||||
enum = [
|
||||
"small"
|
||||
"medium"
|
||||
"large"
|
||||
];
|
||||
in
|
||||
mkOption {
|
||||
type = types.nullOr (types.either (types.enum enum) types.ints.positive);
|
||||
default = null;
|
||||
description = ''
|
||||
The spacing between icons.
|
||||
|
||||
Could be an integer unit, or "small" (1 unit), "medium" (2 units) or "large" (6 units).
|
||||
'';
|
||||
apply =
|
||||
spacing:
|
||||
(
|
||||
if (spacing == null) then
|
||||
null
|
||||
else
|
||||
(
|
||||
if builtins.isInt spacing then
|
||||
spacing
|
||||
else
|
||||
builtins.elemAt
|
||||
[
|
||||
1
|
||||
2
|
||||
6
|
||||
]
|
||||
(
|
||||
lib.lists.findFirstIndex (
|
||||
x: x == spacing
|
||||
) (throw "systemTray: nonexistent spacing ${spacing}! This is a bug!") enum
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
scaleToFit = mkBoolOption ''
|
||||
Whether to automatically scale System Tray icons to fix the available thickness of the panel.
|
||||
|
||||
If false, tray icons will be capped at the smallMedium size (22px) and become a two-row/column
|
||||
layout when the panel is thick.
|
||||
'';
|
||||
};
|
||||
|
||||
# You might be asking yourself... WTH is this?
|
||||
# Simply put, this thing allows us to apply the same defaults as defined by the options,
|
||||
# Instead of forcing downstream converters to provide defaults to everything *again*.
|
||||
# The way to do this is kind of cursed and honestly it might be easier if `lib.evalOptionValue`
|
||||
# is not recommended for public use. Oh well.
|
||||
apply = lib.mapAttrsToList
|
||||
(name: config:
|
||||
items = {
|
||||
showAll = mkBoolOption "If true, all system tray entries will always be in the main bar, outside the popup.";
|
||||
|
||||
hidden = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [
|
||||
# Plasmoid plugin example
|
||||
"org.kde.plasma.brightness"
|
||||
|
||||
# StatusNotifier example
|
||||
"org.kde.plasma.addons.katesessions"
|
||||
];
|
||||
description = ''
|
||||
List of widgets that should be hidden from the main bar, only visible in the popup.
|
||||
|
||||
Expects a list of plasmoid plugin IDs or StatusNotifier IDs.
|
||||
'';
|
||||
};
|
||||
|
||||
shown = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [
|
||||
# Plasmoid plugin example
|
||||
"org.kde.plasma.battery"
|
||||
|
||||
# StatusNotifier example
|
||||
"org.kde.plasma.addons.katesessions"
|
||||
];
|
||||
description = ''
|
||||
List of widgets that should be shown in the main bar.
|
||||
|
||||
Expects a list of plasmoid plugin IDs or StatusNotifier IDs.
|
||||
'';
|
||||
};
|
||||
|
||||
extra = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [ "org.kde.plasma.battery" ];
|
||||
description = ''
|
||||
List of extra widgets that are explicitly enabled in the system tray.
|
||||
|
||||
Expects a list of plasmoid plugin IDs.
|
||||
'';
|
||||
};
|
||||
|
||||
configs = mkOption {
|
||||
# The type here is deliberately NOT modelled exactly correctly,
|
||||
# to allow the apply function to provide better errors with the richer option and type system.
|
||||
type = types.attrsOf (types.attrsOf types.anything);
|
||||
default = { };
|
||||
example = {
|
||||
# Example of a widget-specific config
|
||||
battery.showPercentage = true;
|
||||
keyboardLayout.displayStyle = "label";
|
||||
|
||||
# Example of raw config for an untyped widget
|
||||
"org.kde.plasma.devicenotifier".config.General = {
|
||||
removableDevices = false;
|
||||
nonRemovableDevices = true;
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
Configurations for each widget in the tray.
|
||||
|
||||
Uses widget-specific configs if the key is a known widget type,
|
||||
otherwise uses raw configs that's not specifically checked to be valid,
|
||||
or even idiomatic in Nix!
|
||||
'';
|
||||
|
||||
# You might be asking yourself... WTH is this?
|
||||
# Simply put, this thing allows us to apply the same defaults as defined by the options,
|
||||
# Instead of forcing downstream converters to provide defaults to everything *again*.
|
||||
# The way to do this is kind of cursed and honestly it might be easier if `lib.evalOptionValue`
|
||||
# is not recommended for public use. Oh well.
|
||||
apply = lib.mapAttrsToList (
|
||||
name: config:
|
||||
let
|
||||
isKnownWidget = widgets.isKnownWidget name;
|
||||
# Raw widgets aren't wrapped in an extra attrset layer, unlike known ones
|
||||
# We wrap them back up to ensure the path is accurate
|
||||
loc = options.items.configs.loc ++ lib.optional (!isKnownWidget) name;
|
||||
in
|
||||
widgets.convert (
|
||||
lib.mergeDefinitions loc widgets.type [
|
||||
widgets.convert
|
||||
(lib.mergeDefinitions loc widgets.type [
|
||||
{
|
||||
file = builtins.head options.items.configs.files;
|
||||
# Looks a bit funny, does the job just right.
|
||||
value =
|
||||
if isKnownWidget then
|
||||
{ ${name} = config; }
|
||||
else
|
||||
config // { inherit name; };
|
||||
value = if isKnownWidget then { ${name} = config; } else config // { inherit name; };
|
||||
}
|
||||
]
|
||||
).mergedValue
|
||||
]).mergedValue
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
settings = mkOption {
|
||||
type = with types; nullOr (attrsOf (attrsOf (either (oneOf [ bool float int str ]) (listOf (oneOf [ bool float int str ])))));
|
||||
default = null;
|
||||
description = "Extra configuration options for the widget.";
|
||||
apply = settings: if settings == null then {} else settings;
|
||||
};
|
||||
});
|
||||
settings = mkOption {
|
||||
type = configValueType;
|
||||
default = null;
|
||||
description = "Extra configuration options for the widget.";
|
||||
apply = settings: if settings == null then { } else settings;
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
convert =
|
||||
{ pin
|
||||
, icons
|
||||
, items
|
||||
, settings
|
||||
{
|
||||
position,
|
||||
size,
|
||||
pin,
|
||||
icons,
|
||||
items,
|
||||
settings,
|
||||
}:
|
||||
let
|
||||
sets = {
|
||||
@ -186,7 +223,7 @@ in
|
||||
(widget) => {
|
||||
const tray = desktopById(widget.readConfig("SystrayContainmentId"));
|
||||
if (!tray) return; // if somehow the containment doesn't exist
|
||||
|
||||
|
||||
${widgets.lib.setWidgetSettings "tray" mergedSettings}
|
||||
${widgets.lib.addWidgetStmts "tray" "trayWidgets" items.configs}
|
||||
}
|
||||
|
@ -1,9 +1,17 @@
|
||||
{ lib
|
||||
, config
|
||||
, ...
|
||||
}:
|
||||
with lib.types; let
|
||||
inherit (builtins) length listToAttrs foldl' toString attrNames getAttr concatStringsSep add isAttrs;
|
||||
{ lib, config, ... }:
|
||||
with lib.types;
|
||||
let
|
||||
inherit (builtins)
|
||||
length
|
||||
listToAttrs
|
||||
foldl'
|
||||
toString
|
||||
attrNames
|
||||
getAttr
|
||||
concatStringsSep
|
||||
add
|
||||
isAttrs
|
||||
;
|
||||
inherit (lib) mkOption mkIf;
|
||||
inherit (lib.trivial) mergeAttrs;
|
||||
inherit (lib.lists) imap0;
|
||||
@ -35,8 +43,10 @@ with lib.types; let
|
||||
matchNameMap = {
|
||||
"window-class" = "wmclass";
|
||||
"window-types" = "types";
|
||||
"window-role" = "windowrole";
|
||||
};
|
||||
matchOptionType = hasMatchWhole:
|
||||
matchOptionType =
|
||||
hasMatchWhole:
|
||||
submodule {
|
||||
options =
|
||||
{
|
||||
@ -58,7 +68,12 @@ with lib.types; let
|
||||
};
|
||||
};
|
||||
};
|
||||
basicValueType = oneOf [ bool float int str ];
|
||||
basicValueType = oneOf [
|
||||
bool
|
||||
float
|
||||
int
|
||||
str
|
||||
];
|
||||
applyOptionType = submodule {
|
||||
options = {
|
||||
value = mkOption {
|
||||
@ -72,25 +87,29 @@ with lib.types; let
|
||||
};
|
||||
};
|
||||
};
|
||||
mkMatchOption = name: hasMatchWhole:
|
||||
mkMatchOption =
|
||||
name: hasMatchWhole:
|
||||
mkOption {
|
||||
type = nullOr (coercedTo str (value: { inherit value; }) (matchOptionType hasMatchWhole));
|
||||
default = null;
|
||||
description = "${name} matching";
|
||||
};
|
||||
fixMatchName = name: matchNameMap.${name} or name;
|
||||
buildMatchRule = name: rule: ({
|
||||
"${fixMatchName name}" = rule.value;
|
||||
"${fixMatchName name}match" = getAttr rule.type matchRules;
|
||||
}
|
||||
// optionalAttrs (rule ? match-whole) {
|
||||
"${fixMatchName name}complete" = rule.match-whole;
|
||||
});
|
||||
buildMatchRule =
|
||||
name: rule:
|
||||
(
|
||||
{
|
||||
"${fixMatchName name}" = rule.value;
|
||||
"${fixMatchName name}match" = getAttr rule.type matchRules;
|
||||
}
|
||||
// optionalAttrs (rule ? match-whole) { "${fixMatchName name}complete" = rule.match-whole; }
|
||||
);
|
||||
buildApplyRule = name: rule: {
|
||||
"${name}" = rule.value;
|
||||
"${name}rule" = getAttr rule.apply applyRules;
|
||||
};
|
||||
buildWindowRule = rule:
|
||||
buildWindowRule =
|
||||
rule:
|
||||
let
|
||||
matchOptions = filterAttrs (_name: isAttrs) rule.match;
|
||||
matchRules = mapAttrsToList buildMatchRule matchOptions;
|
||||
@ -100,16 +119,14 @@ with lib.types; let
|
||||
{
|
||||
Description = rule.description;
|
||||
}
|
||||
// optionalAttrs (rule.match.window-types != 0) {
|
||||
types = rule.match.window-types;
|
||||
}
|
||||
// optionalAttrs (rule.match.window-types != 0) { types = rule.match.window-types; }
|
||||
// combinedRules;
|
||||
windowRules = listToAttrs (imap0
|
||||
(i: rule: {
|
||||
windowRules = listToAttrs (
|
||||
imap0 (i: rule: {
|
||||
name = toString (i + 1);
|
||||
value = buildWindowRule rule;
|
||||
})
|
||||
cfg.window-rules);
|
||||
}) cfg.window-rules
|
||||
);
|
||||
in
|
||||
{
|
||||
options.programs.plasma = {
|
||||
@ -150,14 +167,12 @@ in
|
||||
|
||||
config = mkIf (length cfg.window-rules > 0) {
|
||||
programs.plasma.configFile = {
|
||||
kwinrulesrc =
|
||||
{
|
||||
General = {
|
||||
count = length cfg.window-rules;
|
||||
rules = concatStringsSep "," (attrNames windowRules);
|
||||
};
|
||||
}
|
||||
// windowRules;
|
||||
kwinrulesrc = {
|
||||
General = {
|
||||
count = length cfg.window-rules;
|
||||
rules = concatStringsSep "," (attrNames windowRules);
|
||||
};
|
||||
} // windowRules;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -16,12 +16,13 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = (lib.mkIf (cfg.enable && cfg.windows.allowWindowsToRememberPositions != null) {
|
||||
programs.plasma.configFile = {
|
||||
kdeglobals = {
|
||||
General.AllowKDEAppsToRememberWindowPositions = cfg.windows.allowWindowsToRememberPositions;
|
||||
config = (
|
||||
lib.mkIf (cfg.enable && cfg.windows.allowWindowsToRememberPositions != null) {
|
||||
programs.plasma.configFile = {
|
||||
kdeglobals = {
|
||||
General.AllowKDEAppsToRememberWindowPositions = cfg.windows.allowWindowsToRememberPositions;
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,43 +1,76 @@
|
||||
# General workspace behavior settings:
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.plasma;
|
||||
inherit (import ../lib/wallpapers.nix { inherit lib; }) wallpaperPictureOfTheDayType wallpaperSlideShowType;
|
||||
|
||||
cursorType = with lib.types; submodule {
|
||||
options = {
|
||||
theme = lib.mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
example = "Breeze_Snow";
|
||||
description = "The Plasma cursortheme. Run plasma-apply-cursortheme --list-themes for valid options.";
|
||||
};
|
||||
size = lib.mkOption {
|
||||
type = nullOr ints.positive;
|
||||
default = null;
|
||||
example = 24;
|
||||
description = "The size of the cursor. See the settings GUI for allowed sizes for each cursortheme.";
|
||||
inherit (import ../lib/wallpapers.nix { inherit lib; })
|
||||
wallpaperPictureOfTheDayType
|
||||
wallpaperSlideShowType
|
||||
wallpaperFillModeTypes
|
||||
;
|
||||
|
||||
cursorType =
|
||||
with lib.types;
|
||||
submodule {
|
||||
options = {
|
||||
theme = lib.mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
example = "Breeze_Snow";
|
||||
description = "The Plasma cursortheme. Run plasma-apply-cursortheme --list-themes for valid options.";
|
||||
};
|
||||
size = lib.mkOption {
|
||||
type = nullOr ints.positive;
|
||||
default = null;
|
||||
example = 24;
|
||||
description = "The size of the cursor. See the settings GUI for allowed sizes for each cursortheme.";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
anyThemeSet = (cfg.workspace.theme != null ||
|
||||
cfg.workspace.colorScheme != null ||
|
||||
(cfg.workspace.cursor != null && cfg.workspace.cursor.theme != null) ||
|
||||
cfg.workspace.lookAndFeel != null ||
|
||||
cfg.workspace.iconTheme != null);
|
||||
anyThemeSet = (
|
||||
cfg.workspace.theme != null
|
||||
|| cfg.workspace.colorScheme != null
|
||||
|| (cfg.workspace.cursor != null && cfg.workspace.cursor.theme != null)
|
||||
|| cfg.workspace.lookAndFeel != null
|
||||
|| cfg.workspace.iconTheme != null
|
||||
);
|
||||
|
||||
splashScreenEngineDetect = theme: (if (theme == "None") then "none" else "KSplashQML");
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule [ "programs" "plasma" "workspace" "cursorTheme" ] [ "programs" "plasma" "workspace" "cursor" "theme" ])
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"cursorTheme"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"plasma"
|
||||
"workspace"
|
||||
"cursor"
|
||||
"theme"
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
options.programs.plasma.workspace = {
|
||||
clickItemTo = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum [ "open" "select" ]);
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (enum [
|
||||
"open"
|
||||
"select"
|
||||
]);
|
||||
default = null;
|
||||
description = ''
|
||||
Clicking files or folders should open or select them.
|
||||
@ -54,7 +87,7 @@ in
|
||||
};
|
||||
|
||||
theme = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
example = "breeze-dark";
|
||||
description = ''
|
||||
@ -63,7 +96,7 @@ in
|
||||
};
|
||||
|
||||
colorScheme = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
example = "BreezeDark";
|
||||
description = ''
|
||||
@ -74,14 +107,17 @@ in
|
||||
cursor = lib.mkOption {
|
||||
type = lib.types.nullOr cursorType;
|
||||
default = null;
|
||||
example = { theme = "Breeze_Snow"; size = 24; };
|
||||
example = {
|
||||
theme = "Breeze_Snow";
|
||||
size = 24;
|
||||
};
|
||||
description = ''
|
||||
Allows to configure the cursor in plasma. Both the theme and size are configurable.
|
||||
'';
|
||||
};
|
||||
|
||||
lookAndFeel = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
example = "org.kde.breezedark.desktop";
|
||||
description = ''
|
||||
@ -90,7 +126,7 @@ in
|
||||
};
|
||||
|
||||
iconTheme = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
example = "Papirus";
|
||||
description = ''
|
||||
@ -119,14 +155,16 @@ in
|
||||
wallpaperPictureOfTheDay = lib.mkOption {
|
||||
type = lib.types.nullOr wallpaperPictureOfTheDayType;
|
||||
default = null;
|
||||
example = { provider = "apod"; };
|
||||
example = {
|
||||
provider = "apod";
|
||||
};
|
||||
description = ''
|
||||
Allows you to set wallpaper using the picture of the day plugin. Needs the provider.
|
||||
'';
|
||||
};
|
||||
|
||||
wallpaperPlainColor = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
example = "0,64,174,256";
|
||||
description = ''
|
||||
@ -134,6 +172,17 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
wallpaperFillMode = lib.mkOption {
|
||||
type = with lib.types; nullOr (enum (builtins.attrNames wallpaperFillModeTypes));
|
||||
default = null;
|
||||
example = "stretch";
|
||||
description = ''
|
||||
Defines how the wallpaper should be displayed on the screen.
|
||||
Applies only to wallpaperPictureOfTheDay or wallpaperSlideShow.
|
||||
'';
|
||||
apply = value: if value == null then null else (builtins.toString wallpaperFillModeTypes.${value});
|
||||
};
|
||||
|
||||
soundTheme = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
@ -192,109 +241,247 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = (lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
let
|
||||
wallpapers = with cfg.workspace; [ wallpaperSlideShow wallpaper wallpaperPictureOfTheDay wallpaperPlainColor ];
|
||||
in
|
||||
lib.count (x: x != null) wallpapers <= 1;
|
||||
message = "Can set only one of wallpaper, wallpaperSlideShow, wallpaperPictureOfTheDay, and wallpaperPlainColor.";
|
||||
}
|
||||
{
|
||||
assertion = (cfg.workspace.splashScreen.engine == null || cfg.workspace.splashScreen.theme != null);
|
||||
message = ''
|
||||
Cannot set plasma.workspace.splashScreen.engine without a
|
||||
corresponding theme.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = !(lib.xor (cfg.workspace.windowDecorations.theme == null) (cfg.workspace.windowDecorations.library == null));
|
||||
message = ''
|
||||
Must set both plasma.workspace.windowDecorations.library and
|
||||
plasma.workspace.windowDecorations.theme or none.
|
||||
'';
|
||||
}
|
||||
];
|
||||
warnings = (if
|
||||
((cfg.workspace.lookAndFeel != null) &&
|
||||
(cfg.workspace.splashScreen.theme != null ||
|
||||
cfg.workspace.windowDecorations.theme != null)) then
|
||||
[
|
||||
''Setting lookAndFeel together with splashScreen or windowDecorations in
|
||||
plasma-manager is not recommended since lookAndFeel themes often
|
||||
override these settings. Consider setting each part in the lookAndFeel
|
||||
theme manually.''
|
||||
] else [ ]);
|
||||
|
||||
programs.plasma.configFile = (lib.mkMerge [
|
||||
{
|
||||
kdeglobals = {
|
||||
KDE.SingleClick = (lib.mkIf (cfg.workspace.clickItemTo != null) (cfg.workspace.clickItemTo == "open"));
|
||||
Sounds.Theme = (lib.mkIf (cfg.workspace.soundTheme != null) cfg.workspace.soundTheme);
|
||||
};
|
||||
plasmarc = (lib.mkIf (cfg.workspace.tooltipDelay != null) {
|
||||
PlasmaToolTips.Delay = cfg.workspace.tooltipDelay;
|
||||
});
|
||||
kcminputrc = (lib.mkIf (cfg.workspace.cursor != null && cfg.workspace.cursor.size != null) {
|
||||
Mouse.cursorSize = cfg.workspace.cursor.size;
|
||||
});
|
||||
ksplashrc.KSplash = (lib.mkIf (cfg.workspace.splashScreen.theme != null) {
|
||||
Engine = (if (cfg.workspace.splashScreen.engine == null) then
|
||||
(splashScreenEngineDetect cfg.workspace.splashScreen.theme)
|
||||
else cfg.workspace.splashScreen.engine);
|
||||
Theme = cfg.workspace.splashScreen.theme;
|
||||
});
|
||||
kwinrc = (lib.mkIf (cfg.workspace.windowDecorations.theme != null) {
|
||||
"org.kde.kdecoration2".library = cfg.workspace.windowDecorations.library;
|
||||
"org.kde.kdecoration2".theme = cfg.workspace.windowDecorations.theme;
|
||||
});
|
||||
}
|
||||
# We add persistence to some keys in order to not reset the themes on
|
||||
# each generation when we use overrideConfig.
|
||||
(lib.mkIf (cfg.overrideConfig && anyThemeSet) (
|
||||
let
|
||||
colorSchemeIgnore =
|
||||
if (cfg.workspace.colorScheme != null || cfg.workspace.lookAndFeel != null) then
|
||||
(import ../lib/colorscheme.nix {
|
||||
inherit lib;
|
||||
}) else { };
|
||||
in
|
||||
(lib.mkMerge
|
||||
config = (
|
||||
lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
let
|
||||
wallpapers = with cfg.workspace; [
|
||||
wallpaperSlideShow
|
||||
wallpaper
|
||||
wallpaperPictureOfTheDay
|
||||
wallpaperPlainColor
|
||||
];
|
||||
in
|
||||
lib.count (x: x != null) wallpapers <= 1;
|
||||
message = "Can set only one of wallpaper, wallpaperSlideShow, wallpaperPictureOfTheDay, and wallpaperPlainColor.";
|
||||
}
|
||||
{
|
||||
assertion = (cfg.workspace.splashScreen.engine == null || cfg.workspace.splashScreen.theme != null);
|
||||
message = ''
|
||||
Cannot set plasma.workspace.splashScreen.engine without a
|
||||
corresponding theme.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
!(lib.xor (cfg.workspace.windowDecorations.theme == null) (
|
||||
cfg.workspace.windowDecorations.library == null
|
||||
));
|
||||
message = ''
|
||||
Must set both plasma.workspace.windowDecorations.library and
|
||||
plasma.workspace.windowDecorations.theme or none.
|
||||
'';
|
||||
}
|
||||
];
|
||||
warnings = (
|
||||
if
|
||||
(
|
||||
(cfg.workspace.lookAndFeel != null)
|
||||
&& (cfg.workspace.splashScreen.theme != null || cfg.workspace.windowDecorations.theme != null)
|
||||
)
|
||||
then
|
||||
[
|
||||
{
|
||||
kcminputrc.Mouse.cursorTheme.persistent = lib.mkDefault (cfg.workspace.cursor != null && cfg.workspace.cursor.theme != null);
|
||||
kdeglobals.General.ColorScheme.persistent = lib.mkDefault (cfg.workspace.colorScheme != null || cfg.workspace.lookAndFeel != null);
|
||||
kdeglobals.Icons.Theme.persistent = lib.mkDefault (cfg.workspace.iconTheme != null);
|
||||
kdeglobals.KDE.LookAndFeelPackage.persistent = lib.mkDefault (cfg.workspace.lookAndFeel != null);
|
||||
plasmarc.Theme.name.persistent = lib.mkDefault (cfg.workspace.theme != null);
|
||||
}
|
||||
colorSchemeIgnore
|
||||
])
|
||||
))
|
||||
]);
|
||||
''
|
||||
Setting lookAndFeel together with splashScreen or windowDecorations in
|
||||
plasma-manager is not recommended since lookAndFeel themes often
|
||||
override these settings. Consider setting each part in the lookAndFeel
|
||||
theme manually.''
|
||||
]
|
||||
else
|
||||
[ ]
|
||||
);
|
||||
|
||||
# We create a script which applies the different theme settings using
|
||||
# kde tools. We then run this using an autostart script, where this is
|
||||
# run only on the first login (unless overrideConfig is enabled),
|
||||
# granted all the commands succeed (until we change the settings again).
|
||||
programs.plasma.startup.startupScript."apply_themes" = (lib.mkIf anyThemeSet {
|
||||
text = ''
|
||||
${if cfg.workspace.lookAndFeel != null then "plasma-apply-lookandfeel -a ${cfg.workspace.lookAndFeel}" else ""}
|
||||
${if cfg.workspace.theme != null then "plasma-apply-desktoptheme ${cfg.workspace.theme}" else ""}
|
||||
${if (cfg.workspace.cursor != null && cfg.workspace.cursor.theme != null) then
|
||||
"plasma-apply-cursortheme ${cfg.workspace.cursor.theme}" +
|
||||
(if cfg.workspace.cursor.size != null then " --size ${builtins.toString cfg.workspace.cursor.size}" else "")
|
||||
else ""}
|
||||
${if cfg.workspace.colorScheme != null then "plasma-apply-colorscheme ${cfg.workspace.colorScheme}" else ""}
|
||||
${if cfg.workspace.iconTheme != null then "${pkgs.kdePackages.plasma-workspace}/libexec/plasma-changeicons ${cfg.workspace.iconTheme}" else ""}
|
||||
'';
|
||||
priority = 1;
|
||||
});
|
||||
programs.plasma.configFile = (
|
||||
lib.mkMerge [
|
||||
{
|
||||
kdeglobals = {
|
||||
KDE.SingleClick = (
|
||||
lib.mkIf (cfg.workspace.clickItemTo != null) (cfg.workspace.clickItemTo == "open")
|
||||
);
|
||||
Sounds.Theme = (lib.mkIf (cfg.workspace.soundTheme != null) cfg.workspace.soundTheme);
|
||||
};
|
||||
plasmarc = (
|
||||
lib.mkIf (cfg.workspace.tooltipDelay != null) { PlasmaToolTips.Delay = cfg.workspace.tooltipDelay; }
|
||||
);
|
||||
kcminputrc = (
|
||||
lib.mkIf (cfg.workspace.cursor != null && cfg.workspace.cursor.size != null) {
|
||||
Mouse.cursorSize = cfg.workspace.cursor.size;
|
||||
}
|
||||
);
|
||||
ksplashrc.KSplash = (
|
||||
lib.mkIf (cfg.workspace.splashScreen.theme != null) {
|
||||
Engine = (
|
||||
if (cfg.workspace.splashScreen.engine == null) then
|
||||
(splashScreenEngineDetect cfg.workspace.splashScreen.theme)
|
||||
else
|
||||
cfg.workspace.splashScreen.engine
|
||||
);
|
||||
Theme = cfg.workspace.splashScreen.theme;
|
||||
}
|
||||
);
|
||||
kwinrc = (
|
||||
lib.mkIf (cfg.workspace.windowDecorations.theme != null) {
|
||||
"org.kde.kdecoration2".library = cfg.workspace.windowDecorations.library;
|
||||
"org.kde.kdecoration2".theme = cfg.workspace.windowDecorations.theme;
|
||||
}
|
||||
);
|
||||
}
|
||||
# We add persistence to some keys in order to not reset the themes on
|
||||
# each generation when we use overrideConfig.
|
||||
(lib.mkIf (cfg.overrideConfig && anyThemeSet) (
|
||||
let
|
||||
colorSchemeIgnore =
|
||||
if (cfg.workspace.colorScheme != null || cfg.workspace.lookAndFeel != null) then
|
||||
(import ../lib/colorscheme.nix { inherit lib; })
|
||||
else
|
||||
{ };
|
||||
in
|
||||
(lib.mkMerge [
|
||||
{
|
||||
kcminputrc.Mouse.cursorTheme.persistent = lib.mkDefault (
|
||||
cfg.workspace.cursor != null && cfg.workspace.cursor.theme != null
|
||||
);
|
||||
kdeglobals.General.ColorScheme.persistent = lib.mkDefault (
|
||||
cfg.workspace.colorScheme != null || cfg.workspace.lookAndFeel != null
|
||||
);
|
||||
kdeglobals.Icons.Theme.persistent = lib.mkDefault (cfg.workspace.iconTheme != null);
|
||||
kdeglobals.KDE.LookAndFeelPackage.persistent = lib.mkDefault (cfg.workspace.lookAndFeel != null);
|
||||
plasmarc.Theme.name.persistent = lib.mkDefault (cfg.workspace.theme != null);
|
||||
}
|
||||
colorSchemeIgnore
|
||||
])
|
||||
))
|
||||
]
|
||||
);
|
||||
|
||||
# The wallpaper configuration can be found in panels.nix due to wallpaper
|
||||
# configuration and panel configuration being stored in the same file, and
|
||||
# thus should be using the same desktop-script.
|
||||
});
|
||||
# We create a script which applies the different theme settings using
|
||||
# kde tools. We then run this using an autostart script, where this is
|
||||
# run only on the first login (unless overrideConfig is enabled),
|
||||
# granted all the commands succeed (until we change the settings again).
|
||||
programs.plasma.startup = {
|
||||
startupScript."apply_themes" = (
|
||||
lib.mkIf anyThemeSet {
|
||||
text = ''
|
||||
${
|
||||
if cfg.workspace.lookAndFeel != null then
|
||||
"plasma-apply-lookandfeel -a ${cfg.workspace.lookAndFeel}"
|
||||
else
|
||||
""
|
||||
}
|
||||
${if cfg.workspace.theme != null then "plasma-apply-desktoptheme ${cfg.workspace.theme}" else ""}
|
||||
${
|
||||
if (cfg.workspace.cursor != null && cfg.workspace.cursor.theme != null) then
|
||||
"plasma-apply-cursortheme ${cfg.workspace.cursor.theme}"
|
||||
+ (
|
||||
if cfg.workspace.cursor.size != null then
|
||||
" --size ${builtins.toString cfg.workspace.cursor.size}"
|
||||
else
|
||||
""
|
||||
)
|
||||
else
|
||||
""
|
||||
}
|
||||
${
|
||||
if cfg.workspace.colorScheme != null then
|
||||
"plasma-apply-colorscheme ${cfg.workspace.colorScheme}"
|
||||
else
|
||||
""
|
||||
}
|
||||
${
|
||||
if cfg.workspace.iconTheme != null then
|
||||
"${pkgs.kdePackages.plasma-workspace}/libexec/plasma-changeicons ${cfg.workspace.iconTheme}"
|
||||
else
|
||||
""
|
||||
}
|
||||
'';
|
||||
priority = 1;
|
||||
}
|
||||
);
|
||||
|
||||
desktopScript."wallpaper_picture" = (
|
||||
lib.mkIf (cfg.workspace.wallpaper != null) {
|
||||
# We just put this here as we need this script to be a desktop-script in
|
||||
# order to link it together with the other desktop-script (namely
|
||||
# panels). Adding a comment with the wallpaper makes it so that when the
|
||||
# wallpaper changes, the sha256sum also changes for the js file, which
|
||||
# gives us the correct behavior with last_run files.
|
||||
text = "// Wallpaper to set later: ${cfg.workspace.wallpaper}";
|
||||
postCommands = ''
|
||||
plasma-apply-wallpaperimage ${cfg.workspace.wallpaper}
|
||||
'';
|
||||
priority = 3;
|
||||
}
|
||||
);
|
||||
|
||||
desktopScript."wallpaper_potd" = (
|
||||
lib.mkIf (cfg.workspace.wallpaperPictureOfTheDay != null) {
|
||||
text = ''
|
||||
// Wallpaper POTD
|
||||
let allDesktops = desktops();
|
||||
for (const desktop of allDesktops) {
|
||||
desktop.wallpaperPlugin = "org.kde.potd";
|
||||
desktop.currentConfigGroup = ["Wallpaper", "org.kde.potd", "General"];
|
||||
desktop.writeConfig("Provider", "${cfg.workspace.wallpaperPictureOfTheDay.provider}");
|
||||
desktop.writeConfig("UpdateOverMeteredConnection", "${
|
||||
if (cfg.workspace.wallpaperPictureOfTheDay.updateOverMeteredConnection) then "1" else "0"
|
||||
}");
|
||||
${
|
||||
lib.optionalString (
|
||||
cfg.workspace.wallpaperFillMode != null
|
||||
) ''desktop.writeConfig("FillMode", "${cfg.workspace.wallpaperFillMode}");''
|
||||
}
|
||||
}
|
||||
'';
|
||||
priority = 3;
|
||||
}
|
||||
);
|
||||
|
||||
desktopScript."wallpaper_plaincolor" = (
|
||||
lib.mkIf (cfg.workspace.wallpaperPlainColor != null) {
|
||||
text = ''
|
||||
// Wallpaper plain color
|
||||
let allDesktops = desktops();
|
||||
for (var desktopIndex = 0; desktopIndex < allDesktops.length; desktopIndex++) {
|
||||
var desktop = allDesktops[desktopIndex];
|
||||
desktop.wallpaperPlugin = "org.kde.color";
|
||||
desktop.currentConfigGroup = Array("Wallpaper", "org.kde.color", "General");
|
||||
desktop.writeConfig("Color", "${cfg.workspace.wallpaperPlainColor}");
|
||||
}
|
||||
'';
|
||||
priority = 3;
|
||||
}
|
||||
);
|
||||
|
||||
desktopScript."wallpaper_slideshow" = (
|
||||
lib.mkIf (cfg.workspace.wallpaperSlideShow != null) {
|
||||
text = ''
|
||||
// Wallpaper slideshow
|
||||
let allDesktops = desktops();
|
||||
for (var desktopIndex = 0; desktopIndex < allDesktops.length; desktopIndex++) {
|
||||
var desktop = allDesktops[desktopIndex];
|
||||
desktop.wallpaperPlugin = "org.kde.slideshow";
|
||||
desktop.currentConfigGroup = Array("Wallpaper", "org.kde.slideshow", "General");
|
||||
desktop.writeConfig("SlidePaths", ${
|
||||
with cfg.workspace.wallpaperSlideShow;
|
||||
if ((builtins.isPath path) || (builtins.isString path)) then
|
||||
"\"" + (builtins.toString path) + "\""
|
||||
else
|
||||
"[" + (builtins.concatStringsSep "," (map (s: "\"" + s + "\"") path)) + "]"
|
||||
});
|
||||
desktop.writeConfig("SlideInterval", "${builtins.toString cfg.workspace.wallpaperSlideShow.interval}");
|
||||
${
|
||||
lib.optionalString (
|
||||
cfg.workspace.wallpaperFillMode != null
|
||||
) ''desktop.writeConfig("FillMode", "${cfg.workspace.wallpaperFillMode}");''
|
||||
}
|
||||
}
|
||||
'';
|
||||
priority = 3;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
178
script/rc2nix.py
178
script/rc2nix.py
@ -18,43 +18,60 @@ import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Callable, Optional, Tuple
|
||||
from typing import Callable, Dict, List, Optional, Tuple
|
||||
|
||||
# The root directory where configuration files are stored.
|
||||
XDG_CONFIG_HOME: str = os.path.expanduser(os.getenv("XDG_CONFIG_HOME", "~/.config"))
|
||||
XDG_DATA_HOME: str = os.path.expanduser(os.getenv("XDG_DATA_HOME", "~/.local/share"))
|
||||
|
||||
|
||||
class Rc2Nix:
|
||||
# Files that we'll scan by default.
|
||||
KNOWN_FILES: List[str] = [os.path.join(XDG_CONFIG_HOME, f) for f in [
|
||||
"kcminputrc",
|
||||
"kglobalshortcutsrc",
|
||||
"kactivitymanagerdrc",
|
||||
"ksplashrc",
|
||||
"kwin_rules_dialogrc",
|
||||
"kmixrc",
|
||||
"kwalletrc",
|
||||
"kgammarc",
|
||||
"krunnerrc",
|
||||
"klaunchrc",
|
||||
"plasmanotifyrc",
|
||||
"systemsettingsrc",
|
||||
"kscreenlockerrc",
|
||||
"kwinrulesrc",
|
||||
"khotkeysrc",
|
||||
"ksmserverrc",
|
||||
"kded5rc",
|
||||
"plasmarc",
|
||||
"kwinrc",
|
||||
"kdeglobals",
|
||||
"baloofilerc",
|
||||
"dolphinrc",
|
||||
"klipperrc",
|
||||
"plasma-localerc",
|
||||
"kxkbrc",
|
||||
"ffmpegthumbsrc",
|
||||
"kservicemenurc",
|
||||
"kiorc",
|
||||
]]
|
||||
KNOWN_CONFIG_FILES: List[str] = [
|
||||
os.path.join(XDG_CONFIG_HOME, f)
|
||||
for f in [
|
||||
"kcminputrc",
|
||||
"kglobalshortcutsrc",
|
||||
"kactivitymanagerdrc",
|
||||
"ksplashrc",
|
||||
"kwin_rules_dialogrc",
|
||||
"kmixrc",
|
||||
"kwalletrc",
|
||||
"kgammarc",
|
||||
"krunnerrc",
|
||||
"klaunchrc",
|
||||
"plasmanotifyrc",
|
||||
"systemsettingsrc",
|
||||
"kscreenlockerrc",
|
||||
"kwinrulesrc",
|
||||
"khotkeysrc",
|
||||
"ksmserverrc",
|
||||
"kded5rc",
|
||||
"plasmarc",
|
||||
"kwinrc",
|
||||
"kdeglobals",
|
||||
"baloofilerc",
|
||||
"dolphinrc",
|
||||
"klipperrc",
|
||||
"plasma-localerc",
|
||||
"kxkbrc",
|
||||
"ffmpegthumbsrc",
|
||||
"kservicemenurc",
|
||||
"kiorc",
|
||||
"ktrashrc",
|
||||
"kuriikwsfilterrc",
|
||||
"plasmaparc",
|
||||
"spectaclerc",
|
||||
"katerc",
|
||||
]
|
||||
]
|
||||
KNOWN_DATA_FILES: List[str] = [
|
||||
os.path.join(XDG_DATA_HOME, f)
|
||||
for f in [
|
||||
"kate/anonymous.katesession",
|
||||
"dolphin/view_properties/global/.directory",
|
||||
]
|
||||
]
|
||||
|
||||
class RcFile:
|
||||
# Any group that matches a listed regular expression is blocked
|
||||
@ -69,6 +86,7 @@ class Rc2Nix:
|
||||
r"^PlasmaViews",
|
||||
r"^ScreenConnectors$",
|
||||
r"^Session:",
|
||||
r"^Recent (Files|URLs)",
|
||||
]
|
||||
|
||||
# Similar to the GROUP_BLOCK_LIST but for setting keys.
|
||||
@ -97,21 +115,23 @@ class Rc2Nix:
|
||||
def parse(self):
|
||||
|
||||
def is_group_line(line: str) -> bool:
|
||||
return re.match(r'^\s*(\[[^\]]+\]){1,}\s*$', line) is not None
|
||||
return re.match(r"^\s*(\[[^\]]+\])+\s*$", line) is not None
|
||||
|
||||
def is_setting_line(line: str) -> bool:
|
||||
return re.match(r'^\s*([^=]+)=?(.*)\s*$', line) is not None
|
||||
return re.match(r"^\s*([^=]+)=?(.*)\s*$", line) is not None
|
||||
|
||||
def parse_group(line: str) -> str:
|
||||
return re.sub(r'\s*\[([^\]]+)\]\s*', r'\1/', line.replace("/", "\\\\/")).rstrip("/")
|
||||
return re.sub(
|
||||
r"\s*\[([^\]]+)\]\s*", r"\1/", line.replace("/", "\\\\/")
|
||||
).rstrip("/")
|
||||
|
||||
def parse_setting(line: str) -> Tuple[str, str]:
|
||||
match = re.match(r'^\s*([^=]+)=?(.*)\s*$', line)
|
||||
match = re.match(r"^\s*([^=]+)=?(.*)\s*$", line)
|
||||
if match:
|
||||
return match.groups() #type: ignore
|
||||
return match.groups() # type: ignore
|
||||
raise Exception(f"{self.file_name}: can't parse setting line: {line}")
|
||||
|
||||
with open(self.file_name, 'r') as file:
|
||||
with open(self.file_name, "r") as file:
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
@ -139,9 +159,15 @@ class Rc2Nix:
|
||||
val = val.strip()
|
||||
|
||||
if self.last_group is None:
|
||||
raise Exception(f"{self.file_name}: setting outside of group: {key}={val}")
|
||||
raise Exception(
|
||||
f"{self.file_name}: setting outside of group: {key}={val}"
|
||||
)
|
||||
|
||||
if should_skip_group(self.last_group) or should_skip_key(key) or should_skip_by_lambda(self.last_group, key):
|
||||
if (
|
||||
should_skip_group(self.last_group)
|
||||
or should_skip_key(key)
|
||||
or should_skip_by_lambda(self.last_group, key)
|
||||
):
|
||||
return
|
||||
|
||||
if self.last_group not in self.settings:
|
||||
@ -150,12 +176,13 @@ class Rc2Nix:
|
||||
|
||||
class App:
|
||||
def __init__(self, args: List[str]):
|
||||
self.files: List[str] = Rc2Nix.KNOWN_FILES.copy()
|
||||
self.config_files: List[str] = Rc2Nix.KNOWN_CONFIG_FILES.copy()
|
||||
self.data_files: List[str] = Rc2Nix.KNOWN_DATA_FILES.copy()
|
||||
self.config_settings: Dict[str, Dict[str, Dict[str, str]]] = {}
|
||||
self.data_settings: Dict[str, Dict[str, Dict[str, str]]] = {}
|
||||
|
||||
def run(self):
|
||||
settings: Dict[str, Dict[str, Dict[str, str]]] = {}
|
||||
|
||||
for file in self.files:
|
||||
for file in self.config_files:
|
||||
if not os.path.exists(file):
|
||||
continue
|
||||
|
||||
@ -163,62 +190,97 @@ class Rc2Nix:
|
||||
rc.parse()
|
||||
|
||||
path = Path(file).relative_to(XDG_CONFIG_HOME)
|
||||
settings[str(path)] = rc.settings
|
||||
self.config_settings[str(path)] = rc.settings
|
||||
|
||||
self.print_output(settings)
|
||||
for file in self.data_files:
|
||||
if not os.path.exists(file):
|
||||
continue
|
||||
|
||||
def print_output(self, settings: Dict[str, Dict[str, Dict[str, str]]]):
|
||||
rc = Rc2Nix.RcFile(file)
|
||||
rc.parse()
|
||||
|
||||
path = Path(file).relative_to(XDG_DATA_HOME)
|
||||
self.data_settings[str(path)] = rc.settings
|
||||
|
||||
self.print_output()
|
||||
|
||||
def print_output(self):
|
||||
print("{")
|
||||
print(" programs.plasma = {")
|
||||
print(" enable = true;")
|
||||
print(" shortcuts = {")
|
||||
print(self.pp_shortcuts(settings.get("kglobalshortcutsrc", {}), 6))
|
||||
print(
|
||||
self.pp_shortcuts(self.config_settings.get("kglobalshortcutsrc", {}), 6)
|
||||
)
|
||||
print(" };")
|
||||
print(" configFile = {")
|
||||
print(self.pp_settings(settings, 6))
|
||||
print(self.pp_settings(self.config_settings, 6))
|
||||
print(" };")
|
||||
print(" dataFile = {")
|
||||
print(self.pp_settings(self.data_settings, 6))
|
||||
print(" };")
|
||||
print(" };")
|
||||
print("}")
|
||||
|
||||
def pp_settings(self, settings: Dict[str, Dict[str, Dict[str, str]]], indent: int) -> str:
|
||||
result : List[str] = []
|
||||
def pp_settings(
|
||||
self, settings: Dict[str, Dict[str, Dict[str, str]]], indent: int
|
||||
) -> str:
|
||||
result: List[str] = []
|
||||
for file in sorted(settings.keys()):
|
||||
if file != "kglobalshortcutsrc":
|
||||
for group in sorted(settings[file].keys()):
|
||||
for key in sorted(settings[file][group].keys()):
|
||||
if key != "_k_friendly_name":
|
||||
result.append(f"{' ' * indent}\"{file}\".\"{group}\".\"{key}\" = {nix_val(settings[file][group][key])};")
|
||||
result.append(
|
||||
f"{' ' * indent}\"{file}\".\"{group}\".\"{key}\" = {nix_val(settings[file][group][key])};"
|
||||
)
|
||||
return "\n".join(result)
|
||||
|
||||
def pp_shortcuts(self, groups: Dict[str, Dict[str, str]], indent: int) -> str:
|
||||
if not groups:
|
||||
return ""
|
||||
|
||||
result : List[str] = []
|
||||
result: List[str] = []
|
||||
for group in sorted(groups.keys()):
|
||||
for action in sorted(groups[group].keys()):
|
||||
if action != "_k_friendly_name":
|
||||
keys = groups[group][action].split(r'(?<!\\),')[0].replace(r'\?', ',').replace(r'\t', '\t').split('\t')
|
||||
keys = (
|
||||
groups[group][action]
|
||||
.split(r"(?<!\\),")[0]
|
||||
.replace(r"\?", ",")
|
||||
.replace(r"\t", "\t")
|
||||
.split("\t")
|
||||
)
|
||||
|
||||
if not keys or keys[0] == "none":
|
||||
keys_str = "[ ]"
|
||||
elif len(keys) > 1:
|
||||
keys_str = f"[{' '.join(nix_val(k.rstrip(',')) for k in keys)}]"
|
||||
keys_str = (
|
||||
f"[{' '.join(nix_val(k.rstrip(',')) for k in keys)}]"
|
||||
)
|
||||
else:
|
||||
ks = keys[0].split(",")
|
||||
k = ks[0] if len(ks) == 3 and ks[0] == ks[1] else keys[0]
|
||||
keys_str = "[ ]" if k == "" or k == "none" else nix_val(k.rstrip(","))
|
||||
keys_str = (
|
||||
"[ ]"
|
||||
if k == "" or k == "none"
|
||||
else nix_val(k.rstrip(","))
|
||||
)
|
||||
|
||||
result.append(f"{' ' * indent}\"{group}\".\"{action}\" = {keys_str};")
|
||||
result.append(
|
||||
f"{' ' * indent}\"{group}\".\"{action}\" = {keys_str};"
|
||||
)
|
||||
return "\n".join(result)
|
||||
|
||||
|
||||
def nix_val(s: Optional[str]) -> str:
|
||||
if s is None:
|
||||
return "null"
|
||||
if re.match(r'^true|false$', s, re.IGNORECASE):
|
||||
if re.match(r"^(true|false)$", s, re.IGNORECASE):
|
||||
return s.lower()
|
||||
if re.match(r'^[0-9]+(\.[0-9]+)?$', s):
|
||||
if re.match(r"^[0-9]+(\.[0-9]+)?$", s):
|
||||
return s
|
||||
return '"' + re.sub(r'(?<!\\)"', r'\\"', s) + '"'
|
||||
|
||||
|
||||
Rc2Nix.App(sys.argv[1:]).run()
|
||||
|
@ -3,15 +3,14 @@ import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Optional, Self, Set
|
||||
from typing import Any, Optional, Self
|
||||
|
||||
|
||||
# KDE has a bespoke escape format:
|
||||
# https://github.com/KDE/kconfig/blob/44f98ff5cb9008436ba5ba385cae03bbd0ab33e6/src/core/kconfigini.cpp#L882
|
||||
def unescape(s: str) -> str:
|
||||
out = []
|
||||
out: list[str] = []
|
||||
while s:
|
||||
parts = s.split("\\", 1)
|
||||
out.append(parts.pop(0))
|
||||
@ -57,41 +56,28 @@ def escape_bytes(c: str) -> str:
|
||||
def escape(s: str) -> str:
|
||||
if not s:
|
||||
return s
|
||||
s = list(s)
|
||||
for i, c in enumerate(s):
|
||||
ls: list[str] = list(s)
|
||||
for i, c in enumerate(ls):
|
||||
match c:
|
||||
case "\n":
|
||||
s[i] = "\\n"
|
||||
ls[i] = "\\n"
|
||||
case "\t":
|
||||
s[i] = "\\t"
|
||||
ls[i] = "\\t"
|
||||
case "\r":
|
||||
s[i] = "\\r"
|
||||
ls[i] = "\\r"
|
||||
case "\\":
|
||||
s[i] = "\\\\"
|
||||
ls[i] = "\\\\"
|
||||
case "=" | "[" | "]":
|
||||
s[i] = escape_bytes(c)
|
||||
ls[i] = escape_bytes(c)
|
||||
case _ if ord(c) < 32:
|
||||
s[i] = escape_bytes(c)
|
||||
ls[i] = escape_bytes(c)
|
||||
case _:
|
||||
pass
|
||||
for i in (0, -1):
|
||||
if s[i] == " ":
|
||||
s[i] = "\\s"
|
||||
return "".join(s)
|
||||
if ls[i] == " ":
|
||||
ls[i] = "\\s"
|
||||
return "".join(ls)
|
||||
|
||||
# Some values need to be "transformed", such as being converted from hex to decimal
|
||||
def transformValues(d_orig):
|
||||
d = d_orig.copy()
|
||||
|
||||
c_path = os.path.expanduser("~/.config/kcminputrc")
|
||||
if c_path in d:
|
||||
for item_str in list(d[c_path]):
|
||||
if item_str.startswith("Libinput/"):
|
||||
item = d[c_path].pop(item_str)
|
||||
item_str_list = item_str.split("/")
|
||||
item_str_list[1] = str(int(item_str_list[1],16))
|
||||
item_str_list[2] = str(int(item_str_list[2],16))
|
||||
item_str = "/".join(item_str_list)
|
||||
d[c_path][item_str] = item
|
||||
return d;
|
||||
|
||||
@dataclass
|
||||
class ConfigValue:
|
||||
@ -118,14 +104,14 @@ class ConfigValue:
|
||||
return key, value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, value: dict) -> Self:
|
||||
def from_json(cls, value: dict[str, Any]) -> Self:
|
||||
key_value = (
|
||||
str(value["value"])
|
||||
if not isinstance(value["value"], bool)
|
||||
else str(value["value"]).lower()
|
||||
)
|
||||
return cls(
|
||||
value=escape(key_value),
|
||||
value=escape(key_value) if value["escapeValue"] else key_value,
|
||||
immutable=value["immutable"],
|
||||
shellExpand=value["shellExpand"],
|
||||
)
|
||||
@ -155,31 +141,34 @@ class ConfigValue:
|
||||
|
||||
class KConfManager:
|
||||
def __init__(
|
||||
self, filepath: str, json_dict: Dict, reset: bool, immutable_by_default: bool
|
||||
self,
|
||||
filepath: str,
|
||||
json_dict: dict[str, Any],
|
||||
reset: bool,
|
||||
immutable_by_default: bool,
|
||||
):
|
||||
"""
|
||||
filepath (str): The full path to the config-file to manage
|
||||
json_dict (Dict): The nix-configuration presented in a dictionary (converted from json)
|
||||
reset (bool): Whether to reset the file, i.e. remove all the lines not present in the configuration
|
||||
"""
|
||||
self.data = {}
|
||||
self.json_dict = json_dict
|
||||
self.data: dict[tuple[str, ...], dict[str, ConfigValue]] = {}
|
||||
self.filepath = filepath
|
||||
self.reset = reset
|
||||
self.immutable_by_default = immutable_by_default
|
||||
self._json_value_checks()
|
||||
self._json_value_checks(json_dict)
|
||||
# The nix expressions will have / to separate groups, and \/ to escape a /.
|
||||
# This parses the groups into tuples of unescaped group names.
|
||||
self.json_dict = {
|
||||
self.json_dict: dict[tuple[str, ...], Any] = {
|
||||
tuple(
|
||||
g.replace("\\/", "/")
|
||||
for g in re.findall(r"(/|(?:[^/\\]|\\.)+)", group)[::2]
|
||||
): entry
|
||||
for group, entry in self.json_dict.items()
|
||||
for group, entry in json_dict.items()
|
||||
}
|
||||
|
||||
def _json_value_checks(self):
|
||||
for group, entry in self.json_dict.items():
|
||||
def _json_value_checks(self, json_dict: dict[str, Any]):
|
||||
for group, entry in json_dict.items():
|
||||
for key, value in entry.items():
|
||||
non_default_immutability = (
|
||||
value["immutable"] != self.immutable_by_default
|
||||
@ -209,7 +198,7 @@ class KConfManager:
|
||||
f"{base_msg} with shell-expansion enabled. Persistency with shell-expansion enabled is not supported"
|
||||
)
|
||||
|
||||
def key_is_persistent(self, group, key) -> bool:
|
||||
def key_is_persistent(self, group: tuple[str, ...], key: str) -> bool:
|
||||
"""
|
||||
Checks if a key in a group in the nix config is persistent.
|
||||
"""
|
||||
@ -271,14 +260,14 @@ class KConfManager:
|
||||
if not value["persistent"]:
|
||||
self.set_value(group, key, ConfigValue.from_json(value))
|
||||
|
||||
def set_value(self, group, key, value):
|
||||
def set_value(self, group: tuple[str, ...], key: str, value: ConfigValue):
|
||||
"""Adds an entry to the config. Creates necessary groups if needed."""
|
||||
if not group in self.data:
|
||||
self.data[group] = {}
|
||||
|
||||
self.data[group][key] = value
|
||||
|
||||
def remove_value(self, group, key):
|
||||
def remove_value(self, group: tuple[str, ...], key: str):
|
||||
"""Removes an entry from the config. Does nothing if the entry isn't there."""
|
||||
if group in self.data and key in self.data[group]:
|
||||
del self.data[group][key]
|
||||
@ -313,18 +302,18 @@ class KConfManager:
|
||||
f.write(f"{value.to_line(key)}\n")
|
||||
|
||||
|
||||
def remove_config_files(d: Dict, reset_files: Set):
|
||||
def remove_config_files(d: dict[str, Any], reset_files: set[str]):
|
||||
"""
|
||||
Removes files which doesn't have any configuration entries in d and which is
|
||||
in the list of files to be reset by overrideConfig.
|
||||
"""
|
||||
for del_path in reset_files - set(d.keys()):
|
||||
for file_to_del in glob.glob(del_path):
|
||||
for file_to_del in glob.glob(del_path, recursive=True):
|
||||
if os.path.isfile(file_to_del):
|
||||
os.remove(file_to_del)
|
||||
|
||||
|
||||
def write_configs(d: Dict, reset_files: Set, immutable_by_default: bool):
|
||||
def write_configs(d: dict[str, Any], reset_files: set[str], immutable_by_default: bool):
|
||||
for filepath, c in d.items():
|
||||
config = KConfManager(
|
||||
filepath, c, filepath in reset_files, immutable_by_default
|
||||
@ -343,10 +332,9 @@ def main():
|
||||
with open(json_path, "r") as f:
|
||||
json_str = f.read()
|
||||
|
||||
reset_files = set(sys.argv[2].split(" ")) if sys.argv[2] != "" else set()
|
||||
reset_files: set[str] = set(sys.argv[2].split(" ")) if sys.argv[2] != "" else set()
|
||||
immutable_by_default = bool(sys.argv[3])
|
||||
d_raw = json.loads(json_str)
|
||||
d = transformValues(d_raw)
|
||||
d = json.loads(json_str)
|
||||
remove_config_files(d, reset_files)
|
||||
write_configs(d, reset_files, immutable_by_default)
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ testers, home-manager-module, plasma-module, writeShellScriptBin, kdePackages }:
|
||||
{
|
||||
testers,
|
||||
home-manager-module,
|
||||
plasma-module,
|
||||
writeShellScriptBin,
|
||||
kdePackages,
|
||||
}:
|
||||
let
|
||||
script = writeShellScriptBin "plasma-basic-test" ''
|
||||
set -eu
|
||||
@ -48,39 +54,41 @@ testers.nixosTest {
|
||||
isNormalUser = true;
|
||||
};
|
||||
|
||||
home-manager.users.fake = { lib, ... }: {
|
||||
home.stateVersion = "23.11";
|
||||
imports = [ plasma-module ];
|
||||
programs.plasma = {
|
||||
enable = true;
|
||||
workspace.clickItemTo = "select";
|
||||
# Test a variety of weird keys and groups
|
||||
configFile.kdeglobals = {
|
||||
group = {
|
||||
" leading space" = " leading space";
|
||||
key1 = 1;
|
||||
key2 = {
|
||||
value = 2;
|
||||
immutable = true;
|
||||
home-manager.users.fake =
|
||||
{ lib, ... }:
|
||||
{
|
||||
home.stateVersion = "23.11";
|
||||
imports = [ plasma-module ];
|
||||
programs.plasma = {
|
||||
enable = true;
|
||||
workspace.clickItemTo = "select";
|
||||
# Test a variety of weird keys and groups
|
||||
configFile.kdeglobals = {
|
||||
group = {
|
||||
" leading space" = " leading space";
|
||||
key1 = 1;
|
||||
key2 = {
|
||||
value = 2;
|
||||
immutable = true;
|
||||
};
|
||||
"escaped[$i]" = {
|
||||
value = "\${HOME}";
|
||||
shellExpand = true;
|
||||
};
|
||||
};
|
||||
"escaped[$i]" = {
|
||||
value = "\${HOME}";
|
||||
shellExpand = true;
|
||||
"escaped\\/nested/group" = {
|
||||
key3 = 3;
|
||||
};
|
||||
};
|
||||
"escaped\\/nested/group" = {
|
||||
key3 = 3;
|
||||
};
|
||||
};
|
||||
home.activation.preseed = lib.hm.dag.entryBefore [ "configure-plasma" ] ''
|
||||
mkdir -p ~/.config
|
||||
cat <<EOF >> ~/.config/kdeglobals
|
||||
[escaped/nested][group]
|
||||
untouched = \svalue
|
||||
EOF
|
||||
'';
|
||||
};
|
||||
home.activation.preseed = lib.hm.dag.entryBefore [ "configure-plasma" ] ''
|
||||
mkdir -p ~/.config
|
||||
cat <<EOF >> ~/.config/kdeglobals
|
||||
[escaped/nested][group]
|
||||
untouched = \svalue
|
||||
EOF
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
|
@ -1,6 +1,4 @@
|
||||
{ home-manager-module
|
||||
, plasma-module
|
||||
}:
|
||||
{ home-manager-module, plasma-module }:
|
||||
|
||||
{ modulesPath, ... }:
|
||||
{
|
||||
@ -28,11 +26,13 @@
|
||||
];
|
||||
};
|
||||
|
||||
virtualisation.forwardPorts = [{
|
||||
from = "host";
|
||||
host.port = 2222;
|
||||
guest.port = 22;
|
||||
}];
|
||||
virtualisation.forwardPorts = [
|
||||
{
|
||||
from = "host";
|
||||
host.port = 2222;
|
||||
guest.port = 22;
|
||||
}
|
||||
];
|
||||
|
||||
services.xserver.enable = true;
|
||||
services.displayManager = {
|
||||
|
@ -1,30 +1,47 @@
|
||||
#!/usr/bin/env nix
|
||||
#! nix shell nixpkgs#python3Packages.python nixpkgs#ruby -c python3
|
||||
import unittest
|
||||
import subprocess
|
||||
import os
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
|
||||
def red(s: str) -> str:
|
||||
return '\033[91m' + s + '\033[0m'
|
||||
return "\033[91m" + s + "\033[0m"
|
||||
|
||||
|
||||
def green(s: str) -> str:
|
||||
return '\033[32m' + s + '\033[0m'
|
||||
return "\033[32m" + s + "\033[0m"
|
||||
|
||||
|
||||
def gray(s: str) -> str:
|
||||
return '\033[90m' + s + '\033[0m'
|
||||
return "\033[90m" + s + "\033[0m"
|
||||
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def path(relative_path: str) -> str:
|
||||
return os.path.abspath(os.path.join(current_dir, relative_path))
|
||||
|
||||
|
||||
rc2nix_py = path("../../script/rc2nix.py")
|
||||
rc2nix_rb = path("../../script/rc2nix.rb")
|
||||
|
||||
class TestRc2nix (unittest.TestCase):
|
||||
|
||||
class TestRc2nix(unittest.TestCase):
|
||||
|
||||
def test(self):
|
||||
def run_script(*command: str) -> str:
|
||||
rst = subprocess.run(command, env={'XDG_CONFIG_HOME': path('./test_data'), 'PATH': os.environ["PATH"]}, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
rst = subprocess.run(
|
||||
command,
|
||||
env={
|
||||
"XDG_CONFIG_HOME": path("./test_data"),
|
||||
"PATH": os.environ["PATH"],
|
||||
},
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
)
|
||||
print(red(rst.stderr))
|
||||
rst.check_returncode()
|
||||
return rst.stdout
|
||||
@ -35,5 +52,5 @@ class TestRc2nix (unittest.TestCase):
|
||||
self.assertEqual(rst_py.splitlines(), rst_rb.splitlines())
|
||||
|
||||
|
||||
if __name__ == '__main__': # pragma: no cover
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
_ = unittest.main()
|
||||
|
11
treefmt.toml
Normal file
11
treefmt.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[formatter.nixfmt-rfc-style]
|
||||
command = "nixfmt"
|
||||
includes = ["*.nix"]
|
||||
|
||||
[formatter.black]
|
||||
command = "black"
|
||||
includes = ["*.py", "*.pyi"]
|
||||
|
||||
[formatter.isort]
|
||||
command = "isort"
|
||||
includes = ["*.py", "*.pyi"]
|
Loading…
Reference in New Issue
Block a user