205 lines
6.1 KiB
Lua
205 lines
6.1 KiB
Lua
-- -*- coding: utf-8 -*-
|
|
--------------------------------------------------------------------------------
|
|
-- @author Nicolas Berthier <nberthier@gmail.com>
|
|
-- @copyright 2010 Nicolas Berthier
|
|
--------------------------------------------------------------------------------
|
|
--
|
|
-- Bowls are kind of helpers that can be drawn (at the bottom --- for now) of an
|
|
-- area, and displaying the current key prefix. It is inspired by emacs'
|
|
-- behavior, that prints prefix keys in the minibuffer after a certain time.
|
|
--
|
|
-- I call it `bowl' as a reference to the bowl that one might have at home,
|
|
-- where one puts its actual keys... A more serious name would be `hint' or
|
|
-- `tooltip' (but they do not fit well for this usage).
|
|
--
|
|
-- Example usage: see `rc.lua' file.
|
|
--
|
|
--------------------------------------------------------------------------------
|
|
|
|
--{{{ Grab environment (mostly aliases)
|
|
local setmetatable = setmetatable
|
|
local ipairs = ipairs
|
|
local type = type
|
|
local pairs = pairs
|
|
local string = string
|
|
local print = print
|
|
local error = error
|
|
|
|
local capi = capi
|
|
local client = client
|
|
local awesome = awesome
|
|
local root = root
|
|
local timer = timer
|
|
|
|
local infoline = require ("infoline")
|
|
--}}}
|
|
|
|
module ("bowl")
|
|
|
|
-- Privata data: we use weak keys in order to allow collection of private data
|
|
-- if keys (clients) are collected (i.e., no longer used, after having been
|
|
-- killed for instance)
|
|
local data = setmetatable ({}, { __mode = 'k' })
|
|
|
|
--{{{ Default values
|
|
|
|
--- Default modifier filter
|
|
local modfilter = {
|
|
["Mod1"] = "M",
|
|
["Mod4"] = "S",
|
|
["Control"] = "C",
|
|
["Shift"] = string.upper,
|
|
}
|
|
|
|
-- Timers configuration
|
|
local use_timers = true
|
|
local timeout = 2.0
|
|
|
|
--}}}
|
|
|
|
--{{{ Keychain pretty-printing
|
|
|
|
function mod_to_string (mods, k)
|
|
local ret, k = "", k
|
|
for _, mod in ipairs (mods) do
|
|
if modfilter[mod] then
|
|
local t = type (modfilter[mod])
|
|
if t == "function" then
|
|
k = modfilter[mod](k)
|
|
elseif t == "string" then
|
|
ret = ret .. modfilter[mod] .. "-"
|
|
else
|
|
error ("Invalid modifier key filter: got a " .. t)
|
|
end
|
|
else
|
|
ret = ret .. mod .. "-"
|
|
end
|
|
end
|
|
return ret, k
|
|
end
|
|
|
|
function ks_to_string (m, k)
|
|
local m, k = mod_to_string (m, k)
|
|
return m .. k
|
|
end
|
|
|
|
--}}}
|
|
|
|
--{{{ Timer management
|
|
|
|
local function delete_timer_maybe (d)
|
|
if d.timer then -- stop and remove the timer
|
|
d.timer:remove_signal ("timeout", d.timer_function)
|
|
d.timer:stop ()
|
|
d.timer = nil
|
|
d.timer_expired = true
|
|
end
|
|
end
|
|
|
|
local function delayed_call_maybe (d, f)
|
|
if use_timers then
|
|
if not d.timer_expired and not d.timer then
|
|
-- create and start the timer
|
|
d.timer = timer ({ timeout = timeout })
|
|
d.timer_function = function () f (); delete_timer_maybe (d) end
|
|
d.timer:add_signal ("timeout", d.timer_function)
|
|
d.timer:start ()
|
|
d.timer_expired = false
|
|
elseif not d.timer_expired then
|
|
-- restart the timer...
|
|
|
|
-- XXX: What is the actual semantics of the call to `start' (ie,
|
|
-- does it restart the timer with the initial timeout)?
|
|
d.timer:stop ()
|
|
d.timer.timeout = timeout -- reset timeout
|
|
d.timer:start ()
|
|
end
|
|
else -- timers disabled
|
|
f () -- call the given function directly
|
|
end
|
|
end
|
|
|
|
--}}}
|
|
|
|
--{{{ Infoline management
|
|
|
|
function dispose (w)
|
|
local d = data[w]
|
|
if d.bowl then -- if bowl was enabled... (should always be true...)
|
|
infoline.dispose (d.bowl)
|
|
d.bowl = nil
|
|
end
|
|
delete_timer_maybe (d)
|
|
data[w] = nil
|
|
end
|
|
|
|
function append (w, m, k)
|
|
local d = data[w]
|
|
local pretty_ks = ks_to_string (m, k) .. " "
|
|
infoline.set_text (d.bowl, infoline.get_text (d.bowl) .. pretty_ks)
|
|
|
|
local function enable_bowl ()
|
|
-- XXX: is there a possible bad interleaving that could make
|
|
-- this function execute while the bowl has already been
|
|
-- disposed of? in which case the condition should be checked
|
|
-- first...
|
|
|
|
-- if d.bowl then
|
|
infoline.attach (d.bowl, w)
|
|
-- end
|
|
end
|
|
|
|
delayed_call_maybe (d, enable_bowl)
|
|
end
|
|
|
|
function create (w)
|
|
-- XXX: Note the prefix text could be customizable...
|
|
data[w] = { bowl = infoline.new (" ") }
|
|
end
|
|
|
|
--}}}
|
|
|
|
|
|
--- Initializes the bowl module, with given properties; should be called before
|
|
--- ANY other function of this module.
|
|
-- Configurations fields include:
|
|
--
|
|
-- `use_timers', `timeout': A boolean defining whether bowls drawing should be
|
|
-- delayed, along with a number being this time shift, in seconds (Default
|
|
-- values are `true' and `2').
|
|
--
|
|
-- `modfilter': A table associating modifiers (Mod1, Mod4, Control, Shift, etc.)
|
|
-- with either a string (in this case it will replace the modifier when printed
|
|
-- in heplers) or functions (in this case the key string will be repaced by a
|
|
-- call to this function with the key string as parameter). Default value is:
|
|
-- { ["Mod1"] = "M", ["Mod4"] = "S", ["Control"] = "C", ["Shift"] =
|
|
-- string.upper }
|
|
--
|
|
-- @param c The table of properties.
|
|
function init (c)
|
|
local c = c or { }
|
|
modfilter = c.modfilter and c.modfilter or modfilter
|
|
if c.use_timers ~= nil then use_timers = c.use_timers end
|
|
if use_timers then
|
|
timeout = c.timeout ~= nil and c.timeout or timeout
|
|
end
|
|
end
|
|
|
|
--- Setup signal listeners, that trigger appropriate functions for a default
|
|
--- behavior.
|
|
function default_setup ()
|
|
local function to_root (f) return function (...) f (root, ...) end end
|
|
client.add_signal ("keychain::enter", create)
|
|
client.add_signal ("keychain::append", append)
|
|
client.add_signal ("keychain::leave", dispose)
|
|
awesome.add_signal ("keychain::enter", to_root (create))
|
|
awesome.add_signal ("keychain::append", to_root (append))
|
|
awesome.add_signal ("keychain::leave", to_root (dispose))
|
|
end
|
|
|
|
-- Local variables:
|
|
-- indent-tabs-mode: nil
|
|
-- fill-column: 80
|
|
-- lua-indent-level: 4
|
|
-- End:
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|