From 1ecfd8e5626b27a9610f468be32cd6a7011f56a0 Mon Sep 17 00:00:00 2001 From: Florent Charpentier <114689807+C0Florent@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:37:34 +1000 Subject: [PATCH] hyprland: add support for submaps (#6062) (#7277) --- modules/services/window-managers/hyprland.nix | 95 ++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/modules/services/window-managers/hyprland.nix b/modules/services/window-managers/hyprland.nix index 49ec0f8d..49392f64 100644 --- a/modules/services/window-managers/hyprland.nix +++ b/modules/services/window-managers/hyprland.nix @@ -216,6 +216,70 @@ in ''; }; + submaps = lib.mkOption { + description = '' + Attribute set of Hyprland submaps. + + See to learn about submaps + ''; + default = { }; + type = lib.types.attrsOf ( + lib.types.submodule ( + { name, config, ... }: + { + options = { + settings = lib.mkOption { + type = (with lib.types; attrsOf (listOf str)) // { + description = "Hyprland binds"; + }; + default = { }; + description = '' + Hyprland binds to be put in the submap + ''; + example = lib.literalExpression '' + { + binde = [ + ", right, resizeactive, 10 0" + ", left, resizeactive, -10 0" + ", up, resizeactive, 0 -10" + ", down, resizeactive, 0 10" + ]; + + bind = [ + ", escape, submap, reset" + ]; + } + ''; + }; + }; + } + ) + ); + example = lib.literalExpression '' + { + # submap to change window focus with vim keys + move_focus = { + settings = { + bind = [ + ", h, movefocus, l" + ", j, movefocus, d" + ", k, movefocus, u" + ", l, movefocus, r" + + ", escape, submap, reset" + ]; + }; + }; + + other_submap = { + settings = { + # ... + }; + }; + } + ''; + }; + extraConfig = lib.mkOption { type = lib.types.lines; default = ""; @@ -265,6 +329,10 @@ in config = lib.mkIf cfg.enable { assertions = [ (lib.hm.assertions.assertPlatform "wayland.windowManager.hyprland" pkgs lib.platforms.linux) + { + assertion = !builtins.hasAttr "reset" cfg.submaps; + message = "Submaps can't be named 'reset'. The name 'reset' is reserved in order to have a way to switch to the default submap; as if 'reset' was its name."; + } ]; warnings = @@ -272,8 +340,21 @@ in inconsistent = (cfg.systemd.enable || cfg.plugins != [ ]) && cfg.extraConfig == "" && cfg.settings == { }; warning = "You have enabled hyprland.systemd.enable or listed plugins in hyprland.plugins but do not have any configuration in hyprland.settings or hyprland.extraConfig. This is almost certainly a mistake."; + + filterNonBinds = + attrs: + builtins.filter (n: builtins.match ''bind[[:lower:]]*'' n == null) (builtins.attrNames attrs); + + # attrset of { = ; } for all submaps + submapWarningsAttrset = builtins.mapAttrs ( + name: submap: filterNonBinds submap.settings + ) cfg.submaps; + + submapWarnings = lib.mapAttrsToList (submapName: nonBinds: '' + wayland.windowManager.hyprland.submaps."${submapName}".settings: found non-bind entries: [${builtins.toString nonBinds}], which will have no effect in a submap + '') (lib.filterAttrs (n: v: v != [ ]) submapWarningsAttrset); in - lib.optional inconsistent warning; + submapWarnings ++ lib.optional inconsistent warning; home.packages = lib.mkIf (cfg.package != null) ( [ cfg.finalPackage ] ++ lib.optional cfg.xwayland.enable pkgs.xwayland @@ -299,6 +380,17 @@ in }; inherit importantPrefixes; }; + + mkSubMap = name: attrs: '' + submap = ${name} + ${lib.hm.generators.toHyprconf { + attrs = attrs.settings; + indentLevel = 1; + }} + submap = reset + ''; + + submapsToHyprConf = lib.concatMapAttrsStringSep "\n" mkSubMap; in lib.mkIf shouldGenerate { text = @@ -310,6 +402,7 @@ in inherit importantPrefixes; } ) + + lib.optionalString (cfg.submaps != { }) (submapsToHyprConf cfg.submaps) + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig; onChange = lib.mkIf (cfg.package != null) ''