diff --git a/README.md b/README.md index 25953bf0..3dd2fc71 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ 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) diff --git a/modules/default.nix b/modules/default.nix index 9b993ee7..fc245a55 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -3,6 +3,7 @@ { imports = [ ./apps + ./desktop.nix ./files.nix ./fonts.nix ./hotkeys.nix diff --git a/modules/desktop.nix b/modules/desktop.nix new file mode 100644 index 00000000..573b6f96 --- /dev/null +++ b/modules/desktop.nix @@ -0,0 +1,269 @@ +{ 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; + }); + }; + }); +} diff --git a/modules/panels.nix b/modules/panels.nix index 90888b07..0a120c93 100644 --- a/modules/panels.nix +++ b/modules/panels.nix @@ -7,7 +7,11 @@ let cfg = config.programs.plasma; inherit (import ../lib/wallpapers.nix { inherit lib; }) wallpaperFillModeTypes; - 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. diff --git a/modules/widgets/application-title-bar.nix b/modules/widgets/application-title-bar.nix index 839de974..ef448681 100644 --- a/modules/widgets/application-title-bar.nix +++ b/modules/widgets/application-title-bar.nix @@ -2,6 +2,7 @@ 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; @@ -114,6 +115,16 @@ 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; @@ -455,7 +466,9 @@ in }; }; convert = - { layout + { position + , size + , layout , windowControlButtons , windowTitle , overrideForMaximized diff --git a/modules/widgets/battery.nix b/modules/widgets/battery.nix index 243e2126..91584b4b 100644 --- a/modules/widgets/battery.nix +++ b/modules/widgets/battery.nix @@ -1,12 +1,23 @@ { 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; @@ -25,13 +36,18 @@ in { }; }; - 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; }; }; } diff --git a/modules/widgets/default.nix b/modules/widgets/default.nix index 4516b44a..ef036cc9 100644 --- a/modules/widgets/default.nix +++ b/modules/widgets/default.nix @@ -19,6 +19,36 @@ let ./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 { @@ -69,15 +99,93 @@ let }; }; + 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 + 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. + ''; + }; + }; + }; + isKnownWidget = lib.flip builtins.hasAttr sources; self = { - inherit isKnownWidget; + inherit isKnownWidget positionType sizeType; type = lib.types.oneOf [ lib.types.str compositeWidgetType simpleWidgetType ]; + desktopType = lib.types.oneOf [ compositeWidgetType desktopSimpleWidgetType ]; lib = import ./lib.nix (args // { widgets = self; }); + desktopConvert = widget: + let + 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; diff --git a/modules/widgets/digital-clock.nix b/modules/widgets/digital-clock.nix index 95b75c3e..8c67d4f2 100644 --- a/modules/widgets/digital-clock.nix +++ b/modules/widgets/digital-clock.nix @@ -2,6 +2,7 @@ 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; @@ -52,6 +53,16 @@ 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."; @@ -238,7 +249,9 @@ in }; convert = - { date + { position + , size + , date , time , timeZone , calendar diff --git a/modules/widgets/icon-tasks.nix b/modules/widgets/icon-tasks.nix index d23ea66b..5aaeea0d 100644 --- a/modules/widgets/icon-tasks.nix +++ b/modules/widgets/icon-tasks.nix @@ -2,6 +2,7 @@ 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; @@ -40,6 +41,16 @@ in 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; @@ -158,7 +169,9 @@ in }; }; convert = - { appearance + { position + , size + , appearance , behavior , launchers , settings diff --git a/modules/widgets/keyboard-layout.nix b/modules/widgets/keyboard-layout.nix index de8d2b86..eb6465a7 100644 --- a/modules/widgets/keyboard-layout.nix +++ b/modules/widgets/keyboard-layout.nix @@ -1,6 +1,7 @@ { lib, ... }: let inherit (import ./lib.nix { inherit lib; }) configValueType; + inherit (import ./default.nix { inherit lib; }) positionType sizeType; getIndexFromEnum = enum: value: if value == null @@ -16,6 +17,16 @@ in 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 { @@ -37,13 +48,18 @@ in }; }; - convert = { displayStyle, settings }: { - name = "org.kde.plasma.keyboardlayout"; - config = lib.recursiveUpdate { - General = lib.filterAttrs (_: v: v != null) { - inherit displayStyle; - }; - } settings; + convert = + { position + , size + , displayStyle + , settings + }: { + name = "org.kde.plasma.keyboardlayout"; + config = lib.recursiveUpdate { + General = lib.filterAttrs (_: v: v != null) { + inherit displayStyle; + }; + } settings; }; }; } diff --git a/modules/widgets/kicker.nix b/modules/widgets/kicker.nix index 74c4d9dd..6e1bcc0b 100644 --- a/modules/widgets/kicker.nix +++ b/modules/widgets/kicker.nix @@ -2,6 +2,7 @@ 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; @@ -36,6 +37,16 @@ in ''; 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; @@ -95,7 +106,9 @@ in }; }; convert = - { icon + { position + , size + , icon , customButtonImage , applicationNameFormat , behavior diff --git a/modules/widgets/kickerdash.nix b/modules/widgets/kickerdash.nix index daa764ba..5577efbd 100644 --- a/modules/widgets/kickerdash.nix +++ b/modules/widgets/kickerdash.nix @@ -2,6 +2,7 @@ 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; @@ -32,6 +33,16 @@ in 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; @@ -88,7 +99,9 @@ in }; }; convert = - { icon + { position + , size + , icon , customButtonImage , applicationNameFormat , behavior diff --git a/modules/widgets/kickoff.nix b/modules/widgets/kickoff.nix index 2197bee9..26bb342d 100644 --- a/modules/widgets/kickoff.nix +++ b/modules/widgets/kickoff.nix @@ -2,6 +2,7 @@ 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; @@ -29,6 +30,16 @@ 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; @@ -108,7 +119,9 @@ in }; }; convert = - { icon + { position + , size + , icon , label , sortAlphabetically , compactDisplayStyle diff --git a/modules/widgets/lib.nix b/modules/widgets/lib.nix index 5b6967af..e18088f7 100644 --- a/modules/widgets/lib.nix +++ b/modules/widgets/lib.nix @@ -63,11 +63,32 @@ let 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} = {}; + ${lib.concatMapStringsSep "\n" addStmt ws} + ''; in { inherit stringIfNotNull setWidgetSettings addWidgetStmts + addDesktopWidgetStmts configValueType; } diff --git a/modules/widgets/plasma-panel-colorizer.nix b/modules/widgets/plasma-panel-colorizer.nix index 5233608a..e2a4941c 100644 --- a/modules/widgets/plasma-panel-colorizer.nix +++ b/modules/widgets/plasma-panel-colorizer.nix @@ -2,6 +2,7 @@ let inherit (lib) mkOption types; inherit (import ./lib.nix { inherit lib; }) configValueType; + inherit (import ./default.nix { inherit lib; }) positionType sizeType; mkBoolOption = description: mkOption { @@ -100,6 +101,16 @@ in 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"; @@ -717,7 +728,9 @@ in }; }; convert = - { general + { position + , size + , general , presetAutoLoading , widgetBackground , textAndIcons diff --git a/modules/widgets/plasmusic-toolbar.nix b/modules/widgets/plasmusic-toolbar.nix index 96d174fe..f1a95a6e 100644 --- a/modules/widgets/plasmusic-toolbar.nix +++ b/modules/widgets/plasmusic-toolbar.nix @@ -2,6 +2,7 @@ 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; }; @@ -217,6 +218,16 @@ 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; @@ -327,7 +338,9 @@ in }; }; convert = - { panelIcon + { position + , size + , panelIcon , preferredSource , songText , musicControls @@ -336,6 +349,7 @@ in , settings }: { name = "plasmusic-toolbar"; + config = lib.recursiveUpdate { General = lib.filterAttrs (_: v: v != null) ( { diff --git a/modules/widgets/system-monitor.nix b/modules/widgets/system-monitor.nix index dea11d37..c8484cc2 100644 --- a/modules/widgets/system-monitor.nix +++ b/modules/widgets/system-monitor.nix @@ -2,6 +2,7 @@ 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 @@ -55,6 +56,16 @@ 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; @@ -146,7 +157,9 @@ in }; convert = - { title + { position + , size + , title , showTitle , showLegend , displayStyle diff --git a/modules/widgets/system-tray.nix b/modules/widgets/system-tray.nix index 6d6cb5a0..4d7c733a 100644 --- a/modules/widgets/system-tray.nix +++ b/modules/widgets/system-tray.nix @@ -2,6 +2,7 @@ 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; @@ -16,6 +17,16 @@ in opts = ({ options, ... }: { # See https://invent.kde.org/plasma/plasma-workspace/-/blob/master/applets/systemtray/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)"; + }; pin = mkBoolOption "Whether the popup should remain open when another window is activated."; icons = { @@ -162,7 +173,9 @@ in }); convert = - { pin + { position + , size + , pin , icons , items , settings diff --git a/modules/workspace.nix b/modules/workspace.nix index fa87e95a..2bc38554 100644 --- a/modules/workspace.nix +++ b/modules/workspace.nix @@ -3,8 +3,8 @@ let cfg = config.programs.plasma; + inherit (import ../lib/wallpapers.nix { inherit lib; }) wallpaperPictureOfTheDayType wallpaperSlideShowType wallpaperFillModeTypes; - inherit (import ./widgets/lib.nix { inherit lib; }) stringIfNotNull; cursorType = with lib.types; submodule { options = { @@ -23,41 +23,12 @@ let }; }; - 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); - 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); - # Becomes true if any option under "cfg.workspace.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.workspace.desktop.icons; - - # Becomes true if any option under "cfg.workspace.desktop.mouseActions" is set to something other than null. - anyDesktopMouseActionsSet = lib.any (v: v != null) (builtins.attrValues cfg.workspace.desktop.mouseActions); - splashScreenEngineDetect = theme: (if (theme == "None") then "none" else "KSplashQML"); in { @@ -231,140 +202,6 @@ in ''; }; }; - - 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}; - }; - }; - }; }; config = (lib.mkIf cfg.enable { @@ -468,42 +305,6 @@ in ''; priority = 1; }); - - 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.workspace.desktop.icons.arrangement == "topToBottom") ''desktop.writeConfig("arrangement", 1);''} - ${lib.optionalString (cfg.workspace.desktop.icons.alignment == "right") ''desktop.writeConfig("alignment", 1);''} - ${lib.optionalString (cfg.workspace.desktop.icons.lockInPlace == true) ''desktop.writeConfig("locked", true);''} - ${stringIfNotNull cfg.workspace.desktop.icons.size ''desktop.writeConfig("iconSize", ${builtins.toString cfg.workspace.desktop.icons.size});''} - ${lib.optionalString (cfg.workspace.desktop.icons.folderPreviewPopups == false) ''desktop.writeConfig("popups", false);''} - ${stringIfNotNull cfg.workspace.desktop.icons.previewPlugins ''desktop.writeConfig("previewPlugins", "${lib.strings.concatStringsSep "," cfg.workspace.desktop.icons.previewPlugins}");''} - ${stringIfNotNull cfg.workspace.desktop.icons.sorting.mode ''desktop.writeConfig("sortMode", ${builtins.toString cfg.workspace.desktop.icons.sorting.mode});''} - ${lib.optionalString (cfg.workspace.desktop.icons.sorting.descending == true) ''desktop.writeConfig("sortDesc", true);''} - ${lib.optionalString (cfg.workspace.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) - ${stringIfNotNull cfg.workspace.desktop.mouseActions.leftClick ''actionPluginSubSection.writeEntry("LeftButton;NoModifier", "${cfg.workspace.desktop.mouseActions.leftClick}");''} - ${stringIfNotNull cfg.workspace.desktop.mouseActions.middleClick ''actionPluginSubSection.writeEntry("MiddleButton;NoModifier", "${cfg.workspace.desktop.mouseActions.middleClick}");''} - ${stringIfNotNull cfg.workspace.desktop.mouseActions.rightClick ''actionPluginSubSection.writeEntry("RightButton;NoModifier", "${cfg.workspace.desktop.mouseActions.rightClick}");''} - ${stringIfNotNull cfg.workspace.desktop.mouseActions.verticalScroll ''actionPluginSubSection.writeEntry("wheel:Vertical;NoModifier", "${cfg.workspace.desktop.mouseActions.verticalScroll}");''} - ''; - priority = 3; - restartServices = [ "plasma-plasmashell" ]; - }); }; # The wallpaper configuration can be found in panels.nix due to wallpaper