Merge pull request #2937 from Elv13/more_keyboard

More awful.key / awful.button improvements.
This commit is contained in:
Emmanuel Lepage Vallée 2019-12-05 23:50:23 -05:00 committed by GitHub
commit 6ed1f643cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 622 additions and 181 deletions

View File

@ -102,49 +102,6 @@ mykeyboardlayout = awful.widget.keyboardlayout()
-- Create a textclock widget
mytextclock = wibox.widget.textclock()
-- Create a wibox for each screen and add it
-- @TAGLIST_BUTTON@
local taglist_buttons = {
awful.button({ }, 1, function(t) t:view_only() end),
awful.button({ modkey }, 1, function(t)
if client.focus then
client.focus:move_to_tag(t)
end
end),
awful.button({ }, 3, awful.tag.viewtoggle),
awful.button({ modkey }, 3, function(t)
if client.focus then
client.focus:toggle_tag(t)
end
end),
awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end),
}
-- @TASKLIST_BUTTON@
local tasklist_buttons = {
awful.button({ }, 1, function (c)
if c == client.focus then
c.minimized = true
else
c:emit_signal(
"request::activate",
"tasklist",
{raise = true}
)
end
end),
awful.button({ }, 3, function()
awful.menu.client_list({ theme = { width = 250 } })
end),
awful.button({ }, 4, function ()
awful.client.focus.byidx(1)
end),
awful.button({ }, 5, function ()
awful.client.focus.byidx(-1)
end),
}
-- @DOC_WALLPAPER@
screen.connect_signal("request::wallpaper", function(s)
-- Wallpaper
@ -165,6 +122,7 @@ screen.connect_signal("request::desktop_decoration", function(s)
-- Create a promptbox for each screen
s.mypromptbox = awful.widget.prompt()
-- Create an imagebox widget which will contain an icon indicating which layout we're using.
-- We need one layoutbox per screen.
s.mylayoutbox = awful.widget.layoutbox {
@ -181,14 +139,44 @@ screen.connect_signal("request::desktop_decoration", function(s)
s.mytaglist = awful.widget.taglist {
screen = s,
filter = awful.widget.taglist.filter.all,
buttons = taglist_buttons
buttons = {
awful.button({ }, 1, function(t) t:view_only() end),
awful.button({ modkey }, 1, function(t)
if client.focus then
client.focus:move_to_tag(t)
end
end),
awful.button({ }, 3, awful.tag.viewtoggle),
awful.button({ modkey }, 3, function(t)
if client.focus then
client.focus:toggle_tag(t)
end
end),
awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end),
}
}
-- Create a tasklist widget
s.mytasklist = awful.widget.tasklist {
screen = s,
filter = awful.widget.tasklist.filter.currenttags,
buttons = tasklist_buttons
buttons = {
awful.button({ }, 1, function (c)
if c == client.focus then
c.minimized = true
else
c:emit_signal(
"request::activate",
"tasklist",
{raise = true}
)
end
end),
awful.button({ }, 3, function() awful.menu.client_list { theme = { width = 250 } } end),
awful.button({ }, 4, function() awful.client.focus.byidx( 1) end),
awful.button({ }, 5, function() awful.client.focus.byidx(-1) end),
}
}
-- @DOC_WIBAR@
@ -332,125 +320,128 @@ awful.keyboard.append_global_keybindings({
{description = "select previous", group = "layout"}),
})
-- @DOC_CLIENT_KEYBINDINGS@
clientkeys = {
awful.key({ modkey, }, "f",
function (c)
c.fullscreen = not c.fullscreen
c:raise()
end,
{description = "toggle fullscreen", group = "client"}),
awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end,
{description = "close", group = "client"}),
awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle ,
{description = "toggle floating", group = "client"}),
awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
{description = "move to master", group = "client"}),
awful.key({ modkey, }, "o", function (c) c:move_to_screen() end,
{description = "move to screen", group = "client"}),
awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end,
{description = "toggle keep on top", group = "client"}),
awful.key({ modkey, }, "n",
function (c)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
c.minimized = true
end ,
{description = "minimize", group = "client"}),
awful.key({ modkey, }, "m",
function (c)
c.maximized = not c.maximized
c:raise()
end ,
{description = "(un)maximize", group = "client"}),
awful.key({ modkey, "Control" }, "m",
function (c)
c.maximized_vertical = not c.maximized_vertical
c:raise()
end ,
{description = "(un)maximize vertically", group = "client"}),
awful.key({ modkey, "Shift" }, "m",
function (c)
c.maximized_horizontal = not c.maximized_horizontal
c:raise()
end ,
{description = "(un)maximize horizontally", group = "client"}),
}
-- @DOC_NUMBER_KEYBINDINGS@
-- Bind all key numbers to tags.
-- Be careful: we use keycodes to make it work on any keyboard layout.
-- This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, 9 do
-- View tag only.
awful.keyboard.append_global_keybinding(awful.key(
{ modkey }, "#" .. i + 9,
function ()
awful.keyboard.append_global_keybindings({
awful.key {
modifiers = { modkey },
keygroup = "numrow",
description = "only view tag",
group = "tag",
on_press = function (index)
local screen = awful.screen.focused()
local tag = screen.tags[i]
local tag = screen.tags[index]
if tag then
tag:view_only()
end
end,
{description = "view tag #"..i, group = "tag"}
))
-- Toggle tag display.
awful.keyboard.append_global_keybinding(awful.key(
{ modkey, "Control" }, "#" .. i + 9,
function ()
},
awful.key {
modifiers = { modkey, "Control" },
keygroup = "numrow",
description = "toggle tag",
group = "tag",
on_press = function (index)
local screen = awful.screen.focused()
local tag = screen.tags[i]
local tag = screen.tags[index]
if tag then
awful.tag.viewtoggle(tag)
end
end,
{description = "toggle tag #" .. i, group = "tag"}
))
-- Move client to tag.
awful.keyboard.append_global_keybinding(awful.key(
{ modkey, "Shift" }, "#" .. i + 9,
function ()
},
awful.key {
modifiers = { modkey, "Shift" },
keygroup = "numrow",
description = "move focused client to tag",
group = "tag",
on_press = function (index)
if client.focus then
local tag = client.focus.screen.tags[i]
local tag = client.focus.screen.tags[index]
if tag then
client.focus:move_to_tag(tag)
end
end
end,
{description = "move focused client to tag #"..i, group = "tag"}
))
-- Toggle tag on focused client.
awful.keyboard.append_global_keybinding(awful.key(
{ modkey, "Control", "Shift" }, "#" .. i + 9,
function ()
},
awful.key {
modifiers = { modkey, "Control", "Shift" },
keygroup = "numrow",
description = "toggle focused client on tag",
group = "tag",
on_press = function (index)
if client.focus then
local tag = client.focus.screen.tags[i]
local tag = client.focus.screen.tags[index]
if tag then
client.focus:toggle_tag(tag)
end
end
end,
{description = "toggle focused client on tag #" .. i, group = "tag"}
))
end
}
})
-- @DOC_CLIENT_BUTTONS@
clientbuttons = {
awful.button({ }, 1, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
end),
awful.button({ modkey }, 1, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
awful.mouse.client.move(c)
end),
awful.button({ modkey }, 3, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
awful.mouse.client.resize(c)
end),
}
client.connect_signal("request::default_mousebindings", function()
awful.mouse.append_client_mousebindings({
awful.button({ }, 1, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
end),
awful.button({ modkey }, 1, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
awful.mouse.client.move(c)
end),
awful.button({ modkey }, 3, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
awful.mouse.client.resize(c)
end),
})
end)
-- @DOC_CLIENT_KEYBINDINGS@
client.connect_signal("request::default_keybindings", function()
awful.keyboard.append_client_keybindings({
awful.key({ modkey, }, "f",
function (c)
c.fullscreen = not c.fullscreen
c:raise()
end,
{description = "toggle fullscreen", group = "client"}),
awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end,
{description = "close", group = "client"}),
awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle ,
{description = "toggle floating", group = "client"}),
awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
{description = "move to master", group = "client"}),
awful.key({ modkey, }, "o", function (c) c:move_to_screen() end,
{description = "move to screen", group = "client"}),
awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end,
{description = "toggle keep on top", group = "client"}),
awful.key({ modkey, }, "n",
function (c)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
c.minimized = true
end ,
{description = "minimize", group = "client"}),
awful.key({ modkey, }, "m",
function (c)
c.maximized = not c.maximized
c:raise()
end ,
{description = "(un)maximize", group = "client"}),
awful.key({ modkey, "Control" }, "m",
function (c)
c.maximized_vertical = not c.maximized_vertical
c:raise()
end ,
{description = "(un)maximize vertically", group = "client"}),
awful.key({ modkey, "Shift" }, "m",
function (c)
c.maximized_horizontal = not c.maximized_horizontal
c:raise()
end ,
{description = "(un)maximize horizontally", group = "client"}),
})
end)
-- }}}
@ -465,8 +456,6 @@ awful.rules.rules = {
border_color = beautiful.border_normal,
focus = awful.client.focus.filter,
raise = true,
keys = clientkeys,
buttons = clientbuttons,
screen = awful.screen.preferred,
placement = awful.placement.no_overlap+awful.placement.no_offscreen
}

View File

@ -57,6 +57,11 @@ This document was last updated at commit v4.3-197-g9085ed631.
* `naughty.dbus` now uses Gio for talking to DBus. This is a first step in the
deprecation of Awesome's own DBus bindings and could lead to behaviour changes
on DBus.
* The client `keys` and `buttons` property now return `awful.key`
and `awful.buttons` objects rather than the lower level `key` and `button`
objects. If you used these low level APIs to add keys and buttons dynamically,
please migrate your code to the corresponding `:append_` and `:remove_`
client methods.
<a name="v43"></a>
# Awesome window manager framework version 4.3 changes

View File

@ -365,6 +365,29 @@ function client.cycle(clockwise, s, stacked)
end
end
--- Append a keybinding.
--
-- @method append_keybinding
-- @tparam awful.key key The key.
-- @see remove_keybinding
-- @see append_mousebinding
-- @see remove_mousebinding
--- Remove a keybinding.
--
-- @method remove_keybinding
-- @tparam awful.key key The key.
--- Append a mousebinding.
--
-- @method append_mousebinding
-- @tparam awful.button button The button.
--- Remove a mousebinding.
--
-- @method remove_mousebinding
-- @tparam awful.button button The button.
--- Get the master window.
--
-- @legacylayout awful.client.getmaster
@ -1348,13 +1371,13 @@ object.properties._legacy_accessors(client, "buttons", "_buttons", true, functio
return new_btns[1] and (
type(new_btns[1]) == "button" or new_btns[1]._is_capi_button
) or false
end, true)
end, true, true, "mousebinding")
object.properties._legacy_accessors(client, "keys", "_keys", true, function(new_btns)
return new_btns[1] and (
type(new_btns[1]) == "key" or new_btns[1]._is_capi_key
) or false
end, true)
end, true, true, "keybinding")
--- Set the client shape.
-- @property shape

View File

@ -274,7 +274,9 @@ function widget.new(args)
end
table.sort(
sorted_table,
function(a,b) return (a.mod or '')..a.key<(b.mod or '')..b.key end
function(a,b)
local k1, k2 = a.key or a.keys[1][1], b.key or b.keys[1][1]
return (a.mod or '')..k1<(b.mod or '')..k2 end
)
target[group] = sorted_table
end
@ -287,7 +289,9 @@ function widget.new(args)
return
end
for _, data in pairs(awful.key.hotkeys) do
self:_add_hotkey(data.key, data, self._cached_awful_keys)
for _, key_pair in ipairs(data.keys) do
self:_add_hotkey(key_pair[1], data, self._cached_awful_keys)
end
end
self:_sort_hotkeys(self._cached_awful_keys)
end

View File

@ -24,6 +24,17 @@ local gobject = require("gears.object")
-- @property key
-- @param string
--- A group of keys.
--
-- The valid keygroups are:
--
-- * **numrow**: The row above the letters in the US PC-105/PC-104 keyboards
-- and its derivative. This is usually the number 1-9 followed by 0.
-- * **arrows**: The Left/Right/Top/Bottom keys usually located right of the
-- spacebar.
--
-- @property keygroup
--- The table of modifier keys.
--
-- A modifier, such as `Control` are a predetermined set of keys that can be
@ -222,7 +233,7 @@ end
-- @treturn table A table with one or several key objects.
-- @constructorfct awful.key
local function new_common(mod, _key, press, release, data)
local function new_common(mod, _keys, press, release, data)
if type(release)=='table' then
data=release
release=nil
@ -230,38 +241,52 @@ local function new_common(mod, _key, press, release, data)
local ret = {}
local subsets = gmath.subsets(key.ignore_modifiers)
for _, set in ipairs(subsets) do
local sub_key = capi.key {
modifiers = gtable.join(mod, set),
key = _key
}
for _, key_pair in ipairs(_keys) do
for _, set in ipairs(subsets) do
local sub_key = capi.key {
modifiers = gtable.join(mod, set),
key = key_pair[1]
}
sub_key._private._legacy_convert_to = ret
sub_key._private._legacy_convert_to = ret
sub_key:connect_signal("press", function(_, ...)
if ret.on_press then
ret.on_press(...)
end
end)
sub_key:connect_signal("press", function(_, ...)
if ret.on_press then
if key_pair[2] ~= nil then
ret.on_press(key_pair[2], ...)
else
ret.on_press(...)
end
end
end)
sub_key:connect_signal("release", function(_, ...)
if ret.on_release then
ret.on_release(...)
end
end)
sub_key:connect_signal("release", function(_, ...)
if ret.on_release then
if key_pair[2] ~= nil then
ret.on_release(key_pair[2], ...)
else
ret.on_release(...)
end
end
end)
ret[#ret + 1] = sub_key
ret[#ret + 1] = sub_key
end
end
-- append custom userdata (like description) to a hotkey
data = data and gtable.clone(data) or {}
data.mod = mod
data.key = _key
data.keys = _keys
data.on_press = press
data.on_release = release
data._is_capi_key = false
assert((not data.key) or type(data.key) == "string")
table.insert(key.hotkeys, data)
data.execute = function(_) key.execute(mod, _key) end
data.execute = function(_)
assert(#_keys == 1, "key:execute() makes no sense for groups")
key.execute(mod, _keys[1])
end
-- Store the private data
reverse_map[ret] = data
@ -273,19 +298,54 @@ local function new_common(mod, _key, press, release, data)
return setmetatable(ret, obj_mt)
end
local keygroups = {
numrow = {},
arrows = {
{"Left" , "Left" },
{"Right" , "Right" },
{"Top" , "Top" },
{"Bottom", "Bottom"},
}
}
-- Technically, this isn't very popular, but we have been doing this for 12
-- years and nobody complained too loudly.
for i = 1, 10 do
table.insert(keygroups.numrow, {"#" .. i + 9, i == 10 and 0 or i})
end
-- Allow key objects to provide more than 1 key.
--
-- Some "groups" like arrows, the numpad, F-keys or numrow "belong together"
local function get_keys(args)
if not args.keygroup then return {{args.key}} end
-- Make sure nothing weird happens.
assert(
not args.key,
"Please provide either the `key` or `keygroup` property, not both"
)
assert(keygroups[args.keygroup], "Please provide a valid keygroup")
return keygroups[args.keygroup]
end
function key.new(args, _key, press, release, data)
-- Assume this is the new constructor.
if not _key then
assert(not (press or release or data), "Calling awful.key() requires a key name")
local keys = get_keys(args)
return new_common(
args.modifiers,
args.key,
keys,
args.on_press,
args.on_release,
args
)
else
return new_common(args, _key, press, release, data)
return new_common(args, {{_key}}, press, release, data)
end
end

View File

@ -6,7 +6,7 @@
-- @inputmodule awful.keyboard
---------------------------------------------------------------------------
local capi = {root = root, awesome = awesome}
local capi = {root = root, awesome = awesome, client = client}
local module = {}
--- Convert the modifiers into pc105 key names
@ -138,4 +138,72 @@ function module.remove_global_keybinding(key)
capi.root._remove_key(key)
end
local default_keys = {}
--- Add an `awful.key` to the default client keys.
--
-- @staticfct awful.keyboard.append_client_keybinding
-- @tparam awful.key key The key.
-- @emits client_keybinding::added
-- @emitstparam client_keybinding::added awful.key key The key.
-- @see awful.key
-- @see awful.keyboard.append_client_keybindings
function module.append_client_keybinding(key)
table.insert(default_keys, key)
for _, c in ipairs(capi.client.get(nil, false)) do
c:append_keybinding(key)
end
capi.client.emit_signal("client_keybinding::added", key)
end
--- Add a `awful.key`s to the default client keys.
--
-- @staticfct awful.keyboard.append_client_keybindings
-- @tparam table keys A table containing `awful.key` objects.
-- @emits client_keybinding::added
-- @emitstparam client_keybinding::added awful.key key The key.
-- @see awful.key
-- @see awful.keyboard.append_client_keybinding
function module.append_client_keybindings(keys)
for _, key in ipairs(keys) do
module.append_client_keybinding(key)
end
end
--- Remove a key from the default client keys.
--
-- @staticfct awful.keyboard.remove_client_keybinding
-- @tparam awful.key key The key.
-- @treturn boolean True if the key was removed and false if it wasn't found.
-- @see awful.keyboard.append_client_keybinding
function module.remove_client_keybinding(key)
for k, v in ipairs(default_keys) do
if key == v then
table.remove(default_keys, k)
for _, c in ipairs(capi.client.get(nil, false)) do
c:remove_keybinding(key)
end
return true
end
end
return false
end
capi.client.connect_signal("scanning", function()
capi.client.emit_signal("request::default_keybindings", "context")
end)
-- Private function to be used by `ruled.client`.
function module._get_client_keybindings()
return default_keys
end
return module

View File

@ -421,6 +421,65 @@ function mouse.remove_global_mousebinding(button)
capi.root._remove_button(button)
end
local default_buttons = {}
--- Add an `awful.button` to the default client buttons.
--
-- @staticfct awful.mouse.append_client_mousebinding
-- @tparam awful.button button The button.
-- @emits client_mousebinding::added
-- @emitstparam client_mousebinding::added awful.button button The button.
-- @see awful.button
-- @see awful.keyboard.append_client_keybinding
function mouse.append_client_mousebinding(button)
table.insert(default_buttons, button)
for _, c in ipairs(capi.client.get(nil, false)) do
c:append_mousebinding(button)
end
capi.client.emit_signal("client_mousebinding::added", button)
end
--- Add a `awful.button`s to the default client buttons.
--
-- @staticfct awful.mouse.append_client_mousebindings
-- @tparam table buttons A table containing `awful.button` objects.
-- @see awful.button
-- @see awful.keyboard.append_client_keybinding
-- @see awful.mouse.append_client_mousebinding
-- @see awful.keyboard.append_client_keybindings
function mouse.append_client_mousebindings(buttons)
for _, button in ipairs(buttons) do
mouse.append_client_mousebinding(button)
end
end
--- Remove a mousebinding from the default client buttons.
--
-- @staticfct awful.mouse.remove_client_mousebinding
-- @tparam awful.button button The button.
-- @treturn boolean True if the button was removed and false if it wasn't found.
-- @see awful.keyboard.append_client_keybinding
function mouse.remove_client_mousebinding(button)
for k, v in ipairs(default_buttons) do
if button == v then
table.remove(default_buttons, k)
for _, c in ipairs(capi.client.get(nil, false)) do
c:remove_mousebinding(button)
end
return true
end
end
return false
end
for _, b in ipairs {"left", "right", "middle"} do
mouse.object["is_".. b .."_mouse_button_pressed"] = function()
return capi.mouse.coords().buttons[1]
@ -470,6 +529,14 @@ end)
-- when button 1 is pressed.
-- @staticfct mouse.coords
capi.client.connect_signal("scanning", function()
capi.client.emit_signal("request::default_mousebindings", "startup")
end)
-- Private function to be used by `ruled.client`.
function mouse._get_client_mousebindings()
return default_buttons
end
return mouse

View File

@ -121,6 +121,8 @@ local protected_call = require("gears.protected_call")
local aspawn = require("awful.spawn")
local gdebug = require("gears.debug")
local gmatcher = require("gears.matcher")
local amouse = require("awful.mouse")
local akeyboard = require("awful.keyboard")
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local rules = {}
@ -529,6 +531,13 @@ end
-- @staticfct awful.rules.execute
crules._execute = function(_, c, props, callbacks)
-- Set the default buttons and keys
local btns = amouse._get_client_mousebindings()
local keys = akeyboard._get_client_keybindings()
props.keys = props.keys or keys
props.buttons = props.buttons or btns
-- This has to be done first, as it will impact geometry related props.
if props.titlebars_enabled and (type(props.titlebars_enabled) ~= "function"
or props.titlebars_enabled(c,props)) then

View File

@ -8,6 +8,7 @@
---------------------------------------------------------------------------
local gtable = require("gears.table")
local gtimer = nil --require("gears.timer")
-- local gdebug = require("gears.debug")
local object = {}
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
@ -83,6 +84,8 @@ function object.capi_index_fallback(class, args)
end
end
assert(type(class) ~= "function")
-- Attach the accessor methods
class.set_index_miss_handler(getter)
class.set_newindex_miss_handler(setter)
@ -132,7 +135,7 @@ end
--
-- TO BE USED FOR DEPRECATION ONLY.
local function copy_object(obj, to_set, name, capi_name, is_object, join_if, set_empty)
local function copy_object(obj, to_set, name, capi_name, is_object, join_if, set_empty)-- luacheck: no unused
local ret = gtable.clone(to_set, false)
-- .buttons used to be a function taking the result of `gears.table.join`.
@ -145,12 +148,14 @@ local function copy_object(obj, to_set, name, capi_name, is_object, join_if, set
-- {deprecated_in=5}
-- )
new_objs, self = is_object and new_objs or self, is_object and self or obj
if not is_object then
new_objs, self = self, obj
end
local has_content = new_objs and next(new_objs)
-- Setter
if new_objs and #new_objs > 0 then
if (not set_empty) and not next(new_objs) then return end
if new_objs and has_content then
local is_formatted = join_if(new_objs)
-- Because modules may rely on :buttons taking a list of
@ -190,7 +195,8 @@ local function copy_object(obj, to_set, name, capi_name, is_object, join_if, set
})
end
function object._legacy_accessors(obj, name, capi_name, is_object, join_if, set_empty)
function object._legacy_accessors(obj, name, capi_name, is_object, join_if, set_empty, delay, add_append_name)
delay = delay or add_append_name
-- Some objects have a special "object" property to add more properties, but
-- not all.
@ -248,21 +254,64 @@ function object._legacy_accessors(obj, name, capi_name, is_object, join_if, set_
--TODO v6 Use the original directly and drop this legacy copy
local result = is_formatted and objs
or gtable.join(unpack(objs))
local function apply()
local result = is_formatted and objs
or gtable.join(unpack(objs))
if is_object and capi_name then
self[capi_name](self, result)
elseif capi_name then
obj[capi_name](result)
if is_object and capi_name then
self[capi_name](self, result)
elseif capi_name then
obj[capi_name](result)
else
self._private[name.."_formatted"] = result
end
end
-- Some properties, like keys, are expensive to set, schedule them
-- instead.
if not delay then
apply()
else
self._private[name.."_formatted"] = result
if not self._private["_delayed_"..name] then
gtimer = gtimer or require("gears.timer")
gtimer.delayed_call(function()
self._private["_delayed_"..name]()
self._private["_delayed_"..name] = nil
end)
end
self._private["_delayed_"..name] = apply
end
self._private[name] = copy_object(
obj, objs, name, capi_name, is_object, join_if, set_empty
)
end
if add_append_name then
magic_obj["append_"..add_append_name] = function(self, obj2)
self._private[name] = self._private[name]
or magic_obj["get_"..name](self, nil)
table.insert(self._private[name], obj2)
magic_obj["set_"..name](self, self._private[name])
end
magic_obj["remove_"..add_append_name] = function(self, obj2)
self._private[name] = self._private[name]
or magic_obj["get_"..name](self, nil)
for k, v in ipairs(self._private[name]) do
if v == obj2 then
table.remove(self._private[name], k)
break
end
end
magic_obj["set_"..name](self, self._private[name])
end
end
end
return setmetatable( object, {__call = function(_,...) object.capi_index_fallback(...) end})

View File

@ -244,6 +244,28 @@
* @signal request::urgent
*/
/** Emitted during startup to gather the default client mousebindings.
*
* This signals gives a chance to all module to register new client keybindings.
* Assuming the client rules does not overwrite them with the `keys` property,
* they will be added to all clients.
*
* @signal request::default_mousebindings
* @tparam string context The reason why the signal was sent (currently always
* `startup`).
*/
/** Emitted during startup to gather the default client keybindings.
*
* This signals gives a chance to all module to register new client keybindings.
* Assuming the client rules does not overwrite them with the `keys` property,
* they will be added to all clients.
*
* @signal request::default_keybindings
* @tparam string context The reason why the signal was sent (currently always
* `startup`).
*/
/** When a client gets tagged.
* @signal tagged
* @tag t The tag object.

View File

@ -57,6 +57,9 @@ insulate('main', function ()
run = function() end,
stop = function() end,
}
_G.client = {
connect_signal = function() end
}
-- luacheck: globals string
function string.wlen(self)
return #self

View File

@ -228,6 +228,25 @@ local steps = {
return true
end,
-- Add more keybindings for make sure the popup has many pages.
function()
for j=1, 10 do
for i=1, 200 do
awful.keyboard.append_global_keybinding(
awful.key {
key = "#"..(300+i),
modifiers = {},
on_press = function() end,
description = "Fake "..i,
group = "Fake"..j,
}
)
end
end
return true
end,
-- Hotkeys popup should be displayed and hidden
function(count)
local s = awful.screen.focused()

View File

@ -4,6 +4,8 @@ local runner = require( "_runner" )
local placement = require( "awful.placement" )
local gtable = require( "gears.table" )
local test_client = require( "_client" )
local akeyboard = require( "awful.keyboard" )
local amouse = require( "awful.mouse" )
local module = {
key = require( "awful.key" ),
@ -249,6 +251,127 @@ for _, type_name in ipairs { "key", "button" } do
end
end
local exe1, exe2, exe3, exe4 = false, false, false, false
local new1, new2 = nil, nil
-- Check that you can add new default key/mousebindings at any time.
table.insert(steps, function()
assert(#mouse.screen.clients == 0)
new1 = module.key {
key = "a",
modifiers = {},
on_press = function() exe1 = true end
}
new2 = module.button {
button = 8,
modifiers = {},
on_press = function() exe2 = true end
}
akeyboard.append_client_keybinding(new1)
amouse.append_client_mousebinding(new2)
-- Spawn a client.
test_client("myclient")
return true
end)
table.insert(steps, function()
if #mouse.screen.clients == 0 then return end
client.focus = mouse.screen.clients[1]
-- That should trigger the newly added keybinding.
root.fake_input("key_press" , "a")
root.fake_input("key_release", "a")
-- Move the mouse over the client so mousebindings work.
placement.centered(mouse, {parent=client.focus})
awesome.sync()
-- Same thing for the mouse.
root.fake_input("button_press" , 8)
root.fake_input("button_release", 8)
return true
end)
table.insert(steps, function()
if (not exe1) or (not exe2) then return end
akeyboard.remove_client_keybinding(new1)
amouse.remove_client_mousebinding(new2)
exe1, exe2 = false, false
return true
end)
-- Removing is async, so wait until the next loop.
table.insert(steps, function()
root.fake_input("key_press" , "a")
root.fake_input("key_release", "a")
root.fake_input("button_press" , 8)
root.fake_input("button_release", 8)
awesome.sync()
return true
end)
-- Try adding key/mousebindings to existing clients.
table.insert(steps, function(count)
if count ~= 3 then return end
assert(not exe1)
assert(not exe2)
-- Append both types at the same time to make sure nothing overwrite
-- the list.
akeyboard.append_client_keybinding(new1)
amouse.append_client_mousebinding(new2)
new1 = module.key {
key = "b",
modifiers = {},
on_press = function() exe3 = true end
}
new2 = module.button {
button = 7,
modifiers = {},
on_press = function() exe4 = true end
}
-- Append directly on the client.
client.focus:append_keybinding(new1)
client.focus:append_mousebinding(new2)
return true
end)
-- Removing is async, so wait until the next loop.
table.insert(steps, function()
root.fake_input("key_press" , "b")
root.fake_input("key_release", "b")
root.fake_input("button_press" , 7)
root.fake_input("button_release", 7)
return true
end)
table.insert(steps, function()
if (not exe3) or (not exe4) then return end
-- Note, there is no point to try remove_keybinding, it was already called
-- (indirectly) 4 steps ago.
return true
end)
runner.run_steps(steps)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80