local awful = require "awful" local gtable = require "gears.table" local h = require "misc.helpers" local vars = require "misc.vars" local wibox = require "wibox" local rubato = require "lib.rubato" local btn = awful.button.names local first_time = true local mouse = "󰍽 " local insightful = {} insightful._toggled = false insightful._bindings = {} insightful._selected_group = "" insightful._keymap = { Control = "Control", Mod1 = "Alt", ISO_Level3_Shift = "Alt Gr", Mod4 = "Super", Insert = "Insert", Delete = "Delete", Next = "Page Up", Prior = "Page Down", Left = "", Up = "", Right = "", Down = "", KP_End = "1", KP_Down = "#2", KP_Next = "#3", KP_Left = "#4", KP_Begin = "#5", KP_Right = "#6", KP_Home = "#7", KP_Up = "#8", KP_Prior = "#9", KP_Insert = "#0", KP_Delete = "#.", KP_Divide = "#/", KP_Multiply = "#*", KP_Subtract = "#-", KP_Add = "#+", KP_Enter = "#Enter", Escape = "Esc", Tab = "Tab", space = "Space", Return = "Enter", dead_acute = "´", dead_circumflex = "^", dead_grave = "`", XF86MonBrightnessUp = "🔆+", XF86MonBrightnessDown = "🔅-", XF86AudioRaiseVolume = "ﱛ", XF86AudioLowerVolume = "ﱜ", XF86AudioMute = "ﱝ", XF86AudioPlay = "⏯", XF86AudioPrev = "⏮", XF86AudioNext = "⏭", XF86AudioStop = "⏹", [tostring(btn.LEFT)] = mouse .. "Left", [tostring(btn.MIDDLE)] = mouse .. "Middle", [tostring(btn.RIGHT)] = mouse .. "Right" } insightful._widget = h.popup { visible = false, ontop = true, placement = awful.placement.centered, minimum_height = screen[1].geometry.height / 2, minimum_width = screen[1].geometry.width / 2, widget = { layout = wibox.layout.fixed.vertical, spacing = vars.big_padding, id = "layout_container" } } function insightful:_generate() local grouped_binds = {} local layout_container = insightful._widget.widget.layout_container for _, keybind in ipairs(self._bindings) do local group = keybind.group or "general" local group_exists = grouped_binds[group] ~= nil if not group_exists then grouped_binds[group] = {} end table.insert(grouped_binds[group], { mods = keybind.mods, triggers = keybind.triggers, desc = keybind.desc }) end for group, keybinds in h.opairs(grouped_binds) do local group_layout = { spacing = vars.padding, layout = wibox.layout.fixed.vertical } for _, keybind in ipairs(keybinds) do local key_layout = { layout = wibox.layout.fixed.horizontal } local key_and_desc_layout = { nil, -- key nil, nil, -- description? layout = wibox.layout.align.horizontal } for _, mod in ipairs(keybind.mods) do table.insert(key_layout, 1, h.styled { widget = wibox.container.background, bg = vars.colors.bright.black, border_width = 0, { widget = wibox.container.margin, margins = vars.padding, { widget = wibox.widget.textbox, text = insightful._keymap[mod] or mod } } }) table.insert(key_layout, 2, { widget = wibox.widget.textbox, text = " + " }) end if type(keybind.triggers) == "string" or type(keybind.triggers) == "number" then table.insert(key_layout, { widget = wibox.widget.textbox, text = insightful._keymap[tostring(keybind.triggers)] or keybind.triggers }) elseif type(keybind.triggers) == "table" then local display_trigger = {} for _, trigger in ipairs(keybind.triggers) do table.insert(display_trigger, insightful._keymap[trigger[1]] or trigger[1]) end table.insert(key_layout, { widget = wibox.widget.textbox, text = table.concat(display_trigger, "/") }) end if keybind.desc then key_and_desc_layout[3] = { widget = wibox.container.background, fg = vars.colors.dim.fg, { widget = wibox.widget.textbox, text = keybind.desc } } end key_and_desc_layout[1] = key_layout table.insert(group_layout, key_and_desc_layout) end layout_container:add { { { widget = wibox.container.background, bg = vars.colors.yellow, fg = vars.colors.bg, shape = vars.shape, { widget = wibox.container.margin, margins = vars.padding, { widget = wibox.widget.textbox, text = group } } }, nil, layout = wibox.layout.align.horizontal }, group_layout, spacing = vars.padding, layout = wibox.layout.fixed.vertical } end end local timed = rubato.timed { duration = vars.anim_duration, intro = vars.anim_intro, pos = 0, subscribed = function(pos) insightful._widget.opacity = pos if pos == 0 then insightful._widget.visible = false else insightful._widget.visible = true end end } function insightful:toggle() if first_time then insightful:_generate_widget() first_time = false end timed.target = insightful._toggled and 0 or 1 insightful._toggled = not insightful._toggled end local function get_binding_function(trigger) if type(trigger) == "number" and trigger <= 5 and trigger > 0 then return "button" elseif type(trigger) == "string" then return "key" end error("trigger can only be a mouse button or a key", 2) end local function translate_binding(binding, trigger, multiple) local value = nil if multiple then value = trigger[2] trigger = trigger[1] end local awful_binding = { modifiers = binding.mods, [get_binding_function(trigger)] = trigger, on_press = multiple and function(...) binding.press(value, ...) end or binding.press } if binding.desc then awful_binding.description = binding.desc end if binding.group then awful_binding.group = binding.group end return awful[get_binding_function(trigger)](awful_binding) end function insightful:bind(binding) local awful_bindings = {} table.insert(self._bindings, binding) if type(binding.triggers) == "table" then for _, trigger in ipairs(binding.triggers) do 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, false) else error("binding.triggers can only be a string or a table") 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 insightful