diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index c751ff5bc..10871421b 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -10,6 +10,7 @@ local UIManager = require("ui/uimanager") local logger = require("logger") local _ = require("gettext") local T = require("ffi/util").template +local Screen = Device.screen local ReaderHighlight = InputContainer:new{} @@ -140,6 +141,11 @@ function ReaderHighlight:clear(clear_id) else self.ui.document:clearSelection() end + if self.restore_page_mode_func then + self.restore_page_mode_func() + self.restore_page_mode_func = nil + end + self.selected_text_start_xpointer = nil if self.hold_pos then self.hold_pos = nil self.selected_text = nil @@ -336,8 +342,62 @@ function ReaderHighlight:onHoldPan(_, ges) self.holdpan_pos = self.view:screenToPageTransform(ges.pos) logger.dbg("holdpan position in page", self.holdpan_pos) + + if not self.ui.document.info.has_pages and self.selected_text_start_xpointer then + -- With CreDocuments, allow text selection across multiple pages + -- by (temporarily) switching to scroll mode when panning to the + -- top left or bottom right corners. + local is_in_top_left_corner = self.holdpan_pos.y < 1/8*Screen:getHeight() + and self.holdpan_pos.x < 1/8*Screen:getWidth() + local is_in_bottom_right_corner = self.holdpan_pos.y > 7/8*Screen:getHeight() + and self.holdpan_pos.x > 7/8*Screen:getWidth() + if is_in_top_left_corner or is_in_bottom_right_corner then + if self.was_in_some_corner then + -- Do nothing, wait for the user to move his finger out of that corner + return true + end + self.was_in_some_corner = true + -- We'll adjust hold_pos.y after the mode switch and the scroll + -- so it's accurate in the new screen coordinates + local orig_y = self.ui.document:getScreenPositionFromXPointer(self.selected_text_start_xpointer) + if self.view.view_mode ~= "scroll" then + -- Switch from page mode to scroll mode + local restore_page_mode_xpointer = self.ui.document:getXPointer() -- top of current page + self.restore_page_mode_func = function() + self.ui:handleEvent(Event:new("SetViewMode", "page")) + self.ui.rolling:onGotoXPointer(restore_page_mode_xpointer, self.selected_text_start_xpointer) + end + self.ui:handleEvent(Event:new("SetViewMode", "scroll")) + end + -- (using rolling:onGotoViewRel(1/3) has some strange side effects) + local scroll_distance = math.floor(Screen:getHeight() * 1/3) + local move_y = is_in_bottom_right_corner and scroll_distance or -scroll_distance + self.ui.rolling:_gotoPos(self.ui.document:getCurrentPos() + move_y) + local new_y = self.ui.document:getScreenPositionFromXPointer(self.selected_text_start_xpointer) + self.hold_pos.y = self.hold_pos.y - orig_y + new_y + UIManager:setDirty(self.dialog, "ui") + return true + else + self.was_in_some_corner = nil + end + end + 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 self.selected_text.pos0 then + -- Remember original highlight start position, so we can show + -- a marker when back from scroll mode if that switch happened. + -- (getTextFromPositions() does order pos0 and pos1, so it's not + -- certain pos0 is where we started from - we get the one from the + -- first pan, which is hopefully small enough to not span too + -- much height, so the marker points to the start position if the + -- user later pans backward) + if not self.selected_text_start_xpointer then + self.selected_text_start_xpointer = self.selected_text.pos0 + end + end + if self.selected_text and old_text and old_text == self.selected_text.text then -- no modification return