refactor code structure
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
-- Base visual behavior. Theme colors are intentionally applied separately at
|
||||
-- the end of this module because Noctalia generates them.
|
||||
hl.config({
|
||||
-- Window spacing, borders, and compositor-wide rendering behavior.
|
||||
general = {
|
||||
gaps_in = 5,
|
||||
gaps_out = 20,
|
||||
border_size = 2,
|
||||
resize_on_border = false,
|
||||
allow_tearing = false,
|
||||
},
|
||||
|
||||
-- Window shape, opacity, shadows, and background blur.
|
||||
decoration = {
|
||||
rounding = 10,
|
||||
rounding_power = 2,
|
||||
active_opacity = 1.0,
|
||||
inactive_opacity = 1.0,
|
||||
|
||||
shadow = {
|
||||
enabled = true,
|
||||
range = 4,
|
||||
render_power = 3,
|
||||
color = 0xee1a1a1a,
|
||||
},
|
||||
|
||||
blur = {
|
||||
enabled = true,
|
||||
size = 3,
|
||||
passes = 1,
|
||||
vibrancy = 0.1696,
|
||||
},
|
||||
},
|
||||
|
||||
-- This is the global animation switch; individual animation tracks below
|
||||
-- control timing and style.
|
||||
animations = {
|
||||
enabled = true,
|
||||
},
|
||||
|
||||
-- Keep the built-in wallpaper enabled as a fallback.
|
||||
misc = {
|
||||
force_default_wallpaper = -1,
|
||||
disable_hyprland_logo = false,
|
||||
},
|
||||
})
|
||||
|
||||
-- Curves are data-driven so adding or tuning one does not require another
|
||||
-- standalone `hl.curve` call.
|
||||
local curves = {
|
||||
{ "easeOutQuint", { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } } },
|
||||
{ "easeInOutCubic", { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } } },
|
||||
{ "linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } } },
|
||||
{ "almostLinear", { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1 } } } },
|
||||
{ "quick", { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } } },
|
||||
{ "easy", { type = "spring", mass = 1, stiffness = 238.1191, dampening = 24.21279333 } },
|
||||
}
|
||||
|
||||
for _, curve in ipairs(curves) do
|
||||
hl.curve(curve[1], curve[2])
|
||||
end
|
||||
|
||||
-- Animation tracks inherit from broader leaves unless a more specific track
|
||||
-- (for example windowsIn or workspacesOut) overrides them.
|
||||
local animations = {
|
||||
{ leaf = "global", enabled = true, speed = 10, bezier = "default" },
|
||||
{ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" },
|
||||
{ leaf = "windows", enabled = true, speed = 4.79, spring = "easy" },
|
||||
{ leaf = "windowsIn", enabled = true, speed = 4.1, spring = "easy", style = "popin 87%" },
|
||||
{ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" },
|
||||
{ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" },
|
||||
{ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" },
|
||||
{ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" },
|
||||
{ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" },
|
||||
{ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" },
|
||||
{ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" },
|
||||
{ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" },
|
||||
{ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" },
|
||||
{ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" },
|
||||
{ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "almostLinear", style = "fade" },
|
||||
{ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" },
|
||||
{ leaf = "zoomFactor", enabled = true, speed = 7, bezier = "quick" },
|
||||
}
|
||||
|
||||
for _, animation in ipairs(animations) do
|
||||
hl.animation(animation)
|
||||
end
|
||||
|
||||
-- Noctalia generates the palette module; appearance owns when it is applied.
|
||||
require("noctalia").apply_theme()
|
||||
@@ -0,0 +1,26 @@
|
||||
-- Build the startup list once, using the same program names and UWSM wrapper as
|
||||
-- the keybinding module.
|
||||
local commands = require("lib.commands")
|
||||
local settings = require("config.settings")
|
||||
|
||||
local startup_commands = {
|
||||
-- Session services that manage their own lifecycle.
|
||||
"fcitx5 -d",
|
||||
"hyprpm reload -n",
|
||||
|
||||
-- Graphical/session applications launched inside UWSM scopes.
|
||||
commands.uwsm(settings.programs.terminal),
|
||||
commands.uwsm("hyprpaper"),
|
||||
commands.uwsm("firefox"),
|
||||
commands.uwsm("wl-paste --type text --watch cliphist store"),
|
||||
commands.uwsm("wl-paste --type image --watch cliphist store"),
|
||||
commands.uwsm("noctalia"),
|
||||
}
|
||||
|
||||
-- This event only fires when Hyprland starts; reloading the config will not
|
||||
-- launch duplicate copies of these applications.
|
||||
hl.on("hyprland.start", function()
|
||||
for _, command in ipairs(startup_commands) do
|
||||
hl.exec_cmd(command)
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Keep XCursor and Hyprcursor at the same size for consistent pointers across
|
||||
-- XWayland and native Wayland applications.
|
||||
local cursor_size = "24"
|
||||
|
||||
hl.env("XCURSOR_SIZE", cursor_size)
|
||||
hl.env("HYPRCURSOR_SIZE", cursor_size)
|
||||
@@ -0,0 +1,31 @@
|
||||
-- Keyboard, pointer, and touchpad defaults shared by all input devices.
|
||||
hl.config({
|
||||
input = {
|
||||
kb_layout = "us",
|
||||
kb_variant = "",
|
||||
kb_model = "",
|
||||
-- Turn Caps Lock into MOD3, which settings.lua exposes as Hyper.
|
||||
kb_options = "caps:hyper",
|
||||
kb_rules = "",
|
||||
follow_mouse = 1,
|
||||
sensitivity = 0,
|
||||
|
||||
touchpad = {
|
||||
natural_scroll = false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
-- Three-finger horizontal swipes move between workspaces.
|
||||
hl.gesture({
|
||||
fingers = 3,
|
||||
direction = "horizontal",
|
||||
action = "workspace",
|
||||
})
|
||||
|
||||
-- Example device-specific override retained from the original configuration.
|
||||
-- It only applies when Hyprland sees a device with this exact name.
|
||||
hl.device({
|
||||
name = "epic-mouse-v1",
|
||||
sensitivity = -0.5,
|
||||
})
|
||||
@@ -0,0 +1,167 @@
|
||||
-- All keybindings live in this module so conflicts can be audited in one
|
||||
-- place. Shared values come from settings; command wrapping comes from lib.
|
||||
local commands = require("lib.commands")
|
||||
local settings = require("config.settings")
|
||||
|
||||
local main = settings.modifiers.main
|
||||
local hyper = settings.modifiers.hyper
|
||||
|
||||
-- Build normalized bind strings and avoid hand-written separator mistakes such
|
||||
-- as doubled `+` characters.
|
||||
local function chord(...)
|
||||
return table.concat({ ... }, " + ")
|
||||
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)
|
||||
for workspace = 1, count do
|
||||
local key = workspace % 10
|
||||
|
||||
hl.bind(
|
||||
chord(modifier, key),
|
||||
hl.dsp.focus({ workspace = workspace }),
|
||||
{ description = "Switch to workspace " .. workspace }
|
||||
)
|
||||
hl.bind(
|
||||
chord(modifier, move_modifier, key),
|
||||
hl.dsp.window.move({ workspace = workspace }),
|
||||
{ description = "Send focused window to workspace " .. workspace }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- Core application and window-management shortcuts.
|
||||
hl.bind(
|
||||
chord(main, "SPACE"),
|
||||
hl.dsp.exec_cmd("noctalia msg panel-toggle launcher"),
|
||||
{ description = "Toggle Noctalia launcher" }
|
||||
)
|
||||
hl.bind(chord(main, "W"), hl.dsp.window.close())
|
||||
hl.bind(chord(main, "Q"), hl.dsp.exec_cmd(commands.uwsm(settings.programs.terminal)))
|
||||
hl.bind(chord(main, "E"), hl.dsp.exec_cmd(commands.uwsm(settings.programs.file_manager)))
|
||||
hl.bind(chord(main, "V"), hl.dsp.window.float({ action = "toggle" }))
|
||||
hl.bind(chord(main, "R"), hl.dsp.exec_cmd(settings.programs.launcher))
|
||||
hl.bind(chord(main, "P"), hl.dsp.window.pseudo())
|
||||
hl.bind(chord(main, "J"), hl.dsp.layout("togglesplit"))
|
||||
|
||||
-- One direction table drives arrow-key focus, Hyper+WASD focus/movement, and
|
||||
-- moving the active workspace between adjacent monitors.
|
||||
local directions = {
|
||||
{ arrow = "left", key = "A", direction = "l" },
|
||||
{ arrow = "right", key = "D", direction = "r" },
|
||||
{ arrow = "up", key = "W", direction = "u" },
|
||||
{ arrow = "down", key = "S", direction = "d" },
|
||||
}
|
||||
|
||||
for _, direction in ipairs(directions) do
|
||||
hl.bind(
|
||||
chord(main, direction.arrow),
|
||||
hl.dsp.focus({ direction = direction.arrow })
|
||||
)
|
||||
hl.bind(
|
||||
chord(hyper, direction.key),
|
||||
hl.dsp.focus({ direction = direction.direction })
|
||||
)
|
||||
hl.bind(
|
||||
chord(hyper, "CTRL", direction.key),
|
||||
hl.dsp.window.move({ direction = direction.direction })
|
||||
)
|
||||
hl.bind(
|
||||
chord(hyper, main, direction.key),
|
||||
hl.dsp.workspace.move({ monitor = direction.direction }),
|
||||
{ description = "Move workspace " .. direction.direction }
|
||||
)
|
||||
end
|
||||
|
||||
bind_numbered_workspaces(main, "SHIFT", 10)
|
||||
bind_numbered_workspaces(hyper, "CTRL", 9)
|
||||
|
||||
-- Cycle workspaces with the wheel and manipulate windows with mouse buttons.
|
||||
hl.bind(chord(main, "mouse_down"), hl.dsp.focus({ workspace = "e+1" }))
|
||||
hl.bind(chord(main, "mouse_up"), hl.dsp.focus({ workspace = "e-1" }))
|
||||
hl.bind(chord(main, "mouse:272"), hl.dsp.window.drag(), { mouse = true })
|
||||
hl.bind(chord(main, "mouse:273"), hl.dsp.window.resize(), { mouse = true })
|
||||
|
||||
-- Locked media keys also work while the session is locked. Volume and
|
||||
-- brightness binds repeat while held; playback controls fire once.
|
||||
local media_binds = {
|
||||
{ "XF86AudioRaiseVolume", "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+", { locked = true, repeating = true } },
|
||||
{ "XF86AudioLowerVolume", "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-", { locked = true, repeating = true } },
|
||||
{ "XF86AudioMute", "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", { locked = true, repeating = true } },
|
||||
{ "XF86AudioMicMute", "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle", { locked = true, repeating = true } },
|
||||
{ "XF86MonBrightnessUp", "brightnessctl -e4 -n2 set 5%+", { locked = true, repeating = true } },
|
||||
{ "XF86MonBrightnessDown", "brightnessctl -e4 -n2 set 5%-", { locked = true, repeating = true } },
|
||||
{ "XF86AudioNext", "playerctl next", { locked = true } },
|
||||
{ "XF86AudioPause", "playerctl play-pause", { locked = true } },
|
||||
{ "XF86AudioPlay", "playerctl play-pause", { locked = true } },
|
||||
{ "XF86AudioPrev", "playerctl previous", { locked = true } },
|
||||
}
|
||||
|
||||
for _, bind in ipairs(media_binds) do
|
||||
hl.bind(bind[1], hl.dsp.exec_cmd(bind[2]), bind[3])
|
||||
end
|
||||
|
||||
-- Generate focus/move shortcuts for every named workspace in settings.lua.
|
||||
for _, workspace in ipairs(settings.named_workspaces) do
|
||||
hl.bind(
|
||||
chord(hyper, workspace.key),
|
||||
hl.dsp.focus({ workspace = workspace.selector }),
|
||||
{ description = "Switch to " .. workspace.name .. " workspace" }
|
||||
)
|
||||
hl.bind(
|
||||
chord(hyper, "CTRL", workspace.key),
|
||||
hl.dsp.window.move({ workspace = workspace.selector }),
|
||||
{ description = "Send focused window to " .. workspace.name .. " workspace" }
|
||||
)
|
||||
end
|
||||
|
||||
-- Promote the focused client when a workspace is using master layout.
|
||||
hl.bind(
|
||||
chord(hyper, "M"),
|
||||
hl.dsp.layout("swapwithmaster master ignoremaster"),
|
||||
{ description = "Promote to master" }
|
||||
)
|
||||
|
||||
-- Hyprtasking provides a visual overview of windows on all workspaces.
|
||||
hl.bind(
|
||||
chord(hyper, "SPACE"),
|
||||
function()
|
||||
hl.plugin.hyprtasking.toggle("all")
|
||||
end
|
||||
)
|
||||
|
||||
-- Toggle a focused client into an 80%-sized centered floating mode. Returning
|
||||
-- it to tiled mode lets the active layout restore its previous geometry.
|
||||
hl.bind(chord(hyper, "F"), function()
|
||||
local window = hl.get_active_window()
|
||||
local monitor = hl.get_active_monitor()
|
||||
|
||||
if not window or not monitor then
|
||||
return
|
||||
end
|
||||
|
||||
local was_tiled = not window.floating
|
||||
hl.dispatch(hl.dsp.window.float({ action = "toggle" }))
|
||||
|
||||
if not was_tiled then
|
||||
return
|
||||
end
|
||||
|
||||
hl.dispatch(hl.dsp.window.resize({
|
||||
x = math.floor(monitor.width * 0.80),
|
||||
y = math.floor(monitor.height * 0.80),
|
||||
relative = false,
|
||||
}))
|
||||
hl.dispatch(hl.dsp.window.center())
|
||||
hl.dispatch(hl.dsp.window.alter_zorder({ mode = "top" }))
|
||||
end, {
|
||||
description = "Toggle focused floating window",
|
||||
})
|
||||
|
||||
-- Open the HyprCapture plugin's interactive region/window picker.
|
||||
hl.bind(
|
||||
chord(main, "SHIFT", "S"),
|
||||
hl.plugin.hyprcapture.open,
|
||||
{ description = "HyprCapture window capture" }
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
-- Keep layout defaults together so a later module cannot silently override a
|
||||
-- single field from another `hl.config` call.
|
||||
hl.config({
|
||||
-- Dwindle is the default layout for ordinary workspaces.
|
||||
general = {
|
||||
layout = "dwindle",
|
||||
},
|
||||
|
||||
-- Remember the user's most recent split direction in dwindle.
|
||||
dwindle = {
|
||||
preserve_split = true,
|
||||
},
|
||||
|
||||
-- Named workspaces opt into master layout. New windows enter the slave
|
||||
-- column, leaving the current master in place.
|
||||
master = {
|
||||
mfact = 0.80,
|
||||
orientation = "left",
|
||||
new_status = "slave",
|
||||
},
|
||||
|
||||
-- Expand the only column when using Hyprland's scrolling layout.
|
||||
scrolling = {
|
||||
fullscreen_on_one_column = true,
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Apply the physical display topology defined in the shared settings module.
|
||||
local settings = require("config.settings")
|
||||
|
||||
-- HDMI is the left/high-refresh display; DisplayPort is positioned to its
|
||||
-- right and also hosts the dedicated named workspaces.
|
||||
hl.monitor(settings.monitors.primary)
|
||||
hl.monitor(settings.monitors.secondary)
|
||||
@@ -0,0 +1,49 @@
|
||||
-- Central user-editable values. Domain modules consume this table so monitor
|
||||
-- names, applications, modifiers, and workspace selectors stay in sync.
|
||||
return {
|
||||
-- SUPER is used for conventional desktop shortcuts. Caps Lock is mapped to
|
||||
-- MOD3 in input.lua and acts as the Hyper modifier.
|
||||
modifiers = {
|
||||
main = "SUPER",
|
||||
hyper = "MOD3",
|
||||
},
|
||||
|
||||
-- Store raw executable names here. Callers decide whether a command should
|
||||
-- be wrapped with `uwsm-app` or executed directly.
|
||||
programs = {
|
||||
terminal = "alacritty",
|
||||
file_manager = "dolphin",
|
||||
launcher = "hyprlauncher",
|
||||
},
|
||||
|
||||
-- Named outputs make cross-module references (such as workspace routing)
|
||||
-- clearer than array indexes.
|
||||
monitors = {
|
||||
primary = {
|
||||
output = "HDMI-A-1",
|
||||
mode = "2560x1440@144",
|
||||
position = "0x0",
|
||||
scale = 1,
|
||||
},
|
||||
secondary = {
|
||||
output = "DP-3",
|
||||
mode = "2560x1440@120",
|
||||
position = "2560x0",
|
||||
scale = 1,
|
||||
},
|
||||
},
|
||||
|
||||
-- Both workspace rules and keybindings are generated from this list.
|
||||
named_workspaces = {
|
||||
{
|
||||
name = "bilibili",
|
||||
selector = "name:bilibili",
|
||||
key = "F1",
|
||||
},
|
||||
{
|
||||
name = "im",
|
||||
selector = "name:im",
|
||||
key = "F2",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
-- Window rules are kept as data and registered in order at the bottom. This
|
||||
-- makes adding a rule independent from the registration mechanism.
|
||||
local rules = {
|
||||
-- Render terminal image previews as undecorated, non-focusing overlays.
|
||||
{
|
||||
name = "ueberzugpp-preview",
|
||||
match = {
|
||||
class = "ueberzugpp_.*",
|
||||
},
|
||||
float = true,
|
||||
no_initial_focus = true,
|
||||
border_size = 0,
|
||||
rounding = 0,
|
||||
no_shadow = true,
|
||||
},
|
||||
-- Ignore application requests to maximize themselves.
|
||||
{
|
||||
name = "suppress-maximize-events",
|
||||
match = {
|
||||
class = ".*",
|
||||
},
|
||||
suppress_event = "maximize",
|
||||
},
|
||||
-- Prevent empty XWayland drag helper windows from stealing focus.
|
||||
{
|
||||
name = "fix-xwayland-drags",
|
||||
match = {
|
||||
class = "^$",
|
||||
title = "^$",
|
||||
xwayland = true,
|
||||
float = true,
|
||||
fullscreen = false,
|
||||
pin = false,
|
||||
},
|
||||
no_focus = true,
|
||||
},
|
||||
-- Place the Hyprland runner near the lower-left edge as a floating window.
|
||||
{
|
||||
name = "move-hyprland-run",
|
||||
match = {
|
||||
class = "hyprland-run",
|
||||
},
|
||||
move = "20 monitor_h-120",
|
||||
float = true,
|
||||
},
|
||||
}
|
||||
|
||||
-- Register every declarative rule with Hyprland.
|
||||
for _, rule in ipairs(rules) do
|
||||
hl.window_rule(rule)
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Create persistent master-layout workspaces from the shared workspace list.
|
||||
local settings = require("config.settings")
|
||||
|
||||
for _, workspace in ipairs(settings.named_workspaces) do
|
||||
hl.workspace_rule({
|
||||
workspace = workspace.selector,
|
||||
layout = "master",
|
||||
layout_opts = {
|
||||
orientation = "left",
|
||||
},
|
||||
persistent = true,
|
||||
-- Keep communication/media workspaces on the right-hand monitor.
|
||||
monitor = settings.monitors.secondary.output,
|
||||
})
|
||||
end
|
||||
Reference in New Issue
Block a user