notification: Allow to pause automatic expiration.

When the mouse is over or a keyboard driven menu is open, avoid
unexpected expiration to mess with the current notifications.

This commit also improve the `suspended` behavior to correctly
emit some signals.
This commit is contained in:
Emmanuel Lepage Vallee 2019-01-03 01:52:13 -05:00
parent 6d5d016a2a
commit faa553e47c
2 changed files with 69 additions and 20 deletions

View File

@ -98,7 +98,19 @@ gtable.crush(naughty, require("naughty.constants"))
-- @property suspended -- @property suspended
-- @param boolean -- @param boolean
local suspended = false --- Do not allow notifications to auto-expire.
--
-- When navigating the notifications, for example on mouse over or when
-- keyboard navigation is enabled, it is very annoying when notifications
-- just vanish.
--
-- @property expiration_paused
-- @param[opt=false] boolean
local properties = {
suspended = false,
expiration_paused = false
}
--TODO v5 Deprecate the public `naughty.notifications` (to make it private) --TODO v5 Deprecate the public `naughty.notifications` (to make it private)
@ -112,7 +124,7 @@ local suspended = false
-- @field die Function to be executed on timeout -- @field die Function to be executed on timeout
-- @field id Unique notification id based on a counter -- @field id Unique notification id based on a counter
-- @table notifications -- @table notifications
naughty.notifications = { suspended = { } } naughty.notifications = { suspended = { }, _expired = {{}} }
screen.connect_for_each_screen(function(s) screen.connect_for_each_screen(function(s)
naughty.notifications[s] = { naughty.notifications[s] = {
top_left = {}, top_left = {},
@ -137,7 +149,7 @@ end)
-- @deprecated naughty.is_suspended -- @deprecated naughty.is_suspended
function naughty.is_suspended() function naughty.is_suspended()
gdebug.deprecate("Use naughty.suspended", {deprecated_in=5}) gdebug.deprecate("Use naughty.suspended", {deprecated_in=5})
return suspended return properties.suspended
end end
--- Suspend notifications. --- Suspend notifications.
@ -147,7 +159,7 @@ end
-- @deprecated naughty.suspend -- @deprecated naughty.suspend
function naughty.suspend() function naughty.suspend()
gdebug.deprecate("Use naughty.suspended = true", {deprecated_in=5}) gdebug.deprecate("Use naughty.suspended = true", {deprecated_in=5})
suspended = true properties.suspended = true
end end
local conns = {} local conns = {}
@ -196,9 +208,14 @@ function naughty.disconnect_signal(name, func)
end end
local function resume() local function resume()
suspended = false properties.suspended = false
for _, v in pairs(naughty.notifications.suspended) do for _, v in pairs(naughty.notifications.suspended) do
v:emit_signal("request::display") local args = v._private.args
assert(args)
v._private.args = nil
naughty.emit_signal("added", v, args)
naughty.emit_signal("request::display", v, args)
if v.timer then v.timer:start() end if v.timer then v.timer:start() end
end end
naughty.notifications.suspended = { } naughty.notifications.suspended = { }
@ -221,7 +238,7 @@ end
-- @deprecated naughty.toggle -- @deprecated naughty.toggle
function naughty.toggle() function naughty.toggle()
gdebug.deprecate("Use naughty.suspended = not naughty.suspended", {deprecated_in=5}) gdebug.deprecate("Use naughty.suspended = not naughty.suspended", {deprecated_in=5})
if suspended then if properties.suspended then
naughty.resume() naughty.resume()
else else
naughty.suspend() naughty.suspend()
@ -339,10 +356,10 @@ end
-- Remove the notification from the internal list(s) -- Remove the notification from the internal list(s)
local function cleanup(self, reason) local function cleanup(self, reason)
if suspended then if properties.suspended then
for k, v in pairs(naughty.suspended) do for k, v in pairs(naughty.notifications.suspended) do
if v == self then if v == self then
table.remove(naughty.suspended, k) table.remove(naughty.notifications.suspended, k)
break break
end end
end end
@ -357,7 +374,8 @@ local function cleanup(self, reason)
n.idx = k n.idx = k
end end
if self.timer then -- `self.timer.started` will be false if the expiration was paused.
if self.timer and self.timer.started then
self.timer:stop() self.timer:stop()
end end
@ -374,7 +392,17 @@ end
-- Proxy the global suspension state on all notification objects -- Proxy the global suspension state on all notification objects
local function get_suspended(self) local function get_suspended(self)
return suspended and not self.ignore_suspend return properties.suspended and not self.ignore_suspend
end
function naughty.set_expiration_paused(p)
properties.expiration_paused = p
if not p then
for _, n in ipairs(naughty.notifications._expired[1]) do
n:destroy(naughty.notification_closed_reason.expired)
end
end
end end
--- Emitted when a notification is created. --- Emitted when a notification is created.
@ -421,12 +449,13 @@ local function register(notification, args)
notification.idx = #naughty.notifications[s][notification.position] notification.idx = #naughty.notifications[s][notification.position]
notification.screen = s notification.screen = s
if suspended and not args.ignore_suspend then if properties.suspended and not args.ignore_suspend then
notification._private.args = args
table.insert(naughty.notifications.suspended, notification) table.insert(naughty.notifications.suspended, notification)
else
naughty.emit_signal("added", notification, args)
end end
naughty.emit_signal("added", notification, args)
assert(rawget(notification, "preset")) assert(rawget(notification, "preset"))
-- return the notification -- return the notification
@ -436,18 +465,22 @@ end
naughty.connect_signal("new", register) naughty.connect_signal("new", register)
local function index_miss(_, key) local function index_miss(_, key)
if key == "suspended" then if rawget(naughty, "get_"..key) then
return suspended return rawget(naughty, "get_"..key)()
elseif properties[key] ~= nil then
return properties[key]
else else
return nil return nil
end end
end end
local function set_index_miss(_, key, value) local function set_index_miss(_, key, value)
if key == "suspended" then if rawget(naughty, "set_"..key) then
return rawget(naughty, "set_"..key)(value)
elseif properties[key] ~= nil then
assert(type(value) == "boolean") assert(type(value) == "boolean")
suspended = value properties[key] = value
if value then if not value then
resume() resume()
end end
else else

View File

@ -216,6 +216,11 @@ local notification = {}
--@property suspended --@property suspended
--@param boolean --@param boolean
--- If the notification is expired.
-- @property is_expired
-- @param boolean
-- @see naughty.expiration_paused
--- Emitted when the notification is destroyed. --- Emitted when the notification is destroyed.
-- @signal destroyed -- @signal destroyed
-- @tparam number reason Why it was destroyed -- @tparam number reason Why it was destroyed
@ -262,6 +267,14 @@ end
function notification:set_timeout(timeout) function notification:set_timeout(timeout)
local die = function (reason) local die = function (reason)
if reason == cst.notification_closed_reason.expired then
self.is_expired = true
if naughty.expiration_paused then
table.insert(naughty.notifications._expired[1], self)
return
end
end
self:destroy(reason) self:destroy(reason)
end end
@ -454,6 +467,9 @@ local function create(args)
private[k] = v private[k] = v
end end
-- It's an automatic property
n.is_expired = false
rawset(n, "_private", private) rawset(n, "_private", private)
gtable.crush(n, notification, true) gtable.crush(n, notification, true)