mirror of
https://github.com/koreader/koreader.git
synced 2025-12-18 12:02:09 +01:00
Notifications: stack them instead of overdrawing them
This commit is contained in:
@@ -10,9 +10,11 @@ local FrameContainer = require("ui/widget/container/framecontainer")
|
|||||||
local Geom = require("ui/geometry")
|
local Geom = require("ui/geometry")
|
||||||
local GestureRange = require("ui/gesturerange")
|
local GestureRange = require("ui/gesturerange")
|
||||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||||
|
local RectSpan = require("ui/widget/rectspan")
|
||||||
local Size = require("ui/size")
|
local Size = require("ui/size")
|
||||||
local TextWidget = require("ui/widget/textwidget")
|
local TextWidget = require("ui/widget/textwidget")
|
||||||
local UIManager = require("ui/uimanager")
|
local UIManager = require("ui/uimanager")
|
||||||
|
local VerticalGroup = require("ui/widget/verticalgroup")
|
||||||
local Input = Device.input
|
local Input = Device.input
|
||||||
local Screen = Device.screen
|
local Screen = Device.screen
|
||||||
|
|
||||||
@@ -23,6 +25,8 @@ local Notification = InputContainer:new{
|
|||||||
padding = Size.padding.default,
|
padding = Size.padding.default,
|
||||||
timeout = 2, -- default to 2 seconds
|
timeout = 2, -- default to 2 seconds
|
||||||
toast = true, -- closed on any event, and let the event propagate to next top widget
|
toast = true, -- closed on any event, and let the event propagate to next top widget
|
||||||
|
|
||||||
|
_nums_shown = {}, -- array of stacked notifications
|
||||||
}
|
}
|
||||||
|
|
||||||
function Notification:init()
|
function Notification:init()
|
||||||
@@ -48,18 +52,12 @@ function Notification:init()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- we construct the actual content here because self.text is only available now
|
|
||||||
local text_widget = TextWidget:new{
|
local text_widget = TextWidget:new{
|
||||||
text = self.text,
|
text = self.text,
|
||||||
face = self.face,
|
face = self.face,
|
||||||
}
|
}
|
||||||
local widget_size = text_widget:getSize()
|
local widget_size = text_widget:getSize()
|
||||||
self[1] = CenterContainer:new{
|
self.frame = FrameContainer:new{
|
||||||
dimen = Geom:new{
|
|
||||||
w = Screen:getWidth(),
|
|
||||||
h = math.floor(Screen:getHeight() / 10),
|
|
||||||
},
|
|
||||||
FrameContainer:new{
|
|
||||||
background = Blitbuffer.COLOR_WHITE,
|
background = Blitbuffer.COLOR_WHITE,
|
||||||
radius = 0,
|
radius = 0,
|
||||||
margin = self.margin,
|
margin = self.margin,
|
||||||
@@ -72,12 +70,51 @@ function Notification:init()
|
|||||||
text_widget,
|
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
|
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()
|
function Notification:onCloseWidget()
|
||||||
|
self:_cleanShownStack(self.num)
|
||||||
|
self.num = nil -- avoid mess in case onCloseWidget is called multiple times
|
||||||
UIManager:setDirty(nil, function()
|
UIManager:setDirty(nil, function()
|
||||||
return "ui", self[1][1].dimen
|
return "ui", self.frame.dimen
|
||||||
end)
|
end)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -85,7 +122,7 @@ end
|
|||||||
function Notification:onShow()
|
function Notification:onShow()
|
||||||
-- triggered by the UIManager after we got successfully shown (not yet painted)
|
-- triggered by the UIManager after we got successfully shown (not yet painted)
|
||||||
UIManager:setDirty(self, function()
|
UIManager:setDirty(self, function()
|
||||||
return "ui", self[1][1].dimen
|
return "ui", self.frame.dimen
|
||||||
end)
|
end)
|
||||||
if self.timeout then
|
if self.timeout then
|
||||||
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)
|
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)
|
||||||
|
|||||||
Reference in New Issue
Block a user