Merge pull request #3029 from Elv13/notification_markup

Fix most reported notification issues.
This commit is contained in:
Emmanuel Lepage Vallée 2020-03-09 14:16:35 -04:00 committed by GitHub
commit 1e1f5380a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 117 additions and 34 deletions

View File

@ -1478,8 +1478,10 @@ for _, prop in ipairs { "border_width", "border_color", "opacity" } do
return self["_"..prop] return self["_"..prop]
end end
client.object["set_"..prop] = function(self, value) client.object["set_"..prop] = function(self, value)
self._private["_user_"..prop] = true if value ~= nil then
self["_"..prop] = value self._private["_user_"..prop] = true
self["_"..prop] = value
end
end end
end end

View File

@ -323,6 +323,13 @@ function notif_methods.Notify(sender, object_path, interface, method, parameters
notification[k] = v notification[k] = v
end end
-- Update the icon if necessary.
if app_icon ~= notification._private.app_icon then
notification._private.app_icon = app_icon
notification._private.icon = nil
notification:emit_signal("property::icon")
end
-- Even if no property changed, restart the timeout. -- Even if no property changed, restart the timeout.
notification:reset_timeout() notification:reset_timeout()
else else

View File

@ -618,6 +618,17 @@ for _, prop in ipairs(properties) do
end end
-- Changing the image will change the icon, make sure property::icon is emitted.
for _, prop in ipairs {"image", "images" } do
local cur = notification["set_"..prop]
notification["set_"..prop] = function(self, value)
cur(self, value)
self._private.icon = nil
self:emit_signal("property::icon")
end
end
local hints_default = { local hints_default = {
urgency = "normal", urgency = "normal",
resident = false, resident = false,

View File

@ -0,0 +1,62 @@
local beautiful = require("beautiful")
local module = {}
-- Since some escaping needs to be undone, we have to escape the escaped <>.
local pre_escape = {["&lt;"] = "&zzlt;", ["&gt;"] = "&zzgt;"}
local escape_pattern = "[<>&]"
local escape_subs = { ['<'] = "&lt;", ['>'] = "&gt;", ['&'] = "&amp;" }
-- Also reverse escaping some allowed tags because people actually use them.
local escape_undo = {['&lt;span '] = "<span "}
for _, allowed in ipairs {'b', 'i', 'u', 'span'} do
escape_undo['&lt;' ..allowed..'&gt;'] = "<" ..allowed..">"
escape_undo['&lt;/'..allowed..'&gt;'] = "</"..allowed..">"
end
-- Best effort attempt to allow a subset of pango markup in the text while
-- removing invalid content. If invalid content is present, nothing is
-- displayed.
local function escape_text(text)
-- Take care of the already escaped content.
for pattern, subs in pairs(pre_escape) do
text = text:gsub(pattern, subs)
end
-- Try to set the text while only interpreting <br>.
text = text:gsub("<br[ /]*>", "\n")
-- Since the title cannot contain markup, it must be escaped first so that
-- it is not interpreted by Pango later.
text = text:gsub(escape_pattern, escape_subs)
-- Restore a subset of markup tags.
for pattern, subs in pairs(escape_undo) do
text = text:gsub(pattern, subs)
end
-- Restore pre-escaped content.
for subs, pattern in pairs(pre_escape) do
text = text:gsub(pattern, subs)
end
return text
end
function module.set_markup(wdg, text, fg, font)
local ret = escape_text(text or "")
fg = fg or beautiful.notification_fg
wdg:set_font(font or beautiful.notification_font)
if fg then
ret = "<span color='" .. fg .. "'>" .. ret .. "</span>"
end
wdg:set_markup_silently(ret)
return ret
end
return module

View File

@ -65,7 +65,10 @@ function icon:draw(_, cr, width, height)
local x, y = 0, 0 local x, y = 0, 0
if (strategy == "center" and aspect > 1) or strategy == "resize" then if (strategy == "center" and aspect < 1) or strategy == "resize" then
x = math.floor((width - w*aspect) / 2)
y = math.floor((height - h*aspect) / 2)
elseif strategy == "center" and aspect > 1 then
x = math.floor((width - w) / 2) x = math.floor((width - w) / 2)
y = math.floor((height - h) / 2) y = math.floor((height - h) / 2)
end end
@ -153,10 +156,11 @@ local function new(args)
gtable.crush(tb, icon, true) gtable.crush(tb, icon, true)
function tb._private.icon_changed_callback() function tb._private.icon_changed_callback()
local icn = gsurface.load_silently(tb._private.notification.icon) local icn = gsurface.load_silently(tb._private.notification.icon)
if icn then if icn then
tb:set_image() tb:set_image(icn)
end end
end end

View File

@ -17,22 +17,10 @@
local textbox = require("wibox.widget.textbox") local textbox = require("wibox.widget.textbox")
local gtable = require("gears.table") local gtable = require("gears.table")
local beautiful = require("beautiful") local beautiful = require("beautiful")
local markup = require("naughty.widget._markup").set_markup
local message = {} local message = {}
local function markup(notif, wdg)
local ret = notif.message or ""
local fg = notif.fg or beautiful.notification_fg
wdg:set_font(notif.font or beautiful.notification_font)
if fg then
ret = "<span color='" .. fg .. "'>" .. ret .. "</span>"
end
return ret
end
--- The attached notification. --- The attached notification.
-- @property notification -- @property notification
-- @tparam naughty.notification notification -- @tparam naughty.notification notification
@ -48,7 +36,7 @@ function message:set_notification(notif)
self._private.message_changed_callback) self._private.message_changed_callback)
end end
self:set_markup(markup(notif, self)) markup(self, notif.message, notif.fg, notif.font)
self._private.notification = notif self._private.notification = notif
@ -73,7 +61,12 @@ local function new(args)
gtable.crush(tb, message, true) gtable.crush(tb, message, true)
function tb._private.message_changed_callback() function tb._private.message_changed_callback()
tb:set_markup(markup(tb._private.notification, tb)) markup(
tb,
tb._private.notification.message,
tb._private.notification.fg,
tb._private.notification.font
)
end end
if args.notification then if args.notification then

View File

@ -17,22 +17,10 @@
local textbox = require("wibox.widget.textbox") local textbox = require("wibox.widget.textbox")
local gtable = require("gears.table") local gtable = require("gears.table")
local beautiful = require("beautiful") local beautiful = require("beautiful")
local markup = require("naughty.widget._markup").set_markup
local title = {} local title = {}
local function markup(notif, wdg)
local ret = "<b>"..(notif.title or "").."</b>"
local fg = notif.fg or beautiful.notification_fg
wdg:set_font(notif.font or beautiful.notification_font)
if fg then
ret = "<span color='" .. fg .. "'>" .. ret .. "</span>"
end
return ret
end
--- The attached notification. --- The attached notification.
-- @property notification -- @property notification
-- @tparam naughty.notification notification -- @tparam naughty.notification notification
@ -48,7 +36,7 @@ function title:set_notification(notif)
self._private.title_changed_callback) self._private.title_changed_callback)
end end
self:set_markup(markup(notif, self)) markup(self, notif.title, notif.fg, notif.font)
self._private.notification = notif self._private.notification = notif
self._private.title_changed_callback() self._private.title_changed_callback()
@ -74,7 +62,12 @@ local function new(args)
gtable.crush(tb, title, true) gtable.crush(tb, title, true)
function tb._private.title_changed_callback() function tb._private.title_changed_callback()
tb:set_markup(markup(tb._private.notification, tb)) markup(
tb,
tb._private.notification.title,
tb._private.notification.fg,
tb._private.notification.font
)
end end
if args.notification then if args.notification then

View File

@ -1095,6 +1095,11 @@ table.insert(steps, function()
assert(not n._private.widget_template_failed) assert(not n._private.widget_template_failed)
assert(not n.box) assert(not n.box)
-- Check adding messages later
n.title = "foo"
n.message = "bar"
n.icon = big_icon
n:destroy() n:destroy()
handler_called = false handler_called = false
@ -1111,6 +1116,12 @@ table.insert(steps, function()
assert(had_error) assert(had_error)
assert(not n.box) assert(not n.box)
-- Check changing existing content.
n.title = "bar"
n.message = "foo"
n.icon = big_icon
n.image = small_icon
handler_called = false handler_called = false
had_error = false had_error = false