notification.action: Allow actions to be shared by multiple notification
The reason is that if actions are provided by rules, only one instance exist. It was a mistake to couple actions with their notifications. It could not work reliably and has to be removed. The commit also change the notification action storage to be a copy instead of the original table. This allows to append actions (not part of this commit) without risking adding them to the wrong notification. **WARNING** This break an unreleased API by removing the `notification` property of an action.
This commit is contained in:
parent
f8cbb54913
commit
e524f93baa
|
@ -2,7 +2,8 @@
|
||||||
--- A notification action.
|
--- A notification action.
|
||||||
--
|
--
|
||||||
-- A notification can have multiple actions to chose from. This module allows
|
-- A notification can have multiple actions to chose from. This module allows
|
||||||
-- to manage such actions.
|
-- to manage such actions. An action object can be shared by multiple
|
||||||
|
-- notifications.
|
||||||
--
|
--
|
||||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||||
-- @copyright 2019 Emmanuel Lepage Vallee
|
-- @copyright 2019 Emmanuel Lepage Vallee
|
||||||
|
@ -29,8 +30,8 @@ local action = {}
|
||||||
|
|
||||||
-- If the action is selected.
|
-- If the action is selected.
|
||||||
--
|
--
|
||||||
-- Only a single action can be selected per notification. It will be applied
|
-- Only a single action can be selected per notification. This is useful to
|
||||||
-- when `my_notification:apply()` is called.
|
-- implement keyboard navigation.
|
||||||
--
|
--
|
||||||
-- @property selected
|
-- @property selected
|
||||||
-- @param boolean
|
-- @param boolean
|
||||||
|
@ -50,10 +51,6 @@ local action = {}
|
||||||
-- @property icon_only
|
-- @property icon_only
|
||||||
-- @param[opt=false] boolean
|
-- @param[opt=false] boolean
|
||||||
|
|
||||||
--- The notification.
|
|
||||||
-- @property notification
|
|
||||||
-- @tparam naughty.notification notification
|
|
||||||
|
|
||||||
--- When a notification is invoked.
|
--- When a notification is invoked.
|
||||||
-- @signal invoked
|
-- @signal invoked
|
||||||
|
|
||||||
|
@ -64,10 +61,7 @@ end
|
||||||
function action:set_selected(value)
|
function action:set_selected(value)
|
||||||
self._private.selected = value
|
self._private.selected = value
|
||||||
self:emit_signal("property::selected", value)
|
self:emit_signal("property::selected", value)
|
||||||
|
self:emit_signal("_changed")
|
||||||
if self._private.notification then
|
|
||||||
self._private.notification:emit_signal("property::actions")
|
|
||||||
end
|
|
||||||
|
|
||||||
--TODO deselect other actions from the same notification
|
--TODO deselect other actions from the same notification
|
||||||
end
|
end
|
||||||
|
@ -79,15 +73,12 @@ end
|
||||||
function action:set_position(value)
|
function action:set_position(value)
|
||||||
self._private.position = value
|
self._private.position = value
|
||||||
self:emit_signal("property::position", value)
|
self:emit_signal("property::position", value)
|
||||||
|
self:emit_signal("_changed")
|
||||||
if self._private.notification then
|
|
||||||
self._private.notification:emit_signal("property::actions")
|
|
||||||
end
|
|
||||||
|
|
||||||
--TODO make sure the position is unique
|
--TODO make sure the position is unique
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, prop in ipairs { "name", "icon", "notification", "icon_only" } do
|
for _, prop in ipairs { "name", "icon", "icon_only" } do
|
||||||
action["get_"..prop] = function(self)
|
action["get_"..prop] = function(self)
|
||||||
return self._private[prop]
|
return self._private[prop]
|
||||||
end
|
end
|
||||||
|
@ -95,22 +86,18 @@ for _, prop in ipairs { "name", "icon", "notification", "icon_only" } do
|
||||||
action["set_"..prop] = function(self, value)
|
action["set_"..prop] = function(self, value)
|
||||||
self._private[prop] = value
|
self._private[prop] = value
|
||||||
self:emit_signal("property::"..prop, value)
|
self:emit_signal("property::"..prop, value)
|
||||||
|
self:emit_signal("_changed")
|
||||||
-- Make sure widgets with as an actionlist is updated.
|
|
||||||
if self._private.notification then
|
|
||||||
self._private.notification:emit_signal("property::actions")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local set_notif = action.set_notification
|
--TODO v4.5, remove this.
|
||||||
|
function action.set_notification()
|
||||||
function action.set_notification(self, value)
|
-- It didn't work because it prevented actions defined in the rules to be
|
||||||
local old = self._private.notification
|
-- in multiple notifications at once.
|
||||||
set_notif(self, value)
|
assert(
|
||||||
if old then
|
false,
|
||||||
old:emit_signal("property::actions")
|
"Setting a notification object was a bad idea and is now forbidden"
|
||||||
end
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Execute this action.
|
--- Execute this action.
|
||||||
|
@ -118,11 +105,12 @@ end
|
||||||
-- This only emits the `invoked` signal.
|
-- This only emits the `invoked` signal.
|
||||||
--
|
--
|
||||||
-- @method invoke
|
-- @method invoke
|
||||||
function action:invoke()
|
-- @tparam[opt={}] naughty.notification notif A notification object on which
|
||||||
assert(self._private.notification,
|
-- the action was invoked. If a notification is shared by many object (like
|
||||||
"Cannot invoke an action without a notification")
|
-- a "mute" or "snooze" action added to all notification), calling `:invoke()`
|
||||||
|
-- without adding the `notif` context will cause unexpected results.
|
||||||
self:emit_signal("invoked")
|
function action:invoke(notif)
|
||||||
|
self:emit_signal("invoked", notif)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function new(_, args)
|
local function new(_, args)
|
||||||
|
|
|
@ -430,12 +430,10 @@ function naughty.default_notification_handler(notification, args)
|
||||||
|
|
||||||
actionmarginbox:buttons(gtable.join(
|
actionmarginbox:buttons(gtable.join(
|
||||||
button({ }, 1, function()
|
button({ }, 1, function()
|
||||||
action:invoke()
|
action:invoke(notification)
|
||||||
notification:destroy()
|
|
||||||
end),
|
end),
|
||||||
button({ }, 3, function()
|
button({ }, 3, function()
|
||||||
action:invoke()
|
action:invoke(notification)
|
||||||
notification:destroy()
|
|
||||||
end)
|
end)
|
||||||
))
|
))
|
||||||
actionslayout:add(actionmarginbox)
|
actionslayout:add(actionmarginbox)
|
||||||
|
|
|
@ -117,10 +117,6 @@ local module = {}
|
||||||
-- @tparam gears.surface|string action_bgimage_selected
|
-- @tparam gears.surface|string action_bgimage_selected
|
||||||
-- @see gears.surface
|
-- @see gears.surface
|
||||||
|
|
||||||
local default_buttons = gtable.join(
|
|
||||||
abutton({ }, 1, function(a) a:invoke() end)
|
|
||||||
)
|
|
||||||
|
|
||||||
local props = {"shape_border_color", "bg_image" , "fg",
|
local props = {"shape_border_color", "bg_image" , "fg",
|
||||||
"shape_border_width", "underline", "bg",
|
"shape_border_width", "underline", "bg",
|
||||||
"shape", "icon_size", }
|
"shape", "icon_size", }
|
||||||
|
@ -176,7 +172,7 @@ local function update(self)
|
||||||
|
|
||||||
awcommon.list_update(
|
awcommon.list_update(
|
||||||
self._private.layout,
|
self._private.layout,
|
||||||
default_buttons,
|
self._private.default_buttons,
|
||||||
function(o) return wb_label(o, self) end,
|
function(o) return wb_label(o, self) end,
|
||||||
self._private.data,
|
self._private.data,
|
||||||
self._private.notification.actions,
|
self._private.notification.actions,
|
||||||
|
@ -273,7 +269,7 @@ end
|
||||||
--- Create an action list.
|
--- Create an action list.
|
||||||
--
|
--
|
||||||
-- @tparam table args
|
-- @tparam table args
|
||||||
-- @tparam naughty.notification args.notification The notification/
|
-- @tparam naughty.notification args.notification The notification.
|
||||||
-- @tparam widget args.base_layout The action layout.
|
-- @tparam widget args.base_layout The action layout.
|
||||||
-- @tparam table args.style Override the beautiful values.
|
-- @tparam table args.style Override the beautiful values.
|
||||||
-- @tparam boolean args.style.underline_normal
|
-- @tparam boolean args.style.underline_normal
|
||||||
|
@ -312,6 +308,10 @@ local function new(_, args)
|
||||||
|
|
||||||
update_style(wdg)
|
update_style(wdg)
|
||||||
|
|
||||||
|
wdg._private.default_buttons = gtable.join(
|
||||||
|
abutton({ }, 1, function(a) a:invoke(args.notification) end)
|
||||||
|
)
|
||||||
|
|
||||||
return wdg
|
return wdg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -641,6 +641,34 @@ function notification.get_clients(self)
|
||||||
return self._private.clients
|
return self._private.clients
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function notification.set_actions(self, new_actions)
|
||||||
|
for _, a in ipairs(self._private.actions or {}) do
|
||||||
|
a:disconnect_signal("_changed", self._private.action_cb )
|
||||||
|
a:disconnect_signal("invoked" , self._private.invoked_cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clone so `append_actions` doesn't add unwanted actions to other
|
||||||
|
-- notifications.
|
||||||
|
self._private.actions = gtable.clone(new_actions, false)
|
||||||
|
|
||||||
|
for _, a in ipairs(self._private.actions or {}) do
|
||||||
|
a:connect_signal("_changed", self._private.action_cb )
|
||||||
|
a:connect_signal("invoked" , self._private.invoked_cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:emit_signal("property::actions", new_actions)
|
||||||
|
|
||||||
|
-- When a notification is updated over dbus or by setting a property,
|
||||||
|
-- it is usually convenient to reset the timeout.
|
||||||
|
local reset = ((not self.suspended)
|
||||||
|
and self.auto_reset_timeout ~= false
|
||||||
|
and naughty.auto_reset_timeout)
|
||||||
|
|
||||||
|
if reset then
|
||||||
|
self:reset_timeout()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--TODO v6: remove this
|
--TODO v6: remove this
|
||||||
local function convert_actions(actions)
|
local function convert_actions(actions)
|
||||||
gdebug.deprecate(
|
gdebug.deprecate(
|
||||||
|
@ -808,21 +836,34 @@ local function create(args)
|
||||||
private[k] = v
|
private[k] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
-- notif.actions should not be nil to allow cheching if there is actions
|
|
||||||
-- using the shorthand `if #notif.actions > 0 then`
|
|
||||||
private.actions = private.actions or {}
|
|
||||||
|
|
||||||
-- Make sure the action are for this notification. Sharing actions with
|
|
||||||
-- multiple notification is not supported.
|
|
||||||
for _, a in ipairs(private.actions) do
|
|
||||||
a.notification = n
|
|
||||||
end
|
|
||||||
|
|
||||||
-- It's an automatic property
|
-- It's an automatic property
|
||||||
n.is_expired = false
|
n.is_expired = false
|
||||||
|
|
||||||
gtable.crush(n, notification, true)
|
gtable.crush(n, notification, true)
|
||||||
|
|
||||||
|
-- Always emit property::actions when any of the action change to allow
|
||||||
|
-- some widgets to be updated without over complicated built-in tracking
|
||||||
|
-- of all options.
|
||||||
|
function n._private.action_cb() n:emit_signal("property::actions") end
|
||||||
|
|
||||||
|
-- Listen to action press and destroy non-resident notifications.
|
||||||
|
function n._private.invoked_cb(a, notif)
|
||||||
|
if (not notif) or notif == n then
|
||||||
|
n:emit_signal("invoked", a)
|
||||||
|
|
||||||
|
if not n.resident then
|
||||||
|
n:destroy(cst.notification_closed_reason.dismissed_by_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- notif.actions should not be nil to allow checking if there is actions
|
||||||
|
-- using the shorthand `if #notif.actions > 0 then`
|
||||||
|
private.actions = {}
|
||||||
|
if args.actions then
|
||||||
|
notification.set_actions(n, args.actions)
|
||||||
|
end
|
||||||
|
|
||||||
n.id = n.id or notification._gen_next_id()
|
n.id = n.id or notification._gen_next_id()
|
||||||
|
|
||||||
-- Register the notification before requesting a widget
|
-- Register the notification before requesting a widget
|
||||||
|
|
Loading…
Reference in New Issue