diff --git a/home/desktop/cosmic.nix b/home/desktop/cosmic.nix new file mode 100644 index 0000000..3337b21 --- /dev/null +++ b/home/desktop/cosmic.nix @@ -0,0 +1,32 @@ +_: + +{ + imports = [ ../modules/cosmic ]; + + programs.cosmic = { + enable = true; + settings = { + "com.system76.CosmicTk".v1 = { + icon_theme = "Cosmic"; + }; + "com.system76.CosmicFiles".v1 = { + tab.show_hidden = true; + favorites = [ + "Home" + "Documents" + "Downloads" + "Music" + "Pictures" + "Videos" + ''Path("/home/felschr/dev")'' + ''Path("/home/felschr/dev/work")'' + ''Path("/home/felschr/dev/mscs")'' + ''Path("/home/felschr/Documents/CU Boulder MSCS")'' + ]; + }; + "com.system76.CosmicAppletAudio".v1 = { + show_media_controls_in_top_panel = true; + }; + }; + }; +} diff --git a/home/desktop/default.nix b/home/desktop/default.nix index b5d0adb..ee28be4 100644 --- a/home/desktop/default.nix +++ b/home/desktop/default.nix @@ -4,6 +4,7 @@ imports = [ ./gtk.nix ./gnome.nix + ./cosmic.nix ./mimeapps.nix ]; } diff --git a/home/flake-module.nix b/home/flake-module.nix index caee67c..2849f00 100644 --- a/home/flake-module.nix +++ b/home/flake-module.nix @@ -33,6 +33,7 @@ in firefox = import ./modules/firefox/firefox.nix; tor-browser = import ./modules/firefox/tor-browser.nix; mullvad-browser = import ./modules/firefox/mullvad-browser.nix; + cosmic = import ./modules/cosmic; # users felschr = import ./felschr.nix; diff --git a/home/modules/cosmic/default.nix b/home/modules/cosmic/default.nix new file mode 100644 index 0000000..7a5cc08 --- /dev/null +++ b/home/modules/cosmic/default.nix @@ -0,0 +1,53 @@ +{ config, lib, ... }: + +# Based on: +# https://github.com/tristanbeedell/home-manager/tree/efa4d272f6c2b14d4a3b67b0b1e4b38ae46e5588/modules/programs/cosmic + +let + cfg = config.programs.cosmic; + + ron = import ./ron.nix { inherit lib; }; +in +{ + imports = [ ./settings.nix ]; + + options.programs.cosmic = { + enable = lib.mkEnableOption "COSMIC Desktop"; + settings = lib.mkOption { + default = { }; + type = lib.types.submodule { + freeformType = lib.types.submodule { + freeformType = lib.types.attrsOf lib.types.anything; + }; + }; + description = '' + An attrset of explicit settings for COSMIC apps, using their full config path. + ''; + example = lib.literalExpression '' + { + "com.system76.CosmicPanel.Dock".v1 = { + opacity = 0.8; + }; + }; + ''; + }; + }; + + config = lib.mkIf cfg.enable { + xdg.configFile = lib.concatMapAttrs ( + component: + lib.concatMapAttrs ( + version: + lib.concatMapAttrs ( + option: value: + lib.optionalAttrs (value != null) { + "cosmic/${component}/${version}/${option}" = { + enable = true; + text = ron.serialise value; + }; + } + ) + ) + ) cfg.settings; + }; +} diff --git a/home/modules/cosmic/ron.nix b/home/modules/cosmic/ron.nix new file mode 100644 index 0000000..3727ea9 --- /dev/null +++ b/home/modules/cosmic/ron.nix @@ -0,0 +1,129 @@ +{ lib }: +let + inherit (lib) + filterAttrs + concatStrings + concatStringsSep + mapAttrsToList + concatLists + foldlAttrs + boolToString + ; + inherit (builtins) typeOf toString stringLength; + + # list -> array + array = a: "[${concatStringsSep "," (map serialise a)}]"; + + # attrset -> hashmap + _assoc = a: mapAttrsToList (name: val: "${name}: ${val},") a; + assoc = a: '' + { + ${concatStringsSep "\n " (concatLists (map _assoc a))} + } + ''; + + stringArray = a: array (map toQuotedString a); + + tuple = a: "(${concatStringsSep "," (map serialise a)})"; + enum = + s: + if isNull s.value then + s.name + else + concatStrings [ + s.name + "(" + (serialise s.value) + ")" + ]; + + option = + value: + if isNull value then + "None" + else + enum { + name = "Some"; + inherit value; + }; + + # attrset -> struct + _struct_kv = + k: v: + if v == null then + "" + else + (concatStringsSep ": " [ + k + (serialise v) + ]); + _struct_concat = + s: + foldlAttrs ( + acc: k: v: + if stringLength acc > 0 then + concatStringsSep ", " [ + acc + (_struct_kv k v) + ] + else + _struct_kv k v + ) "" s; + _struct_filt = s: _struct_concat (filterAttrs (k: v: v != null) s); + struct = s: "(${_struct_filt s})"; + + toQuotedString = s: ''"${toString s}"''; + + # this is an enum, but use string interp to make sure it's put in the nix store + path = p: ''Path("${p}")''; + + # attrset for best-effort serialisation of nix types + # currently, no way to differentiate between enums and structs + serialisers = { + int = toString; + float = toString; + bool = boolToString; + # can't assume quoted string, sometimes it's a Rust enum + string = toString; + path = path; + null = _: "None"; + set = struct; + list = array; + }; + + serialise = v: serialisers.${typeOf v} v; + +in +{ + inherit + array + assoc + tuple + stringArray + serialise + option + path + struct + toQuotedString + enum + ; + + # some handy wrapper types, to reduce transformation effort in modules producing Ron + types = { + # string type, but implicitly wraps in quote marks for usage in Ron + str = (lib.types.coercedTo lib.types.str toQuotedString lib.types.str) // { + description = "string"; + }; + + # takes a (non submodule) type, and implicitly wraps in Rust Option<> type + option = + type: + let + wantedType = lib.types.nullOr (type); + in + (lib.types.coercedTo (wantedType) (option) lib.types.str) + // { + description = wantedType.description; + }; + }; +} diff --git a/home/modules/cosmic/settings.nix b/home/modules/cosmic/settings.nix new file mode 100644 index 0000000..8a8a101 --- /dev/null +++ b/home/modules/cosmic/settings.nix @@ -0,0 +1,108 @@ +{ lib, ... }: + +# Known definitions for settings options + +let + inherit (lib) types; + ron = import ./ron.nix { inherit lib; }; + cosmic = { + types = import ./types.nix { inherit lib ron; }; + }; +in +{ + options.programs.cosmic.settings = { + # https://github.com/pop-os/libcosmic/blob/1fce5df160f595d1b1e5a8e2bb2a24775419f82d/src/config/mod.rs#L85 + "com.system76.CosmicTk".v1 = { + apply_theme_global = lib.mkOption { + type = types.nullOr types.bool; + default = null; + description = "Apply the theme to other toolkits."; + }; + show_minimize = lib.mkOption { + type = types.nullOr types.bool; + default = null; + description = "Show minimize button in window header."; + }; + show_maximize = lib.mkOption { + type = types.nullOr types.bool; + default = null; + description = "Show maximize button in window header."; + }; + icon_theme = lib.mkOption { + type = types.nullOr ron.types.str; + default = null; + description = "Preferred icon theme."; + }; + header_size = lib.mkOption { + type = types.nullOr cosmic.types.density; + default = null; + description = "Density of CSD/SSD header bars."; + }; + interface_density = lib.mkOption { + type = types.nullOr cosmic.types.density; + default = null; + description = "Interface density."; + }; + interface_font = lib.mkOption { + type = types.nullOr cosmic.types.font_config; + default = null; + description = "Interface font family"; + }; + monospace_font = lib.mkOption { + type = types.nullOr cosmic.types.font_config; + default = null; + description = "Mono font family"; + }; + }; + + # https://github.com/pop-os/cosmic-files/blob/1a5a4501ee501b3155295cbfccc1c992b5ec9c01/src/config.rs#L106 + "com.system76.CosmicFiles".v1 = { + app_theme = lib.mkOption { + type = types.nullOr cosmic.types.theme; + default = null; + description = ""; + }; + desktop = lib.mkOption { + type = types.nullOr cosmic.types.files_desktop; + default = null; + description = ""; + }; + favorites = lib.mkOption { + type = types.nullOr cosmic.types.files_favorites; + default = null; + description = ""; + }; + show_details = lib.mkOption { + type = types.nullOr types.bool; + default = null; + description = ""; + }; + tab = lib.mkOption { + type = types.nullOr cosmic.types.files_tab_config; + default = null; + description = ""; + }; + type_to_search = lib.mkOption { + type = types.nullOr ( + types.enum [ + "Recursive" + "EnterPath" + ] + ); + default = null; + description = ""; + }; + }; + + # https://github.com/pop-os/cosmic-applets/blob/b4b465712218be8d26b6772a382d19f4c3ff97f8/cosmic-applet-audio/src/config.rs#L9 + "com.system76.CosmicAppletAudio".v1 = { + show_media_controls_in_top_panel = lib.mkOption { + type = types.nullOr types.bool; + default = null; + description = ""; + }; + }; + + # ... + }; +} diff --git a/home/modules/cosmic/types.nix b/home/modules/cosmic/types.nix new file mode 100644 index 0000000..9ad749a --- /dev/null +++ b/home/modules/cosmic/types.nix @@ -0,0 +1,145 @@ +{ lib, ron }: + +let + inherit (lib) types; +in +lib.fix (self: { + theme = types.enum [ + "Dark" + "Light" + "System" + ]; + density = types.enum [ + "Compact" + "Spacious" + "Standard" + ]; + weight = types.enum [ + "Thin" + "ExtraLight" + "Light" + "Normal" + "Medium" + "Semibold" + "Bold" + "ExtraBold" + "Black" + ]; + stretch = types.enum [ + "UltraCondensed" + "ExtraCondensed" + "Condensed" + "SemiCondensed" + "Normal" + "SemiExpanded" + "Expanded" + "ExtraExpanded" + "UltraExpanded" + ]; + style = types.enum [ + "Normal" + "Italic" + "Oblique" + ]; + + # libcosmic + font_config = types.submodule { + options = { + family = lib.mkOption { + type = ron.types.str; + default = null; + }; + weight = lib.mkOption { + type = types.nullOr self.weight; + default = null; + }; + stretch = lib.mkOption { + type = types.nullOr self.stretch; + default = null; + }; + style = lib.mkOption { + type = types.nullOr self.style; + default = null; + }; + }; + }; + + # Files + files_desktop = types.submodule { + options = { + grid_spacing = lib.mkOption { + type = types.nullOr types.int; + default = null; + }; + icon_size = lib.mkOption { + type = types.nullOr types.int; + default = null; + }; + show_content = lib.mkOption { + type = types.nullOr types.bool; + default = null; + }; + show_mounted_drives = lib.mkOption { + type = types.nullOr types.bool; + default = null; + }; + show_trash = lib.mkOption { + type = types.nullOr types.bool; + default = null; + }; + }; + }; + files_favorites = types.listOf ( + types.oneOf [ + (types.enum [ + "Home" + "Documents" + "Downloads" + "Music" + "Pictures" + "Videos" + ]) + (lib.types.strMatching ''^Path\(".*"\)$'') + ] + ); + files_view = types.enum [ + "Grid" + "List" + ]; + files_icon_sizes = types.submodule { + options = { + list = lib.mkOption { + type = types.nullOr types.int; + default = null; + }; + grid = lib.mkOption { + type = types.nullOr types.int; + default = null; + }; + }; + }; + files_tab_config = types.submodule { + options = { + view = lib.mkOption { + type = types.nullOr self.files_view; + default = null; + }; + folders_first = lib.mkOption { + type = types.nullOr types.bool; + default = null; + }; + show_hidden = lib.mkOption { + type = types.nullOr types.bool; + default = null; + }; + icon_sizes = lib.mkOption { + type = types.nullOr self.files_icon_sizes; + default = null; + }; + single_click = lib.mkOption { + type = types.nullOr types.bool; + default = null; + }; + }; + }; +})