naughty: Turn actions into object.
The current API is non-compliant with the 1.0 spec and cannot represent the v1.2 spec at all. The pair of name and callback fails to represent the explicit ordering and cannot support the icons cleanly. Plus to support the keyboard navigation use case, the notification action need to be able to get some sort of focus state. Having an object makes this easy.
This commit is contained in:
parent
e70822a6a4
commit
6d5d016a2a
|
@ -0,0 +1,127 @@
|
|||
---------------------------------------------------------------------------
|
||||
--- A notification action.
|
||||
--
|
||||
-- A notification can have multiple actions to chose from. This module allows
|
||||
-- to manage such actions.
|
||||
--
|
||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||
-- @copyright 2019 Emmanuel Lepage Vallee
|
||||
-- @classmod naughty.action
|
||||
---------------------------------------------------------------------------
|
||||
local gtable = require("gears.table" )
|
||||
local gobject = require("gears.object")
|
||||
|
||||
local action = {}
|
||||
|
||||
--- Create a new action.
|
||||
-- @function naughty.action
|
||||
-- @tparam table args The arguments.
|
||||
-- @tparam string args.name The name.
|
||||
-- @tparam string args.position The position.
|
||||
-- @tparam string args.icon The icon.
|
||||
-- @tparam naughty.notification args.notification The notification object.
|
||||
-- @tparam boolean args.selected If this action is currently selected.
|
||||
-- @return A new action.
|
||||
|
||||
-- The action name.
|
||||
-- @property name
|
||||
-- @tparam string name The name.
|
||||
|
||||
-- If the action is selected.
|
||||
--
|
||||
-- Only a single action can be selected per notification. It will be applied
|
||||
-- when `my_notification:apply()` is called.
|
||||
--
|
||||
-- @property selected
|
||||
-- @param boolean
|
||||
|
||||
--- The action position (index).
|
||||
-- @property position
|
||||
-- @param number
|
||||
|
||||
--- The action icon.
|
||||
-- @property icon
|
||||
-- @param gears.surface
|
||||
|
||||
--- The notification.
|
||||
-- @property notification
|
||||
-- @tparam naughty.notification notification
|
||||
|
||||
--- When a notification is invoked.
|
||||
-- @signal invoked
|
||||
|
||||
function action:get_selected()
|
||||
return self._private.selected
|
||||
end
|
||||
|
||||
function action:set_selected(value)
|
||||
self._private.selected = value
|
||||
self:emit_signal("property::selected", value)
|
||||
|
||||
if self._private.notification then
|
||||
self._private.notification:emit_signal("property::actions")
|
||||
end
|
||||
|
||||
--TODO deselect other actions from the same notification
|
||||
end
|
||||
|
||||
function action:get_position()
|
||||
return self._private.position
|
||||
end
|
||||
|
||||
function action:set_position(value)
|
||||
self._private.position = value
|
||||
self:emit_signal("property::position", value)
|
||||
|
||||
if self._private.notification then
|
||||
self._private.notification:emit_signal("property::actions")
|
||||
end
|
||||
|
||||
--TODO make sure the position is unique
|
||||
end
|
||||
|
||||
for _, prop in ipairs { "name", "icon", "notification" } do
|
||||
action["get_"..prop] = function(self)
|
||||
return self._private[prop]
|
||||
end
|
||||
|
||||
action["set_"..prop] = function(self, value)
|
||||
self._private[prop] = value
|
||||
self:emit_signal("property::"..prop, value)
|
||||
|
||||
-- Make sure widgets with as an actionlist is updated.
|
||||
if self._private.notification then
|
||||
self._private.notification:emit_signal("property::actions")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Execute this action.
|
||||
function action:invoke()
|
||||
assert(self._private.notification,
|
||||
"Cannot invoke an action without a notification")
|
||||
|
||||
self:emit_signal("invoked")
|
||||
end
|
||||
|
||||
local function new(_, args)
|
||||
args = args or {}
|
||||
local ret = gobject { enable_properties = true }
|
||||
|
||||
gtable.crush(ret, action, true)
|
||||
|
||||
local default = {
|
||||
-- See "table 1" of the spec about the default name
|
||||
name = args.name or "default",
|
||||
selected = args.selected == true,
|
||||
position = args.position,
|
||||
icon = args.icon,
|
||||
notification = args.notification,
|
||||
}
|
||||
|
||||
rawset(ret, "_private", default)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
return setmetatable(action, {__call = new})
|
|
@ -497,8 +497,7 @@ end
|
|||
-- @tparam[opt] func args.callback Function that will be called with all arguments.
|
||||
-- The notification will only be displayed if the function returns true.
|
||||
-- Note: this function is only relevant to notifications sent via dbus.
|
||||
-- @tparam[opt] table args.actions Mapping that maps a string to a callback when this
|
||||
-- action is selected.
|
||||
-- @tparam[opt] table args.actions A list of `naughty.action`s.
|
||||
-- @bool[opt=false] args.ignore_suspend If set to true this notification
|
||||
-- will be shown even if notifications are suspended via `naughty.suspend`.
|
||||
-- @usage naughty.notify({ title = "Achtung!", text = "You're idling", timeout = 0 })
|
||||
|
|
|
@ -27,6 +27,7 @@ local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility
|
|||
local naughty = require("naughty.core")
|
||||
local cst = require("naughty.constants")
|
||||
local nnotif = require("naughty.notification")
|
||||
local naction = require("naughty.action")
|
||||
|
||||
--- Notification library, dbus bindings
|
||||
local dbus = { config = {} }
|
||||
|
@ -158,10 +159,17 @@ capi.dbus.connect_signal("org.freedesktop.Notifications",
|
|||
notification:destroy(cst.notification_closed_reason.dismissed_by_user)
|
||||
end
|
||||
elseif action_id ~= nil and action_text ~= nil then
|
||||
args.actions[action_text] = function()
|
||||
local a = naction {
|
||||
name = action_text,
|
||||
position = action_id,
|
||||
}
|
||||
|
||||
a:connect_signal("invoked", function()
|
||||
sendActionInvoked(notification.id, action_id)
|
||||
notification:destroy(cst.notification_closed_reason.dismissed_by_user)
|
||||
end
|
||||
end)
|
||||
|
||||
table.insert(args.actions, a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -404,23 +404,25 @@ function naughty.default_notification_handler(notification, args)
|
|||
local actions_max_width = 0
|
||||
local actions_total_height = 0
|
||||
if actions then
|
||||
for action, callback in pairs(actions) do
|
||||
for _, action in ipairs(actions) do
|
||||
assert(type(action) == "table")
|
||||
assert(action.name ~= nil)
|
||||
local actiontextbox = wibox.widget.textbox()
|
||||
local actionmarginbox = wibox.container.margin()
|
||||
actionmarginbox:set_margins(margin)
|
||||
actionmarginbox:set_widget(actiontextbox)
|
||||
actiontextbox:set_valign("middle")
|
||||
actiontextbox:set_font(font)
|
||||
actiontextbox:set_markup(string.format('☛ <u>%s</u>', action))
|
||||
actiontextbox:set_markup(string.format('☛ <u>%s</u>', action.name))
|
||||
-- calculate the height and width
|
||||
local w, h = actiontextbox:get_preferred_size(s)
|
||||
local action_height = h + 2 * margin
|
||||
local action_width = w + 2 * margin
|
||||
|
||||
actionmarginbox:buttons(gtable.join(
|
||||
button({ }, 1, callback),
|
||||
button({ }, 3, callback)
|
||||
))
|
||||
button({ }, 1, function() action:trigger() end),
|
||||
button({ }, 3, function() action:trigger() end)
|
||||
))
|
||||
actionslayout:add(actionmarginbox)
|
||||
|
||||
actions_total_height = actions_total_height + action_height
|
||||
|
|
|
@ -325,6 +325,43 @@ for _, prop in ipairs(properties) do
|
|||
|
||||
end
|
||||
|
||||
--TODO v6: remove this
|
||||
local function convert_actions(actions)
|
||||
gdebug.deprecate(
|
||||
"The notification actions should now be of type `naughty.action`, "..
|
||||
"not strings or callback functions",
|
||||
{deprecated_in=5}
|
||||
)
|
||||
|
||||
local naction = require("naughty.action")
|
||||
|
||||
-- Does not attempt to handle when there is a mix of strings and objects
|
||||
for idx, name in pairs(actions) do
|
||||
local cb = nil
|
||||
|
||||
if type(name) == "function" then
|
||||
cb = name
|
||||
end
|
||||
|
||||
if type(idx) == "string" then
|
||||
name, idx = idx, nil
|
||||
end
|
||||
|
||||
local a = naction {
|
||||
position = idx,
|
||||
name = name,
|
||||
}
|
||||
|
||||
if cb then
|
||||
a:connect_signal("invoked", cb)
|
||||
end
|
||||
|
||||
-- Yes, it modifies `args`, this is legacy code, cloning the args
|
||||
-- just for this isn't worth it.
|
||||
actions[idx] = a
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a notification.
|
||||
--
|
||||
-- @tab args The argument table containing any of the arguments below.
|
||||
|
@ -364,8 +401,7 @@ end
|
|||
-- @tparam[opt] func args.callback Function that will be called with all arguments.
|
||||
-- The notification will only be displayed if the function returns true.
|
||||
-- Note: this function is only relevant to notifications sent via dbus.
|
||||
-- @tparam[opt] table args.actions Mapping that maps a string to a callback when this
|
||||
-- action is selected.
|
||||
-- @tparam[opt] table args.actions A list of `naughty.action`s.
|
||||
-- @bool[opt=false] args.ignore_suspend If set to true this notification
|
||||
-- will be shown even if notifications are suspended via `naughty.suspend`.
|
||||
-- @usage naughty.notify({ title = "Achtung!", text = "You're idling", timeout = 0 })
|
||||
|
@ -378,6 +414,16 @@ local function create(args)
|
|||
if not args then return end
|
||||
end
|
||||
|
||||
args = args or {}
|
||||
|
||||
-- Old actions usually have callbacks and names. But this isn't non
|
||||
-- compliant with the spec. The spec has explicit ordering and optional
|
||||
-- icons. The old format doesn't allow these metadata to be stored.
|
||||
local is_old_action = args.actions and (
|
||||
(args.actions[1] and type(args.actions[1]) == "string") or
|
||||
(type(next(args.actions)) == "string")
|
||||
)
|
||||
|
||||
local n = gobject {
|
||||
enable_properties = true,
|
||||
}
|
||||
|
@ -396,6 +442,10 @@ local function create(args)
|
|||
rawget(n, "preset") or {}
|
||||
))
|
||||
|
||||
if is_old_action then
|
||||
convert_actions(args.actions)
|
||||
end
|
||||
|
||||
for k, v in pairs(n.preset) do
|
||||
private[k] = v
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue