From 2f0999538a3e19fcb5e7cc30fb7c22be2823a50a Mon Sep 17 00:00:00 2001 From: bricked Date: Wed, 26 Feb 2025 19:41:10 +0100 Subject: [PATCH] godot: init module and lib This adds the `programs.godot` module with support for editor configurations and text editor themes. It's accompanied by the `godot` library module which exposes constructors for godot data types. --- modules/lib/default.nix | 1 + modules/lib/godot.nix | 76 ++++++++++++++++++++ modules/modules.nix | 1 + modules/programs/godot.nix | 137 +++++++++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 modules/lib/godot.nix create mode 100644 modules/programs/godot.nix diff --git a/modules/lib/default.nix b/modules/lib/default.nix index 8014c625e..bf0d368b7 100644 --- a/modules/lib/default.nix +++ b/modules/lib/default.nix @@ -15,4 +15,5 @@ rec { shell = import ./shell.nix { inherit lib; }; zsh = import ./zsh.nix { inherit lib; }; nushell = import ./nushell.nix { inherit lib; }; + godot = import ./godot.nix { inherit lib; }; } diff --git a/modules/lib/godot.nix b/modules/lib/godot.nix new file mode 100644 index 000000000..8df44973d --- /dev/null +++ b/modules/lib/godot.nix @@ -0,0 +1,76 @@ +{ lib }: + +let + inherit (lib) + concatMapStringsSep escape replaceStrings boolToString floatToString + concatStringsSep mapAttrsToList; + + inherit (builtins) mapAttrs; + + mkPrimitive = t: v: { + _type = "godotData"; + type = t; + value = v; + __toString = self: toString self.value; + }; + +in rec { + isGodotData = v: v._type or "" == "godotData"; + + mkValue = v: + if builtins.isBool v then + mkBool v + else if builtins.isInt v then + mkInt v + else if builtins.isFloat v then + mkFloat v + else if builtins.isString v then + mkString v + else if builtins.isList v then + mkList v + else if builtins.isAttrs v && (v._type or "") == "godotData" then + v + else if builtins.isAttrs v then + mkAttrs v + else + null; + + mkList = elems: + mkPrimitive "list" (map mkValue elems) // { + __toString = self: "[${concatMapStringsSep ", " toString self.value}]"; + }; + + mkAttrs = attrs: + mkPrimitive "attrs" (mapAttrs (name: mkValue) attrs) // { + __toString = self: + "{${ + concatStringsSep ", " + (mapAttrsToList (name: value: "${mkString name}: ${value}") + self.value) + }}"; + }; + + mkBool = v: + mkPrimitive "bool" v // { + __toString = self: boolToString self.value; + }; + + mkString = v: + let + sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ ''"'' "\\" ] s); + in mkPrimitive "string" v // { + __toString = self: ''"${sanitize self.value}"''; + }; + + mkInt = mkPrimitive "int"; + + mkFloat = mkPrimitive "float"; + + mkCall = name: args: + mkPrimitive "call" { inherit name args; } // { + __toString = self: + "${self.value.name}(${ + concatMapStringsSep ", " toString self.value.args + })"; + }; +} diff --git a/modules/modules.nix b/modules/modules.nix index ef357bd3a..563dd92c1 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -118,6 +118,7 @@ let ./programs/gnome-shell.nix ./programs/gnome-terminal.nix ./programs/go.nix + ./programs/godot.nix ./programs/gpg.nix ./programs/gradle.nix ./programs/granted.nix diff --git a/modules/programs/godot.nix b/modules/programs/godot.nix new file mode 100644 index 000000000..7da2814b4 --- /dev/null +++ b/modules/programs/godot.nix @@ -0,0 +1,137 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) + literalExpression mkEnableOption mkOption mkIf types hm mapAttrsToList + splitString mkOptionDefault singleton concatMapStringsSep mkMerge mapAttrs' + nameValuePair; + + inherit (hm.godot) mkValue; + + inherit (builtins) concatStringsSep typeOf toString head; + + cfg = config.programs.godot; + + primitiveType = with types; oneOf [ str int float bool attrs ]; + + primitiveTypeOrAttrs = with types; + either primitiveType (attrsOf primitiveType); + + composedType = with types; + either primitiveTypeOrAttrs (listOf primitiveTypeOrAttrs); + + dataType = with types; coercedTo composedType mkValue attrs; + + # See https://docs.godotengine.org/en/latest/contributing/development/file_formats/tscn.html. + genResources = concatMapStringsSep "\n\n" genResource; + + genResource = { type, attributes ? { }, data ? { } }: + concatStringsSep "\n" ([ + (genHeader { inherit type attributes; }) + ] ++ (mapAttrsToList (name: value: "${name} = ${value}") data)); + + genHeader = { type, attributes }: + "[${ + concatStringsSep " " ([ type ] + ++ (mapAttrsToList (name: val: "${name}=${mkValue val}") attributes)) + }]"; + +in { + meta.maintainers = [ hm.maintainers.bricked ]; + + options.programs.godot = { + enable = mkEnableOption "Godot"; + + package = lib.mkPackageOption pkgs "godot_4" { }; + + version = lib.mkOption { + type = types.str; + description = '' + Semantic version of the Godot package. + ''; + internal = true; + }; + + settings = mkOption { + type = with types; nullOr (attrsOf dataType); + description = '' + Attribute set of godot editor settings. + + For a list of available options see https://docs.godotengine.org/en/stable/classes/class_editorsettings.html. + + Primitive values, lists and attrs work as expected. For function calls, 'lib.hm.godot.mkCall name args' is used. + ''; + example = literalExpression '' + { + "resource_local_to_scene" = false; + "interface/editor/editor_language" = "en"; + "interface/editor/main_font_size" = 14; + "interface/theme/accent_color" = lib.hm.godot.mkCall "Color" [ 0.5, 0.5, 1.0, 1.0 ]; + "text_editor/theme/color_theme" = "Dracula"; + }''; + }; + + textThemes = mkOption { + type = with types; + attrsOf (submodule ({ name, ... }: { + options = { + name = mkOption { + type = types.str; + description = '' + The name of the text editor theme. + ''; + example = "Dracula"; + default = name; + }; + + settings = mkOption { + type = with types; attrsOf dataType; + description = '' + Attribute set of text editor theme data. + ''; + example = { + text_color = "cdd6f4ff"; + number_color = "fab387ff"; + }; + }; + }; + })); + description = '' + Attribute set of text editor themes. + + The default text editor theme can be defined using the "text_editor/theme/color_theme" setting. + ''; + default = { }; + }; + }; + + config = mkIf cfg.enable { + programs.godot.version = + mkOptionDefault (head (splitString "-" cfg.package.version)); + + home.packages = [ cfg.package ]; + + xdg.configFile = { + "godot/editor_settings-${cfg.version}.tres".text = + mkIf (cfg.settings != null) (genResources [ + { + type = "gd_resource"; + attributes = { + type = "EditorSettings"; + format = 3; + }; + } + + { + type = "resource"; + data = cfg.settings; + } + ]); + } // mapAttrs' (name: theme: + nameValuePair "godot/text_editor_themes/${theme.name}.tet" { + text = genResource { + type = "color_theme"; + data = theme.settings; + }; + }) cfg.textThemes; + }; +}