mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Enable HW dithering in a few key places (#4541)
* Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4)
* FileManager and co. (where appropriate, i.e., when covers are shown)
* Book Status
* Reader, where appropriate:
* CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone).
* Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices).
* ScreenSaver
* ImageViewer
* Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it).
(The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu).
* Hunted down a few redundant repaints (unneeded setDirty("all") calls),
either by switching the widget to nil when only a refresh was needed, and not a repaint,
or by passing the appropritate widget to setDirty.
(Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard).
There were also a few instances of 'em right behind a widget close.
* Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog.
We unfortunately do need to do it when switching tabs, because of their variable heights.
* On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes!
* Fix another debug guard in Kobo sysfs_light
* Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not.
PS: (Almost 💯 commits! :D)
This commit is contained in:
2
base
2
base
Submodule base updated: bef742eb7e...afd0c6c5a6
@@ -159,7 +159,7 @@ function FileManager:init()
|
||||
function file_chooser:onPathChanged(path) -- luacheck: ignore
|
||||
FileManager.instance.path_text:setText(truncatePath(filemanagerutil.abbreviate(path)))
|
||||
UIManager:setDirty(FileManager.instance, function()
|
||||
return "partial", FileManager.instance.path_text.dimen
|
||||
return "partial", FileManager.instance.path_text.dimen, FileManager.instance.dithered
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -417,7 +417,7 @@ function ReaderFooter:addToMainMenu(menu_items)
|
||||
end
|
||||
if should_update then
|
||||
self:updateFooter()
|
||||
UIManager:setDirty("all", "partial")
|
||||
UIManager:setDirty(nil, "ui")
|
||||
end
|
||||
end,
|
||||
}
|
||||
@@ -436,7 +436,7 @@ function ReaderFooter:addToMainMenu(menu_items)
|
||||
callback = function()
|
||||
self.settings.disable_progress_bar = not self.settings.disable_progress_bar
|
||||
self:updateFooter()
|
||||
UIManager:setDirty("all", "partial")
|
||||
UIManager:setDirty(nil, "ui")
|
||||
end,
|
||||
},
|
||||
getMinibarOption("toc_markers", self.setTocMarkers),
|
||||
@@ -579,6 +579,8 @@ function ReaderFooter:_updateFooterText()
|
||||
end
|
||||
self.text_container.dimen.w = self.text_width
|
||||
self.horizontal_group:resetLayout()
|
||||
-- NOTE: This is essentially preventing us from truly using "fast" for panning,
|
||||
-- since it'll get coalesced in the "fast" panning update, upgrading it to "ui".
|
||||
UIManager:setDirty(self.view.dialog, function()
|
||||
return "ui", self.footer_content.dimen
|
||||
end)
|
||||
|
||||
@@ -152,7 +152,8 @@ function ReaderStatus:showStatus(before_show_callback)
|
||||
if before_show_callback then
|
||||
before_show_callback()
|
||||
end
|
||||
UIManager:show(status_page)
|
||||
status_page.dithered = true
|
||||
UIManager:show(status_page, "full")
|
||||
end
|
||||
|
||||
function ReaderStatus:onReadSettings(config)
|
||||
|
||||
@@ -76,7 +76,7 @@ function ReaderToc:onPageUpdate(pageno)
|
||||
if self:isChapterEnd(pageno, 0) then
|
||||
self.chapter_refresh = true
|
||||
elseif self:isChapterBegin(pageno, 0) and self.chapter_refresh then
|
||||
UIManager:setDirty("all", "full")
|
||||
UIManager:setDirty(nil, "full")
|
||||
self.chapter_refresh = false
|
||||
else
|
||||
self.chapter_refresh = false
|
||||
|
||||
@@ -213,6 +213,23 @@ function ReaderView:paintTo(bb, x, y)
|
||||
end
|
||||
-- stop activity indicator
|
||||
self.ui:handleEvent(Event:new("StopActivityIndicator"))
|
||||
|
||||
-- Most pages should not require dithering
|
||||
self.dialog.dithered = nil
|
||||
-- For KOpt, let the user choose.
|
||||
if self.ui.document.info.has_pages then
|
||||
if self.document.configurable.hw_dithering == 1 then
|
||||
self.dialog.dithered = true
|
||||
end
|
||||
else
|
||||
-- Whereas for CRe,
|
||||
-- If we're attempting to show a large enough amount of image data, request dithering (without triggering another repaint ;)).
|
||||
local img_count, img_coverage = self.ui.document:getDrawnImagesStatistics()
|
||||
-- With some nil guards because this may not be implemented in every engine ;).
|
||||
if img_count and img_count > 0 and img_coverage and img_coverage >= 0.075 then
|
||||
self.dialog.dithered = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
@@ -545,6 +562,9 @@ end
|
||||
This method is supposed to be only used by ReaderPaging
|
||||
--]]
|
||||
function ReaderView:recalculate()
|
||||
-- Start by resetting the dithering flag early, so it doesn't carry over from the previous page.
|
||||
self.dialog.dithered = nil
|
||||
|
||||
if self.ui.document.info.has_pages and self.state.page then
|
||||
self.page_area = self:getPageArea(
|
||||
self.state.page,
|
||||
@@ -579,6 +599,7 @@ function ReaderView:recalculate()
|
||||
self.state.offset.x = (self.dimen.w - self.visible_area.w) / 2
|
||||
end
|
||||
-- flag a repaint so self:paintTo will be called
|
||||
-- NOTE: This is also unfortunately called during panning, essentially making sure we'll never be using "fast" for pans ;).
|
||||
UIManager:setDirty(self.dialog, "partial")
|
||||
end
|
||||
|
||||
@@ -588,7 +609,7 @@ function ReaderView:PanningUpdate(dx, dy)
|
||||
self.visible_area:offsetWithin(self.page_area, dx, dy)
|
||||
if self.visible_area ~= old then
|
||||
-- flag a repaint
|
||||
UIManager:setDirty(self.dialog, "partial")
|
||||
UIManager:setDirty(self.dialog, "fast")
|
||||
logger.dbg("on pan: page_area", self.page_area)
|
||||
logger.dbg("on pan: visible_area", self.visible_area)
|
||||
self.ui:handleEvent(
|
||||
@@ -605,7 +626,7 @@ function ReaderView:PanningStart(x, y)
|
||||
self.visible_area = self.panning_visible_area:copy()
|
||||
self.visible_area:offsetWithin(self.page_area, x, y)
|
||||
self.ui:handleEvent(Event:new("ViewRecalculate", self.visible_area, self.page_area))
|
||||
UIManager:setDirty(self.dialog, "partial")
|
||||
UIManager:setDirty(self.dialog, "fast")
|
||||
end
|
||||
|
||||
function ReaderView:PanningStop()
|
||||
|
||||
@@ -26,6 +26,7 @@ local Device = {
|
||||
needsTouchScreenProbe = no,
|
||||
hasClipboard = yes, -- generic internal clipboard on all devices
|
||||
hasEinkScreen = yes,
|
||||
canHWDither = no,
|
||||
hasColorScreen = no,
|
||||
hasBGRFrameBuffer = no,
|
||||
canToggleGSensor = no,
|
||||
@@ -190,7 +191,9 @@ function Device:onPowerEvent(ev)
|
||||
self.orig_rotation_mode = nil
|
||||
end
|
||||
require("ui/screensaver"):show()
|
||||
self.screen:refreshFull()
|
||||
if self:needsScreenRefreshAfterResume() then
|
||||
self.screen:refreshFull()
|
||||
end
|
||||
self.screen_saver_mode = true
|
||||
UIManager:scheduleIn(0.1, function()
|
||||
local network_manager = require("ui/network/manager")
|
||||
|
||||
@@ -32,6 +32,8 @@ local Kobo = Generic:new{
|
||||
touch_mirrored_x = true,
|
||||
-- enforce portrait mode on Kobos
|
||||
isAlwaysPortrait = yes,
|
||||
-- we don't need an extra refreshFull on resume, thank you very much.
|
||||
needsScreenRefreshAfterResume = no,
|
||||
-- the internal storage mount point users can write to
|
||||
internal_storage_mount_point = "/mnt/onboard/",
|
||||
-- currently only the Aura One and Forma have coloured frontlights
|
||||
|
||||
@@ -103,9 +103,9 @@ end
|
||||
|
||||
dbg:guard(SysfsLight, 'setNaturalBrightness',
|
||||
function(self, brightness, warmth)
|
||||
assert(brightness >= 0 and brightness <= 100,
|
||||
assert(brightness == nil or (brightness >= 0 and brightness <= 100),
|
||||
"Wrong brightness value given!")
|
||||
assert(warmth >= 0 and warmth <= 100,
|
||||
assert(warmth == nil or (warmth >= 0 and warmth <= 100),
|
||||
"Wrong warmth value given!")
|
||||
end)
|
||||
|
||||
|
||||
@@ -251,6 +251,16 @@ This can also be used to remove some gray background or to convert a grayscale o
|
||||
end,
|
||||
name_text_hold_callback = optionsutil.showValues,
|
||||
},
|
||||
{
|
||||
name = "hw_dithering",
|
||||
name_text = S.HW_DITHERING,
|
||||
toggle = {S.ON, S.OFF},
|
||||
values = {1, 0},
|
||||
default_value = 0,
|
||||
advanced = true,
|
||||
show = Device:hasEinkScreen() and Device:canHWDither(),
|
||||
name_text_hold_callback = optionsutil.showValues,
|
||||
},
|
||||
{
|
||||
name = "forced_ocr",
|
||||
name_text = S.FORCED_OCR,
|
||||
|
||||
@@ -32,6 +32,7 @@ S.EMBEDDED_FONTS = _("Embedded Fonts")
|
||||
S.WRITING_DIR = _("Writing Direction")
|
||||
S.PROGRESS_BAR = _("Progress Bar")
|
||||
S.FORCED_OCR = _("Forced OCR")
|
||||
S.HW_DITHERING = _("Dithering")
|
||||
S.INVERSE_READING_ORDER = _("Inverse Order")
|
||||
|
||||
S.ON = _("on")
|
||||
|
||||
@@ -387,7 +387,8 @@ function Screensaver:show(event, fallback_message)
|
||||
covers_fullscreen = covers_fullscreen,
|
||||
}
|
||||
self.left_msg.modal = true
|
||||
-- refresh whole screen for other types
|
||||
-- Refresh whole screen for other types
|
||||
self.left_msg.dithered = true
|
||||
UIManager:show(self.left_msg, "full")
|
||||
end
|
||||
end
|
||||
@@ -400,8 +401,7 @@ function Screensaver:close()
|
||||
UIManager:scheduleIn(screensaver_delay_number, function()
|
||||
logger.dbg("close screensaver")
|
||||
if self.left_msg then
|
||||
UIManager:close(self.left_msg)
|
||||
UIManager:setDirty("all", "full")
|
||||
UIManager:close(self.left_msg, "full")
|
||||
self.left_msg = nil
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -56,7 +56,9 @@ function UIManager:init()
|
||||
self._entered_poweroff_stage = true;
|
||||
Screen:setRotationMode(0)
|
||||
require("ui/screensaver"):show("poweroff", _("Powered off"))
|
||||
Screen:refreshFull()
|
||||
if Device:needsScreenRefreshAfterResume() then
|
||||
Screen:refreshFull()
|
||||
end
|
||||
UIManager:nextTick(function()
|
||||
Device:saveSettings()
|
||||
self:broadcastEvent(Event:new("Close"))
|
||||
@@ -67,7 +69,9 @@ function UIManager:init()
|
||||
self._entered_poweroff_stage = true;
|
||||
Screen:setRotationMode(0)
|
||||
require("ui/screensaver"):show("reboot", _("Rebooting..."))
|
||||
Screen:refreshFull()
|
||||
if Device:needsScreenRefreshAfterResume() then
|
||||
Screen:refreshFull()
|
||||
end
|
||||
UIManager:nextTick(function()
|
||||
Device:saveSettings()
|
||||
self:broadcastEvent(Event:new("Close"))
|
||||
@@ -261,13 +265,14 @@ For refreshtype & refreshregion see description of setDirty().
|
||||
---- @param refreshregion a Geom object
|
||||
---- @int x
|
||||
---- @int y
|
||||
---- @param refreshdither an optional bool
|
||||
---- @see setDirty
|
||||
function UIManager:show(widget, refreshtype, refreshregion, x, y)
|
||||
function UIManager:show(widget, refreshtype, refreshregion, x, y, refreshdither)
|
||||
if not widget then
|
||||
logger.dbg("widget not exist to be shown")
|
||||
return
|
||||
end
|
||||
logger.dbg("show widget", widget.id or widget.name or "unknown")
|
||||
logger.dbg("show widget:", widget.id or widget.name or tostring(widget))
|
||||
|
||||
self._running = true
|
||||
local window = {x = x or 0, y = y or 0, widget = widget}
|
||||
@@ -281,7 +286,7 @@ function UIManager:show(widget, refreshtype, refreshregion, x, y)
|
||||
end
|
||||
end
|
||||
-- and schedule it to be painted
|
||||
self:setDirty(widget, refreshtype, refreshregion)
|
||||
self:setDirty(widget, refreshtype, refreshregion, refreshdither)
|
||||
-- tell the widget that it is shown now
|
||||
widget:handleEvent(Event:new("Show"))
|
||||
-- check if this widget disables double tap gesture
|
||||
@@ -300,13 +305,14 @@ For refreshtype & refreshregion see description of setDirty().
|
||||
---- @param widget a widget object
|
||||
---- @param refreshtype "full", "flashpartial", "flashui", "partial", "ui", "fast"
|
||||
---- @param refreshregion a Geom object
|
||||
---- @param refreshdither an optional bool
|
||||
---- @see setDirty
|
||||
function UIManager:close(widget, refreshtype, refreshregion)
|
||||
function UIManager:close(widget, refreshtype, refreshregion, refreshdither)
|
||||
if not widget then
|
||||
logger.dbg("widget to be closed does not exist")
|
||||
return
|
||||
end
|
||||
logger.dbg("close widget", widget.id or widget.name)
|
||||
logger.dbg("close widget:", widget.name or widget.id or tostring(widget))
|
||||
local dirty = false
|
||||
-- Ensure all the widgets can get onFlushSettings event.
|
||||
widget:handleEvent(Event:new("FlushSettings"))
|
||||
@@ -317,10 +323,19 @@ function UIManager:close(widget, refreshtype, refreshregion)
|
||||
-- then remove all references to that widget on stack and refresh
|
||||
for i = #self._window_stack, 1, -1 do
|
||||
if self._window_stack[i].widget == widget then
|
||||
self._dirty[self._window_stack[i].widget] = nil
|
||||
table.remove(self._window_stack, i)
|
||||
dirty = true
|
||||
elseif self._window_stack[i].widget.disable_double_tap == false then
|
||||
Input.disable_double_tap = false
|
||||
else
|
||||
-- If anything else on the stack was dithered, honor the hint
|
||||
if self._window_stack[i].widget.dithered then
|
||||
refreshdither = true
|
||||
logger.dbg("Lower widget", self._window_stack[i].widget.name or self._window_stack[i].widget.id or tostring(self._window_stack[i].widget), "was dithered, honoring the dithering hint")
|
||||
end
|
||||
|
||||
if self._window_stack[i].widget.disable_double_tap == false then
|
||||
Input.disable_double_tap = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if dirty and not widget.invisible then
|
||||
@@ -328,7 +343,7 @@ function UIManager:close(widget, refreshtype, refreshregion)
|
||||
for i = 1, #self._window_stack do
|
||||
self:setDirty(self._window_stack[i].widget)
|
||||
end
|
||||
self:_refresh(refreshtype, refreshregion)
|
||||
self:_refresh(refreshtype, refreshregion, refreshdither)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -464,6 +479,8 @@ NOTE: You'll notice a trend on UI elements that are usually shown *over* some ki
|
||||
That said, depending on your use case, using "ui" onClose can be a perfectly valid decision, and will ensure
|
||||
never seeing a flash because of that widget.
|
||||
|
||||
The final parameter (refreshdither) is an optional hint for devices with hardware dithering support that this repaint
|
||||
could benefit from dithering (i.e., it contains an image).
|
||||
|
||||
@usage
|
||||
|
||||
@@ -475,15 +492,32 @@ UIManager:setDirty(self.widget, function() return "ui", self.someelement.dimen e
|
||||
---- @param widget a widget object
|
||||
---- @param refreshtype "full", "flashpartial", "flashui", "partial", "ui", "fast"
|
||||
---- @param refreshregion a Geom object
|
||||
function UIManager:setDirty(widget, refreshtype, refreshregion)
|
||||
---- @param refreshdither an optional bool
|
||||
function UIManager:setDirty(widget, refreshtype, refreshregion, refreshdither)
|
||||
if widget then
|
||||
if widget == "all" then
|
||||
-- special case: set all top-level widgets as being "dirty".
|
||||
for i = 1, #self._window_stack do
|
||||
self._dirty[self._window_stack[i].widget] = true
|
||||
-- If any of 'em were dithered, honor their dithering hint
|
||||
if self._window_stack[i].widget.dithered then
|
||||
-- NOTE: That works when refreshtype is NOT a function,
|
||||
-- which is why _repaint does another pass of this check ;).
|
||||
refreshdither = true
|
||||
end
|
||||
end
|
||||
elseif not widget.invisible then
|
||||
self._dirty[widget] = true
|
||||
-- We only ever check the dirty flag on top-level widgets, so only set it there!
|
||||
-- NOTE: Enable verbose debug to catch misbehaving widgets via our post-guard.
|
||||
for i = 1, #self._window_stack do
|
||||
if self._window_stack[i].widget == widget then
|
||||
self._dirty[widget] = true
|
||||
end
|
||||
end
|
||||
-- Again, if it's flagged as dithered, honor that
|
||||
if widget.dithered then
|
||||
refreshdither = true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- handle refresh information
|
||||
@@ -493,23 +527,23 @@ function UIManager:setDirty(widget, refreshtype, refreshregion)
|
||||
if dbg.is_on then
|
||||
-- FIXME: We can't consume the return values of refreshtype by running it, because for a reason that is beyond me (scoping? gc?), that renders it useless later, meaning we then enqueue refreshes with bogus arguments...
|
||||
-- Thankfully, we can track them in _refresh()'s logging very soon after that...
|
||||
logger.dbg("setDirty via a func from widget", widget and (widget.name or widget.id or tostring(widget)))
|
||||
logger.dbg("setDirty via a func from widget", widget and (widget.name or widget.id or tostring(widget)) or "nil")
|
||||
end
|
||||
else
|
||||
-- otherwise, enqueue refresh
|
||||
self:_refresh(refreshtype, refreshregion)
|
||||
self:_refresh(refreshtype, refreshregion, refreshdither)
|
||||
if dbg.is_on then
|
||||
if refreshregion then
|
||||
logger.dbg("setDirty", refreshtype and refreshtype or "nil", "from widget", widget and (widget.name or widget.id or tostring(widget)) or "nil", "w/ region", refreshregion.x, refreshregion.y, refreshregion.w, refreshregion.h)
|
||||
logger.dbg("setDirty", refreshtype and refreshtype or "nil", "from widget", widget and (widget.name or widget.id or tostring(widget)) or "nil", "w/ region", refreshregion.x, refreshregion.y, refreshregion.w, refreshregion.h, refreshdither and "AND w/ HW dithering" or "")
|
||||
else
|
||||
logger.dbg("setDirty", refreshtype and refreshtype or "nil", "from widget", widget and (widget.name or widget.id or tostring(widget)) or "nil", "w/ NO region")
|
||||
logger.dbg("setDirty", refreshtype and refreshtype or "nil", "from widget", widget and (widget.name or widget.id or tostring(widget)) or "nil", "w/ NO region", refreshdither and "AND w/ HW dithering" or "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
dbg:guard(UIManager, 'setDirty',
|
||||
nil,
|
||||
function(self, widget, refreshtype, refreshregion)
|
||||
function(self, widget, refreshtype, refreshregion, refreshdither)
|
||||
if not widget or widget == "all" then return end
|
||||
-- when debugging, we check if we get handed a valid widget,
|
||||
-- which would be a dialog that was previously passed via show()
|
||||
@@ -680,6 +714,8 @@ end
|
||||
|
||||
-- precedence of refresh modes:
|
||||
local refresh_modes = { fast = 1, ui = 2, partial = 3, flashui = 4, flashpartial = 5, full = 6 }
|
||||
-- NOTE: We might want to introduce a "force_fast" that points to fast, but has the highest priority,
|
||||
-- for the few cases where we might *really* want to enforce fast (for stuff like panning or skimming?).
|
||||
-- refresh methods in framebuffer implementation
|
||||
local refresh_methods = {
|
||||
fast = "refreshFast",
|
||||
@@ -704,6 +740,20 @@ local function update_mode(mode1, mode2)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Compares dither hints.
|
||||
|
||||
Dither always wins.
|
||||
--]]
|
||||
local function update_dither(dither1, dither2)
|
||||
if dither1 and not dither2 then
|
||||
logger.dbg("update_dither: Update dither hint", dither2, "to", dither1)
|
||||
return dither1
|
||||
else
|
||||
return dither2
|
||||
end
|
||||
end
|
||||
|
||||
--[[--
|
||||
Enqueues a refresh.
|
||||
|
||||
@@ -716,9 +766,20 @@ UIManager that a certain part of the screen is to be refreshed.
|
||||
Rect() that specifies the region to be updated
|
||||
optional, update will affect whole screen if not specified.
|
||||
Note that this should be the exception.
|
||||
@param dither
|
||||
Bool, a hint to request hardware dithering (if supported)
|
||||
optional, no dithering requested if not specified or not supported.
|
||||
--]]
|
||||
function UIManager:_refresh(mode, region)
|
||||
if not mode then return end
|
||||
function UIManager:_refresh(mode, region, dither)
|
||||
if not mode then
|
||||
-- If we're trying to float a dither hint up from a lower widget after a close, mode might be nil...
|
||||
-- So use the lowest priority refresh mode (short of fast, because that'd do half-toning).
|
||||
if dither then
|
||||
mode = "ui"
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
if not region and mode == "full" then
|
||||
self.refresh_count = 0 -- reset counter on explicit full refresh
|
||||
end
|
||||
@@ -763,6 +824,9 @@ function UIManager:_refresh(mode, region)
|
||||
-- if no region is specified, define default region
|
||||
region = region or Geom:new{w=Screen:getWidth(), h=Screen:getHeight()}
|
||||
|
||||
-- if no dithering hint was specified, don't request dithering
|
||||
dither = dither or false
|
||||
|
||||
-- NOTE: While, ideally, we shouldn't merge refreshes w/ different waveform modes,
|
||||
-- this allows us to optimize away a number of quirks of our rendering stack
|
||||
-- (f.g., multiple setDirty calls queued when showing/closing a widget because of update mechanisms),
|
||||
@@ -775,16 +839,18 @@ function UIManager:_refresh(mode, region)
|
||||
local combined = region:combine(self._refresh_stack[i].region)
|
||||
-- update the mode, if needed
|
||||
mode = update_mode(mode, self._refresh_stack[i].mode)
|
||||
-- dithering hints are viral, one is enough to infect the whole queue
|
||||
dither = update_dither(dither, self._refresh_stack[i].dither)
|
||||
-- remove colliding refresh
|
||||
table.remove(self._refresh_stack, i)
|
||||
-- and try again with combined data
|
||||
return self:_refresh(mode, combined)
|
||||
return self:_refresh(mode, combined, dither)
|
||||
end
|
||||
end
|
||||
|
||||
-- if we've stopped hitting collisions, enqueue the refresh
|
||||
logger.dbg("_refresh: Enqueued", mode, "update for region", region.x, region.y, region.w, region.h)
|
||||
table.insert(self._refresh_stack, {mode = mode, region = region})
|
||||
logger.dbg("_refresh: Enqueued", mode, "update for region", region.x, region.y, region.w, region.h, dither and "w/ HW dithering" or "")
|
||||
table.insert(self._refresh_stack, {mode = mode, region = region, dither = dither})
|
||||
end
|
||||
|
||||
--- Repaints dirty widgets.
|
||||
@@ -792,6 +858,8 @@ function UIManager:_repaint()
|
||||
-- flag in which we will record if we did any repaints at all
|
||||
-- will trigger a refresh if set.
|
||||
local dirty = false
|
||||
-- remember if any of our repaints were dithered
|
||||
local dithered = false
|
||||
|
||||
-- We don't need to call paintTo() on widgets that are under
|
||||
-- a widget that covers the full screen
|
||||
@@ -820,13 +888,21 @@ function UIManager:_repaint()
|
||||
|
||||
-- trigger repaint
|
||||
dirty = true
|
||||
|
||||
-- if any of 'em were dithered, we'll want to dither the final refresh
|
||||
if widget.widget.dithered then
|
||||
logger.dbg("_repaint: it was dithered, infecting the refresh queue")
|
||||
dithered = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- execute pending refresh functions
|
||||
for _, refreshfunc in ipairs(self._refresh_func_stack) do
|
||||
local refreshtype, region = refreshfunc()
|
||||
if refreshtype then self:_refresh(refreshtype, region) end
|
||||
local refreshtype, region, dither = refreshfunc()
|
||||
-- honor dithering hints from *anywhere* in the dirty stack
|
||||
dither = update_dither(dither, dithered)
|
||||
if refreshtype then self:_refresh(refreshtype, region, dither) end
|
||||
end
|
||||
self._refresh_func_stack = {}
|
||||
|
||||
@@ -845,7 +921,8 @@ function UIManager:_repaint()
|
||||
-- but checkBounds & getPhysicalRect will sanitize that in mxc_update @ ffi/framebuffer_mxcfb ;).
|
||||
Screen[refresh_methods[refresh.mode]](Screen,
|
||||
refresh.region.x - 1, refresh.region.y - 1,
|
||||
refresh.region.w + 2, refresh.region.h + 2)
|
||||
refresh.region.w + 2, refresh.region.h + 2,
|
||||
refresh.dither)
|
||||
end
|
||||
self._refresh_stack = {}
|
||||
self.refresh_counted = false
|
||||
@@ -855,6 +932,16 @@ function UIManager:forceRePaint()
|
||||
self:_repaint()
|
||||
end
|
||||
|
||||
-- Used to repaint a specific sub-widget that isn't on the _window_stack itself
|
||||
-- Useful to avoid repainting a complex widget when we just want to invert an icon, for instance.
|
||||
-- No safety checks on x & y *by design*. I want this to blow up if used wrong.
|
||||
function UIManager:widgetRepaint(widget, x, y)
|
||||
if not widget then return end
|
||||
|
||||
logger.dbg("Explicit widgetRepaint:", widget.name or widget.id or tostring(widget), "@ (", x, ",", y, ")")
|
||||
widget:paintTo(Screen.bb, x, y)
|
||||
end
|
||||
|
||||
function UIManager:setInputTimeout(timeout)
|
||||
self.INPUT_TIMEOUT = timeout or 200*1000
|
||||
end
|
||||
|
||||
@@ -111,6 +111,8 @@ function BookStatusWidget:init()
|
||||
padding = 0,
|
||||
self:getStatusContent(screen_size.w),
|
||||
}
|
||||
|
||||
self.dithered = true
|
||||
end
|
||||
|
||||
function BookStatusWidget:getStats()
|
||||
@@ -254,7 +256,9 @@ function BookStatusWidget:setStar(num)
|
||||
|
||||
table.insert(self.stars_container, stars_group)
|
||||
|
||||
UIManager:setDirty(nil, "ui")
|
||||
-- Individual stars are Button, w/ flash_ui, they'll have their own flash.
|
||||
-- And we need to redraw the full widget, because we don't know the coordinates of stars_container :/.
|
||||
UIManager:setDirty(self, "ui", nil, true)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -540,7 +544,7 @@ function BookStatusWidget:onConfigChoose(values, name, event, args, events, posi
|
||||
if values then
|
||||
self:onChangeBookStatus(args, position)
|
||||
end
|
||||
UIManager:setDirty("all", "ui")
|
||||
UIManager:setDirty(nil, "ui", nil, true)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -558,7 +562,7 @@ function BookStatusWidget:onSwipe(arg, ges_ev)
|
||||
do end -- luacheck: ignore 541
|
||||
else -- diagonal swipe
|
||||
-- trigger full refresh
|
||||
UIManager:setDirty(nil, "full")
|
||||
UIManager:setDirty(nil, "full", nil, true)
|
||||
-- a long diagonal swipe may also be used for taking a screenshot,
|
||||
-- so let it propagate
|
||||
return false
|
||||
@@ -568,8 +572,7 @@ end
|
||||
function BookStatusWidget:onClose()
|
||||
self:saveSummary()
|
||||
-- NOTE: Flash on close to avoid ghosting, since we show an image.
|
||||
UIManager:setDirty("all", "flashpartial")
|
||||
UIManager:close(self)
|
||||
UIManager:close(self, "flashpartial")
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -193,16 +193,18 @@ function Button:onTapSelectButton()
|
||||
if G_reader_settings:isFalse("flash_ui") then
|
||||
self.callback()
|
||||
else
|
||||
-- NOTE: Flag all widgets as dirty to force a repaint, so we actually get to see the highlight.
|
||||
-- (For some reason (wrong widget passed to setDirty?), we never saw the effects on the FM chevrons without this hack).
|
||||
self[1].invert = true
|
||||
UIManager:setDirty("all", function()
|
||||
-- For most of our buttons, we can't avoid that initial repaint...
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self[1].dimen
|
||||
end)
|
||||
-- And we also often have to delay the callback to both see the flash and/or avoid tearing artefacts w/ fast refreshes...
|
||||
UIManager:tickAfterNext(function()
|
||||
self.callback()
|
||||
self[1].invert = false
|
||||
UIManager:setDirty("all", function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self[1].dimen
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -96,14 +96,16 @@ function CheckButton:onTapCheckButton()
|
||||
if G_reader_settings:isFalse("flash_ui") then
|
||||
self.callback()
|
||||
else
|
||||
self.invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
self[1].invert = true
|
||||
UIManager:widgetRepaint(self[1], self.dimen.x, self.dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self.dimen
|
||||
end)
|
||||
UIManager:tickAfterNext(function()
|
||||
self.callback()
|
||||
self.invert = false
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
self[1].invert = false
|
||||
UIManager:widgetRepaint(self[1], self.dimen.x, self.dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self.dimen
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -711,6 +711,7 @@ Widget that displays config menubar and config panel
|
||||
local ConfigDialog = FocusManager:new{
|
||||
--is_borderless = false,
|
||||
panel_index = 1,
|
||||
is_fresh = true,
|
||||
}
|
||||
|
||||
function ConfigDialog:init()
|
||||
@@ -783,7 +784,7 @@ end
|
||||
|
||||
function ConfigDialog:onCloseWidget()
|
||||
-- NOTE: As much as we would like to flash here, don't, because of adverse interactions with touchmenu that might lead to a double flash...
|
||||
UIManager:setDirty("all", function()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "partial", self.dialog_frame.dimen
|
||||
end)
|
||||
end
|
||||
@@ -794,10 +795,11 @@ function ConfigDialog:onShowConfigPanel(index)
|
||||
self:update()
|
||||
-- NOTE: Keep that one as UI to avoid delay when both this and the topmenu are shown.
|
||||
-- Plus, this is also called for each tab anyway, so that wouldn't have been great.
|
||||
UIManager:setDirty("all", function()
|
||||
UIManager:setDirty(self.is_fresh and self or "all", function()
|
||||
local refresh_dimen =
|
||||
old_dimen and old_dimen:combine(self.dialog_frame.dimen)
|
||||
or self.dialog_frame.dimen
|
||||
self.is_fresh = false
|
||||
return "ui", refresh_dimen
|
||||
end)
|
||||
return true
|
||||
|
||||
@@ -551,7 +551,7 @@ function DictQuickLookup:update()
|
||||
dimen = self.region,
|
||||
self.movable,
|
||||
}
|
||||
UIManager:setDirty("all", function()
|
||||
UIManager:setDirty(self, function()
|
||||
local update_region = self.dict_frame and self.dict_frame.dimen and self.dict_frame.dimen:combine(orig_dimen) or orig_dimen
|
||||
logger.dbg("update dict region", update_region)
|
||||
return "partial", update_region
|
||||
|
||||
@@ -276,7 +276,7 @@ function FrontLightWidget:setProgress(num, step, num_warmth)
|
||||
-- Reset container height to what it actually contains
|
||||
self.fl_container.dimen.h = vertical_group:getSize().h
|
||||
|
||||
UIManager:setDirty("all", "ui")
|
||||
UIManager:setDirty(self, "ui")
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -596,7 +596,7 @@ function FrontLightWidget:naturalLightConfigOpen()
|
||||
self.nl_configure_open = true
|
||||
-- Move to the bottom to make place for the new widget
|
||||
self[1].align="bottom"
|
||||
UIManager:setDirty("all", "ui")
|
||||
UIManager:setDirty(self, "ui")
|
||||
end
|
||||
|
||||
function FrontLightWidget:naturalLightConfigClose()
|
||||
@@ -606,7 +606,7 @@ function FrontLightWidget:naturalLightConfigClose()
|
||||
self.configure_button:enable()
|
||||
self.nl_configure_open = false
|
||||
self[1].align="center"
|
||||
UIManager:setDirty("all", "ui")
|
||||
UIManager:setDirty(self, "ui")
|
||||
end
|
||||
|
||||
return FrontLightWidget
|
||||
|
||||
@@ -96,14 +96,17 @@ function IconButton:onTapIconButton()
|
||||
self.callback()
|
||||
else
|
||||
self.image.invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
-- For ConfigDialog icons, we can't avoid that initial repaint...
|
||||
UIManager:widgetRepaint(self.image, self.dimen.x + self.padding_left, self.dimen.y + self.padding_top)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self.dimen
|
||||
end)
|
||||
-- Make sure button reacts before doing callback
|
||||
-- And, we usually need to delay the callback for the same reasons as Button...
|
||||
UIManager:tickAfterNext(function()
|
||||
self.callback()
|
||||
self.image.invert = false
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self.image, self.dimen.x + self.padding_left, self.dimen.y + self.padding_top)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self.dimen
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -392,16 +392,22 @@ function ImageViewer:update()
|
||||
self.main_frame,
|
||||
}
|
||||
}
|
||||
UIManager:setDirty("all", function()
|
||||
-- NOTE: We use UI instead of partial, because we do NOT want to end up using a REAGL waveform...
|
||||
-- NOTE: Disabling dithering here makes for a perfect test-case of how well it works:
|
||||
-- page turns will show color quantization artefacts (i.e., banding) like crazy,
|
||||
-- while a long touch will trigger a dithered, flashing full-refresh that'll make everything shiny :).
|
||||
self.dithered = true
|
||||
UIManager:setDirty(self, function()
|
||||
local update_region = self.main_frame.dimen:combine(orig_dimen)
|
||||
logger.dbg("update image region", update_region)
|
||||
return "partial", update_region
|
||||
return "ui", update_region, true
|
||||
end)
|
||||
end
|
||||
|
||||
function ImageViewer:onShow()
|
||||
self.dithered = true
|
||||
UIManager:setDirty(self, function()
|
||||
return "full", self.main_frame.dimen
|
||||
return "full", self.main_frame.dimen, true
|
||||
end)
|
||||
return true
|
||||
end
|
||||
@@ -508,7 +514,8 @@ function ImageViewer:onHoldRelease(_, ges)
|
||||
self._pan_relative_y = ges.pos.y - self._pan_relative_y
|
||||
if math.abs(self._pan_relative_x) < self.pan_threshold and math.abs(self._pan_relative_y) < self.pan_threshold then
|
||||
-- Hold with no move (or less than pan_threshold): use this to trigger full refresh
|
||||
UIManager:setDirty(nil, "full")
|
||||
self.dithered = true
|
||||
UIManager:setDirty(nil, "full", nil, true)
|
||||
else
|
||||
self:panBy(-self._pan_relative_x, -self._pan_relative_y)
|
||||
end
|
||||
@@ -601,6 +608,7 @@ function ImageViewer:onCloseWidget()
|
||||
if self._images_list and self._images_list_disposable and self._images_list.free then
|
||||
self._images_list.free()
|
||||
end
|
||||
-- NOTE: Assume there's no image beneath us, so, no dithering request
|
||||
UIManager:setDirty(nil, function()
|
||||
return "flashui", self.main_frame.dimen
|
||||
end)
|
||||
|
||||
@@ -334,8 +334,9 @@ function ImageWidget:panBy(x, y)
|
||||
if new_offset_x ~= self._offset_x or new_offset_y ~= self._offset_y then
|
||||
self._offset_x = new_offset_x
|
||||
self._offset_y = new_offset_y
|
||||
self.dithered = true
|
||||
UIManager:setDirty("all", function()
|
||||
return "partial", self.dimen
|
||||
return "ui", self.dimen, true
|
||||
end)
|
||||
end
|
||||
-- return new center ratio, so caller can use them later to create a new
|
||||
|
||||
@@ -234,13 +234,15 @@ function KeyValueItem:onTap()
|
||||
self.callback()
|
||||
else
|
||||
self[1].invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self[1].dimen
|
||||
end)
|
||||
UIManager:tickAfterNext(function()
|
||||
self.callback()
|
||||
self[1].invert = false
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self[1].dimen
|
||||
end)
|
||||
end)
|
||||
@@ -553,7 +555,7 @@ function KeyValuePage:onReturn()
|
||||
if self.callback_return then
|
||||
self:callback_return()
|
||||
UIManager:close(self)
|
||||
UIManager:setDirty("all", "ui")
|
||||
UIManager:setDirty(nil, "ui")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -403,19 +403,21 @@ function MenuItem:onTapSelect(arg, ges)
|
||||
coroutine.resume(co)
|
||||
else
|
||||
self[1].invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self[1].dimen
|
||||
end)
|
||||
UIManager:tickAfterNext(function()
|
||||
self[1].invert = false
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self[1].dimen
|
||||
end)
|
||||
logger.dbg("creating coroutine for menu select")
|
||||
local co = coroutine.create(function()
|
||||
self.menu:onMenuSelect(self.table, pos)
|
||||
end)
|
||||
coroutine.resume(co)
|
||||
self[1].invert = false
|
||||
--UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self[1].dimen
|
||||
end)
|
||||
end)
|
||||
end
|
||||
return true
|
||||
@@ -427,15 +429,17 @@ function MenuItem:onHoldSelect(arg, ges)
|
||||
self.menu:onMenuHold(self.table, pos)
|
||||
else
|
||||
self[1].invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self[1].dimen
|
||||
end)
|
||||
UIManager:tickAfterNext(function()
|
||||
self.menu:onMenuHold(self.table, pos)
|
||||
self[1].invert = false
|
||||
--UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self[1].dimen
|
||||
end)
|
||||
self.menu:onMenuHold(self.table, pos)
|
||||
end)
|
||||
end
|
||||
return true
|
||||
@@ -587,25 +591,25 @@ function Menu:init()
|
||||
icon = "resources/icons/appbar.chevron.left.png",
|
||||
callback = function() self:onPrevPage() end,
|
||||
bordersize = 0,
|
||||
show_parent = self,
|
||||
show_parent = self.show_parent,
|
||||
}
|
||||
self.page_info_right_chev = Button:new{
|
||||
icon = "resources/icons/appbar.chevron.right.png",
|
||||
callback = function() self:onNextPage() end,
|
||||
bordersize = 0,
|
||||
show_parent = self,
|
||||
show_parent = self.show_parent,
|
||||
}
|
||||
self.page_info_first_chev = Button:new{
|
||||
icon = "resources/icons/appbar.chevron.first.png",
|
||||
callback = function() self:onFirstPage() end,
|
||||
bordersize = 0,
|
||||
show_parent = self,
|
||||
show_parent = self.show_parent,
|
||||
}
|
||||
self.page_info_last_chev = Button:new{
|
||||
icon = "resources/icons/appbar.chevron.last.png",
|
||||
callback = function() self:onLastPage() end,
|
||||
bordersize = 0,
|
||||
show_parent = self,
|
||||
show_parent = self.show_parent,
|
||||
}
|
||||
self.page_info_spacer = HorizontalSpan:new{
|
||||
width = Screen:scaleBySize(32),
|
||||
@@ -699,7 +703,7 @@ function Menu:init()
|
||||
if self.onReturn then self:onReturn() end
|
||||
end,
|
||||
bordersize = 0,
|
||||
show_parent = self,
|
||||
show_parent = self.show_parent,
|
||||
readonly = self.return_arrow_propagation,
|
||||
}
|
||||
self.page_return_arrow:hide()
|
||||
@@ -953,7 +957,7 @@ function Menu:updateItems(select_number)
|
||||
self.path_text.text = self:truncatePath(self.path)
|
||||
end
|
||||
|
||||
UIManager:setDirty("all", function()
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
local refresh_dimen =
|
||||
old_dimen and old_dimen:combine(self.dimen)
|
||||
or self.dimen
|
||||
|
||||
@@ -122,7 +122,7 @@ function MultiInputDialog:init()
|
||||
},
|
||||
self.dialog_frame,
|
||||
}
|
||||
UIManager:setDirty("all", "full")
|
||||
UIManager:setDirty(self, "ui")
|
||||
end
|
||||
|
||||
function MultiInputDialog:getFields()
|
||||
|
||||
@@ -353,7 +353,7 @@ function NaturalLightWidget:createMainContent(width, height)
|
||||
table.insert(self.fl_container, vertical_group)
|
||||
-- Reset container height to what it actually contains
|
||||
self.fl_container.dimen.h = vertical_group:getSize().h
|
||||
UIManager:setDirty("all", "ui")
|
||||
UIManager:setDirty(self, "ui")
|
||||
return self.fl_container
|
||||
end
|
||||
|
||||
|
||||
@@ -116,14 +116,17 @@ function RadioButton:onTapCheckButton()
|
||||
if G_reader_settings:isFalse("flash_ui") then
|
||||
self.callback()
|
||||
else
|
||||
self.invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
-- While I'd like to only flash the button itself, we have to make do with flashing the full width of the TextWidget...
|
||||
self.frame.invert = true
|
||||
UIManager:widgetRepaint(self.frame, self.dimen.x, self.dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self.dimen
|
||||
end)
|
||||
UIManager:tickAfterNext(function()
|
||||
self.callback()
|
||||
self.invert = false
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
self.frame.invert = false
|
||||
UIManager:widgetRepaint(self.frame, self.dimen.x, self.dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self.dimen
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -49,8 +49,9 @@ function ScreenSaverWidget:update()
|
||||
height = self.height,
|
||||
self.widget,
|
||||
}
|
||||
self.dithered = true
|
||||
self[1] = self.main_frame
|
||||
UIManager:setDirty("all", function()
|
||||
UIManager:setDirty(self, function()
|
||||
local update_region = self.main_frame.dimen
|
||||
return "partial", update_region
|
||||
end)
|
||||
@@ -66,14 +67,12 @@ end
|
||||
function ScreenSaverWidget:onTap(_, ges)
|
||||
if ges.pos:intersectWith(self.main_frame.dimen) then
|
||||
self:onClose()
|
||||
UIManager:setDirty("all", "full")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function ScreenSaverWidget:onClose()
|
||||
UIManager:close(self)
|
||||
UIManager:setDirty("all", "full")
|
||||
UIManager:close(self, "full")
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ function TextViewer:init()
|
||||
dimen = self.region,
|
||||
self.movable,
|
||||
}
|
||||
UIManager:setDirty("all", function()
|
||||
UIManager:setDirty(self, function()
|
||||
local update_region = self.frame.dimen:combine(orig_dimen)
|
||||
logger.dbg("update region", update_region)
|
||||
return "partial", update_region
|
||||
|
||||
@@ -141,7 +141,8 @@ function TouchMenuItem:onTapSelect(arg, ges)
|
||||
self.menu:onMenuSelect(self.item)
|
||||
else
|
||||
self.item_frame.invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self.dimen
|
||||
end)
|
||||
-- yield to main UI loop to invert item
|
||||
@@ -153,6 +154,7 @@ function TouchMenuItem:onTapSelect(arg, ges)
|
||||
-- Since it's an *un*highlight containing text, we make it "ui" and not "fast", both so it won't mangle text,
|
||||
-- and because "fast" can have some weird side-effects on some devices in this specific instance...
|
||||
if self.item.hold_keep_menu_open or self.item.keep_menu_open then
|
||||
--UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self.dimen
|
||||
end)
|
||||
@@ -173,7 +175,8 @@ function TouchMenuItem:onHoldSelect(arg, ges)
|
||||
self.menu:onMenuHold(self.item)
|
||||
else
|
||||
self.item_frame.invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self.dimen
|
||||
end)
|
||||
UIManager:tickAfterNext(function()
|
||||
@@ -181,6 +184,8 @@ function TouchMenuItem:onHoldSelect(arg, ges)
|
||||
end)
|
||||
UIManager:scheduleIn(0.5, function()
|
||||
self.item_frame.invert = false
|
||||
-- NOTE: For some reason, this is finicky (I end up with a solid black bar, i.e., text gets inverted, but not the bg?!)
|
||||
--UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self.dimen
|
||||
end)
|
||||
@@ -431,13 +436,13 @@ function TouchMenu:init()
|
||||
icon = "resources/icons/appbar.chevron.left.png",
|
||||
callback = function() self:onPrevPage() end,
|
||||
bordersize = 0,
|
||||
show_parent = self,
|
||||
show_parent = self.show_parent,
|
||||
}
|
||||
self.page_info_right_chev = Button:new{
|
||||
icon = "resources/icons/appbar.chevron.right.png",
|
||||
callback = function() self:onNextPage() end,
|
||||
bordersize = 0,
|
||||
show_parent = self,
|
||||
show_parent = self.show_parent,
|
||||
}
|
||||
self.page_info_left_chev:hide()
|
||||
self.page_info_right_chev:hide()
|
||||
@@ -518,8 +523,8 @@ function TouchMenu:init()
|
||||
end
|
||||
|
||||
function TouchMenu:onCloseWidget()
|
||||
-- NOTE: We pass a nil region to ensure a full-screen flash to avoid ghosting
|
||||
UIManager:setDirty(nil, "flashui", nil)
|
||||
-- NOTE: We don't pass a region in order to ensure a full-screen flash to avoid ghosting
|
||||
UIManager:setDirty(nil, "flashui")
|
||||
end
|
||||
|
||||
function TouchMenu:_recalculatePageLayout()
|
||||
@@ -605,7 +610,8 @@ function TouchMenu:updateItems()
|
||||
|
||||
-- NOTE: We use a slightly ugly hack to detect a brand new menu vs. a tab switch,
|
||||
-- in order to optionally flash on initial menu popup...
|
||||
UIManager:setDirty("all", function()
|
||||
-- NOTE: Also avoid repainting what's underneath us on initial popup.
|
||||
UIManager:setDirty(self.is_fresh and self.show_parent or "all", function()
|
||||
local refresh_dimen =
|
||||
old_dimen and old_dimen:combine(self.dimen)
|
||||
or self.dimen
|
||||
|
||||
@@ -133,7 +133,9 @@ function VirtualKey:update_keyboard(want_flash, want_fast)
|
||||
if want_fast then
|
||||
refresh_type = "fast"
|
||||
end
|
||||
UIManager:setDirty(self.keyboard, function()
|
||||
-- Only repaint the key itself, not the full board...
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
logger.dbg("update key region", self[1].dimen)
|
||||
return refresh_type, self[1].dimen
|
||||
end)
|
||||
|
||||
@@ -146,7 +146,7 @@ function BookInfoManager:createDB()
|
||||
-- Check version (not updated by previous exec if already there)
|
||||
local res = db_conn:exec("SELECT value FROM config where key='version';")
|
||||
if res[1][1] ~= BOOKINFO_DB_VERSION then
|
||||
logger.warn("BookInfo cache DB schema updated from version ", res[1][1], "to version", BOOKINFO_DB_VERSION)
|
||||
logger.warn("BookInfo cache DB schema updated from version", res[1][1], "to version", BOOKINFO_DB_VERSION)
|
||||
logger.warn("Deleting existing", self.db_location, "to recreate it")
|
||||
db_conn:close()
|
||||
os.remove(self.db_location)
|
||||
@@ -451,7 +451,7 @@ function BookInfoManager:extractBookInfo(filepath, cover_specs)
|
||||
-- release memory used by uncompressed data:
|
||||
cover_data = nil -- luacheck: no unused
|
||||
dbrow.cover_dataz = SQ3.blob(cover_dataz) -- cast to blob for sqlite
|
||||
logger.dbg("cover for", filename, "scaled by", scale_factor, "=>", cbb_w, "x", cbb_h, "(compressed from ", dbrow.cover_datalen, " to ", cover_dataz:len())
|
||||
logger.dbg("cover for", filename, "scaled by", scale_factor, "=>", cbb_w, "x", cbb_h, ", compressed from", dbrow.cover_datalen, "to", cover_dataz:len())
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -554,7 +554,7 @@ function BookInfoManager:collectSubprocesses()
|
||||
-- have caused a terminateBackgroundJobs() - if we're here, it's
|
||||
-- that user has left reader in FileBrower and went away)
|
||||
if util.gettime() > self.subprocesses_last_added_ts + self.subprocesses_killall_timeout_seconds then
|
||||
logger.warn("Some subprocess were running for too long, killing them")
|
||||
logger.warn("Some subprocesses were running for too long, killing them")
|
||||
self:terminateBackgroundJobs()
|
||||
-- we'll collect them next time we're run
|
||||
end
|
||||
|
||||
@@ -77,6 +77,7 @@ function CoverMenu:updateItems(select_number)
|
||||
collectgarbage()
|
||||
|
||||
-- Specific UI building implementation (defined in some other module)
|
||||
self._has_cover_images = false
|
||||
self:_updateItemsBuildUI()
|
||||
|
||||
-- Set the local variables with the things we know
|
||||
@@ -89,11 +90,12 @@ function CoverMenu:updateItems(select_number)
|
||||
if self.show_path then
|
||||
self.path_text.text = self:truncatePath(self.path)
|
||||
end
|
||||
UIManager:setDirty("all", function()
|
||||
self.show_parent.dithered = self._has_cover_images
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
local refresh_dimen =
|
||||
old_dimen and old_dimen:combine(self.dimen)
|
||||
or self.dimen
|
||||
return "partial", refresh_dimen
|
||||
return "partial", refresh_dimen, self.show_parent.dithered
|
||||
end)
|
||||
|
||||
-- As additionally done in FileChooser:updateItems()
|
||||
@@ -137,13 +139,14 @@ function CoverMenu:updateItems(select_number)
|
||||
item:update()
|
||||
if item.bookinfo_found then
|
||||
logger.dbg(" found", item.text)
|
||||
self.show_parent.dithered = item._has_cover_image
|
||||
local refreshfunc = function()
|
||||
if item.refresh_dimen then
|
||||
-- MosaicMenuItem may exceed its own dimen in its paintTo
|
||||
-- with its "description" hint
|
||||
return "ui", item.refresh_dimen
|
||||
return "ui", item.refresh_dimen, self.show_parent.dithered
|
||||
else
|
||||
return "ui", item[1].dimen
|
||||
return "ui", item[1].dimen, self.show_parent.dithered
|
||||
end
|
||||
end
|
||||
UIManager:setDirty(self.show_parent, refreshfunc)
|
||||
|
||||
@@ -288,6 +288,9 @@ function ListMenuItem:update()
|
||||
wimage,
|
||||
}
|
||||
}
|
||||
-- Let menu know it has some item with images
|
||||
self.menu._has_cover_images = true
|
||||
self._has_cover_image = true
|
||||
else
|
||||
-- empty element the size of an image
|
||||
wleft = CenterContainer:new{
|
||||
|
||||
@@ -498,6 +498,9 @@ function MosaicMenuItem:update()
|
||||
image,
|
||||
}
|
||||
}
|
||||
-- Let menu know it has some item with images
|
||||
self.menu._has_cover_images = true
|
||||
self._has_cover_image = true
|
||||
else
|
||||
-- add Series metadata if requested
|
||||
if bookinfo.series then
|
||||
|
||||
@@ -173,14 +173,16 @@ function DoubleKeyValueItem:onTap()
|
||||
UIManager:close(info)
|
||||
else
|
||||
self[1].invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "fast", self[1].dimen
|
||||
end)
|
||||
UIManager:tickAfterNext(function()
|
||||
self.callback()
|
||||
UIManager:close(info)
|
||||
self[1].invert = false
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
UIManager:widgetRepaint(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self[1].dimen
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -260,8 +260,7 @@ function GoodreadsBook:onAnyKeyPressed()
|
||||
end
|
||||
|
||||
function GoodreadsBook:onClose()
|
||||
UIManager:setDirty("all")
|
||||
UIManager:close(self)
|
||||
UIManager:close(self, "flashui")
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -235,8 +235,7 @@ function KoboLight:addToMainMenu(menu_items)
|
||||
(self:disabled() and _("Do you want to enable the frontlight gesture controller?") or _("Do you want to disable the frontlight gesture controller?")),
|
||||
ok_text = self:disabled() and _("Enable") or _("Disable"),
|
||||
ok_callback = function()
|
||||
UIManager:close(image)
|
||||
UIManager:setDirty("all", "full")
|
||||
UIManager:close(image, "full")
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = T(_("You have %1 the frontlight gesture controller. It will take effect on next restart."),
|
||||
self:disabled() and _("enabled") or _("disabled"))
|
||||
@@ -245,8 +244,7 @@ function KoboLight:addToMainMenu(menu_items)
|
||||
end,
|
||||
cancel_text = _("Close"),
|
||||
cancel_callback = function()
|
||||
UIManager:close(image)
|
||||
UIManager:setDirty("all", "full")
|
||||
UIManager:close(image, "full")
|
||||
end,
|
||||
})
|
||||
UIManager:setDirty("all", "full")
|
||||
|
||||
Reference in New Issue
Block a user