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 -- Create a textclock widget
mytextclock = wibox.widget.textclock() 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@ -- @DOC_WALLPAPER@
screen.connect_signal("request::wallpaper", function(s) screen.connect_signal("request::wallpaper", function(s)
-- Wallpaper -- Wallpaper
@ -165,6 +122,7 @@ screen.connect_signal("request::desktop_decoration", function(s)
-- Create a promptbox for each screen -- Create a promptbox for each screen
s.mypromptbox = awful.widget.prompt() s.mypromptbox = awful.widget.prompt()
-- Create an imagebox widget which will contain an icon indicating which layout we're using. -- Create an imagebox widget which will contain an icon indicating which layout we're using.
-- We need one layoutbox per screen. -- We need one layoutbox per screen.
s.mylayoutbox = awful.widget.layoutbox { s.mylayoutbox = awful.widget.layoutbox {
@ -181,14 +139,44 @@ screen.connect_signal("request::desktop_decoration", function(s)
s.mytaglist = awful.widget.taglist { s.mytaglist = awful.widget.taglist {
screen = s, screen = s,
filter = awful.widget.taglist.filter.all, 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 -- Create a tasklist widget
s.mytasklist = awful.widget.tasklist { s.mytasklist = awful.widget.tasklist {
screen = s, screen = s,
filter = awful.widget.tasklist.filter.currenttags, 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@ -- @DOC_WIBAR@
@ -332,8 +320,85 @@ awful.keyboard.append_global_keybindings({
{description = "select previous", group = "layout"}), {description = "select previous", group = "layout"}),
}) })
-- @DOC_NUMBER_KEYBINDINGS@
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[index]
if tag then
tag:view_only()
end
end,
},
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[index]
if tag then
awful.tag.viewtoggle(tag)
end
end,
},
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[index]
if tag then
client.focus:move_to_tag(tag)
end
end
end,
},
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[index]
if tag then
client.focus:toggle_tag(tag)
end
end
end,
}
})
-- @DOC_CLIENT_BUTTONS@
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@ -- @DOC_CLIENT_KEYBINDINGS@
clientkeys = { client.connect_signal("request::default_keybindings", function()
awful.keyboard.append_client_keybindings({
awful.key({ modkey, }, "f", awful.key({ modkey, }, "f",
function (c) function (c)
c.fullscreen = not c.fullscreen c.fullscreen = not c.fullscreen
@ -375,82 +440,8 @@ clientkeys = {
c:raise() c:raise()
end , end ,
{description = "(un)maximize horizontally", group = "client"}), {description = "(un)maximize horizontally", group = "client"}),
} })
end)
-- @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 ()
local screen = awful.screen.focused()
local tag = screen.tags[i]
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 ()
local screen = awful.screen.focused()
local tag = screen.tags[i]
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 ()
if client.focus then
local tag = client.focus.screen.tags[i]
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 ()
if client.focus then
local tag = client.focus.screen.tags[i]
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),
}
-- }}} -- }}}
@ -465,8 +456,6 @@ awful.rules.rules = {
border_color = beautiful.border_normal, border_color = beautiful.border_normal,
focus = awful.client.focus.filter, focus = awful.client.focus.filter,
raise = true, raise = true,
keys = clientkeys,
buttons = clientbuttons,
screen = awful.screen.preferred, screen = awful.screen.preferred,
placement = awful.placement.no_overlap+awful.placement.no_offscreen 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 * `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 deprecation of Awesome's own DBus bindings and could lead to behaviour changes
on DBus. 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> <a name="v43"></a>
# Awesome window manager framework version 4.3 changes # Awesome window manager framework version 4.3 changes

View File

@ -365,6 +365,29 @@ function client.cycle(clockwise, s, stacked)
end end
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. --- Get the master window.
-- --
-- @legacylayout awful.client.getmaster -- @legacylayout awful.client.getmaster
@ -1348,13 +1371,13 @@ object.properties._legacy_accessors(client, "buttons", "_buttons", true, functio
return new_btns[1] and ( return new_btns[1] and (
type(new_btns[1]) == "button" or new_btns[1]._is_capi_button type(new_btns[1]) == "button" or new_btns[1]._is_capi_button
) or false ) or false
end, true) end, true, true, "mousebinding")
object.properties._legacy_accessors(client, "keys", "_keys", true, function(new_btns) object.properties._legacy_accessors(client, "keys", "_keys", true, function(new_btns)
return new_btns[1] and ( return new_btns[1] and (
type(new_btns[1]) == "key" or new_btns[1]._is_capi_key type(new_btns[1]) == "key" or new_btns[1]._is_capi_key
) or false ) or false
end, true) end, true, true, "keybinding")
--- Set the client shape. --- Set the client shape.
-- @property shape -- @property shape

View File

@ -274,7 +274,9 @@ function widget.new(args)
end end
table.sort( table.sort(
sorted_table, 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 target[group] = sorted_table
end end
@ -287,7 +289,9 @@ function widget.new(args)
return return
end end
for _, data in pairs(awful.key.hotkeys) do 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 end
self:_sort_hotkeys(self._cached_awful_keys) self:_sort_hotkeys(self._cached_awful_keys)
end end

View File

@ -24,6 +24,17 @@ local gobject = require("gears.object")
-- @property key -- @property key
-- @param string -- @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. --- The table of modifier keys.
-- --
-- A modifier, such as `Control` are a predetermined set of keys that can be -- 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. -- @treturn table A table with one or several key objects.
-- @constructorfct awful.key -- @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 if type(release)=='table' then
data=release data=release
release=nil release=nil
@ -230,38 +241,52 @@ local function new_common(mod, _key, press, release, data)
local ret = {} local ret = {}
local subsets = gmath.subsets(key.ignore_modifiers) local subsets = gmath.subsets(key.ignore_modifiers)
for _, key_pair in ipairs(_keys) do
for _, set in ipairs(subsets) do for _, set in ipairs(subsets) do
local sub_key = capi.key { local sub_key = capi.key {
modifiers = gtable.join(mod, set), modifiers = gtable.join(mod, set),
key = _key key = key_pair[1]
} }
sub_key._private._legacy_convert_to = ret sub_key._private._legacy_convert_to = ret
sub_key:connect_signal("press", function(_, ...) sub_key:connect_signal("press", function(_, ...)
if ret.on_press then if ret.on_press then
if key_pair[2] ~= nil then
ret.on_press(key_pair[2], ...)
else
ret.on_press(...) ret.on_press(...)
end end
end
end) end)
sub_key:connect_signal("release", function(_, ...) sub_key:connect_signal("release", function(_, ...)
if ret.on_release then if ret.on_release then
if key_pair[2] ~= nil then
ret.on_release(key_pair[2], ...)
else
ret.on_release(...) ret.on_release(...)
end end
end
end) end)
ret[#ret + 1] = sub_key ret[#ret + 1] = sub_key
end end
end
-- append custom userdata (like description) to a hotkey -- append custom userdata (like description) to a hotkey
data = data and gtable.clone(data) or {} data = data and gtable.clone(data) or {}
data.mod = mod data.mod = mod
data.key = _key data.keys = _keys
data.on_press = press data.on_press = press
data.on_release = release data.on_release = release
data._is_capi_key = false data._is_capi_key = false
assert((not data.key) or type(data.key) == "string")
table.insert(key.hotkeys, data) 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 -- Store the private data
reverse_map[ret] = data reverse_map[ret] = data
@ -273,19 +298,54 @@ local function new_common(mod, _key, press, release, data)
return setmetatable(ret, obj_mt) return setmetatable(ret, obj_mt)
end 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) function key.new(args, _key, press, release, data)
-- Assume this is the new constructor. -- Assume this is the new constructor.
if not _key then if not _key then
assert(not (press or release or data), "Calling awful.key() requires a key name") assert(not (press or release or data), "Calling awful.key() requires a key name")
local keys = get_keys(args)
return new_common( return new_common(
args.modifiers, args.modifiers,
args.key, keys,
args.on_press, args.on_press,
args.on_release, args.on_release,
args args
) )
else else
return new_common(args, _key, press, release, data) return new_common(args, {{_key}}, press, release, data)
end end
end end

View File

@ -6,7 +6,7 @@
-- @inputmodule awful.keyboard -- @inputmodule awful.keyboard
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
local capi = {root = root, awesome = awesome} local capi = {root = root, awesome = awesome, client = client}
local module = {} local module = {}
--- Convert the modifiers into pc105 key names --- Convert the modifiers into pc105 key names
@ -138,4 +138,72 @@ function module.remove_global_keybinding(key)
capi.root._remove_key(key) capi.root._remove_key(key)
end 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 return module

View File

@ -421,6 +421,65 @@ function mouse.remove_global_mousebinding(button)
capi.root._remove_button(button) capi.root._remove_button(button)
end 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 for _, b in ipairs {"left", "right", "middle"} do
mouse.object["is_".. b .."_mouse_button_pressed"] = function() mouse.object["is_".. b .."_mouse_button_pressed"] = function()
return capi.mouse.coords().buttons[1] return capi.mouse.coords().buttons[1]
@ -470,6 +529,14 @@ end)
-- when button 1 is pressed. -- when button 1 is pressed.
-- @staticfct mouse.coords -- @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 return mouse

View File

@ -121,6 +121,8 @@ local protected_call = require("gears.protected_call")
local aspawn = require("awful.spawn") local aspawn = require("awful.spawn")
local gdebug = require("gears.debug") local gdebug = require("gears.debug")
local gmatcher = require("gears.matcher") 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 unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local rules = {} local rules = {}
@ -529,6 +531,13 @@ end
-- @staticfct awful.rules.execute -- @staticfct awful.rules.execute
crules._execute = function(_, c, props, callbacks) 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. -- This has to be done first, as it will impact geometry related props.
if props.titlebars_enabled and (type(props.titlebars_enabled) ~= "function" if props.titlebars_enabled and (type(props.titlebars_enabled) ~= "function"
or props.titlebars_enabled(c,props)) then or props.titlebars_enabled(c,props)) then

View File

@ -8,6 +8,7 @@
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
local gtable = require("gears.table") local gtable = require("gears.table")
local gtimer = nil --require("gears.timer")
-- local gdebug = require("gears.debug") -- local gdebug = require("gears.debug")
local object = {} local object = {}
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) 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
end end
assert(type(class) ~= "function")
-- Attach the accessor methods -- Attach the accessor methods
class.set_index_miss_handler(getter) class.set_index_miss_handler(getter)
class.set_newindex_miss_handler(setter) class.set_newindex_miss_handler(setter)
@ -132,7 +135,7 @@ end
-- --
-- TO BE USED FOR DEPRECATION ONLY. -- 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) local ret = gtable.clone(to_set, false)
-- .buttons used to be a function taking the result of `gears.table.join`. -- .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} -- {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 -- Setter
if new_objs and #new_objs > 0 then if new_objs and has_content then
if (not set_empty) and not next(new_objs) then return end
local is_formatted = join_if(new_objs) local is_formatted = join_if(new_objs)
-- Because modules may rely on :buttons taking a list of -- 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 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 -- Some objects have a special "object" property to add more properties, but
-- not all. -- not all.
@ -248,6 +254,7 @@ function object._legacy_accessors(obj, name, capi_name, is_object, join_if, set_
--TODO v6 Use the original directly and drop this legacy copy --TODO v6 Use the original directly and drop this legacy copy
local function apply()
local result = is_formatted and objs local result = is_formatted and objs
or gtable.join(unpack(objs)) or gtable.join(unpack(objs))
@ -258,11 +265,53 @@ function object._legacy_accessors(obj, name, capi_name, is_object, join_if, set_
else else
self._private[name.."_formatted"] = result self._private[name.."_formatted"] = result
end end
end
-- Some properties, like keys, are expensive to set, schedule them
-- instead.
if not delay then
apply()
else
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( self._private[name] = copy_object(
obj, objs, name, capi_name, is_object, join_if, set_empty obj, objs, name, capi_name, is_object, join_if, set_empty
) )
end 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 end
return setmetatable( object, {__call = function(_,...) object.capi_index_fallback(...) end}) return setmetatable( object, {__call = function(_,...) object.capi_index_fallback(...) end})

View File

@ -244,6 +244,28 @@
* @signal request::urgent * @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. /** When a client gets tagged.
* @signal tagged * @signal tagged
* @tag t The tag object. * @tag t The tag object.

View File

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

View File

@ -228,6 +228,25 @@ local steps = {
return true return true
end, 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 -- Hotkeys popup should be displayed and hidden
function(count) function(count)
local s = awful.screen.focused() local s = awful.screen.focused()

View File

@ -4,6 +4,8 @@ local runner = require( "_runner" )
local placement = require( "awful.placement" ) local placement = require( "awful.placement" )
local gtable = require( "gears.table" ) local gtable = require( "gears.table" )
local test_client = require( "_client" ) local test_client = require( "_client" )
local akeyboard = require( "awful.keyboard" )
local amouse = require( "awful.mouse" )
local module = { local module = {
key = require( "awful.key" ), key = require( "awful.key" ),
@ -249,6 +251,127 @@ for _, type_name in ipairs { "key", "button" } do
end end
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) runner.run_steps(steps)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80