aboutsummaryrefslogtreecommitdiff
path: root/.config/awesome/quarrel/bind/init.lua
blob: a8514403bd92a3e60c96fff40ca371ee0f38acfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
local C = require "quarrel.bind.consts"
local awful = require "awful"
local gtable = require "gears.table"

local M = gtable.crush({
    bindings = {},
}, C)

---@alias mouse_button
---| 0 Left mouse button
---| 1 Middle mouse click
---| 2 Right mouse button
---| 3 Scroll up
---| 4 Scroll down

---@alias bind
---| mouse_button
---| string

---@alias modifier
---| "Mod1" Alt
---| "Mod4" Super
---| "Mod5" AltGr
---| "Shift" Shift
---| "Control" Ctrl

---@class Trigger
---@field [1] bind
---@field [2] any Value passed to the press callback

---@class Binding
---@field hidden boolean? Whether the binding shows up in the help menu
---@field mods modifier[]? Modifiers
---@field press fun(...) | fun(any, ...) Function to run when the trigger is pressed
---@field desc string? Description
---@field group string? What group the binding will show up in
---@field triggers Trigger[] | bind[] | bind

---Get the corresponding binding creation function for a trigger
---@param bind bind
---@return string
local function get_binding_function(bind)
    if type(bind) == "number" and bind <= 5 and bind > 0 then
        return "button"
    elseif type(bind) == "string" then
        return "key"
    end
    error("bind can only be a mouse button or a key", 2)
end

--- Translate a qbind binding into an awful binding
---@param binding Binding
---@param trigger Trigger | bind
---@param multi boolean specifies whether this is a multi trigger bind
---@return awful.key
local function translate_binding(binding, trigger, multi)
    local awful_binding
    if multi then
        awful_binding = {
            modifiers = binding.mods,
            [get_binding_function(trigger[1])] = trigger[1],
            on_press = function(...)
                binding.press(trigger[2], ...)
            end,
        }
    else
        awful_binding = {
            modifiers = binding.mods,
            [get_binding_function(trigger --[[@as bind]])] = trigger,
            on_press = binding.press,
        }
    end

    awful_binding.description = binding.desc
    awful_binding.group = binding.group

    return awful[
        get_binding_function(multi and trigger[1] or trigger --[[@as bind]])
    ](awful_binding)
end

--- Create a new binding
---@param binding Binding
---@return awful.key[]
function M:new(binding)
    if not binding.hidden then
        table.insert(self.bindings, binding)
    end
    binding.mods = binding.mods or {}

    local awful_bindings = {}

    if type(binding.triggers) == "table" then
        for _, _trigger in
            ipairs(binding.triggers --[[@as bind[] | Trigger[]])
        do
            local trigger
            if type(_trigger) == "table" then
                trigger = _trigger
            elseif type(_trigger) == "string" or type(_trigger) == "number" then
                trigger = { _trigger, _trigger }
            end
            table.insert(awful_bindings, translate_binding(binding, trigger, true))
        end
    elseif type(binding.triggers) == "string" or type(binding.triggers) == "number" then
        return translate_binding(binding, binding.triggers --[[@as bind]], false)
    else
        error("binding.triggers can only be a string or a table (passed in " .. type(binding.triggers) .. ")")
    end

    -- for some reason multi-trigger bindings only work if i do this
    -- i spent a day debugging this
    -- thanks awesome
    return gtable.join(table.unpack(awful_bindings))
end

return setmetatable(M, {
    __call = M.new,
})