From 63e5e7e577a0fb18b27f0b50400e38ef650b3a71 Mon Sep 17 00:00:00 2001 From: chrox Date: Sun, 10 Mar 2013 14:23:26 +0800 Subject: [PATCH] add scroll mode for pdf/djvu reader --- frontend/document/koptinterface.lua | 9 ++ frontend/ui/reader/readercropping.lua | 6 + frontend/ui/reader/readerkopt.lua | 5 + frontend/ui/reader/readerpaging.lua | 184 +++++++++++++++++++++-- frontend/ui/reader/readerview.lua | 203 ++++++++++++++++++-------- frontend/ui/reader/readerzooming.lua | 1 + 6 files changed, 337 insertions(+), 71 deletions(-) diff --git a/frontend/document/koptinterface.lua b/frontend/document/koptinterface.lua index ec887959d..0bb4b1658 100644 --- a/frontend/document/koptinterface.lua +++ b/frontend/document/koptinterface.lua @@ -47,6 +47,15 @@ KoptOptions = { event = "SetFullScreen", args = {true, false}, }, + { + name = "page_scroll", + name_text = "Scroll Mode", + toggle = {"on", "off"}, + values = {1, 0}, + default_value = 1, + event = "ToggleScrollMode", + args = {true, false}, + }, { name = "page_margin", name_text = "Page Margin", diff --git a/frontend/ui/reader/readercropping.lua b/frontend/ui/reader/readercropping.lua index 58b7f8936..0dd9fd571 100644 --- a/frontend/ui/reader/readercropping.lua +++ b/frontend/ui/reader/readercropping.lua @@ -60,6 +60,9 @@ function ReaderCropping:onPageCrop(mode) self.view.outer_page_color = 7 -- gray bgcolor -- backup original zoom mode as cropping use "page" zoom mode self.orig_zoom_mode = self.view.zoom_mode + -- backup original page scroll + self.orig_page_scroll = self.view.page_scroll + self.view.page_scroll = false -- backup original reflow mode as cropping use non-reflow mode self.orig_reflow_mode = self.document.configurable.text_wrap if self.orig_reflow_mode == 1 then @@ -107,6 +110,8 @@ function ReaderCropping:onCancelPageCrop() end function ReaderCropping:exitPageCrop(confirmed) + -- restore page scroll + self.view.page_scroll = self.orig_page_scroll -- restore view bgcolor self.view.outer_page_color = self.orig_view_bgcolor -- restore view dimens @@ -123,6 +128,7 @@ function ReaderCropping:exitPageCrop(confirmed) if confirmed then -- if original zoom mode is not "content", set zoom mode to "content" self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode:find("content") and self.orig_zoom_mode or "content")) + self.ui:handleEvent(Event:new("InitScrollPageStates")) else self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode)) end diff --git a/frontend/ui/reader/readerkopt.lua b/frontend/ui/reader/readerkopt.lua index aeffd6e17..d7269fb84 100644 --- a/frontend/ui/reader/readerkopt.lua +++ b/frontend/ui/reader/readerkopt.lua @@ -32,3 +32,8 @@ function ReaderKoptListener:onSetZoomMode(zoom_mode, orig) self.normal_zoom_mode = zoom_mode self:setZoomMode(self.normal_zoom_mode) end + +function ReaderKoptListener:onSetDimensions(dimensions) + -- called later than reader zooming + self.ui:handleEvent(Event:new("InitScrollPageStates")) +end \ No newline at end of file diff --git a/frontend/ui/reader/readerpaging.lua b/frontend/ui/reader/readerpaging.lua index 6c7bba0c3..52cc2d0bc 100644 --- a/frontend/ui/reader/readerpaging.lua +++ b/frontend/ui/reader/readerpaging.lua @@ -6,7 +6,7 @@ ReaderPaging = InputContainer:new{ visible_area = nil, page_area = nil, show_overlap_enable = true, - overlap = 20, + overlap = 20 * Screen:getDPI()/167, } function ReaderPaging:init() @@ -117,12 +117,12 @@ function ReaderPaging:onCloseDocument() end function ReaderPaging:onTapForward() - self:onGotoPageRel(1) + self:onPagingRel(1) return true end function ReaderPaging:onTapBackward() - self:onGotoPageRel(-1) + self:onPagingRel(-1) return true end @@ -135,9 +135,9 @@ end function ReaderPaging:onSwipe(arg, ges) if self.flipping_page == nil then if ges.direction == "left" or ges.direction == "up" then - self:onGotoPageRel(1) + self:onPagingRel(1) elseif ges.direction == "right" or ges.direction == "down" then - self:onGotoPageRel(-1) + self:onPagingRel(-1) end elseif self.flipping_page then self:gotoPage(self.flipping_page) @@ -172,8 +172,9 @@ function ReaderPaging:onZoomModeUpdate(new_mode) self.zoom_mode = new_mode end -function ReaderPaging:onPageUpdate(new_page_no) +function ReaderPaging:onPageUpdate(new_page_no, orig) self.current_page = new_page_no + self.ui:handleEvent(Event:new("InitScrollPageStates", orig)) end function ReaderPaging:onViewRecalculate(visible_area, page_area) @@ -193,6 +194,172 @@ function ReaderPaging:onGotoPercent(percent) return true end +function ReaderPaging:onPagingRel(diff) + if self.view.page_scroll then + self:onScrollPageRel(diff) + else + self:onGotoPageRel(diff) + end + return true +end + +function ReaderPaging:onInitScrollPageStates(orig) + DEBUG("init scroll page states") + if orig == "scrolling" then return true end + if self.view.page_scroll then + self.view.page_states = {} + local blank_area = Geom:new{} + blank_area:setSizeTo(self.view.dimen) + while blank_area.h > 0 do + local state = self:getNextPageState(blank_area, Geom:new{}) + --DEBUG("init new state", state) + table.insert(self.view.page_states, state) + if blank_area.h > 0 then + blank_area.h = blank_area.h - self.view.page_gap.height + end + if blank_area.h > 0 then + self:gotoPage(self.current_page + 1, "scrolling") + end + end + end + return true +end + +function ReaderPaging:onUpdateScrollPageRotation(rotation) + for _, state in ipairs(self.view.page_states) do + state.rotation = rotation + end + return true +end + +function ReaderPaging:onUpdateScrollPageGamma(gamma) + for _, state in ipairs(self.view.page_states) do + state.gamma = gamma + end + return true +end + +function ReaderPaging:getNextPageState(blank_area, offset) + local page_area = self.view:getPageArea( + self.view.state.page, + self.view.state.zoom, + self.view.state.rotation) + local visible_area = Geom:new{x = 0, y = 0} + visible_area.w, visible_area.h = blank_area.w, blank_area.h + visible_area.x, visible_area.y = page_area.x, page_area.y + visible_area = visible_area:shrinkInside(page_area, offset.x, offset.y) + -- shrink blank area by the height of visible area + blank_area.h = blank_area.h - visible_area.h + return { + page = self.view.state.page, + zoom = self.view.state.zoom, + rotation = self.view.state.rotation, + gamma = self.view.state.gamma, + offset = Geom:new{ x = self.view.state.offset.x, y = 0}, + visible_area = visible_area, + page_area = page_area, + } +end + +function ReaderPaging:getPrevPageState(blank_area, offset) + local page_area = self.view:getPageArea( + self.view.state.page, + self.view.state.zoom, + self.view.state.rotation) + local visible_area = Geom:new{x = 0, y = 0} + visible_area.w, visible_area.h = blank_area.w, blank_area.h + visible_area.x = page_area.x + visible_area.y = page_area.y + page_area.h - visible_area.h + visible_area = visible_area:shrinkInside(page_area, offset.x, offset.y) + -- shrink blank area by the height of visible area + blank_area.h = blank_area.h - visible_area.h + return { + page = self.view.state.page, + zoom = self.view.state.zoom, + rotation = self.view.state.rotation, + gamma = self.view.state.gamma, + offset = Geom:new{ x = self.view.state.offset.x, y = 0}, + visible_area = visible_area, + page_area = page_area, + } +end + +function ReaderPaging:updateLastPageState(state, blank_area, offset) + local visible_area = Geom:new{x = 0, y = 0} + visible_area.w, visible_area.h = blank_area.w, blank_area.h + visible_area.x, visible_area.y = state.visible_area.x, state.visible_area.y + visible_area = visible_area:shrinkInside(state.page_area, offset.x, offset.y) + -- shrink blank area by the height of visible area + blank_area.h = blank_area.h - visible_area.h + state.visible_area = visible_area + return state +end + +function ReaderPaging:updateFirstPageState(state, blank_area, offset) + local visible_area = Geom:new{x = 0, y = 0} + visible_area.w, visible_area.h = blank_area.w, blank_area.h + visible_area.x = state.page_area.x + visible_area.y = state.visible_area.y + state.visible_area.h - visible_area.h + visible_area = visible_area:shrinkInside(state.page_area, offset.x, offset.y) + -- shrink blank area by the height of visible area + blank_area.h = blank_area.h - visible_area.h + state.visible_area = visible_area + return state +end + +function ReaderPaging:onScrollPageRel(diff) + DEBUG("scroll relative page:", diff) + local blank_area = Geom:new{} + blank_area:setSizeTo(self.view.dimen) + if diff > 0 then + local last_page_state = table.remove(self.view.page_states) + local offset = Geom:new{ + x = 0, + y = last_page_state.visible_area.h - self.overlap + } + local state = self:updateLastPageState(last_page_state, blank_area, offset) + --DEBUG("updated state", state) + self.view.page_states = {} + if state.visible_area.h > 0 then + table.insert(self.view.page_states, state) + end + --DEBUG("blank area", blank_area) + while blank_area.h > 0 do + blank_area.h = blank_area.h - self.view.page_gap.height + if blank_area.h > 0 then + self:gotoPage(state.page + 1, "scrolling") + local state = self:getNextPageState(blank_area, Geom:new{}) + --DEBUG("new state", state) + table.insert(self.view.page_states, state) + end + end + end + if diff < 0 then + local first_page_state = table.remove(self.view.page_states, 1) + local offset = Geom:new{ + x = 0, + y = -first_page_state.visible_area.h + self.overlap + } + local state = self:updateFirstPageState(first_page_state, blank_area, offset) + --DEBUG("updated state", state) + self.view.page_states = {} + if state.visible_area.h > 0 then + table.insert(self.view.page_states, state) + end + --DEBUG("blank area", blank_area) + while blank_area.h > 0 do + blank_area.h = blank_area.h - self.view.page_gap.height + if blank_area.h > 0 then + self:gotoPage(state.page - 1, "scrolling") + local state = self:getPrevPageState(blank_area, Geom:new{}) + --DEBUG("new state", state) + table.insert(self.view.page_states, 1, state) + end + end + end + UIManager:setDirty(self.view.dialog) +end + function ReaderPaging:onGotoPageRel(diff) DEBUG("goto relative page:", diff) local new_va = self.visible_area:copy() @@ -300,7 +467,7 @@ function ReaderPaging:onSetDimensions() end -- wrapper for bounds checking -function ReaderPaging:gotoPage(number) +function ReaderPaging:gotoPage(number, orig) if number == self.current_page then return true end @@ -312,8 +479,7 @@ function ReaderPaging:gotoPage(number) DEBUG("going to page number", number) -- this is an event to allow other controllers to be aware of this change - self.ui:handleEvent(Event:new("PageUpdate", number)) - + self.ui:handleEvent(Event:new("PageUpdate", number, orig)) return true end diff --git a/frontend/ui/reader/readerview.lua b/frontend/ui/reader/readerview.lua index 8cc5f2110..9c208f360 100644 --- a/frontend/ui/reader/readerview.lua +++ b/frontend/ui/reader/readerview.lua @@ -5,7 +5,8 @@ require "ui/reader/readerdogear" ReaderView = OverlapGroup:new{ _name = "ReaderView", document = nil, - + + -- single page state state = { page = 0, pos = 0, @@ -16,6 +17,16 @@ ReaderView = OverlapGroup:new{ bbox = nil, }, outer_page_color = 0, + -- PDF/DjVu continuous paging + page_scroll = nil, + page_bgcolor = 0, + page_states = {}, + scroll_mode = "vertical", + page_gap = { + width = 8 * Screen:getDPI()/167, + height = 8 * Screen:getDPI()/167, + color = 8, + }, -- DjVu page rendering mode (used in djvu.c:drawPage()) render_mode = 0, -- default to COLOR -- Crengine view mode @@ -56,50 +67,27 @@ end function ReaderView:paintTo(bb, x, y) DEBUG("painting", self.visible_area, "to", x, y) - local inner_offset = Geom:new{x = 0, y = 0} - - -- draw surrounding space, if any - if self.dimen.h > self.visible_area.h then - inner_offset.y = (self.dimen.h - self.visible_area.h) / 2 - bb:paintRect(x, y, self.dimen.w, inner_offset.y, self.outer_page_color) - bb:paintRect(x, y + self.dimen.h - inner_offset.y - 1, self.dimen.w, inner_offset.y + 1, self.outer_page_color) - end - if self.dimen.w > self.visible_area.w then - inner_offset.x = (self.dimen.w - self.visible_area.w) / 2 - bb:paintRect(x, y, inner_offset.x, self.dimen.h, self.outer_page_color) - bb:paintRect(x + self.dimen.w - inner_offset.x - 1, y, inner_offset.x + 1, self.dimen.h, self.outer_page_color) + if self.page_scroll then + self:drawPageBackground(bb, x, y) + else + self:drawPageSurround(bb, x, y) end - self.state.offset = inner_offset - -- draw content + + -- draw page content if self.ui.document.info.has_pages then - self.ui.document:drawPage( - bb, - x + inner_offset.x, - y + inner_offset.y, - self.visible_area, - self.state.page, - self.state.zoom, - self.state.rotation, - self.state.gamma, - self.render_mode) - UIManager:scheduleIn(0, function() self.ui:handleEvent(Event:new("HintPage")) end) + 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.ui.document:drawCurrentViewByPage( - bb, - x + inner_offset.x, - y + inner_offset.y, - self.visible_area, - self.state.page) - else - self.ui.document:drawCurrentViewByPos( - bb, - x + inner_offset.x, - y + inner_offset.y, - self.visible_area, - self.state.pos) + self:drawPageView(bb, x, y) + elseif self.view_mode == "scroll" then + self:drawScrollView(bb, x, y) end end + -- dim last read area if self.document.view_mode ~= "page" and self.dim_area.w ~= 0 and self.dim_area.h ~= 0 then @@ -108,6 +96,7 @@ function ReaderView:paintTo(bb, x, y) self.dim_area.w, self.dim_area.h ) end + -- paint dogear if self.dogear_visible then self.dogear:paintTo(bb, x, y) @@ -122,23 +111,104 @@ function ReaderView:paintTo(bb, x, y) end end +function ReaderView:drawPageBackground(bb, x, y) + bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.page_bgcolor) +end + +function ReaderView:drawPageSurround(bb, x, y) + 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 +end + +function ReaderView:drawScrollPages(bb, x, y) + 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:scheduleIn(0, function() self.ui:handleEvent(Event:new("HintPage")) end) +end + +function ReaderView:drawPageGap(bb, x, y) + 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 +end + +function ReaderView:drawSinglePage(bb, x, y) + 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:scheduleIn(0, function() self.ui:handleEvent(Event:new("HintPage")) end) +end + +function ReaderView:drawPageView(bb, x, y) + self.ui.document:drawCurrentViewByPage( + bb, + x + self.state.offset.x, + y + self.state.offset.y, + self.visible_area, + self.state.page) +end + +function ReaderView:drawScrollView(bb, x, y) + self.ui.document:drawCurrentViewByPos( + bb, + x + self.state.offset.x, + y + self.state.offset.y, + self.visible_area, + self.state.pos) +end + +function ReaderView:getPageArea(page, zoom, rotation) + if self.use_bbox then + return self.ui.document:getUsedBBoxDimensions(page, zoom, rotation) + else + return self.ui.document:getPageDimensions(page, zoom, rotation) + end +end + --[[ This method is supposed to be only used by ReaderPaging --]] function ReaderView:recalculate() local page_size = nil if self.ui.document.info.has_pages then - if not self.bbox then - self.page_area = self.ui.document:getPageDimensions( - self.state.page, - self.state.zoom, - self.state.rotation) - else - self.page_area = self.ui.document:getUsedBBoxDimensions( - self.state.page, - self.state.zoom, - self.state.rotation) - end + self.page_area = self:getPageArea( + self.state.page, + self.state.zoom, + self.state.rotation) -- starts from left top of page_area self.visible_area.x = self.page_area.x self.visible_area.y = self.page_area.y @@ -149,6 +219,13 @@ function ReaderView:recalculate() -- clear dim area self.dim_area.w = 0 self.dim_area.h = 0 + 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 self.ui:handleEvent( Event:new("ViewRecalculate", self.visible_area, self.page_area)) else @@ -182,6 +259,7 @@ function ReaderView:onSetScreenMode(new_mode) if new_mode == "landscape" and self.document.info.has_pages then self.ui:handleEvent(Event:new("SetZoomMode", "contentwidth")) + self.ui:handleEvent(Event:new("InitScrollPageStates")) end return true end @@ -219,6 +297,14 @@ function ReaderView:onSetFullScreen(full_screen) self:onSetDimensions(Screen:getSize()) end +function ReaderView:onToggleScrollMode(page_scroll) + self.page_scroll = page_scroll + self:recalculate() + if self.page_scroll then + self.ui:handleEvent(Event:new("InitScrollPageStates")) + end +end + function ReaderView:onReadSettings(config) self.render_mode = config:readSetting("render_mode") or 0 local screen_mode = config:readSetting("screen_mode") @@ -235,6 +321,8 @@ function ReaderView:onReadSettings(config) self.footer_visible = full_screen == 0 and true or false end self:resetLayout() + local page_scroll = config:readSetting("kopt_page_scroll") + self.page_scroll = (page_scroll == nil or page_scroll == 1) and true or false end function ReaderView:onPageUpdate(new_page_no) @@ -253,7 +341,7 @@ function ReaderView:onZoomUpdate(zoom) end function ReaderView:onBBoxUpdate(bbox) - self.bbox = bbox + self.use_bbox = bbox and true or false end function ReaderView:onRotationUpdate(rotation) @@ -263,18 +351,9 @@ end function ReaderView:onGammaUpdate(gamma) self.state.gamma = gamma -end - -function ReaderView:onHintPage() - if self.state.page < self.ui.document.info.number_of_pages then - self.ui.document:hintPage( - self.state.page+1, - self.state.zoom, - self.state.rotation, - self.state.gamma, - self.render_mode) + if self.page_scroll then + self.ui:handleEvent(Event:new("UpdateScrollPageGamma", gamma)) end - return true end function ReaderView:onSetViewMode(new_mode) diff --git a/frontend/ui/reader/readerzooming.lua b/frontend/ui/reader/readerzooming.lua index 936608af6..3528c5ac2 100644 --- a/frontend/ui/reader/readerzooming.lua +++ b/frontend/ui/reader/readerzooming.lua @@ -174,6 +174,7 @@ end function ReaderZooming:genSetZoomModeCallBack(mode) return function() self.ui:handleEvent(Event:new("SetZoomMode", mode)) + self.ui:handleEvent(Event:new("InitScrollPageStates")) end end