Paged documents: rework zoom options (#6885)

- Move zoom options from top menu to bottom config
- Add option to manually define zoom (relative to
  page width) and overlap (in percent)
- Add options to zoom to columns or rows, possibly
  with overlap. Add panning direction options when
  page forward in these modes
This commit is contained in:
jperon
2020-11-28 17:18:57 +01:00
committed by GitHub
parent 52f66a89d2
commit 8eeb010dc9
37 changed files with 2189 additions and 232 deletions

View File

@@ -6,8 +6,10 @@ local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local SpinWidget = require("ui/widget/spinwidget")
local UIManager = require("ui/uimanager")
local logger = require("logger")
local util = require("util")
local _ = require("gettext")
local Input = Device.input
local Screen = Device.screen
@@ -15,9 +17,35 @@ local T = require("ffi/util").template
local ReaderZooming = InputContainer:new{
zoom = 1.0,
available_zoom_modes = {
"page",
"pagewidth",
"pageheight",
"content",
"contentwidth",
"contentheight",
"columns",
"rows",
"manual",
},
-- default to nil so we can trigger ZoomModeUpdate events on start up
zoom_mode = nil,
DEFAULT_ZOOM_MODE = "pagewidth",
-- for pan mode: fit to width/zoom_factor,
-- with overlap of zoom_overlap_h % (horizontally)
-- and zoom_overlap_v % (vertically).
zoom_factor = 2,
zoom_pan_settings = {
"zoom_factor",
"zoom_overlap_h",
"zoom_overlap_v",
"zoom_bottom_to_top",
"zoom_direction_vertical",
},
zoom_overlap_h = 40,
zoom_overlap_v = 40,
zoom_bottom_to_top = nil, -- true for bottom-to-top
zoom_direction_vertical = nil, -- true for column mode
current_page = 1,
rotation = 0,
paged_modes = {
@@ -71,25 +99,35 @@ function ReaderZooming:init()
doc = "zoom to fit content height",
event = "SetZoomMode", args = "contentheight"
},
ZoomToFitColumn = {
{ "Shift", "C" },
doc = "zoom to fit column",
event = "SetZoomMode", args = "colu"
ZoomManual = {
{ "Shift", "M" },
doc = "manual zoom mode",
event = "SetZoomMode", args = "manual"
},
}
end
self.ui.menu:registerToMainMenu(self)
end
function ReaderZooming:onReadSettings(config)
local zoom_mode = config:readSetting("zoom_mode") or
G_reader_settings:readSetting("zoom_mode") or
self.DEFAULT_ZOOM_MODE
local zoom_mode = config:readSetting("zoom_mode")
or G_reader_settings:readSetting("zoom_mode")
or self.DEFAULT_ZOOM_MODE
zoom_mode = util.arrayContains(self.available_zoom_modes, zoom_mode)
and zoom_mode
or self.DEFAULT_ZOOM_MODE
self:setZoomMode(zoom_mode, true) -- avoid informative message on load
for _, setting in ipairs(self.zoom_pan_settings) do
self[setting] = config:readSetting(setting) or
G_reader_settings:readSetting(setting) or
self[setting]
end
end
function ReaderZooming:onSaveSettings()
self.ui.doc_settings:saveSetting("zoom_mode", self.orig_zoom_mode or self.zoom_mode)
for _, setting in ipairs(self.zoom_pan_settings) do
self.ui.doc_settings:saveSetting(setting, self[setting])
end
end
function ReaderZooming:onSpread(arg, ges)
@@ -161,6 +199,108 @@ function ReaderZooming:onZoom(direction)
return true
end
function ReaderZooming:onDefineZoom(btn)
local config = self.ui.document.configurable
local settings = ({
[7] = {right_to_left = false, zoom_bottom_to_top = false, zoom_direction_vertical = false},
[6] = {right_to_left = false, zoom_bottom_to_top = false, zoom_direction_vertical = true },
[5] = {right_to_left = false, zoom_bottom_to_top = true, zoom_direction_vertical = false},
[4] = {right_to_left = false, zoom_bottom_to_top = true, zoom_direction_vertical = true },
[3] = {right_to_left = true, zoom_bottom_to_top = true, zoom_direction_vertical = true },
[2] = {right_to_left = true, zoom_bottom_to_top = true, zoom_direction_vertical = false},
[1] = {right_to_left = true, zoom_bottom_to_top = false, zoom_direction_vertical = true },
[0] = {right_to_left = true, zoom_bottom_to_top = false, zoom_direction_vertical = false},
})[config.zoom_direction]
local zoom_range_number = config.zoom_range_number
local zoom_factor = config.zoom_factor
local zoom_mode_genus = ({
[4] = "page",
[3] = "content",
[2] = "columns",
[1] = "rows",
[0] = "manual",
})[config.zoom_mode_genus]
local zoom_mode_type = ({
[2] = "",
[1] = "width",
[0] = "height",
})[config.zoom_mode_type]
settings.zoom_overlap_h = config.zoom_overlap_h
settings.zoom_overlap_v = config.zoom_overlap_v
if btn == "set_zoom_overlap_h" then
self:_zoomPanChange(_("Set horizontal overlap"), "zoom_overlap_h")
settings.zoom_overlap_h = self.zoom_overlap_h
elseif btn == "set_zoom_overlap_v" then
self:_zoomPanChange(_("Set vertical overlap"), "zoom_overlap_v")
settings.zoom_overlap_v = self.zoom_overlap_v
end
local zoom_mode
if zoom_mode_genus == "page" or zoom_mode_genus == "content" then
zoom_mode = zoom_mode_genus..zoom_mode_type
else
zoom_mode = zoom_mode_genus
self.ui:handleEvent(Event:new("SetScrollMode", false))
end
zoom_mode = util.arrayContains(self.available_zoom_modes, zoom_mode) and zoom_mode or self.DEFAULT_ZOOM_MODE
settings.zoom_mode = zoom_mode
if settings.right_to_left then
if settings.zoom_bottom_to_top then
config.writing_direction = 2
else
config.writing_direction = 1
end
else
config.writing_direction = 0
end
settings.right_to_left = nil
if zoom_mode == "columns" or zoom_mode == "rows" then
if btn ~= "columns" and btn ~= "rows" then
self.ui:handleEvent(Event:new("SetZoomPan", settings, true))
settings.zoom_factor = self:setNumberOf(
zoom_mode,
zoom_range_number,
zoom_mode == "columns" and settings.zoom_overlap_h or settings.zoom_overlap_v
)
end
elseif zoom_mode == "manual" then
if btn == "manual" then
config.zoom_factor = self:getNumberOf("columns")
else
self:setNumberOf("columns", zoom_factor)
end
self.ui:handleEvent(Event:new("SetZoomPan", settings, true))
end
self.ui:handleEvent(Event:new("SetZoomMode", zoom_mode))
if btn == "columns" or btn == "rows" then
config.zoom_range_number = self:getNumberOf(
zoom_mode,
btn == "columns" and settings.zoom_overlap_h or settings.zoom_overlap_v
)
end
if tonumber(btn) then
UIManager:show(InfoMessage:new{
timeout = 2,
text = T(_([[Zoom set to:
mode: %1
number of columns: %2
number of rows: %4
horizontal overlap: %3 %
vertical overlap: %5 %
zoom factor: %6]]),
zoom_mode,
("%.2f"):format(self:getNumberOf("columns", settings.zoom_overlap_h)),
settings.zoom_overlap_h,
("%.2f"):format(self:getNumberOf("rows", settings.zoom_overlap_v)),
settings.zoom_overlap_v,
("%.2f"):format(self:getNumberOf("columns"))),
})
end
end
function ReaderZooming:onSetZoomMode(new_mode)
self.view.zoom_mode = new_mode
if self.zoom_mode ~= new_mode then
@@ -168,7 +308,11 @@ function ReaderZooming:onSetZoomMode(new_mode)
self.ui:handleEvent(Event:new("ZoomModeUpdate", new_mode))
self.zoom_mode = new_mode
self:setZoom()
self.ui:handleEvent(Event:new("InitScrollPageStates", new_mode))
if new_mode == "manual" then
self.ui:handleEvent(Event:new("SetScrollMode", false))
else
self.ui:handleEvent(Event:new("InitScrollPageStates", new_mode))
end
end
end
@@ -241,12 +385,9 @@ end
function ReaderZooming:getZoom(pageno)
-- check if we're in bbox mode and work on bbox if that's the case
local zoom = nil
local zoom
local page_size = self.ui.document:getNativePageDimensions(pageno)
if self.zoom_mode == "content"
or self.zoom_mode == "contentwidth"
or self.zoom_mode == "contentheight"
or self.zoom_mode == "column" then
if not (self.zoom_mode and self.zoom_mode:match("^page") or self.ui.document.configurable.trim_page == 3) then
local ubbox_dimen = self.ui.document:getUsedBBoxDimensions(pageno, 1)
-- if bbox is larger than the native page dimension render the full page
-- See discussion in koreader/koreader#970.
@@ -283,12 +424,15 @@ function ReaderZooming:getZoom(pageno)
end
elseif self.zoom_mode == "contentwidth" or self.zoom_mode == "pagewidth" then
zoom = zoom_w
elseif self.zoom_mode == "column" then
zoom = zoom_w * 2
elseif self.zoom_mode == "contentheight" or self.zoom_mode == "pageheight" then
zoom = zoom_h
elseif self.zoom_mode == "free" then
zoom = self.zoom
else
local zoom_factor = self.ui.doc_settings:readSetting("zoom_factor")
or G_reader_settings:readSetting("zoom_factor")
or self.zoom_factor
zoom = zoom_w * zoom_factor
end
if zoom and zoom > 10 and not Cache:willAccept(zoom * (self.dimen.w * self.dimen.h + 64)) then
logger.dbg("zoom too large, adjusting")
@@ -309,7 +453,7 @@ function ReaderZooming:getZoom(pageno)
if zoom < 0 then return 0 end
end
end
return zoom
return zoom, zoom_w, zoom_h
end
function ReaderZooming:getRegionalZoomCenter(pageno, pos)
@@ -345,54 +489,109 @@ function ReaderZooming:genSetZoomModeCallBack(mode)
end
function ReaderZooming:setZoomMode(mode, no_warning)
if not no_warning and self.ui.view.page_scroll and self.paged_modes[mode] then
UIManager:show(InfoMessage:new{
text = T(_([[
if not no_warning and self.ui.view.page_scroll then
local message
if self.paged_modes[mode] then
message = T(_([[
%1
In combination with continuous view (scroll mode), this can cause unexpected vertical shifts when turning pages.]]), self.paged_modes[mode]),
timeout = 5,
})
In combination with continuous view (scroll mode), this can cause unexpected vertical shifts when turning pages.]]),
self.paged_modes[mode])
elseif self.zoom_mode == "manual" then
message = _([[
"Manual zoom works best with page view."
Please enable page view instead of continuous view (scroll mode).]])
end
if message then
UIManager:show(InfoMessage:new{text = message, timeout = 5})
end
end
self.ui:handleEvent(Event:new("SetZoomMode", mode))
self.ui:handleEvent(Event:new("InitScrollPageStates"))
end
function ReaderZooming:addToMainMenu(menu_items)
if self.ui.document.info.has_pages then
local function getZoomModeMenuItem(text, mode, separator)
return {
text_func = function()
local default_zoom_mode = G_reader_settings:readSetting("zoom_mode") or self.DEFAULT_ZOOM_MODE
return text .. (mode == default_zoom_mode and "" or "")
end,
checked_func = function()
return self.zoom_mode == mode
end,
callback = self:genSetZoomModeCallBack(mode),
hold_callback = function(touchmenu_instance)
self:makeDefault(mode, touchmenu_instance)
end,
separator = separator,
}
end
menu_items.switch_zoom_mode = {
text = _("Switch zoom mode"),
enabled_func = function()
return self.ui.document.configurable.text_wrap ~= 1
end,
sub_item_table = {
getZoomModeMenuItem(_("Zoom to fit content width"), "contentwidth"),
getZoomModeMenuItem(_("Zoom to fit content height"), "contentheight", true),
getZoomModeMenuItem(_("Zoom to fit page width"), "pagewidth"),
getZoomModeMenuItem(_("Zoom to fit page height"), "pageheight", true),
getZoomModeMenuItem(_("Zoom to fit column"), "column"),
getZoomModeMenuItem(_("Zoom to fit content"), "content"),
getZoomModeMenuItem(_("Zoom to fit page"), "page"),
}
}
local function _getOverlapFactorForNum(n, overlap)
-- Auxiliary function to "distribute" an overlap between tiles
overlap = overlap * (n - 1) / n
return (100 / (100 - overlap))
end
function ReaderZooming:getNumberOf(what, overlap)
-- Number of columns (if what ~= "rows") or rows (if what == "rows")
local zoom, zoom_w, zoom_h = self:getZoom(self.current_page)
local zoom_factor = zoom / (what == "rows" and zoom_h or zoom_w)
if overlap then
overlap = (what == "rows" and self.zoom_overlap_v or self.zoom_overlap_h)
zoom_factor = (overlap - 100 * zoom_factor) / (overlap - 100) -- Thanks Xcas for this one...
end
return zoom_factor
end
function ReaderZooming:setNumberOf(what, num, overlap)
-- Sets number of columns (if what ~= "rows") or rows (if what == "rows")
local _, zoom_w, zoom_h = self:getZoom(self.current_page)
local overlap_factor = overlap and _getOverlapFactorForNum(num, overlap) or 1
local zoom_factor = num / overlap_factor
if what == "rows" then
zoom_factor = zoom_factor * zoom_h / zoom_w
end
self.ui:handleEvent(Event:new("SetZoomPan", {zoom_factor = zoom_factor}))
self.ui:handleEvent(Event:new("RedrawCurrentPage"))
end
function ReaderZooming:_zoomFactorChange(title_text, direction, precision)
local zoom_factor, overlap = self:getNumberOf(direction)
UIManager:show(SpinWidget:new{
width = math.floor(Screen:getWidth() * 0.6),
value = zoom_factor,
value_min = 0.1,
value_max = 10,
value_step = 0.1,
value_hold_step = 1,
precision = "%.1f",
ok_text = title_text,
title_text = title_text,
callback = function(spin)
zoom_factor = spin.value
self:setNumberOf(direction, zoom_factor, overlap)
end
})
end
function ReaderZooming:_zoomPanChange(text, setting)
UIManager:show(SpinWidget:new{
width = math.floor(Screen:getWidth() * 0.6),
value = self[setting],
value_min = 0,
value_max = 90,
value_step = 1,
value_hold_step = 10,
ok_text = _("Set"),
title_text = text,
callback = function(spin)
self.ui:handleEvent(Event:new("SetZoomPan", {[setting] = spin.value}))
end
})
end
function ReaderZooming:onZoomFactorChange()
self:_zoomFactorChange(_("Set Zoom factor"), false, "%.1f")
end
function ReaderZooming:onSetZoomPan(settings, no_redraw)
for k, v in pairs(settings) do
self[k] = v
self.ui.doc_settings:saveSetting(k, v)
end
if not no_redraw then
self.ui:handleEvent(Event:new("RedrawCurrentPage"))
end
end
function ReaderZooming:onBBoxUpdate()
self:onDefineZoom()
end
function ReaderZooming:makeDefault(zoom_mode, touchmenu_instance)