diff --git a/config/autostart.lua b/config/autostart.lua index 48da9eb..e7cc13d 100644 --- a/config/autostart.lua +++ b/config/autostart.lua @@ -4,6 +4,9 @@ local commands = require("lib.commands") local settings = require("config.settings") local startup_commands = { + -- Local native layouts must register before a workspace can select them. + "hyprctl plugin load /home/felis/source/hyprstage/build/hyprstage.so", + -- Session services that manage their own lifecycle. "fcitx5 -d", "hyprpm reload -n", diff --git a/config/keybinds.lua b/config/keybinds.lua index bf50c6a..9b2519b 100644 --- a/config/keybinds.lua +++ b/config/keybinds.lua @@ -12,6 +12,60 @@ local function chord(...) return table.concat({ ... }, " + ") end +-- Stage uses vertical navigation to move through its preview ordering. Keep +-- the ordinary directional-focus behavior for every other tiled layout. +local function focus_or_stage(direction, stage_message) + local focus = hl.dsp.focus({ direction = direction }) + + if not stage_message then + return focus + end + + return function() + local workspace = hl.get_active_special_workspace() or hl.get_active_workspace() + + if workspace and workspace.tiled_layout == "stage" then + hl.dispatch(hl.dsp.layout(stage_message)) + return + end + + hl.dispatch(focus) + end +end + +local tiled_layouts = { "dwindle", "master", "scrolling", "monocle", "stage" } +local active_workspace_layout_rules = {} + +local function cycle_active_workspace_layout() + local workspace = hl.get_active_special_workspace() or hl.get_active_workspace() + if not workspace or workspace.special then + return + end + + local next_index = 1 + for index, layout in ipairs(tiled_layouts) do + if layout == workspace.tiled_layout then + next_index = index % #tiled_layouts + 1 + break + end + end + + local selector = workspace.config_name + if not selector or selector == "" then + selector = tostring(workspace.id) + end + + local previous_rule = active_workspace_layout_rules[workspace.id] + if previous_rule then + previous_rule:set_enabled(false) + end + + active_workspace_layout_rules[workspace.id] = hl.workspace_rule({ + workspace = selector, + layout = tiled_layouts[next_index], + }) +end + -- Generate matching focus/move bindings for numbered workspaces. Workspace 10 -- maps to the 0 key because `% 10` returns zero. local function bind_numbered_workspaces(modifier, move_modifier, count) @@ -78,11 +132,16 @@ hl.bind( local directions = { { arrow = "left", key = "A", direction = "l", monitor = "left" }, { arrow = "right", key = "D", direction = "r", monitor = "right" }, - { arrow = "up", key = "W", direction = "u", monitor = "above" }, - { arrow = "down", key = "S", direction = "d", monitor = "below" }, + { arrow = "up", key = "W", direction = "u", monitor = "above", stage_message = "cycleprev" }, + { arrow = "down", key = "S", direction = "d", monitor = "below", stage_message = "cyclenext" }, } for _, direction in ipairs(directions) do + local description = "Focus window " .. direction.arrow + if direction.stage_message then + description = description .. " / navigate Stage" + end + hl.bind( chord(main, direction.arrow), hl.dsp.focus({ direction = direction.arrow }), @@ -90,8 +149,8 @@ for _, direction in ipairs(directions) do ) hl.bind( chord(hyper, direction.key), - hl.dsp.focus({ direction = direction.direction }), - { description = "Focus window " .. direction.arrow } + focus_or_stage(direction.direction, direction.stage_message), + { description = description } ) hl.bind( chord(hyper, "CTRL", direction.key), @@ -105,6 +164,13 @@ for _, direction in ipairs(directions) do ) end +-- Cycle only the active workspace through the configured tiled layouts. +hl.bind( + chord(hyper, "grave"), + cycle_active_workspace_layout, + { description = "Cycle active workspace layout" } +) + bind_numbered_workspaces(main, "SHIFT", 10) bind_numbered_workspaces(hyper, "CTRL", 9) diff --git a/config/layouts.lua b/config/layouts.lua index c713f0d..3151bba 100644 --- a/config/layouts.lua +++ b/config/layouts.lua @@ -23,4 +23,12 @@ hl.config({ scrolling = { fullscreen_on_one_column = true, }, + + -- HyprStage is currently used by the right-monitor bilibili workspace, so + -- keep its tucked preview stack on that monitor's right edge. + plugin = { + hyprstage = { + sidebar_side = "right", + }, + }, }) diff --git a/hyprland.lua b/hyprland.lua index f9b5c07..9790ec5 100644 --- a/hyprland.lua +++ b/hyprland.lua @@ -23,3 +23,6 @@ local modules = { for _, module in ipairs(modules) do require(module) end + +-- For Noctalia Color templates +require("noctalia").apply_theme() diff --git a/noctalia.lua b/noctalia.lua index 57caf27..4562308 100644 --- a/noctalia.lua +++ b/noctalia.lua @@ -1,13 +1,10 @@ -- Generated by Noctalia --- This file is the generated color boundary: config/appearance.lua calls --- apply_theme(), while other modules remain independent of palette values. local primary = "rgb(fff59b)" local surface = "rgb(070722)" local secondary = "rgb(a9aefe)" local error = "rgb(fd4663)" --- Apply generated colors to regular borders, grouped windows, and group tabs. local function apply_theme() hl.config({ general = { @@ -38,14 +35,11 @@ local function apply_theme() end return { - -- Expose the raw palette for future modules that need matching colors. colors = { primary = primary, surface = surface, secondary = secondary, error = error, }, - -- Keep application explicit so requiring this generated file has no side - -- effects on its own. apply_theme = apply_theme }