Notifications: stack them instead of overdrawing them

reviewable/pr7235/r1
poire-z 3 years ago
parent cdafd9438c
commit 68a5fcdbc1

@ -10,9 +10,11 @@ local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange")
local InputContainer = require("ui/widget/container/inputcontainer")
local RectSpan = require("ui/widget/rectspan")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local Input = Device.input
local Screen = Device.screen
@ -23,6 +25,8 @@ local Notification = InputContainer:new{
padding = Size.padding.default,
timeout = 2, -- default to 2 seconds
toast = true, -- closed on any event, and let the event propagate to next top widget
_nums_shown = {}, -- array of stacked notifications
}
function Notification:init()
@ -48,36 +52,69 @@ function Notification:init()
end
end
-- we construct the actual content here because self.text is only available now
local text_widget = TextWidget:new{
text = self.text,
face = self.face,
}
local widget_size = text_widget:getSize()
self[1] = CenterContainer:new{
dimen = Geom:new{
w = Screen:getWidth(),
h = math.floor(Screen:getHeight() / 10),
},
FrameContainer:new{
background = Blitbuffer.COLOR_WHITE,
radius = 0,
margin = self.margin,
padding = self.padding,
CenterContainer:new{
dimen = Geom:new{
w = widget_size.w,
h = widget_size.h
},
text_widget,
}
self.frame = FrameContainer:new{
background = Blitbuffer.COLOR_WHITE,
radius = 0,
margin = self.margin,
padding = self.padding,
CenterContainer:new{
dimen = Geom:new{
w = widget_size.w,
h = widget_size.h
},
text_widget,
}
}
local notif_height = self.frame:getSize().h
self:_cleanShownStack()
table.insert(Notification._nums_shown, os.time())
self.num = #Notification._nums_shown
self[1] = VerticalGroup:new{
align = "center",
-- We use a span to properly position this notification:
RectSpan:new{
-- have this VerticalGroup full width, to ensure centering
width = Screen:getWidth(),
-- push this frame at its y=self.num position
height = notif_height * (self.num - 1),
},
self.frame,
}
end
function Notification:_cleanShownStack(num)
-- Clean stack of shown notifications
if num then
-- This notification is no longer displayed
Notification._nums_shown[num] = false
end
-- We remove from the stack tail all slots no longer displayed.
-- Even if slots at top are available, we'll keep adding new
-- notifications only at the tail/bottom (easier for the eyes
-- to follow what is happening).
-- As a sanity check, we also forget those shown for
-- more than 30s in case no close event was received.
local expire_ts = os.time() - 30
for i=#Notification._nums_shown, 1, -1 do
if Notification._nums_shown[i] and Notification._nums_shown[i] > expire_ts then
break -- still shown (or not yet expired)
end
table.remove(Notification._nums_shown, i)
end
end
function Notification:onCloseWidget()
self:_cleanShownStack(self.num)
self.num = nil -- avoid mess in case onCloseWidget is called multiple times
UIManager:setDirty(nil, function()
return "ui", self[1][1].dimen
return "ui", self.frame.dimen
end)
return true
end
@ -85,7 +122,7 @@ end
function Notification:onShow()
-- triggered by the UIManager after we got successfully shown (not yet painted)
UIManager:setDirty(self, function()
return "ui", self[1][1].dimen
return "ui", self.frame.dimen
end)
if self.timeout then
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)

Loading…
Cancel
Save