2
0
mirror of https://github.com/koreader/koreader synced 2024-11-10 01:10:34 +00:00
koreader/frontend/apps/reader/modules/readerview.lua

935 lines
32 KiB
Lua
Raw Normal View History

--[[--
ReaderView module handles all the screen painting for document browsing.
]]
local AlphaContainer = require("ui/widget/container/alphacontainer")
local Blitbuffer = require("ffi/blitbuffer")
local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device")
2013-10-18 20:38:07 +00:00
local Geom = require("ui/geometry")
local Event = require("ui/event")
local ImageWidget = require("ui/widget/imagewidget")
local InfoMessage = require("ui/widget/infomessage")
local OverlapGroup = require("ui/widget/overlapgroup")
local ReaderDogear = require("apps/reader/modules/readerdogear")
local ReaderFlipping = require("apps/reader/modules/readerflipping")
local ReaderFooter = require("apps/reader/modules/readerfooter")
local UIManager = require("ui/uimanager")
2016-04-19 06:50:36 +00:00
local dbg = require("dbg")
local logger = require("logger")
local _ = require("gettext")
local Screen = Device.screen
local T = require("ffi/util").template
2013-10-18 20:38:07 +00:00
local ReaderView = OverlapGroup:extend{
2014-03-13 13:52:43 +00:00
document = nil,
-- single page state
state = {
page = nil,
pos = 0,
zoom = 1.0,
rotation = 0,
gamma = 1.0,
offset = nil,
bbox = nil,
},
outer_page_color = Blitbuffer.gray(DOUTER_PAGE_COLOR/15),
-- highlight with "lighten" or "underscore" or "invert"
2014-03-13 13:52:43 +00:00
highlight = {
lighten_factor = 0.2,
2014-03-13 13:52:43 +00:00
temp_drawer = "invert",
temp = {},
saved_drawer = "lighten",
saved = {},
},
highlight_visible = true,
-- PDF/DjVu continuous paging
page_scroll = nil,
page_bgcolor = Blitbuffer.gray(DBACKGROUND_COLOR/15),
2014-03-13 13:52:43 +00:00
page_states = {},
scroll_mode = "vertical",
2016-11-05 15:12:19 +00:00
-- properties of the gap drawn between each page in scroll mode:
2014-03-13 13:52:43 +00:00
page_gap = {
2016-11-05 15:12:19 +00:00
-- width in pixels (when scrolling horizontally)
width = Screen:scaleBySize(G_reader_settings:readSetting("page_gap_width") or 8),
-- height in pixels (when scrolling vertically)
height = Screen:scaleBySize(G_reader_settings:readSetting("page_gap_height") or 8),
-- color (0 = white, 8 = gray, 15 = black)
color = Blitbuffer.gray((G_reader_settings:readSetting("page_gap_color") or 8)/15),
2014-03-13 13:52:43 +00:00
},
-- DjVu page rendering mode (used in djvu.c:drawPage())
render_mode = DRENDER_MODE, -- default to COLOR
-- Crengine view mode
view_mode = DCREREADER_VIEW_MODE, -- default to page mode
hinting = true,
-- visible area within current viewing page
visible_area = Geom:new{x = 0, y = 0},
-- dimen for current viewing page
page_area = Geom:new{},
-- dimen for area to dim
dim_area = nil,
2014-03-13 13:52:43 +00:00
-- has footer
footer_visible = nil,
-- has dogear
dogear_visible = false,
-- in flipping state
flipping_visible = false,
2014-03-13 13:52:43 +00:00
-- auto save settings after turning pages
auto_save_paging_count = 0,
autoSaveSettings = function()end
}
function ReaderView:init()
self.view_modules = {}
-- fix recalculate from close document pageno
self.state.page = nil
-- fix inherited dim_area for following opened documents
self:resetDimArea()
self:addWidgets()
self.emitHintPageEvent = function()
self.ui:handleEvent(Event:new("HintPage", self.hinting))
end
end
function ReaderView:resetDimArea()
self.dim_area = Geom:new{w = 0, h = 0}
end
function ReaderView:addWidgets()
2014-03-13 13:52:43 +00:00
self.dogear = ReaderDogear:new{
view = self,
ui = self.ui,
}
self.footer = ReaderFooter:new{
view = self,
ui = self.ui,
}
self.flipping = ReaderFlipping:new{
view = self,
ui = self.ui,
}
self.arrow = AlphaContainer:new{
alpha = 0.6,
ImageWidget:new{
file = "resources/icons/appbar.control.expand.png",
}
}
2014-03-13 13:52:43 +00:00
self[1] = self.dogear
self[2] = self.footer
self[3] = self.flipping
end
--[[--
Register a view UI widget module for document browsing.
@tparam string name module name, registered widget can be accessed by readerui.view.view_modules[name].
@tparam ui.widget.widget.Widget widget paintable widget, i.e. has a paintTo method.
@usage
local ImageWidget = require("ui/widget/imagewidget")
local dummy_image = ImageWidget:new{
file = "resources/icons/appbar.control.expand.png",
}
-- the image will be painted on all book pages
readerui.view:registerViewModule('dummy_image', dummy_image)
]]
function ReaderView:registerViewModule(name, widget)
if not widget.paintTo then
print(name .. " view module does not have paintTo method!")
return
end
widget.view = self
widget.ui = self.ui
self.view_modules[name] = widget
end
function ReaderView:resetLayout()
for _, widget in ipairs(self) do
widget:resetLayout()
end
for _, m in pairs(self.view_modules) do
if m.resetLayout then m:resetLayout() end
end
end
function ReaderView:paintTo(bb, x, y)
dbg:v("readerview painting", self.visible_area, "to", x, y)
2014-03-13 13:52:43 +00:00
if self.page_scroll then
self:drawPageBackground(bb, x, y)
else
self:drawPageSurround(bb, x, y)
end
-- draw page content
if self.ui.document.info.has_pages then
if self.page_scroll then
self:drawScrollPages(bb, x, y)
else
self:drawSinglePage(bb, x, y)
end
else
if self.view_mode == "page" then
self:drawPageView(bb, x, y)
elseif self.view_mode == "scroll" then
self:drawScrollView(bb, x, y)
end
end
-- dim last read area
if self.dim_area.w ~= 0 and self.dim_area.h ~= 0 then
if self.page_overlap_style == "dim" then
bb:dimRect(
self.dim_area.x, self.dim_area.y,
self.dim_area.w, self.dim_area.h
)
elseif self.page_overlap_style == "arrow" then
self.arrow:paintTo(bb, 0, self.dim_area.h)
end
2014-03-13 13:52:43 +00:00
end
-- draw saved highlight
if self.highlight_visible then
self:drawSavedHighlight(bb, x, y)
end
-- draw temporary highlight
if self.highlight.temp then
self:drawTempHighlight(bb, x, y)
end
-- paint dogear
if self.dogear_visible then
self.dogear:paintTo(bb, x, y)
end
-- paint footer
if self.footer_visible then
self.footer:paintTo(bb, x, y)
end
-- paint flipping
if self.flipping_visible then
self.flipping:paintTo(bb, x, y)
end
for _, m in pairs(self.view_modules) do
m:paintTo(bb, x, y)
end
2014-03-13 13:52:43 +00:00
-- stop activity indicator
self.ui:handleEvent(Event:new("StopActivityIndicator"))
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 :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- Most pages should not require dithering
self.dialog.dithered = nil
-- For KOpt, let the user choose.
if self.ui.document.info.has_pages then
-- Also enforce dithering in PicDocument
if self.ui.document.is_pic or self.document.configurable.hw_dithering == 1 then
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 :100: commits! :D)
2019-02-07 00:14:37 +00:00
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
--[[
Given coordinates on the screen return position in original page
]]--
function ReaderView:screenToPageTransform(pos)
2014-03-13 13:52:43 +00:00
if self.ui.document.info.has_pages then
if self.page_scroll then
return self:getScrollPagePosition(pos)
else
return self:getSinglePagePosition(pos)
end
else
pos.page = self.ui.document:getCurrentPage()
2016-02-15 09:33:48 +00:00
-- local last_y = self.ui.document:getCurrentPos()
logger.dbg("document has no pages at", pos)
2014-03-13 13:52:43 +00:00
return pos
end
end
--[[
Given rectangle in original page return rectangle on the screen
]]--
function ReaderView:pageToScreenTransform(page, rect)
2014-03-13 13:52:43 +00:00
if self.ui.document.info.has_pages then
if self.page_scroll then
return self:getScrollPageRect(page, rect)
else
return self:getSinglePageRect(rect)
end
else
return rect
end
end
--[[
Get page area on screen for a given page number
--]]
function ReaderView:getScreenPageArea(page)
if self.ui.document.info.has_pages then
local area = Geom:new{x = 0, y = 0}
if self.page_scroll then
for _, state in ipairs(self.page_states) do
if page ~= state.page then
area.y = area.y + state.visible_area.h + state.offset.y
area.y = area.y + self.page_gap.height
else
area.x = state.offset.x
area.w = state.visible_area.w
area.h = state.visible_area.h
return area
end
end
else
area.x = self.state.offset.x
area.y = self.state.offset.y
area.w = self.visible_area.w
area.h = self.visible_area.h
return area
end
else
return self.dimen
end
end
2013-03-10 06:23:26 +00:00
function ReaderView:drawPageBackground(bb, x, y)
2014-03-13 13:52:43 +00:00
bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.page_bgcolor)
2013-03-10 06:23:26 +00:00
end
function ReaderView:drawPageSurround(bb, x, y)
2014-03-13 13:52:43 +00:00
if self.dimen.h > self.visible_area.h then
bb:paintRect(x, y, self.dimen.w, self.state.offset.y, self.outer_page_color)
bb:paintRect(x, y + self.dimen.h - self.state.offset.y - 1,
self.dimen.w, self.state.offset.y + 1, self.outer_page_color)
end
if self.dimen.w > self.visible_area.w then
bb:paintRect(x, y, self.state.offset.x, self.dimen.h, self.outer_page_color)
bb:paintRect(x + self.dimen.w - self.state.offset.x - 1, y,
self.state.offset.x + 1, self.dimen.h, self.outer_page_color)
end
2013-03-10 06:23:26 +00:00
end
function ReaderView:drawScrollPages(bb, x, y)
2014-03-13 13:52:43 +00:00
local pos = Geom:new{x = x , y = y}
for page, state in ipairs(self.page_states) do
self.ui.document:drawPage(
bb,
pos.x + state.offset.x,
pos.y + state.offset.y,
state.visible_area,
state.page,
state.zoom,
state.rotation,
state.gamma,
self.render_mode)
pos.y = pos.y + state.visible_area.h
-- draw page gap if not the last part
if page ~= #self.page_states then
self:drawPageGap(bb, pos.x, pos.y)
pos.y = pos.y + self.page_gap.height
end
end
UIManager:nextTick(self.emitHintPageEvent)
2013-03-10 06:23:26 +00:00
end
2013-06-15 15:13:19 +00:00
function ReaderView:getCurrentPageList()
2014-03-13 13:52:43 +00:00
local pages = {}
if self.ui.document.info.has_pages then
if self.page_scroll then
for _, state in ipairs(self.page_states) do
table.insert(pages, state.page)
end
else
table.insert(pages, self.state.page)
end
end
return pages
2013-06-15 15:13:19 +00:00
end
function ReaderView:getScrollPagePosition(pos)
2016-02-15 09:33:48 +00:00
local x_p, y_p
2014-03-13 13:52:43 +00:00
local x_s, y_s = pos.x, pos.y
for _, state in ipairs(self.page_states) do
if y_s < state.visible_area.h + state.offset.y then
y_p = (state.visible_area.y + y_s - state.offset.y) / state.zoom
x_p = (state.visible_area.x + x_s - state.offset.x) / state.zoom
return {
x = x_p,
y = y_p,
page = state.page,
zoom = state.zoom,
rotation = state.rotation,
}
else
y_s = y_s - state.visible_area.h - self.page_gap.height
end
end
end
function ReaderView:getScrollPageRect(page, rect_p)
2014-03-13 13:52:43 +00:00
local rect_s = Geom:new{}
for _, state in ipairs(self.page_states) do
local trans_p = Geom:new(rect_p):copy()
trans_p:transformByScale(state.zoom, state.zoom)
if page == state.page and state.visible_area:intersectWith(trans_p) then
2014-03-13 13:52:43 +00:00
rect_s.x = rect_s.x + state.offset.x + trans_p.x - state.visible_area.x
rect_s.y = rect_s.y + state.offset.y + trans_p.y - state.visible_area.y
rect_s.w = trans_p.w
rect_s.h = trans_p.h
return rect_s
end
rect_s.y = rect_s.y + state.visible_area.h + self.page_gap.height
end
end
2013-03-10 06:23:26 +00:00
function ReaderView:drawPageGap(bb, x, y)
2014-03-13 13:52:43 +00:00
if self.scroll_mode == "vertical" then
bb:paintRect(x, y, self.dimen.w, self.page_gap.height, self.page_gap.color)
elseif self.scroll_mode == "horizontal" then
bb:paintRect(x, y, self.page_gap.width, self.dimen.h, self.page_gap.color)
end
2013-03-10 06:23:26 +00:00
end
function ReaderView:drawSinglePage(bb, x, y)
2014-03-13 13:52:43 +00:00
self.ui.document:drawPage(
bb,
x + self.state.offset.x,
y + self.state.offset.y,
self.visible_area,
self.state.page,
self.state.zoom,
self.state.rotation,
self.state.gamma,
self.render_mode)
UIManager:nextTick(self.emitHintPageEvent)
2013-03-10 06:23:26 +00:00
end
function ReaderView:getSinglePagePosition(pos)
2014-03-13 13:52:43 +00:00
local x_s, y_s = pos.x, pos.y
return {
x = (self.visible_area.x + x_s - self.state.offset.x) / self.state.zoom,
y = (self.visible_area.y + y_s - self.state.offset.y) / self.state.zoom,
page = self.state.page,
zoom = self.state.zoom,
rotation = self.state.rotation,
}
end
function ReaderView:getSinglePageRect(rect_p)
2014-03-13 13:52:43 +00:00
local rect_s = Geom:new{}
local trans_p = Geom:new(rect_p):copy()
trans_p:transformByScale(self.state.zoom, self.state.zoom)
if self.visible_area:intersectWith(trans_p) then
2014-03-13 13:52:43 +00:00
rect_s.x = self.state.offset.x + trans_p.x - self.visible_area.x
rect_s.y = self.state.offset.y + trans_p.y - self.visible_area.y
rect_s.w = trans_p.w
rect_s.h = trans_p.h
return rect_s
end
end
2013-03-10 06:23:26 +00:00
function ReaderView:drawPageView(bb, x, y)
2014-03-13 13:52:43 +00:00
self.ui.document:drawCurrentViewByPage(
bb,
x + self.state.offset.x,
y + self.state.offset.y,
self.visible_area,
self.state.page)
2013-03-10 06:23:26 +00:00
end
function ReaderView:drawScrollView(bb, x, y)
2014-03-13 13:52:43 +00:00
self.ui.document:drawCurrentViewByPos(
bb,
x + self.state.offset.x,
y + self.state.offset.y,
self.visible_area,
self.state.pos)
2013-03-10 06:23:26 +00:00
end
2013-06-15 15:13:19 +00:00
function ReaderView:drawTempHighlight(bb, x, y)
2014-03-13 13:52:43 +00:00
for page, boxes in pairs(self.highlight.temp) do
for i = 1, #boxes do
local rect = self:pageToScreenTransform(page, boxes[i])
if rect then
self:drawHighlightRect(bb, x, y, rect, self.highlight.temp_drawer)
end
end
end
2013-06-15 15:13:19 +00:00
end
function ReaderView:drawSavedHighlight(bb, x, y)
2014-03-13 13:52:43 +00:00
if self.ui.document.info.has_pages then
self:drawPageSavedHighlight(bb, x, y)
else
self:drawXPointerSavedHighlight(bb, x, y)
end
end
function ReaderView:drawPageSavedHighlight(bb, x, y)
2014-03-13 13:52:43 +00:00
local pages = self:getCurrentPageList()
for _, page in pairs(pages) do
local items = self.highlight.saved[page]
if not items then items = {} end
for i = 1, #items do
local item = items[i]
local pos0, pos1 = item.pos0, item.pos1
local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1)
if boxes then
for _, box in pairs(boxes) do
local rect = self:pageToScreenTransform(page, box)
if rect then
self:drawHighlightRect(bb, x, y, rect, item.drawer or self.highlight.saved_drawer)
end
end -- end for each box
end -- end if boxes
end -- end for each highlight
end -- end for each page
2013-06-15 15:13:19 +00:00
end
function ReaderView:drawXPointerSavedHighlight(bb, x, y)
-- Getting screen boxes is done for each tap on screen (changing pages,
-- showing menu...). We might want to cache these boxes per page (and
-- clear that cache when page layout change or highlights are added
-- or removed).
local cur_view_top, cur_view_bottom
2014-03-13 13:52:43 +00:00
for page, _ in pairs(self.highlight.saved) do
local items = self.highlight.saved[page]
if not items then items = {} end
for j = 1, #items do
local item = items[j]
local pos0, pos1 = item.pos0, item.pos1
-- document:getScreenBoxesFromPositions() is expensive, so we
-- first check this item is on current page
if not cur_view_top then
-- Even in page mode, it's safer to use pos and ui.dimen.h
-- than pages' xpointers pos, even if ui.dimen.h is a bit
-- larger than pages' heights
cur_view_top = self.ui.document:getCurrentPos()
if self.view_mode == "page" and self.ui.document:getVisiblePageCount() > 1 then
cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h
else
cur_view_bottom = cur_view_top + self.ui.dimen.h
end
end
local spos0 = self.ui.document:getPosFromXPointer(pos0)
local spos1 = self.ui.document:getPosFromXPointer(pos1)
local start_pos = math.min(spos0, spos1)
local end_pos = math.max(spos0, spos1)
if start_pos <= cur_view_bottom and end_pos >= cur_view_top then
local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1, true) -- get_segments=true
if boxes then
for _, box in pairs(boxes) do
local rect = self:pageToScreenTransform(page, box)
if rect then
self:drawHighlightRect(bb, x, y, rect, item.drawer or self.highlight.saved_drawer)
end
end -- end for each box
end -- end if boxes
end
2014-03-13 13:52:43 +00:00
end -- end for each highlight
end -- end for all saved highlight
end
2016-06-27 16:43:23 +00:00
function ReaderView:drawHighlightRect(bb, _x, _y, rect, drawer)
2014-03-13 13:52:43 +00:00
local x, y, w, h = rect.x, rect.y, rect.w, rect.h
2014-03-13 13:52:43 +00:00
if drawer == "underscore" then
self.highlight.line_width = self.highlight.line_width or 2
self.highlight.line_color = self.highlight.line_color or Blitbuffer.COLOR_GRAY
2014-03-13 13:52:43 +00:00
bb:paintRect(x, y+h-1, w,
self.highlight.line_width,
self.highlight.line_color)
elseif drawer == "lighten" then
bb:lightenRect(x, y, w, h, self.highlight.lighten_factor)
2014-03-13 13:52:43 +00:00
elseif drawer == "invert" then
bb:invertRect(x, y, w, h)
end
end
2013-03-10 06:23:26 +00:00
function ReaderView:getPageArea(page, zoom, rotation)
2014-03-13 13:52:43 +00:00
if self.use_bbox then
return self.ui.document:getUsedBBoxDimensions(page, zoom, rotation)
else
return self.ui.document:getPageDimensions(page, zoom, rotation)
end
2013-03-10 06:23:26 +00:00
end
2012-12-08 06:05:10 +00:00
--[[
This method is supposed to be only used by ReaderPaging
--]]
function ReaderView:recalculate()
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 :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- Start by resetting the dithering flag early, so it doesn't carry over from the previous page.
self.dialog.dithered = nil
2014-03-13 13:52:43 +00:00
if self.ui.document.info.has_pages and self.state.page then
self.page_area = self:getPageArea(
self.state.page,
self.state.zoom,
self.state.rotation)
-- reset our size
self.visible_area:setSizeTo(self.dimen)
if self.ui.document.configurable.writing_direction == 0 then
-- starts from left top of page_area
self.visible_area.x = self.page_area.x
self.visible_area.y = self.page_area.y
else
-- start from right top of page_area
self.visible_area.x = self.page_area.x + self.page_area.w - self.visible_area.w
self.visible_area.y = self.page_area.y
end
2014-03-13 13:52:43 +00:00
-- and recalculate it according to page size
self.visible_area:offsetWithin(self.page_area, 0, 0)
-- clear dim area
self.dim_area.w = 0
self.dim_area.h = 0
self.ui:handleEvent(
Event:new("ViewRecalculate", self.visible_area, self.page_area))
else
self.visible_area:setSizeTo(self.dimen)
end
self.state.offset = Geom:new{x = 0, y = 0}
if self.dimen.h > self.visible_area.h then
self.state.offset.y = (self.dimen.h - self.visible_area.h) / 2
end
if self.dimen.w > self.visible_area.w then
self.state.offset.x = (self.dimen.w - self.visible_area.w) / 2
end
-- flag a repaint so self:paintTo will be called
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 :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- 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
function ReaderView:PanningUpdate(dx, dy)
logger.dbg("pan by", dx, dy)
2014-03-13 13:52:43 +00:00
local old = self.visible_area:copy()
self.visible_area:offsetWithin(self.page_area, dx, dy)
if self.visible_area ~= old then
-- flag a repaint
UIManager:setDirty(self.dialog, "partial")
logger.dbg("on pan: page_area", self.page_area)
logger.dbg("on pan: visible_area", self.visible_area)
2014-03-13 13:52:43 +00:00
self.ui:handleEvent(
Event:new("ViewRecalculate", self.visible_area, self.page_area))
end
return true
end
function ReaderView:PanningStart(x, y)
logger.dbg("panning start", x, y)
2014-03-13 13:52:43 +00:00
if not self.panning_visible_area then
self.panning_visible_area = self.visible_area:copy()
end
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")
end
function ReaderView:PanningStop()
2014-03-13 13:52:43 +00:00
self.panning_visible_area = nil
end
function ReaderView:SetZoomCenter(x, y)
2014-03-13 13:52:43 +00:00
local old = self.visible_area:copy()
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, "partial")
2014-03-13 13:52:43 +00:00
end
end
function ReaderView:getViewContext()
if self.page_scroll then
return self.page_states
else
return {
{
page = self.state.page,
pos = self.state.pos,
zoom = self.state.zoom,
rotation = self.state.rotation,
gamma = self.state.gamma,
offset = self.state.offset:copy(),
bbox = self.state.bbox,
},
self.visible_area:copy(),
self.page_area:copy(),
}
end
end
function ReaderView:restoreViewContext(ctx)
if self.page_scroll then
self.page_states = ctx
else
self.state = ctx[1]
self.visible_area = ctx[2]
self.page_area = ctx[3]
end
end
-- NOTE: This is just a shim for koptoptions, because we want to be able to pass an optional second argument to SetScreenMode...
-- This is also used as a sink for gsensor input events, because we can only send a single event per input,
-- and we need to cover both CRe & KOpt...
function ReaderView:onSwapScreenMode(new_mode, rotation)
-- Don't do anything if an explicit rotation was requested, but it hasn't actually changed,
-- because we may be sending this event *right before* a ChangeScreenMode in CRe (gyro)
if rotation ~= nil and rotation ~= true and rotation == Screen:getRotationMode() then
return true
end
-- CRe
self.ui:handleEvent(Event:new("ChangeScreenMode", new_mode, rotation or true))
-- KOpt (On CRe, since it's redundant (RR:onChangeScreenMode already sends one), this'll get discarded early)
self.ui:handleEvent(Event:new("SetScreenMode", new_mode, rotation or true))
end
function ReaderView:onSetScreenMode(new_mode, rotation, noskip)
-- Don't do anything if an explicit rotation was requested, but it hasn't actually changed,
-- because we may be sending this event *right after* a ChangeScreenMode in CRe (gsensor)
-- We only want to let the onReadSettings one go through, otherwise the testsuite blows up...
if noskip == nil and rotation ~= nil and rotation ~= true and rotation == Screen:getRotationMode() then
return true
end
2014-03-13 13:52:43 +00:00
if new_mode == "landscape" or new_mode == "portrait" then
self.screen_mode = new_mode
-- NOTE: Hacky hack! If rotation is "true", that's actually an "interactive" flag for setScreenMode
--- @fixme That's because we can't store nils in a table, which is what Event:new attempts to do ;).
-- c.f., <https://stackoverflow.com/q/7183998/> & <http://lua-users.org/wiki/VarargTheSecondClassCitizen>
-- With a fixed Event implementation, we'd instead stick "interactive" in a third argument,
-- which we could happily pass while still keeping rotation nil ;).
if rotation ~= nil and rotation ~= true then
2014-03-13 13:52:43 +00:00
Screen:setRotationMode(rotation)
else
Screen:setScreenMode(new_mode, rotation)
2014-03-13 13:52:43 +00:00
end
UIManager:setDirty(self.dialog, "full")
local new_screen_size = Screen:getSize()
self.ui:handleEvent(Event:new("SetDimensions", new_screen_size))
self.ui:onScreenResize(new_screen_size)
2014-03-13 13:52:43 +00:00
self.ui:handleEvent(Event:new("InitScrollPageStates"))
end
self.cur_rotation_mode = Screen:getRotationMode()
2014-03-13 13:52:43 +00:00
return true
end
2012-06-12 16:14:23 +00:00
function ReaderView:onSetDimensions(dimensions)
2014-03-13 13:52:43 +00:00
self:resetLayout()
self.dimen = dimensions
-- recalculate view
self:recalculate()
end
function ReaderView:onRestoreDimensions(dimensions)
2014-03-13 13:52:43 +00:00
self:resetLayout()
self.dimen = dimensions
-- recalculate view
self:recalculate()
2012-06-12 16:14:23 +00:00
end
function ReaderView:onSetFullScreen(full_screen)
2014-03-13 13:52:43 +00:00
self.footer_visible = not full_screen
self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize()))
end
function ReaderView:onSetScrollMode(page_scroll)
if self.ui.document.info.has_pages and page_scroll and self.ui.zooming.paged_modes[self.zoom_mode] then
UIManager:show(InfoMessage:new{
text = _([[
Continuous view (scroll mode) works best with zoom to page width or zoom to content width.
In combination with zoom to fit page, page height, content height or content, continuous view can cause unexpected shifts when turning pages.]]),
timeout = 5,
})
end
2014-03-13 13:52:43 +00:00
self.page_scroll = page_scroll
self:recalculate()
self.ui:handleEvent(Event:new("InitScrollPageStates"))
2013-03-10 06:23:26 +00:00
end
function ReaderView:onReadSettings(config)
local screen_mode
2014-03-13 13:52:43 +00:00
self.render_mode = config:readSetting("render_mode") or 0
if self.ui.document.info.has_pages then
screen_mode = config:readSetting("screen_mode") or G_reader_settings:readSetting("kopt_screen_mode") or "portrait"
else
screen_mode = config:readSetting("screen_mode") or G_reader_settings:readSetting("copt_screen_mode") or "portrait"
end
2014-03-13 13:52:43 +00:00
if screen_mode then
Screen:setScreenMode(screen_mode)
self:onSetScreenMode(screen_mode, config:readSetting("rotation_mode"), true)
2014-03-13 13:52:43 +00:00
end
self.state.gamma = config:readSetting("gamma") or DGLOBALGAMMA
local full_screen = config:readSetting("kopt_full_screen") or self.document.configurable.full_screen
if full_screen == 0 then
self.footer_visible = false
end
2014-03-13 13:52:43 +00:00
self:resetLayout()
local page_scroll = config:readSetting("kopt_page_scroll") or self.document.configurable.page_scroll
self.page_scroll = page_scroll == 1 and true or false
self.highlight.saved = config:readSetting("highlight") or {}
self.page_overlap_style = config:readSetting("page_overlap_style") or G_reader_settings:readSetting("page_overlap_style") or "dim"
end
function ReaderView:onPageUpdate(new_page_no)
2014-03-13 13:52:43 +00:00
self.state.page = new_page_no
self:recalculate()
self.highlight.temp = {}
UIManager:nextTick(self.autoSaveSettings)
end
2012-06-05 07:23:36 +00:00
function ReaderView:onPosUpdate(new_pos)
2014-03-13 13:52:43 +00:00
self.state.pos = new_pos
self:recalculate()
self.highlight.temp = {}
UIManager:nextTick(self.autoSaveSettings)
2012-06-05 07:23:36 +00:00
end
function ReaderView:onZoomUpdate(zoom)
2014-03-13 13:52:43 +00:00
self.state.zoom = zoom
self:recalculate()
self.highlight.temp = {}
end
2012-12-02 09:09:32 +00:00
function ReaderView:onBBoxUpdate(bbox)
2014-03-13 13:52:43 +00:00
self.use_bbox = bbox and true or false
2012-12-02 09:09:32 +00:00
end
function ReaderView:onRotationUpdate(rotation)
2014-03-13 13:52:43 +00:00
self.state.rotation = rotation
self:recalculate()
end
function ReaderView:onGammaUpdate(gamma)
2014-03-13 13:52:43 +00:00
self.state.gamma = gamma
if self.page_scroll then
self.ui:handleEvent(Event:new("UpdateScrollPageGamma", gamma))
end
end
function ReaderView:onFontSizeUpdate(font_size)
self.ui:handleEvent(Event:new("ReZoom", font_size))
end
2013-04-14 07:16:42 +00:00
function ReaderView:onDefectSizeUpdate()
2014-03-13 13:52:43 +00:00
self.ui:handleEvent(Event:new("ReZoom"))
2013-04-14 07:16:42 +00:00
end
function ReaderView:onPageCrop()
2014-03-13 13:52:43 +00:00
self.ui:handleEvent(Event:new("ReZoom"))
2013-04-14 07:16:42 +00:00
end
function ReaderView:onMarginUpdate()
2014-03-13 13:52:43 +00:00
self.ui:handleEvent(Event:new("ReZoom"))
2013-04-14 07:16:42 +00:00
end
function ReaderView:onSetViewMode(new_mode)
if new_mode ~= self.view_mode then
self.view_mode = new_mode
self.ui.document:setViewMode(new_mode)
self.ui:handleEvent(Event:new("ChangeViewMode"))
end
2014-03-13 13:52:43 +00:00
return true
end
2013-12-27 15:18:16 +00:00
function ReaderView:onSaveSettings()
2014-03-13 13:52:43 +00:00
self.ui.doc_settings:saveSetting("render_mode", self.render_mode)
self.ui.doc_settings:saveSetting("screen_mode", self.screen_mode)
self.ui.doc_settings:saveSetting("rotation_mode", self.cur_rotation_mode)
self.ui.doc_settings:saveSetting("gamma", self.state.gamma)
self.ui.doc_settings:saveSetting("highlight", self.highlight.saved)
self.ui.doc_settings:saveSetting("page_overlap_style", self.page_overlap_style)
end
2013-10-18 20:38:07 +00:00
function ReaderView:getRenderModeMenuTable()
local view = self
local function make_mode(text, mode)
return {
text = text,
checked_func = function() return view.render_mode == mode end,
callback = function() view.render_mode = mode end,
}
end
return {
-- @translators Selects which layers of the DjVu image should be rendered. Valid rendering modes are color, black, mask, foreground, and background. See http://djvu.sourceforge.net/ and https://en.wikipedia.org/wiki/DjVu for more information about the format.
text = _("DjVu render mode"),
sub_item_table = {
make_mode(_("COLOUR (works for both colour and b&w pages)"), 0),
make_mode(_("BLACK & WHITE (for b&w pages only, much faster)"), 1),
make_mode(_("COLOUR ONLY (slightly faster than COLOUR)"), 2),
make_mode(_("MASK ONLY (for b&w pages only)"), 3),
make_mode(_("COLOUR BACKGROUND (show only background)"), 4),
make_mode(_("COLOUR FOREGROUND (show only foreground)"), 5),
}
}
end
local page_overlap_styles = {
arrow = _("Arrow"),
dim = _("Gray out"),
}
function ReaderView:genOverlapStyleMenu(overlap_enabled_func)
local view = self
local get_overlap_style = function(style)
return {
text = page_overlap_styles[style],
enabled_func = overlap_enabled_func,
checked_func = function()
return view.page_overlap_style == style
end,
callback = function()
view.page_overlap_style = style
end,
hold_callback = function()
UIManager:show(ConfirmBox:new{
text = T(
_("Set default overlap style to %1?"),
style
),
ok_callback = function()
view.page_overlap_style = style
G_reader_settings:saveSetting("page_overlap_style", style)
end,
})
end,
}
end
return {
get_overlap_style("arrow"),
get_overlap_style("dim"),
}
end
function ReaderView:onCloseDocument()
self.hinting = false
-- stop any in fly HintPage event
UIManager:unschedule(self.emitHintPageEvent)
end
function ReaderView:onReaderReady()
if DAUTO_SAVE_PAGING_COUNT ~= nil then
if DAUTO_SAVE_PAGING_COUNT <= 0 then
self.autoSaveSettings = function()
self.ui:saveSettings()
end
else
self.autoSaveSettings = function()
if self.auto_save_paging_count == DAUTO_SAVE_PAGING_COUNT then
self.ui:saveSettings()
self.auto_save_paging_count = 0
else
self.auto_save_paging_count = self.auto_save_paging_count + 1
end
end
end
end
end
2013-10-18 20:38:07 +00:00
return ReaderView