From 2f9386cd69e9f9a52c39e3a6aad22166a39e075e Mon Sep 17 00:00:00 2001 From: Hans-Werner Hilse Date: Fri, 28 Nov 2014 18:53:48 +0000 Subject: [PATCH 1/6] move refresh menu table out of UIManager no need to clutter a core file of the whole UI concept with definitions of UI elements --- .../elements/common_settings_menu_table.lua | 3 +- frontend/ui/elements/refresh_menu_table.lua | 71 +++++++++++++++++++ frontend/ui/uimanager.lua | 66 ----------------- 3 files changed, 73 insertions(+), 67 deletions(-) create mode 100644 frontend/ui/elements/refresh_menu_table.lua diff --git a/frontend/ui/elements/common_settings_menu_table.lua b/frontend/ui/elements/common_settings_menu_table.lua index f47859400..85b252203 100644 --- a/frontend/ui/elements/common_settings_menu_table.lua +++ b/frontend/ui/elements/common_settings_menu_table.lua @@ -16,6 +16,7 @@ if Device:hasFrontlight() then end, }) end + table.insert(common_settings, { text = _("Night mode"), checked_func = function() return G_reader_settings:readSetting("night_mode") end, @@ -37,7 +38,7 @@ table.insert(common_settings, { sub_item_table = { require("ui/elements/screen_dpi_menu_table"), require("ui/elements/screen_eink_opt_menu_table"), - UIManager:getRefreshMenuTable(), + require("ui/elements/refresh_menu_table"), }, }) table.insert(common_settings, Language:getLangMenuTable()) diff --git a/frontend/ui/elements/refresh_menu_table.lua b/frontend/ui/elements/refresh_menu_table.lua new file mode 100644 index 000000000..f12f2f187 --- /dev/null +++ b/frontend/ui/elements/refresh_menu_table.lua @@ -0,0 +1,71 @@ +local UIManager = require("ui/uimanager") +local util = require("ffi/util") +local _ = require("gettext") + +local function custom_1() return G_reader_settings:readSetting("refresh_rate_1") or 12 end + +local function custom_2() return G_reader_settings:readSetting("refresh_rate_2") or 22 end + +local function custom_3() return G_reader_settings:readSetting("refresh_rate_3") or 99 end + +local function custom_input(name) + return { + title = _("Input page number for a full refresh"), + type = "number", + hint = "(1 - 99)", + callback = function(input) + local rate = tonumber(input) + G_reader_settings:saveSetting(name, rate) + UIManager:setRefreshRate(rate) + end, + } +end + +return { + text = _("E-ink full refresh rate"), + sub_item_table = { + { + text = _("Every page"), + checked_func = function() return UIManager:getRefreshRate() == 1 end, + callback = function() UIManager:setRefreshRate(1) end, + }, + { + text = _("Every 6 pages"), + checked_func = function() return UIManager:getRefreshRate() == 6 end, + callback = function() UIManager:setRefreshRate(6) end, + }, + { + text_func = function() + return util.template( + _("Custom 1: %1 pages"), + custom_1() + ) + end, + checked_func = function() return UIManager:getRefreshRate() == custom_1() end, + callback = function() UIManager:setRefreshRate(custom_1()) end, + hold_input = custom_input("refresh_rate_1") + }, + { + text_func = function() + return util.template( + _("Custom 2: %1 pages"), + custom_2() + ) + end, + checked_func = function() return UIManager:getRefreshRate() == custom_2() end, + callback = function() UIManager:setRefreshRate(custom_2()) end, + hold_input = custom_input("refresh_rate_2") + }, + { + text_func = function() + return util.template( + _("Custom 3: %1 pages"), + custom_3() + ) + end, + checked_func = function() return UIManager:getRefreshRate() == custom_3() end, + callback = function() UIManager:setRefreshRate(custom_3()) end, + hold_input = custom_input("refresh_rate_3") + }, + } +} diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index 789a0a0bb..beefb6fe9 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -431,72 +431,6 @@ function UIManager:run() end end -function UIManager:getRefreshMenuTable() - local function custom_1() return G_reader_settings:readSetting("refresh_rate_1") or 12 end - local function custom_2() return G_reader_settings:readSetting("refresh_rate_2") or 22 end - local function custom_3() return G_reader_settings:readSetting("refresh_rate_3") or 99 end - local function custom_input(name) - return { - title = _("Input page number for a full refresh"), - type = "number", - hint = "(1 - 99)", - callback = function(input) - local rate = tonumber(input) - G_reader_settings:saveSetting(name, rate) - UIManager:setRefreshRate(rate) - end, - } - end - return { - text = _("E-ink full refresh rate"), - sub_item_table = { - { - text = _("Every page"), - checked_func = function() return UIManager:getRefreshRate() == 1 end, - callback = function() UIManager:setRefreshRate(1) end, - }, - { - text = _("Every 6 pages"), - checked_func = function() return UIManager:getRefreshRate() == 6 end, - callback = function() UIManager:setRefreshRate(6) end, - }, - { - text_func = function() - return util.template( - _("Custom 1: %1 pages"), - custom_1() - ) - end, - checked_func = function() return UIManager:getRefreshRate() == custom_1() end, - callback = function() UIManager:setRefreshRate(custom_1()) end, - hold_input = custom_input("refresh_rate_1") - }, - { - text_func = function() - return util.template( - _("Custom 2: %1 pages"), - custom_2() - ) - end, - checked_func = function() return UIManager:getRefreshRate() == custom_2() end, - callback = function() UIManager:setRefreshRate(custom_2()) end, - hold_input = custom_input("refresh_rate_2") - }, - { - text_func = function() - return util.template( - _("Custom 3: %1 pages"), - custom_3() - ) - end, - checked_func = function() return UIManager:getRefreshRate() == custom_3() end, - callback = function() UIManager:setRefreshRate(custom_3()) end, - hold_input = custom_input("refresh_rate_3") - }, - } - } -end - UIManager:init() return UIManager From 229c5ad61cfa81b16ae1fd00d8f8823f3171b513 Mon Sep 17 00:00:00 2001 From: Hans-Werner Hilse Date: Fri, 28 Nov 2014 20:12:54 +0000 Subject: [PATCH 2/6] change setDirty/refresh API See documentation in the code. In short: There is now one single method, setDirty(), that triggers repaints and/or refreshes. All variables in UIManager are gone - at least from an external perspective. Everything is done through setDirty(). This also allows for easier debugging, since all requests come in via function calls. --- frontend/ui/uimanager.lua | 226 ++++++++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 82 deletions(-) diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index beefb6fe9..b327fc7e9 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -2,26 +2,16 @@ local Device = require("device") local Screen = Device.screen local Input = require("device").input local Event = require("ui/event") +local Geom = require("ui/geometry") local util = require("ffi/util") local DEBUG = require("dbg") local _ = require("gettext") -- there is only one instance of this local UIManager = { - -- force to repaint all the widget is stack, will be reset to false - -- after each ui loop - repaint_all = false, - -- force to do full refresh, will be reset to false - -- after each ui loop - full_refresh = false, - -- force to do partial refresh, will be reset to false - -- after each ui loop - partial_refresh = false, -- trigger a full refresh when counter reaches FULL_REFRESH_COUNT FULL_REFRESH_COUNT = G_reader_settings:readSetting("full_refresh_count") or DRCOUNTMAX, refresh_count = 0, - -- only update specific regions of the screen - update_regions_func = nil, event_handlers = nil, @@ -31,6 +21,8 @@ local UIManager = { _execution_stack_dirty = false, _dirty = {}, _zeromqs = {}, + _refresh_stack = {}, + _refresh_func_stack = {}, } function UIManager:init() @@ -105,7 +97,7 @@ function UIManager:show(widget, x, y) end end -- and schedule it to be painted - self:setDirty(widget) + self:setDirty(widget, "partial") -- tell the widget that it is shown now widget:handleEvent(Event:new("Show")) -- check if this widget disables double tap gesture @@ -135,7 +127,7 @@ function UIManager:close(widget) if dirty then -- schedule remaining widgets to be painted for i = 1, #self._window_stack do - self:setDirty(self._window_stack[i].widget) + self:setDirty(self._window_stack[i].widget, "partial") end end end @@ -176,16 +168,49 @@ function UIManager:unschedule(action) end end --- register a widget to be repainted -function UIManager:setDirty(widget, refresh_type) - -- "auto": request full refresh - -- "full": force full refresh - -- "partial": partial refresh - if not refresh_type then - refresh_type = "auto" - end +--[[ +register a widget to be repainted and enqueue a refresh + +the second parameter (refreshtype) can either specify a refreshtype +(optionally in combination with a refreshregion - which is suggested) +or a function that returns refreshtype AND refreshregion and is called +after painting the widget. + +E.g.: +UIManager:setDirty(self.widget, "partial") +UIManager:setDirty(self.widget, "partial", Geom:new{x=10,y=10,w=100,h=50}) +UIManager:setDirty(self.widget, function() return "ui", self.someelement.dimen end) +--]] +function UIManager:setDirty(widget, refreshtype, refreshregion) if widget then - self._dirty[widget] = refresh_type + 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 + end + else + self._dirty[widget] = true + if DEBUG.is_on then + -- when debugging, we check if we get handed a valid widget, + -- which would be a dialog that was previously passed via show() + local found = false + for i = 1, #self._window_stack do + if self._window_stack[i].widget == widget then found = true end + end + if not found then + DEBUG("INFO: invalid widget for setDirty()", debug.traceback()) + end + end + end + end + -- handle refresh information + if not refreshtype then return end + if type(refreshtype) == "function" then + -- callback, will be issued after painting + table.insert(self._refresh_func_stack, refreshtype) + else + -- otherwise, enqueue refresh + self:_refresh(refreshtype, refreshregion) end end @@ -252,7 +277,7 @@ function UIManager:sendEvent(event) end end -function UIManager:checkTasks() +function UIManager:_checkTasks() local now = { util.gettime() } -- check if we have timed events in our queue and search next one @@ -286,36 +311,85 @@ function UIManager:checkTasks() return wait_until, now end +-- precedence of refresh modes: +local refresh_modes = { fast = 1, ui = 2, partial = 3, full = 4 } +-- refresh methods in framebuffer implementation +local refresh_methods = { + fast = "refreshFast", + ui = "refreshUI", + partial = "refreshPartial", + full = "refreshFull", +} + +--[[ +refresh mode comparision + +will return the mode that takes precedence +--]] +local function update_mode(mode1, mode2) + if refresh_modes[mode1] > refresh_modes[mode2] then + return mode1 + else + return mode2 + end +end + +--[[ +enqueue a refresh + +Widgets call this in their paintTo() method in order to notify +UIManager that a certain part of the screen is to be refreshed. + +mode: refresh mode ("full", "partial", "ui", "fast") +region: Rect() that specifies the region to be updated + optional, update will affect whole screen if not specified. + Note that this should be the exception. +--]] +function UIManager:_refresh(mode, region) + -- default mode is partial + mode = mode or "partial" + -- special case: full screen partial update + -- will get promoted every self.FULL_REFRESH_COUNT updates + if not region and mode == "partial" then + self.refresh_count = (self.refresh_count + 1) % self.FULL_REFRESH_COUNT + if self.refresh_count == self.FULL_REFRESH_COUNT - 1 then + DEBUG("promote refresh to full refresh") + mode = "full" + end + end + + -- if no region is specified, define default region + region = region or Geom:new{w=Screen:getWidth(), h=Screen:getHeight()} + + for i = 1, #self._refresh_stack do + -- check for collision with updates that are already enqueued + if region:intersectWith(self._refresh_stack[i].region) then + -- combine both refreshes' regions + local combined = region:combine(self._refresh_stack[i].region) + -- update the mode, if needed + local mode = update_mode(mode, self._refresh_stack[i].mode) + -- remove colliding update + table.remove(self._refresh_stack, i) + -- and try again with combined data + return self:_refresh(mode, combined) + end + end + -- if we hit no (more) collides, enqueue the update + table.insert(self._refresh_stack, {mode = mode, region = region}) +end + -- repaint dirty widgets -function UIManager:repaint() +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 - -- we use this to record requests for certain refresh types - -- TODO: fix this, see below - local force_full_refresh = self.full_refresh - self.full_refresh = false - - local force_partial_refresh = self.partial_refresh - self.partial_refresh = false - - local force_fast_refresh = false - for _, widget in ipairs(self._window_stack) do - -- paint if repaint_all is request - -- paint also if current widget or any widget underneath is dirty - if self.repaint_all or dirty or self._dirty[widget.widget] then - widget.widget:paintTo(Screen.bb, widget.x, widget.y) - - -- self._dirty[widget.widget] may also be "auto" - if self._dirty[widget.widget] == "full" then - force_full_refresh = true - elseif self._dirty[widget.widget] == "partial" then - force_partial_refresh = true - elseif self._dirty[widget.widget] == "fast" then - force_fast_refresh = true - end + -- paint if current widget or any widget underneath is dirty + if dirty or self._dirty[widget.widget] then + -- pass hint to widget that we got when setting widget dirty + -- the widget can use this to decide which parts should be refreshed + widget.widget:paintTo(Screen.bb, widget.x, widget.y, self._dirty[widget.widget]) -- and remove from list after painting self._dirty[widget.widget] = nil @@ -324,42 +398,30 @@ function UIManager:repaint() dirty = true end end - self.repaint_all = false - if dirty then - -- select proper refresh mode - -- TODO: fix this. We should probably do separate refreshes - -- by regional refreshes (e.g. fast refresh, some partial refreshes) - -- and full-screen full refresh - local refresh - - if force_fast_refresh then - refresh = Screen.refreshFast - elseif force_partial_refresh then - refresh = Screen.refreshPartial - elseif force_full_refresh or self.refresh_count == self.FULL_REFRESH_COUNT - 1 then - refresh = Screen.refreshFull - -- a full refresh will reset the counter which leads to an automatic full refresh - self.refresh_count = 0 - else - -- default - refresh = Screen.refreshPartial - -- increment refresh counter in this case - self.refresh_count = (self.refresh_count + 1) % self.FULL_REFRESH_COUNT - 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 + end + self._refresh_func_stack = {} + + -- we should have at least one refresh if we did repaint. + -- If we don't, we add one now and print a warning if we + -- are debugging + if dirty and #self._refresh_stack == 0 then + DEBUG("WARNING: no refresh got enqueued. Will do a partial full screen refresh, which might be inefficient") + self:_refresh("partial") + end - if self.update_regions_func then - local update_regions = self.update_regions_func() - for _, update_region in ipairs(update_regions) do - -- in some rare cases update region has 1 pixel offset - refresh(Screen, update_region.x-1, update_region.y-1, - update_region.w+2, update_region.h+2) - end - self.update_regions_func = nil - else - refresh(Screen) - end + -- execute refreshes: + for _, refresh in ipairs(self._refresh_stack) do + DEBUG("triggering refresh", refresh) + Screen[refresh_methods[refresh.mode]](Screen, + refresh.region.x - 1, refresh.region.y - 1, + refresh.region.w + 2, refresh.region.h + 2) end + self._refresh_stack = {} end -- this is the main loop of the UI controller @@ -373,7 +435,7 @@ function UIManager:run() -- that will be honored when calculating the time to wait -- for input events: repeat - wait_until, now = self:checkTasks() + wait_until, now = self:_checkTasks() --DEBUG("---------------------------------------------------") --DEBUG("exec stack", self._execution_stack) @@ -388,7 +450,7 @@ function UIManager:run() return nil end - self:repaint() + self:_repaint() until not self._execution_stack_dirty -- wait for next event From 82c26b1f184f34f63fbeafeac4abfc21cb13637f Mon Sep 17 00:00:00 2001 From: Hans-Werner Hilse Date: Sun, 30 Nov 2014 00:12:00 +0000 Subject: [PATCH 3/6] adapt widgets to new refresh/repaint API --- .../apps/reader/modules/readercropping.lua | 1 - frontend/apps/reader/modules/readerfooter.lua | 17 +++++------ .../apps/reader/modules/readerhighlight.lua | 14 ++++----- frontend/apps/reader/modules/readerpaging.lua | 8 +++-- .../apps/reader/modules/readerrolling.lua | 2 +- .../apps/reader/modules/readerscreenshot.lua | 3 +- frontend/apps/reader/modules/readersearch.lua | 1 + frontend/apps/reader/modules/readerview.lua | 8 ++--- frontend/ui/widget/bboxwidget.lua | 2 +- frontend/ui/widget/button.lua | 12 ++++---- frontend/ui/widget/configdialog.lua | 18 +++++------ frontend/ui/widget/dictquicklookup.lua | 3 +- frontend/ui/widget/focusmanager.lua | 1 + frontend/ui/widget/iconbutton.lua | 11 +++---- frontend/ui/widget/inputdialog.lua | 4 +-- frontend/ui/widget/inputtext.lua | 14 ++++++--- frontend/ui/widget/logindialog.lua | 3 +- frontend/ui/widget/menu.lua | 6 +++- frontend/ui/widget/multiinputdialog.lua | 3 +- frontend/ui/widget/scrolltextwidget.lua | 7 ++--- frontend/ui/widget/toggleswitch.lua | 7 ++--- frontend/ui/widget/touchmenu.lua | 26 +++++++++------- frontend/ui/widget/virtualkeyboard.lua | 30 +++++++++++-------- frontend/up_reg.list | 19 ++++++++++++ 24 files changed, 127 insertions(+), 93 deletions(-) create mode 100644 frontend/up_reg.list diff --git a/frontend/apps/reader/modules/readercropping.lua b/frontend/apps/reader/modules/readercropping.lua index 55404dc9f..a24174be5 100644 --- a/frontend/apps/reader/modules/readercropping.lua +++ b/frontend/apps/reader/modules/readercropping.lua @@ -148,7 +148,6 @@ function ReaderCropping:exitPageCrop(confirmed) else self:setCropZoomMode(confirmed) end - UIManager.repaint_all = true end function ReaderCropping:setCropZoomMode(confirmed) diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index 574baf19d..05b87ccb0 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -267,22 +267,19 @@ function ReaderFooter:onTapFooter(arg, ges) self.mode = 0 end self:applyFooterMode() - local region = Geom:new{ - x = 0, - y = Screen:getHeight() - self.height, - w = Screen:getWidth(), - h = self.height - } - UIManager.update_regions_func = function() - return {region} - end end if self.pageno then self:updateFooterPage() else self:updateFooterPos() end - UIManager:setDirty(self.view.dialog, "partial") + local region = Geom:new{ + x = 0, + y = Screen:getHeight() - self.height, + w = Screen:getWidth(), + h = self.height + } + UIManager:setDirty(self.view.dialog, "partial", region) G_reader_settings:saveSetting("reader_footer_mode", self.mode) return true end diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index 21d0f71e8..b642808c5 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -120,7 +120,6 @@ function ReaderHighlight:clear() else self.ui.document:clearSelection() end - UIManager:setDirty(self.dialog, "full") if self.hold_pos then self.hold_pos = nil self.selected_text = nil @@ -240,13 +239,9 @@ function ReaderHighlight:onHold(arg, ges) table.insert(boxes, self.selected_word.sbox) self.view.highlight.temp[self.hold_pos.page] = boxes end - --[[ - UIManager.update_regions_func = function() - DEBUG("update ReaderHighlight onHold region", self.selected_word.sbox) - return {self.selected_word.sbox} - end - --]] UIManager:setDirty(self.dialog, "partial") + -- TODO: only mark word? + -- UIManager:setDirty(self.dialog, "partial", self.selected_word.sbox) end return true end @@ -265,7 +260,12 @@ function ReaderHighlight:onHoldPan(arg, ges) self.holdpan_pos = self.view:screenToPageTransform(ges.pos) DEBUG("holdpan position in page", self.holdpan_pos) + local old_text = self.selected_text and self.selected_text.text self.selected_text = self.ui.document:getTextFromPositions(self.hold_pos, self.holdpan_pos) + if self.selected_text and old_text and old_text == self.selected_text.text then + -- no modification + return + end DEBUG("selected text:", self.selected_text) if self.selected_text then self.view.highlight.temp[self.hold_pos.page] = self.selected_text.sboxes diff --git a/frontend/apps/reader/modules/readerpaging.lua b/frontend/apps/reader/modules/readerpaging.lua index 6b43d8662..e9381f7ed 100644 --- a/frontend/apps/reader/modules/readerpaging.lua +++ b/frontend/apps/reader/modules/readerpaging.lua @@ -314,7 +314,8 @@ function ReaderPaging:onSwipe(arg, ges) self:onPagingRel(-1) end else - UIManager.full_refresh = true + -- trigger full refresh + UIManager:setDirty(nil, "full") end end @@ -343,7 +344,8 @@ function ReaderPaging:onPanRelease(arg, ges) end else self.last_pan_relative_y = 0 - UIManager.full_refresh = true + -- trigger full refresh + UIManager:setDirty(nil, "full") end end @@ -649,7 +651,7 @@ function ReaderPaging:onScrollPageRel(diff) end -- update current pageno to the very last part in current view self:gotoPage(self.view.page_states[#self.view.page_states].page, "scrolling") - UIManager:setDirty(self.view.dialog) + UIManager:setDirty(self.view.dialog, "partial") end function ReaderPaging:onGotoPageRel(diff) diff --git a/frontend/apps/reader/modules/readerrolling.lua b/frontend/apps/reader/modules/readerrolling.lua index 25ad6b4a8..83b4effad 100644 --- a/frontend/apps/reader/modules/readerrolling.lua +++ b/frontend/apps/reader/modules/readerrolling.lua @@ -389,7 +389,7 @@ function ReaderRolling:updatePos() self.old_page = new_page self.ui:handleEvent(Event:new("UpdateToc")) end - UIManager.repaint_all = true + UIManager:setDirty(self.view.dialog, "partial") end --[[ diff --git a/frontend/apps/reader/modules/readerscreenshot.lua b/frontend/apps/reader/modules/readerscreenshot.lua index b500f4446..346adf2b6 100644 --- a/frontend/apps/reader/modules/readerscreenshot.lua +++ b/frontend/apps/reader/modules/readerscreenshot.lua @@ -42,7 +42,8 @@ function ReaderScreenshot:onScreenshot(filename) timeout = 2, }) Screen:shot(screenshot_name) - UIManager.full_refresh = true + -- trigger full refresh + UIManager:setDirty(nil, "full") return true end diff --git a/frontend/apps/reader/modules/readersearch.lua b/frontend/apps/reader/modules/readersearch.lua index 8851d0156..a21aba836 100644 --- a/frontend/apps/reader/modules/readersearch.lua +++ b/frontend/apps/reader/modules/readersearch.lua @@ -71,6 +71,7 @@ function ReaderSearch:onShowSearchDialog(text) } local res = do_search(self.searchFromCurrent, text, 0)() UIManager:show(self.search_dialog) + -- TODO: regional UIManager:setDirty(self.dialog, "partial") return true end diff --git a/frontend/apps/reader/modules/readerview.lua b/frontend/apps/reader/modules/readerview.lua index e9f7c0273..d765fc27b 100644 --- a/frontend/apps/reader/modules/readerview.lua +++ b/frontend/apps/reader/modules/readerview.lua @@ -508,7 +508,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 - UIManager:setDirty(self.dialog) + UIManager:setDirty(self.dialog, "partial") end function ReaderView:PanningUpdate(dx, dy) @@ -517,7 +517,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) + UIManager:setDirty(self.dialog, "partial") DEBUG("on pan: page_area", self.page_area) DEBUG("on pan: visible_area", self.visible_area) self.ui:handleEvent( @@ -534,7 +534,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) + UIManager:setDirty(self.dialog, "partial") end function ReaderView:PanningStop() @@ -546,7 +546,7 @@ function ReaderView:SetZoomCenter(x, y) self.visible_area:centerWithin(self.page_area, x, y) if self.visible_area ~= old then self.ui:handleEvent(Event:new("ViewRecalculate", self.visible_area, self.page_area)) - UIManager:setDirty(self.dialog) + UIManager:setDirty(self.dialog, "partial") end end diff --git a/frontend/ui/widget/bboxwidget.lua b/frontend/ui/widget/bboxwidget.lua index ff108f47d..8c1a98ef2 100644 --- a/frontend/ui/widget/bboxwidget.lua +++ b/frontend/ui/widget/bboxwidget.lua @@ -190,7 +190,7 @@ function BBoxWidget:adjustScreenBBox(ges, relative) y1 = Math.round(bottom_right.y) } - UIManager.repaint_all = true + UIManager:setDirty("all") end function BBoxWidget:getModifiedPageBBox() diff --git a/frontend/ui/widget/button.lua b/frontend/ui/widget/button.lua index 6a2c0169b..bc35839bf 100644 --- a/frontend/ui/widget/button.lua +++ b/frontend/ui/widget/button.lua @@ -154,16 +154,16 @@ function Button:onTapSelectButton() if self.enabled and self.callback then UIManager:scheduleIn(0.0, function() self[1].invert = true - UIManager.update_regions_func = function() - return {self[1].dimen} - end - UIManager.repaint_all = true -- FIXME: Why? - UIManager:setDirty(self.show_parent, "partial") + UIManager:setDirty(self.show_parent, function() + return "partial", self[1].dimen + end) end) UIManager:scheduleIn(0.1, function() self.callback() self[1].invert = false - UIManager:setDirty(self.show_parent, "partial") + UIManager:setDirty(self.show_parent, function() + return "partial", self[1].dimen + end) end) end return true diff --git a/frontend/ui/widget/configdialog.lua b/frontend/ui/widget/configdialog.lua index c34e17d40..fc9d10b2a 100644 --- a/frontend/ui/widget/configdialog.lua +++ b/frontend/ui/widget/configdialog.lua @@ -70,10 +70,9 @@ function OptionTextItem:onTapSelect() self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item) - UIManager.update_regions_func = function() - return {self[1].dimen} - end - UIManager:setDirty(self.config, "partial") + UIManager:setDirty(self.config, function() + return "partial", self[1].dimen + end) return true end @@ -125,10 +124,9 @@ function OptionIconItem:onTapSelect() self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item) - UIManager.update_regions_func = function() - return {self[1].dimen} - end - UIManager:setDirty(self.config, "partial") + UIManager:setDirty(self.config, function() + return "partial", self[1].dimen + end) return true end @@ -525,7 +523,7 @@ end function ConfigDialog:onShowConfigPanel(index) self.panel_index = index self:update() - UIManager.repaint_all = true + UIManager:setDirty("all") return true end @@ -563,7 +561,7 @@ function ConfigDialog:onConfigChoose(values, name, event, args, events, position if events then self:onConfigEvents(events, position) end - UIManager.repaint_all = true + UIManager:setDirty("all") end) end diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 000f4d292..5c11dd4ad 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -256,8 +256,7 @@ function DictQuickLookup:update() self.dict_frame, } } - UIManager.repaint_all = true - UIManager.partial_refresh = true + UIManager:setDirty("all", "partial") end function DictQuickLookup:isPrevDictAvaiable() diff --git a/frontend/ui/widget/focusmanager.lua b/frontend/ui/widget/focusmanager.lua index df70cdd78..a88200119 100644 --- a/frontend/ui/widget/focusmanager.lua +++ b/frontend/ui/widget/focusmanager.lua @@ -80,6 +80,7 @@ function FocusManager:onFocusMove(args) current_item:handleEvent(Event:new("Unfocus")) self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus")) -- trigger a repaint (we need to be the registered widget!) + -- TODO: is this really needed? UIManager:setDirty(self.show_parent or self, "partial") break end diff --git a/frontend/ui/widget/iconbutton.lua b/frontend/ui/widget/iconbutton.lua index 5ac13f63e..f67afb267 100644 --- a/frontend/ui/widget/iconbutton.lua +++ b/frontend/ui/widget/iconbutton.lua @@ -43,16 +43,17 @@ end function IconButton:onTapClickButton() UIManager:scheduleIn(0.0, function() self.image.invert = true - UIManager.update_regions_func = function() - return {self[1].dimen} - end - UIManager:setDirty(self.show_parent, "partial") + UIManager:setDirty(self.show_parent, function() + return "partial", self[1].dimen + end) end) -- make sure button reacts before doing callback UIManager:scheduleIn(0.1, function() self.callback() self.image.invert = false - UIManager:setDirty(self.show_parent, "partial") + UIManager:setDirty(self.show_parent, function() + return "partial", self[1].dimen + end) end) return true end diff --git a/frontend/ui/widget/inputdialog.lua b/frontend/ui/widget/inputdialog.lua index 9b10e6517..a36d27470 100644 --- a/frontend/ui/widget/inputdialog.lua +++ b/frontend/ui/widget/inputdialog.lua @@ -116,8 +116,8 @@ function InputDialog:init() }, self.dialog_frame, } - UIManager.repaint_all = true - UIManager.full_refresh = true + -- do a full refresh - is this really needed? + UIManager:setDirty("all", "full") end function InputDialog:onShowKeyboard() diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index ae7b6bc96..fc342317f 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -154,7 +154,9 @@ function InputText:addChar(char) table.insert(self.charlist, self.charpos, char) self.charpos = self.charpos + 1 self:initTextBox(table.concat(self.charlist)) - UIManager:setDirty(self.parent, "partial") + UIManager:setDirty(self.parent, function() + return "ui", self.dimen + end) end function InputText:delChar() @@ -162,12 +164,14 @@ function InputText:delChar() self.charpos = self.charpos - 1 table.remove(self.charlist, self.charpos) self:initTextBox(table.concat(self.charlist)) - UIManager:setDirty(self.parent, "partial") + UIManager:setDirty(self.parent, "ui") end function InputText:clear() self:initTextBox("") - UIManager:setDirty(self.parent, "partial") + UIManager:setDirty(self.parent, function() + return "ui", self.dimen + end) end function InputText:getText() @@ -176,7 +180,9 @@ end function InputText:setText(text) self:initTextBox(text) - UIManager:setDirty(self.parent, "partial") + UIManager:setDirty(self.parent, function() + return "partial", self.dimen + end) end return InputText diff --git a/frontend/ui/widget/logindialog.lua b/frontend/ui/widget/logindialog.lua index 1b147be66..006a1f5f4 100644 --- a/frontend/ui/widget/logindialog.lua +++ b/frontend/ui/widget/logindialog.lua @@ -87,8 +87,7 @@ function LoginDialog:init() }, self.dialog_frame, } - UIManager.repaint_all = true - UIManager.full_refresh = true + UIManager:setDirty("all", "full") end function LoginDialog:getCredential() diff --git a/frontend/ui/widget/menu.lua b/frontend/ui/widget/menu.lua index 3b7cdfbe6..fa1cd9599 100644 --- a/frontend/ui/widget/menu.lua +++ b/frontend/ui/widget/menu.lua @@ -283,6 +283,7 @@ end function MenuItem:onTapSelect(arg, ges) local pos = self:getGesPosition(ges) self[1].invert = true + -- TODO: regional refresh UIManager:setDirty(self.show_parent, "partial") UIManager:scheduleIn(0.1, function() self[1].invert = false @@ -295,6 +296,7 @@ end function MenuItem:onHoldSelect(arg, ges) local pos = self:getGesPosition(ges) self[1].invert = true + -- TODO: regional refresh UIManager:setDirty(self.show_parent, "partial") UIManager:scheduleIn(0.1, function() self[1].invert = false @@ -650,7 +652,9 @@ function Menu:updateItems(select_number) -- nicolua -- FIXME: dirty hack to clear previous menus - UIManager:setDirty(self.show_parent or self) + -- TODO: regional refresh + UIManager:setDirty("all", "partial") + --UIManager:setDirty(self.show_parent or self, "partial") end --[[ diff --git a/frontend/ui/widget/multiinputdialog.lua b/frontend/ui/widget/multiinputdialog.lua index 572c0f484..96a46d9e3 100644 --- a/frontend/ui/widget/multiinputdialog.lua +++ b/frontend/ui/widget/multiinputdialog.lua @@ -79,8 +79,7 @@ function MultiInputDialog:init() }, self.dialog_frame, } - UIManager.repaint_all = true - UIManager.full_refresh = true + UIManager:setDirty("all", "full") end function MultiInputDialog:getFields() diff --git a/frontend/ui/widget/scrolltextwidget.lua b/frontend/ui/widget/scrolltextwidget.lua index fdc8b90df..28a86ee00 100644 --- a/frontend/ui/widget/scrolltextwidget.lua +++ b/frontend/ui/widget/scrolltextwidget.lua @@ -77,10 +77,9 @@ function ScrollTextWidget:onScrollText(arg, ges) self.text_widget:scrollUp() self:updateScrollBar(self.text_widget) end - UIManager.update_regions_func = function() - return {self.dimen} - end - UIManager:setDirty(self.dialog, "partial") + UIManager:setDirty(self.dialog, function() + return "partial", self.dimen + end) end return ScrollTextWidget diff --git a/frontend/ui/widget/toggleswitch.lua b/frontend/ui/widget/toggleswitch.lua index ee6004d82..b63a74702 100644 --- a/frontend/ui/widget/toggleswitch.lua +++ b/frontend/ui/widget/toggleswitch.lua @@ -144,10 +144,9 @@ function ToggleSwitch:onTapSelect(arg, gev) --]] self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.position) - UIManager.update_regions_func = function() - return {self.dimen} - end - UIManager:setDirty(self.config, "partial") + UIManager:setDirty(self.config, function() + return "partial", self.dimen + end) return true end diff --git a/frontend/ui/widget/touchmenu.lua b/frontend/ui/widget/touchmenu.lua index 37fe8af2f..7a75eece7 100644 --- a/frontend/ui/widget/touchmenu.lua +++ b/frontend/ui/widget/touchmenu.lua @@ -98,17 +98,18 @@ function TouchMenuItem:onTapSelect(arg, ges) UIManager:scheduleIn(0.0, function() self.item_frame.invert = true - UIManager.update_regions_func = function() - return {self.dimen} - end - UIManager:setDirty(self.show_parent, "partial") + UIManager:setDirty(self.show_parent, function() + return "partial", self.dimen + end) end) UIManager:scheduleIn(0.1, function() self.menu:onMenuSelect(self.item) end) UIManager:scheduleIn(0.5, function() self.item_frame.invert = false - UIManager:setDirty(self.show_parent, "partial") + UIManager:setDirty(self.show_parent, function() + return "partial", self.dimen + end) end) return true end @@ -122,17 +123,18 @@ function TouchMenuItem:onHoldSelect(arg, ges) UIManager:scheduleIn(0.0, function() self.item_frame.invert = true - UIManager.update_regions_func = function() - return {self.dimen} - end - UIManager:setDirty(self.show_parent, "partial") + UIManager:setDirty(self.show_parent, function() + return "partial", self.dimen + end) end) UIManager:scheduleIn(0.1, function() self.menu:onMenuHold(self.item) end) UIManager:scheduleIn(0.5, function() self.item_frame.invert = false - UIManager:setDirty(self.show_parent, "partial") + UIManager:setDirty(self.show_parent, function() + return "partial", self.dimen + end) end) return true end @@ -460,7 +462,9 @@ function TouchMenu:updateItems() self.time_info.text = os.date("%H:%M").." @ "..Device:getPowerDevice():getCapacity().."%" -- FIXME: this is a dirty hack to clear previous menus -- refer to issue #664 (in kindlepdfviewer) - UIManager.repaint_all = true + -- TODO: regional refresh + UIManager:setDirty("all", "partial") + --UIManager:setDirty(self.show_parent or self, "partial") end function TouchMenu:switchMenuTab(tab_num) diff --git a/frontend/ui/widget/virtualkeyboard.lua b/frontend/ui/widget/virtualkeyboard.lua index b0a8fe6b8..1cc861c4c 100644 --- a/frontend/ui/widget/virtualkeyboard.lua +++ b/frontend/ui/widget/virtualkeyboard.lua @@ -96,21 +96,22 @@ function VirtualKey:init() end function VirtualKey:update_keyboard() - UIManager.update_regions_func = function() + UIManager:setDirty(self.keyboard, function() DEBUG("update key region", self[1].dimen) - return {self[1].dimen} - end - UIManager:setDirty(self.keyboard, "partial") + return "ui", self[1].dimen + end) end function VirtualKey:update_keyboard_inputbox() local inputbox = self.keyboard.inputbox - UIManager.update_regions_func = function() - DEBUG("update keyboard and inputbox", self[1].dimen, inputbox.dimen) - return {self[1].dimen, inputbox.dimen} - end - UIManager:setDirty(inputbox, "partial") - UIManager:setDirty(self.keyboard, "partial") + UIManager:setDirty(self.keyboard, function() + DEBUG("update inputbox", inputbox.dimen) + return "ui", inputbox.dimen + end) + UIManager:setDirty(self.keyboard, function() + DEBUG("update keyboard", self[1].dimen) + return "ui", self[1].dimen + end) end function VirtualKey:onTapSelect() @@ -318,8 +319,13 @@ function VirtualKeyboard:setLayout(key) if self.utf8mode then self.umlautmode = false end end self:initLayout() - UIManager.update_regions_func = nil - UIManager:setDirty(self, "partial") + UIManager:setDirty(self, function() + -- correct coordinates of keyboard + local dimen = self.dimen:copy() + dimen.y = Screen:getHeight() - dimen.h + DEBUG("update keyboard layout", dimen) + return "partial", dimen + end) end function VirtualKeyboard:addChar(key) diff --git a/frontend/up_reg.list b/frontend/up_reg.list new file mode 100644 index 000000000..77fddaf52 --- /dev/null +++ b/frontend/up_reg.list @@ -0,0 +1,19 @@ +./apps/reader/modules/readerfooter.lua: UIManager.update_regions_func = function() +./apps/reader/modules/readerhighlight.lua: UIManager.update_regions_func = function() +./ui/widget/button.lua: UIManager.update_regions_func = function() +./ui/widget/iconbutton.lua: UIManager.update_regions_func = function() +./ui/widget/dictquicklookup.lua: UIManager.update_regions_func = function() +./ui/widget/scrolltextwidget.lua: UIManager.update_regions_func = function() +./ui/widget/touchmenu.lua: UIManager.update_regions_func = function() +./ui/widget/touchmenu.lua: UIManager.update_regions_func = function() +./ui/widget/virtualkeyboard.lua: UIManager.update_regions_func = function() +./ui/widget/virtualkeyboard.lua: UIManager.update_regions_func = function() +./ui/widget/virtualkeyboard.lua: UIManager.update_regions_func = nil +./ui/widget/toggleswitch.lua: UIManager.update_regions_func = function() +./ui/widget/configdialog.lua: UIManager.update_regions_func = function() +./ui/widget/configdialog.lua: UIManager.update_regions_func = function() +./ui/uimanager.lua: update_regions_func = nil, +./ui/uimanager.lua: if self.update_regions_func then +./ui/uimanager.lua: local update_regions = self.update_regions_func() +./ui/uimanager.lua: for _, update_region in ipairs(update_regions) do +./ui/uimanager.lua: self.update_regions_func = nil From bc3ef46fbf316b95eac1e9f0ec5b6d814fc19e73 Mon Sep 17 00:00:00 2001 From: Hans-Werner Hilse Date: Sun, 30 Nov 2014 00:13:10 +0000 Subject: [PATCH 4/6] update base: use new refresh wait queue also pull refresh emulation improvement for emulator. --- base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base b/base index a3c50378b..e135c5e8b 160000 --- a/base +++ b/base @@ -1 +1 @@ -Subproject commit a3c50378bbab8ef89066dd2511099f8e21fbbaf7 +Subproject commit e135c5e8b2994edf1d8ef0eb81e09516022706c3 From 6793a4fee149e6ac5ba38e80cac82014513a4f83 Mon Sep 17 00:00:00 2001 From: Hans-Werner Hilse Date: Sun, 30 Nov 2014 11:26:35 +0000 Subject: [PATCH 5/6] reader config dialog: properly refresh config pane This serves as a good example for the way refreshes are done: setDirty("all", function() ... end) * the "all" will have all widgets on screen repainted. In this case that is needed because the config pane has different sizes, covering different parts of underlying widgets. So they need to be repainted every time. * the function will return the area to refresh and is evaluated after painting. In this example, we take the area that is covered by the config pane before switching it (if present at all), and hand it to the refresh area function as an upvalue. When the function is called later after painting, it will use that saved area and combine it with the area that is covered then by the widget. That way, parts that are covered no more are included in the refresh area, too. --- frontend/ui/widget/configdialog.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/ui/widget/configdialog.lua b/frontend/ui/widget/configdialog.lua index fc9d10b2a..9d3cdff3a 100644 --- a/frontend/ui/widget/configdialog.lua +++ b/frontend/ui/widget/configdialog.lua @@ -522,8 +522,14 @@ end function ConfigDialog:onShowConfigPanel(index) self.panel_index = index + local old_dimen = self.dialog_frame.dimen and self.dialog_frame.dimen:copy() self:update() - UIManager:setDirty("all") + UIManager:setDirty("all", function() + local refresh_dimen = + old_dimen and old_dimen:combine(self.dialog_frame.dimen) + or self.dialog_frame.dimen + return "partial", refresh_dimen + end) return true end From f02be20a65616cc73aafe257bec6b5bd6ad6ee85 Mon Sep 17 00:00:00 2001 From: Hans-Werner Hilse Date: Sun, 30 Nov 2014 12:04:33 +0000 Subject: [PATCH 6/6] refresh fixes for the menus this will only refresh the areas that actually need to be refreshed. --- frontend/ui/widget/menu.lua | 12 +++++++----- frontend/ui/widget/touchmenu.lua | 15 ++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/frontend/ui/widget/menu.lua b/frontend/ui/widget/menu.lua index fa1cd9599..b6f94cbd6 100644 --- a/frontend/ui/widget/menu.lua +++ b/frontend/ui/widget/menu.lua @@ -576,6 +576,7 @@ function Menu:init() end function Menu:updateItems(select_number) + local old_dimen = self.dimen and self.dimen:copy() -- self.layout must be updated for focusmanager self.layout = {} self.item_group:clear() @@ -650,11 +651,12 @@ function Menu:updateItems(select_number) self.page_info_text.text = _("no choices available") end - -- nicolua - -- FIXME: dirty hack to clear previous menus - -- TODO: regional refresh - UIManager:setDirty("all", "partial") - --UIManager:setDirty(self.show_parent or self, "partial") + UIManager:setDirty("all", function() + local refresh_dimen = + old_dimen and old_dimen:combine(self.dimen) + or self.dimen + return "partial", refresh_dimen + end) end --[[ diff --git a/frontend/ui/widget/touchmenu.lua b/frontend/ui/widget/touchmenu.lua index 7a75eece7..9ad556edd 100644 --- a/frontend/ui/widget/touchmenu.lua +++ b/frontend/ui/widget/touchmenu.lua @@ -408,6 +408,7 @@ function TouchMenu:_recalculateDimen() end function TouchMenu:updateItems() + local old_dimen = self.dimen and self.dimen:copy() self:_recalculateDimen() self.item_group:clear() table.insert(self.item_group, self.bar) @@ -460,11 +461,13 @@ function TouchMenu:updateItems() self.page_info_left_chev:enableDisable(self.page > 1) self.page_info_right_chev:enableDisable(self.page < self.page_num) self.time_info.text = os.date("%H:%M").." @ "..Device:getPowerDevice():getCapacity().."%" - -- FIXME: this is a dirty hack to clear previous menus - -- refer to issue #664 (in kindlepdfviewer) - -- TODO: regional refresh - UIManager:setDirty("all", "partial") - --UIManager:setDirty(self.show_parent or self, "partial") + + UIManager:setDirty("all", function() + local refresh_dimen = + old_dimen and old_dimen:combine(self.dimen) + or self.dimen + return "partial", refresh_dimen + end) end function TouchMenu:switchMenuTab(tab_num) @@ -493,6 +496,8 @@ function TouchMenu:backToUpperMenu() self.item_table = table.remove(self.item_table_stack) self.page = 1 self:updateItems() + else + self:closeMenu() end end