diff --git a/frontend/apps/reader/modules/readercropping.lua b/frontend/apps/reader/modules/readercropping.lua index 29aaee8dc..e2f75e436 100644 --- a/frontend/apps/reader/modules/readercropping.lua +++ b/frontend/apps/reader/modules/readercropping.lua @@ -25,9 +25,6 @@ function ReaderCropping:onPageCrop(mode) end return elseif mode == "none" then - if self.document.configurable.text_wrap ~= 1 then - self.ui:handleEvent(Event:new("SetZoomMode", "pagewidth", "cropping")) - end return end -- backup original view dimen @@ -51,7 +48,7 @@ function ReaderCropping:onPageCrop(mode) -- mode, just force readerview to recalculate visible_area self.view:recalculate() else - self.ui:handleEvent(Event:new("SetZoomMode", "page", "cropping")) + self.ui:handleEvent(Event:new("SetZoomMode", "page")) end -- prepare bottom buttons so we know the size available for the page above it @@ -152,11 +149,11 @@ end function ReaderCropping:setCropZoomMode(confirmed) if confirmed then - -- if original zoom mode is not "content", set zoom mode to "contentwidth" - self:setZoomMode( - self.orig_zoom_mode:find("content") - and self.orig_zoom_mode - or "contentwidth") + -- if original zoom mode is "page???", set zoom mode to "content???" + local zoom_mode_type = self.orig_zoom_mode:match("page(.*)") + self:setZoomMode(zoom_mode_type + and "content"..zoom_mode_type + or self.orig_zoom_mode) self.ui:handleEvent(Event:new("InitScrollPageStates")) else self:setZoomMode(self.orig_zoom_mode) diff --git a/frontend/apps/reader/modules/readerkoptlistener.lua b/frontend/apps/reader/modules/readerkoptlistener.lua index 3ada332f7..777be3db8 100644 --- a/frontend/apps/reader/modules/readerkoptlistener.lua +++ b/frontend/apps/reader/modules/readerkoptlistener.lua @@ -1,5 +1,7 @@ local EventListener = require("ui/widget/eventlistener") local Event = require("ui/event") +local ReaderZooming = require("apps/reader/modules/readerzooming") +local util = require("util") local ReaderKoptListener = EventListener:new{} @@ -14,9 +16,13 @@ end function ReaderKoptListener:onReadSettings(config) -- normal zoom mode is zoom mode used in non-reflow mode. - self.normal_zoom_mode = config:readSetting("normal_zoom_mode") or + local normal_zoom_mode = config:readSetting("normal_zoom_mode") or G_reader_settings:readSetting("zoom_mode") or "page" - self:setZoomMode(self.normal_zoom_mode) + normal_zoom_mode = util.arrayContains(ReaderZooming.available_zoom_modes, normal_zoom_mode) + and normal_zoom_mode + or "page" + self.normal_zoom_mode = normal_zoom_mode + self:setZoomMode(normal_zoom_mode) self.document.configurable.contrast = config:readSetting("kopt_contrast") or G_reader_settings:readSetting("kopt_contrast") or 1.0 self.ui:handleEvent(Event:new("GammaUpdate", 1/self.document.configurable.contrast)) diff --git a/frontend/apps/reader/modules/readerpaging.lua b/frontend/apps/reader/modules/readerpaging.lua index 9296c1ce0..9b23dd124 100644 --- a/frontend/apps/reader/modules/readerpaging.lua +++ b/frontend/apps/reader/modules/readerpaging.lua @@ -6,6 +6,7 @@ local InputContainer = require("ui/widget/container/inputcontainer") local Math = require("optmath") local MultiConfirmBox = require("ui/widget/multiconfirmbox") local Notification = require("ui/widget/notification") +local ReaderZooming = require("apps/reader/modules/readerzooming") local UIManager = require("ui/uimanager") local bit = require("bit") local logger = require("logger") @@ -193,6 +194,9 @@ function ReaderPaging:onReadSettings(config) if self.inverse_reading_order == nil then self.inverse_reading_order = G_reader_settings:isTrue("inverse_reading_order") end + for _, v in ipairs(ReaderZooming.zoom_pan_settings) do + self[v] = config:readSetting(v) or G_reader_settings:readSetting(v) or ReaderZooming[v] + end end function ReaderPaging:onSaveSettings() @@ -840,134 +844,115 @@ function ReaderPaging:onGotoPageRel(diff) logger.dbg("goto relative page:", diff) local new_va = self.visible_area:copy() local x_pan_off, y_pan_off = 0, 0 - - if self.zoom_mode:find("width") then - y_pan_off = self.visible_area.h * diff - elseif self.zoom_mode:find("height") then - -- negative x panning if writing direction is right to left - local direction = self.ui.document.configurable.writing_direction - x_pan_off = self.visible_area.w * diff * (direction == 1 and -1 or 1) - elseif self.zoom_mode:find("column") then - -- zoom mode for two-column navigation - - y_pan_off = self.visible_area.h * diff - y_pan_off = Math.roundAwayFromZero(y_pan_off) - new_va.x = Math.roundAwayFromZero(self.visible_area.x) - new_va.y = Math.roundAwayFromZero(self.visible_area.y+y_pan_off) - -- intra-column navigation (vertical), this is the default behavior - -- if we do not reach the end of a column - - if new_va:notIntersectWith(self.page_area) then - -- if we leave the page, we must either switch to the other column - -- or switch to another page (we are crossing the end of a column) - - x_pan_off = self.visible_area.w * diff - x_pan_off = Math.roundAwayFromZero(x_pan_off) - new_va.x = Math.roundAwayFromZero(self.visible_area.x+x_pan_off) - new_va.y = Math.roundAwayFromZero(self.visible_area.y) - -- inter-column displacement (horizontal) - - if new_va:notIntersectWith(self.page_area) then - -- if we leave the page with horizontal displacement, then we are - -- already in the border column, we must turn the page - - local new_page = self.current_page + diff - if diff > 0 and new_page == self.number_of_pages + 1 then - self.ui:handleEvent(Event:new("EndOfBook")) - else - self:_gotoPage(new_page) - end - - if y_pan_off < 0 then - -- if we are going back to previous page, reset view area - -- to bottom right of previous page, end of second column - self.view:PanningUpdate(self.page_area.w, self.page_area.h) - end - - else - -- if we do not leave the page with horizontal displacement, - -- it means that we can stay on this page and switch column - - if diff > 0 then - -- end of first column, set view area to the top right of - -- current page, beginning of second column - self.view:PanningUpdate(self.page_area.w, -self.page_area.h) - else - -- move backwards to the first column, set the view area to the - -- bottom left of the current page - self.view:PanningUpdate(-self.page_area.w, self.page_area.h) - end - end - - -- if we are here, the panning has already been updated so return - return true + local right_to_left = (self.ui.document.configurable.writing_direction > 0) + local bottom_to_top = self.ui.zooming.zoom_bottom_to_top + local h_progress = 1 - self.ui.zooming.zoom_overlap_h / 100 + local v_progress = 1 - self.ui.zooming.zoom_overlap_v / 100 + local old_va = self.visible_area + local old_page = self.current_page + local x, y, w, h = "x", "y", "w", "h" + local x_diff = diff + local y_diff = diff + + -- Adjust directions according to settings + if self.ui.zooming.zoom_direction_vertical then -- invert axes + y, x, h, w = x, y, w, h + h_progress, v_progress = v_progress, h_progress + if right_to_left then + x_diff, y_diff = -x_diff, -y_diff end + if bottom_to_top then + x_diff = -x_diff + end + elseif bottom_to_top then + y_diff = -y_diff + end + if right_to_left then + x_diff = -x_diff + end + + if self.zoom_mode ~= "free" then + x_pan_off = Math.roundAwayFromZero(self.visible_area[w] * h_progress * x_diff) + y_pan_off = Math.roundAwayFromZero(self.visible_area[h] * v_progress * y_diff) + end - elseif self.zoom_mode ~= "free" then -- do nothing in "free" zoom mode - -- must be fit content or page zoom mode - if self.visible_area.w == self.page_area.w then - y_pan_off = self.visible_area.h * diff + -- Auxiliary functions to (as much as possible) keep things clear + -- If going backwards (diff < 0) "end" is equivalent to "beginning", "next" to "previous"; + -- in column mode, "line" is equivalent to "column". + local function at_end(axis) + -- returns true if we're at the end of line (axis = x) or page (axis = y) + local len, _diff + if axis == x then + len, _diff = w, x_diff else - x_pan_off = self.visible_area.w * diff + len, _diff = h, y_diff end + return old_va[axis] + old_va[len] + _diff > self.page_area[axis] + self.page_area[len] + or old_va[axis] + _diff < self.page_area[axis] + end + local function goto_end(axis, _diff) + -- updates view area to the end of line (axis = x) or page (axis = y) + local len = axis == x and w or h + _diff = _diff or (axis == x and x_diff or y_diff) + new_va[axis] = _diff > 0 + and old_va[axis] + self.page_area[len] - old_va[len] + or self.page_area[axis] end - -- adjust offset to help with page turn decision - -- we dont take overlap into account here yet, otherwise new_va will - -- always intersect with page_area - x_pan_off = Math.roundAwayFromZero(x_pan_off) - y_pan_off = Math.roundAwayFromZero(y_pan_off) - new_va.x = Math.roundAwayFromZero(self.visible_area.x+x_pan_off) - new_va.y = Math.roundAwayFromZero(self.visible_area.y+y_pan_off) - - if new_va:notIntersectWith(self.page_area) then - -- view area out of page area, do a page turn + local function goto_next_line() + new_va[y] = old_va[y] + y_pan_off + goto_end(x, -x_diff) + end + local function goto_next_page() local new_page = self.current_page + diff - if diff > 0 and new_page == self.number_of_pages + 1 then + if new_page > self.number_of_pages then self.ui:handleEvent(Event:new("EndOfBook")) - else + goto_end(y) + goto_end(x) + elseif new_page > 0 then self:_gotoPage(new_page) + goto_end(y, -y_diff) + else + goto_end(x) end - -- if we are going back to previous page, reset - -- view area to bottom of previous page - if x_pan_off < 0 then - self.view:PanningUpdate(self.page_area.w, 0) - elseif y_pan_off < 0 then - self.view:PanningUpdate(0, self.page_area.h) - end - else - -- not end of page yet, goto next view - -- adjust panning step according to overlap - local overlap = self.overlap - if x_pan_off > overlap then - -- moving to next view, move view - x_pan_off = x_pan_off - overlap - elseif x_pan_off < -overlap then - x_pan_off = x_pan_off + overlap - end - if y_pan_off > overlap then - y_pan_off = y_pan_off - overlap - elseif y_pan_off < -overlap then - y_pan_off = y_pan_off + overlap - end - -- we have to calculate again to count into overlap - new_va.x = Math.roundAwayFromZero(self.visible_area.x+x_pan_off) - new_va.y = Math.roundAwayFromZero(self.visible_area.y+y_pan_off) - -- fit new view area into page area - new_va:offsetWithin(self.page_area, 0, 0) - -- calculate panning offsets - local panned_x = new_va.x - self.visible_area.x - local panned_y = new_va.y - self.visible_area.y - -- adjust for crazy floating point overflow... - if math.abs(panned_x) < 1 then - panned_x = 0 - end - if math.abs(panned_y) < 1 then - panned_y = 0 + end + + -- Move the view area towerds line end + new_va[x] = old_va[x] + x_pan_off + new_va[y] = old_va[y] + + -- Handle cases when the view area gets out of page boundaries + if not self.page_area:contains(new_va) then + if not at_end(x) then + goto_end(x) + else + goto_next_line() + if not self.page_area:contains(new_va) then + if not at_end(y) then + goto_end(y) + else + goto_next_page() + end + end end - -- singal panning update - self.view:PanningUpdate(panned_x, panned_y) - -- update dime area in ReaderView - if self.show_overlap_enable then + end + + -- signal panning update + local panned_x, panned_y = (new_va.x - old_va.x), (new_va.y - old_va.y) + -- adjust for crazy floating point overflow... + if math.abs(panned_x) < 1 then + panned_x = 0 + end + if math.abs(panned_y) < 1 then + panned_y = 0 + end + self.view:PanningUpdate(panned_x, panned_y) + + -- update dim area in ReaderView + if self.show_overlap_enable then + if self.current_page ~= old_page then + self.view.dim_area.x = 0 + self.view.dim_area.y = 0 + else self.view.dim_area.h = new_va.h - math.abs(panned_y) self.view.dim_area.w = new_va.w - math.abs(panned_x) if panned_y < 0 then @@ -981,8 +966,6 @@ function ReaderPaging:onGotoPageRel(diff) self.view.dim_area.x = 0 end end - -- update self.visible_area - self.visible_area = new_va end return true diff --git a/frontend/apps/reader/modules/readerview.lua b/frontend/apps/reader/modules/readerview.lua index 2ab578861..b348183d0 100644 --- a/frontend/apps/reader/modules/readerview.lua +++ b/frontend/apps/reader/modules/readerview.lua @@ -573,12 +573,17 @@ function ReaderView:recalculate() self.visible_area.h = self.visible_area.h - self.ui.view.footer:getHeight() end if self.ui.document.configurable.writing_direction == 0 then - -- starts from left top of page_area + -- starts from left 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 + -- start from right of page_area self.visible_area.x = self.page_area.x + self.page_area.w - self.visible_area.w + end + if self.ui.zooming.zoom_bottom_to_top then + -- starts from bottom of page_area + self.visible_area.y = self.page_area.y + self.page_area.h - self.visible_area.h + else + -- starts from top of page_area self.visible_area.y = self.page_area.y end if not self.page_scroll then @@ -736,6 +741,9 @@ In combination with zoom to fit page, page height, content height or content, co end self.page_scroll = page_scroll + if not page_scroll then + self.ui.document.configurable.page_scroll = 0 + end self:recalculate() self.ui:handleEvent(Event:new("InitScrollPageStates")) end diff --git a/frontend/apps/reader/modules/readerzooming.lua b/frontend/apps/reader/modules/readerzooming.lua index a2657d242..31c987dbb 100644 --- a/frontend/apps/reader/modules/readerzooming.lua +++ b/frontend/apps/reader/modules/readerzooming.lua @@ -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, - } +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 - 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"), - } - } + }) +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) diff --git a/frontend/dispatcher.lua b/frontend/dispatcher.lua index 475ea89db..884f5145f 100644 --- a/frontend/dispatcher.lua +++ b/frontend/dispatcher.lua @@ -28,6 +28,7 @@ Each setting contains: local CreOptions = require("ui/data/creoptions") local Device = require("device") local Event = require("ui/event") +local ReaderZooming = require("apps/reader/modules/readerzooming") local Screen = require("device").screen local UIManager = require("ui/uimanager") local _ = require("gettext") @@ -114,7 +115,7 @@ local settingsList = { book_cover = { category="none", event="ShowBookCover", title=_("Book cover"), rolling=true, paging=true, separator=true,}, show_config_menu = { category="none", event="ShowConfigMenu", title=_("Show bottom menu"), rolling=true, paging=true,}, toggle_bookmark = { category="none", event="ToggleBookmark", title=_("Toggle bookmark"), rolling=true, paging=true,}, - toggle_inverse_reading_order = { category="none", event="ToggleReadingOrder", title=_("Toggle page turn direction"), rolling=true, paging=true,}, + toggle_inverse_reading_order = { category="none", event="ToggleReadingOrder", title=_("Toggle page turn direction"), rolling=true, paging=true, separator=true}, cycle_highlight_action = { category="none", event="CycleHighlightAction", title=_("Cycle highlight action"), rolling=true, paging=true,}, cycle_highlight_style = { category="none", event="CycleHighlightStyle", title=_("Cycle highlight style"), rolling=true, paging=true,}, page_jmp = { category="absolutenumber", event="GotoViewRel", min=-100, max=100, title=_("Go %1 pages"), rolling=true, paging=true,}, @@ -127,7 +128,8 @@ local settingsList = { -- paging reader settings toggle_page_flipping = { category="none", event="TogglePageFlipping", title=_("Toggle page flipping"), paging=true,}, toggle_reflow = { category="none", event="ToggleReflow", title=_("Toggle reflow"), paging=true,}, - zoom = { category="string", event="SetZoomMode", title=_("Zoom to"), args={"contentwidth", "contentheight", "pagewidth", "pageheight", "column", "content", "page"}, toggle={"content width", "content height", "page width", "page height", "column", "content", "page"}, paging=true,}, + zoom = { category="string", event="SetZoomMode", title=_("Zoom mode"), args=ReaderZooming.available_zoom_modes, toggle=ReaderZooming.available_zoom_modes, paging=true,}, + zoom_factor_change = {category="none", event="ZoomFactorChange", title=_("Change zoom factor"), paging=true, separator=true}, -- parsed from CreOptions -- the rest of the table elements are built from their counterparts in CreOptions @@ -254,6 +256,7 @@ local dispatcher_menu_order = { "toggle_reflow", "toggle_inverse_reading_order", "zoom", + "zoom_factor_change", "cycle_highlight_action", "cycle_highlight_style", "panel_zoom_toggle", diff --git a/frontend/document/document.lua b/frontend/document/document.lua index 7b5224478..371bf12f3 100644 --- a/frontend/document/document.lua +++ b/frontend/document/document.lua @@ -273,10 +273,10 @@ function Document:getUsedBBoxDimensions(pageno, zoom, rotation) -- clipping page bbox if bbox.x0 < 0 then bbox.x0 = 0 end if bbox.y0 < 0 then bbox.y0 = 0 end - if bbox.x1 < 0 then bbox.x1 = 0 end - if bbox.y1 < 0 then bbox.y1 = 0 end + if bbox.x1 and bbox.x1 < 0 then bbox.x1 = 0 end + if bbox.y1 and bbox.y1 < 0 then bbox.y1 = 0 end local ubbox_dimen - if (bbox.x0 >= bbox.x1) or (bbox.y0 >= bbox.y1) then + if (not bbox.x1 or bbox.x0 >= bbox.x1) or (not bbox.y1 or bbox.y0 >= bbox.y1) then -- if document's bbox info is corrupted, we use the page size ubbox_dimen = self:getPageDimensions(pageno, zoom, rotation) else diff --git a/frontend/ui/data/koptoptions.lua b/frontend/ui/data/koptoptions.lua index 969740c37..ec176a864 100644 --- a/frontend/ui/data/koptoptions.lua +++ b/frontend/ui/data/koptoptions.lua @@ -76,6 +76,157 @@ In 'semi-auto' and 'manual' modes, you may need to define areas once on an odd p }, } }, + { + icon = "resources/icons/appbar.page.fit.png", + options = { + { + name = "zoom_overlap_h", + name_text = _("Horizontal overlap"), + buttonprogress = true, + fine_tune = true, + values = {0, 12, 24, 36, 48, 60, 72, 84}, + default_pos = 4, + default_value = 36, + show_func = function(config) + return config and config.zoom_mode_genus < 3 + end, + event = "DefineZoom", + args = {0, 12, 24, 36, 48, 60, 72, 84}, + labels = {0, 12, 24, 36, 48, 60, 72, 84}, + name_text_hold_callback = optionsutil.showValues, + help_text = _([[Set horizontal zoom overlap (between columns).]]), + }, + { + name = "zoom_overlap_v", + name_text = _("Vertical overlap"), + buttonprogress = true, + fine_tune = true, + values = {0, 12, 24, 36, 48, 60, 72, 84}, + default_pos = 4, + default_value = 36, + show_func = function(config) + return config and config.zoom_mode_genus < 3 + end, + event = "DefineZoom", + args = {0, 12, 24, 36, 48, 60, 72, 84}, + labels = {0, 12, 24, 36, 48, 60, 72, 84}, + name_text_hold_callback = optionsutil.showValues, + help_text = _([[Set vertical zoom overlap (between lines).]]), + }, + { + name = "zoom_mode_type", + name_text = _("Fit"), + toggle = {_("full"), _("width"), _("height")}, + alternate = false, + values = {2, 1, 0}, + default_value = 2, + show_func = function(config) return config and config.zoom_mode_genus > 2 end, + event = "DefineZoom", + args = {"full", "width", "height"}, + name_text_hold_callback = optionsutil.showValues, + help_text = _([[Set what to fit.]]), + }, + { + name = "zoom_range_number", + name_text_func = function(config) + if config then + if config.zoom_mode_genus == 1 then return _("Rows") + elseif config.zoom_mode_genus == 2 then return _("Columns") + end + end + return _("Number") + end, + name_text_true_values = true, + show_true_value_func = function(str) + return string.format("%.1f", str) + end, + toggle = {_("1"), _("2"), _("3"), _("4"), _("5"), _("6"), _("7"), _("8")}, + more_options = true, + more_options_param = { + value_step = 0.1, value_hold_step = 1, + value_min = 0.1, value_max = 1000, + precision = "%.1f", + }, + values = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}, + default_pos = 2, + default_value = 2, + show_func = function(config) + return config and config.zoom_mode_genus < 3 and config.zoom_mode_genus > 0 + end, + event = "DefineZoom", + args = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}, + name_text_hold_callback = optionsutil.showValues, + help_text = _([[Set the number of columns or rows into which to split the page.]]), + }, + { + name = "zoom_factor", + name_text = _("Zoom factor"), + name_text_true_values = true, + show_true_value_func = function(str) + return string.format("%.1f", str) + end, + toggle = {_("0.7"), _("1"), _("1.5"), _("2"), _("3"), _("5"), _("10"), _("20")}, + more_options = true, + more_options_param = { + value_step = 0.1, value_hold_step = 1, + value_min = 0.1, value_max = 1000, + precision = "%.1f", + }, + values = {0.7, 1.0, 1.5, 2.0, 3.0, 5.0, 10.0, 20.0}, + default_pos = 3, + default_value = 1.5, + show_func = function(config) + return config and config.zoom_mode_genus < 1 + end, + event = "DefineZoom", + args = {0.7, 1.0, 1.5, 2.0, 3.0, 5.0, 10.0, 20.0}, + name_text_hold_callback = optionsutil.showValues, + }, + { + name = "zoom_mode_genus", + name_text = _("Zoom to"), + -- toggle = {_("page"), _("content"), _("columns"), _("rows"), _("manual")}, + item_icons = { + "resources/icons/zoom.page.png", + "resources/icons/zoom.content.png", + "resources/icons/zoom.direction.column.png", + "resources/icons/zoom.direction.row.png", + "resources/icons/zoom.manual.png", + }, + alternate = false, + values = {4, 3, 2, 1, 0}, + default_value = 4, + event = "DefineZoom", + args = {"page", "content", "columns", "rows", "manual"}, + name_text_hold_callback = optionsutil.showValues, + }, + { + name = "zoom_direction", + name_text = _("Direction"), + enabled_func = function(config) + return config.zoom_mode_genus < 3 + end, + item_icons = { + "resources/icons/direction.LRTB.png", + "resources/icons/direction.TBLR.png", + "resources/icons/direction.LRBT.png", + "resources/icons/direction.BTLR.png", + "resources/icons/direction.BTRL.png", + "resources/icons/direction.RLBT.png", + "resources/icons/direction.TBRL.png", + "resources/icons/direction.RLTB.png", + }, + alternate = false, + values = {7, 6, 5, 4, 3, 2, 1, 0}, + default_value = 7, + event = "DefineZoom", + args = {7, 6, 5, 4, 3, 2, 1, 0}, + name_text_hold_callback = optionsutil.showValues, + help_text = _([[Set how paging and swiping forward should move the view on the page: +left to right or reverse, top to bottom or reverse.]]), + }, + } + }, { icon = "resources/icons/appbar.column.two.large.png", options = { @@ -311,12 +462,12 @@ This can also be used to remove some gray background or to convert a grayscale o { name = "writing_direction", name_text = S.WRITING_DIR, - toggle = {S.LTR, S.RTL, S.TBRTL}, - values = {0, 1, 2}, - default_value = 0, enabled_func = function(configurable) return optionsutil.enableIfEquals(configurable, "text_wrap", 1) end, + toggle = {S.LTR, S.RTL, S.TBRTL}, + values = {0, 1, 2}, + default_value = 0, name_text_hold_callback = optionsutil.showValues, help_text = _([[In reflow mode, sets the original text direction. This needs to be set to RTL to correctly extract and reflow RTL languages like Arabic or Hebrew.]]), }, diff --git a/frontend/ui/data/optionsutil.lua b/frontend/ui/data/optionsutil.lua index 7867b639e..76199fa6e 100644 --- a/frontend/ui/data/optionsutil.lua +++ b/frontend/ui/data/optionsutil.lua @@ -99,16 +99,19 @@ function optionsutil.showValues(configurable, option, prefix) help_text = T("\n%1\n", option.help_text) end local text + local name_text = option.name_text_func + and option.name_text_func(configurable) + or option.name_text if option.name_text_true_values and option.toggle and option.values then if value_default then - text = T(_("%1\n%2\nCurrent value: %3 (%4)\nDefault value: %5 (%6)"), option.name_text, help_text, + text = T(_("%1\n%2\nCurrent value: %3 (%4)\nDefault value: %5 (%6)"), name_text, help_text, current, value_current, default, value_default) else - text = T(_("%1\n%2\nCurrent value: %3 (%4)\nDefault value: %5"), option.name_text, help_text, + text = T(_("%1\n%2\nCurrent value: %3 (%4)\nDefault value: %5"), name_text, help_text, current, value_current, default) end else - text = T(_("%1\n%2\nCurrent value: %3\nDefault value: %4"), option.name_text, help_text, current, default) + text = T(_("%1\n%2\nCurrent value: %3\nDefault value: %4"), name_text, help_text, current, default) end UIManager:show(InfoMessage:new{ text=text }) end diff --git a/frontend/ui/widget/configdialog.lua b/frontend/ui/widget/configdialog.lua index 08af59e58..6bdc2e598 100644 --- a/frontend/ui/widget/configdialog.lua +++ b/frontend/ui/widget/configdialog.lua @@ -112,6 +112,7 @@ function OptionIconItem:init() } self[1] = FrameContainer:new{ padding = 0, + padding_top = self.underline_padding, padding_left = self.padding_left, padding_right = self.padding_right, bordersize = 0, @@ -198,12 +199,14 @@ function ConfigOption:init() local show = self.options[c].show -- Prefer show_func over show if there's one if self.options[c].show_func then - show = self.options[c].show_func() + show = self.options[c].show_func(self.config.configurable, self.config.document) end if show ~= false and show_default then local name_font_face = self.options[c].name_font_face and self.options[c].name_font_face or "cfont" local name_font_size = self.options[c].name_font_size and self.options[c].name_font_size or default_name_font_size - local text = self.options[c].name_text + local text = self.options[c].name_text_func + and self.options[c].name_text_func(self.config.configurable, self.config.document) + or self.options[c].name_text local face = Font:getFace(name_font_face, name_font_size) local txt_width = 0 if text ~= nil then @@ -236,7 +239,7 @@ function ConfigOption:init() local show = self.options[c].show -- Prefer show_func over show if there's one if self.options[c].show_func then - show = self.options[c].show_func() + show = self.options[c].show_func(self.config.configurable, self.config.document) end if show ~= false and show_default then local name_align = self.options[c].name_align_right and self.options[c].name_align_right or default_name_align_right @@ -262,20 +265,22 @@ function ConfigOption:init() local horizontal_group = HorizontalGroup:new{} -- Deal with the name on the left - if self.options[c].name_text then + local name_text = self.options[c].name_text_func + and self.options[c].name_text_func(self.config.configurable, self.config.document) + or self.options[c].name_text + if name_text then -- the horizontal padding on the left will be ensured by the RightContainer local name_widget_width = math.floor(name_align * Screen:getWidth()) -- We don't remove default_option_hpadding from name_text_max_width -- to give more to text and avoid truncation: as it is right aligned, -- the text can grow on the left, padding_small is enough. local name_text_max_width = name_widget_width - 2*padding_small - local text = self.options[c].name_text local face = Font:getFace(name_font_face, name_font_size) local option_name_container = RightContainer:new{ dimen = Geom:new{ w = name_widget_width, h = option_height}, } local option_name = Button:new{ - text = text, + text = name_text, max_width = name_text_max_width, bordersize = 0, face = face, @@ -461,7 +466,7 @@ function ConfigOption:init() option_items[d] = option_item option_item.items = option_items option_item.name = self.options[c].name - option_item.name_text = self.options[c].name_text + option_item.name_text = name_text option_item.item_text = self.options[c].item_text option_item.values = self.options[c].values option_item.args = self.options[c].args @@ -476,21 +481,25 @@ function ConfigOption:init() -- Icons (ex: columns, text align, with PDF) if self.options[c].item_icons then local items_count = #self.options[c].item_icons - local first_item = OptionIconItem:new{ - icon = ImageWidget:new{ - file = self.options[c].item_icons[1] - } - } - local max_item_spacing = (option_widget_width - - first_item:getSize().w * items_count) / items_count + local icon_max_height = option_height + local icon_max_width = math.floor(option_widget_width / items_count) + local icon_size = math.min(icon_max_height, icon_max_width) + local max_item_spacing = (option_widget_width - icon_size * items_count) / items_count local horizontal_half_padding = math.min(max_item_spacing, item_spacing_width) / 2 + -- Our icons have a bottom padding that makes 10% to 20% of their height (5-9px in our 48px images) + -- We don't want the underline to be that far away from the image content, + -- so we use some negative padding to eat a bit on their padding. + local underline_padding = - math.floor(0.05 * icon_size) for d = 1, #self.options[c].item_icons do local option_item = OptionIconItem:new{ icon = ImageWidget:new{ file = self.options[c].item_icons[d], dim = not enabled, + width = icon_size, + height = icon_size, + scale_factor = 0, -- scale to fit width and height }, - underline_padding = -padding_button, + underline_padding = underline_padding, padding_left = d > 1 and horizontal_half_padding, padding_right = d < #self.options[c].item_icons and horizontal_half_padding, color = d == current_item and (enabled and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_DARK_GRAY) or Blitbuffer.COLOR_WHITE, @@ -499,7 +508,7 @@ function ConfigOption:init() option_items[d] = option_item option_item.items = option_items option_item.name = self.options[c].name - option_item.name_text = self.options[c].name_text + option_item.name_text = name_text option_item.values = self.options[c].values option_item.args = self.options[c].args option_item.event = self.options[c].event @@ -528,7 +537,7 @@ function ConfigOption:init() font_face = item_font_face, font_size = item_font_size, name = self.options[c].name, - name_text = self.options[c].name_text, + name_text = name_text, toggle = self.options[c].toggle, alternate = self.options[c].alternate, values = self.options[c].values, @@ -545,7 +554,7 @@ function ConfigOption:init() self.options[c].more_options_param.show_true_value_func = self.options[c].show_true_value_func end self.config:onConfigMoreChoose(self.options[c].values, self.options[c].name, - self.options[c].event, arg, self.options[c].name_text, self.options[c].delay_repaint, self.options[c].more_options_param) + self.options[c].event, arg, name_text, self.options[c].delay_repaint, self.options[c].more_options_param) end end } @@ -573,10 +582,11 @@ function ConfigOption:init() callback = function(arg) if arg == "-" or arg == "+" then self.config:onConfigFineTuneChoose(self.options[c].values, self.options[c].name, - self.options[c].event, self.options[c].args, self.options[c].events, arg, self.options[c].delay_repaint) + self.options[c].event, self.options[c].args, self.options[c].events, arg, self.options[c].delay_repaint, + self.options[c].fine_tune_param) elseif arg == "⋮" then self.config:onConfigMoreChoose(self.options[c].values, self.options[c].name, - self.options[c].event, arg, self.options[c].name_text, self.options[c].delay_repaint, self.options[c].more_options_param) + self.options[c].event, arg, name_text, self.options[c].delay_repaint, self.options[c].more_options_param) else self.config:onConfigChoose(self.options[c].values, self.options[c].name, self.options[c].event, self.options[c].args, self.options[c].events, arg, self.options[c].delay_repaint) @@ -587,16 +597,17 @@ function ConfigOption:init() end, hold_callback = function(arg) if arg == "-" or arg == "+" then - self.config:onMakeFineTuneDefault(self.options[c].name, self.options[c].name_text, self.options[c].values, + self.config:onMakeFineTuneDefault(self.options[c].name, name_text, self.options[c].values, self.options[c].labels or self.options[c].args, arg) elseif arg ~= "⋮" then - self.config:onMakeDefault(self.options[c].name, self.options[c].name_text, self.options[c].values, + self.config:onMakeDefault(self.options[c].name, name_text, self.options[c].values, self.options[c].labels or self.options[c].args, arg) end end, show_parrent = self.config, enabled = enabled, fine_tune = self.options[c].fine_tune, + fine_tune_param = self.options[c].fine_tune_param, more_options = self.options[c].more_options, more_options_param = self.options[c].more_options_param, } @@ -963,7 +974,7 @@ function ConfigDialog:onConfigChoose(values, name, event, args, events, position end -- Tweaked variant used with the fine_tune variant of buttonprogress (direction can only be "-" or "+") -function ConfigDialog:onConfigFineTuneChoose(values, name, event, args, events, direction, delay_repaint) +function ConfigDialog:onConfigFineTuneChoose(values, name, event, args, events, direction, delay_repaint, params) UIManager:tickAfterNext(function() -- Repainting may be delayed depending on options local refresh_dialog_func = function() @@ -994,6 +1005,7 @@ function ConfigDialog:onConfigFineTuneChoose(values, name, event, args, events, end if values then local value + local step = params and params.value_step or 1 if direction == "-" then value = self.configurable[name] or values[1] if type(value) == "table" then @@ -1001,7 +1013,7 @@ function ConfigDialog:onConfigFineTuneChoose(values, name, event, args, events, -- to one of the original preset values tables local updated = {} for i=1, #value do - local v = value[i] - 1 + local v = value[i] - step if v < 0 then v = 0 end @@ -1009,7 +1021,7 @@ function ConfigDialog:onConfigFineTuneChoose(values, name, event, args, events, end value = updated else - value = value - 1 + value = value - step if value < 0 then value = 0 end @@ -1019,11 +1031,11 @@ function ConfigDialog:onConfigFineTuneChoose(values, name, event, args, events, if type(value) == "table" then local updated = {} for i=1, #value do - table.insert(updated, value[i] + 1) + table.insert(updated, value[i] + step) end value = updated else - value = value + 1 + value = value + step end end self:onConfigChoice(name, value) diff --git a/frontend/ui/widget/numberpickerwidget.lua b/frontend/ui/widget/numberpickerwidget.lua index 09973cdef..f34eae54f 100644 --- a/frontend/ui/widget/numberpickerwidget.lua +++ b/frontend/ui/widget/numberpickerwidget.lua @@ -129,7 +129,7 @@ function NumberPickerWidget:paintWidget() callback_input = function() input_dialog = InputDialog:new{ title = _("Enter number"), - input = self.value, + input = value, input_type = "number", buttons = { { diff --git a/frontend/util.lua b/frontend/util.lua index 11f148513..6407f3b61 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -339,7 +339,7 @@ function util.arrayAppend(t1, t2) end end --- Reverse array elements in-place in table t +--- Reverse array elements in-place in table t ---- @param t Lua table function util.arrayReverse(t) local i, j = 1, #t @@ -350,6 +350,22 @@ function util.arrayReverse(t) end end +--- Test whether t contains a value equal to v +--- (or such a value that callback returns true), +--- and if so, return the index. +---- @param t Lua table +---- @param v +---- @function callback(v1, v2) +function util.arrayContains(t, v, cb) + cb = cb or function(v1, v2) return v1 == v2 end + for _k, _v in ipairs(t) do + if cb(_v, v) then + return _k + end + end + return false +end + -- Merge t2 into t1, overwriting existing elements if they already exist -- Probably not safe with nested tables (c.f., https://stackoverflow.com/q/1283388) ---- @param t1 Lua table diff --git a/l10n b/l10n index 6d0396d8c..718d53ce4 160000 --- a/l10n +++ b/l10n @@ -1 +1 @@ -Subproject commit 6d0396d8c100024e6c11c01720daee8443877241 +Subproject commit 718d53ce4270d72b6421d0f7acc25ba3c0d348b0 diff --git a/resources/icons/README.md b/resources/icons/README.md index 98992e9f6..ae9545937 100644 --- a/resources/icons/README.md +++ b/resources/icons/README.md @@ -9,3 +9,15 @@ Start from an RGB copy of the image if you end up with a 256c or sRGB PNG (check See https://www.mobileread.com/forums/showpost.php?p=3728291&postcount=17 for more details ;). + +Zoom direction icons are generated from direction.LRBT.png with: + +```bash +convert direction.LRBT.png -rotate 90 direction.TBLR.png +convert direction.LRBT.png -rotate 180 direction.RLTB.png +convert direction.LRBT.png -rotate -90 direction.BTRL.png +convert direction.BTRL.png -flop direction.BTLR.png +convert direction.LRBT.png -flop direction.RLBT.png +convert direction.RLTB.png -flop direction.LRTB.png +convert direction.TBLR.png -flop direction.TBRL.png +``` diff --git a/resources/icons/appbar.magnify.zoom.png b/resources/icons/appbar.magnify.zoom.png new file mode 100644 index 000000000..adb9c2276 Binary files /dev/null and b/resources/icons/appbar.magnify.zoom.png differ diff --git a/resources/icons/appbar.page.fit.png b/resources/icons/appbar.page.fit.png new file mode 100644 index 000000000..7928194d4 Binary files /dev/null and b/resources/icons/appbar.page.fit.png differ diff --git a/resources/icons/direction.BTLR.png b/resources/icons/direction.BTLR.png new file mode 100644 index 000000000..9c77d3a5a Binary files /dev/null and b/resources/icons/direction.BTLR.png differ diff --git a/resources/icons/direction.BTRL.png b/resources/icons/direction.BTRL.png new file mode 100644 index 000000000..356acec96 Binary files /dev/null and b/resources/icons/direction.BTRL.png differ diff --git a/resources/icons/direction.LRBT.png b/resources/icons/direction.LRBT.png new file mode 100644 index 000000000..6d0fca75b Binary files /dev/null and b/resources/icons/direction.LRBT.png differ diff --git a/resources/icons/direction.LRTB.png b/resources/icons/direction.LRTB.png new file mode 100644 index 000000000..a9f6ba59a Binary files /dev/null and b/resources/icons/direction.LRTB.png differ diff --git a/resources/icons/direction.RLBT.png b/resources/icons/direction.RLBT.png new file mode 100644 index 000000000..da9938b3d Binary files /dev/null and b/resources/icons/direction.RLBT.png differ diff --git a/resources/icons/direction.RLTB.png b/resources/icons/direction.RLTB.png new file mode 100644 index 000000000..149a3c09e Binary files /dev/null and b/resources/icons/direction.RLTB.png differ diff --git a/resources/icons/direction.TBLR.png b/resources/icons/direction.TBLR.png new file mode 100644 index 000000000..466f45fb1 Binary files /dev/null and b/resources/icons/direction.TBLR.png differ diff --git a/resources/icons/direction.TBRL.png b/resources/icons/direction.TBRL.png new file mode 100644 index 000000000..989851d9b Binary files /dev/null and b/resources/icons/direction.TBRL.png differ diff --git a/resources/icons/src/appbar.magnify.zoom.svg b/resources/icons/src/appbar.magnify.zoom.svg new file mode 100644 index 000000000..5e97b84e2 --- /dev/null +++ b/resources/icons/src/appbar.magnify.zoom.svg @@ -0,0 +1,53 @@ + +image/svg+xml + + + diff --git a/resources/icons/src/appbar.page.fit.svg b/resources/icons/src/appbar.page.fit.svg new file mode 100644 index 000000000..1fcbf6f95 --- /dev/null +++ b/resources/icons/src/appbar.page.fit.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/icons/src/direction.LRBT.svg b/resources/icons/src/direction.LRBT.svg new file mode 100644 index 000000000..519cfcbfa --- /dev/null +++ b/resources/icons/src/direction.LRBT.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/icons/src/zoom.content.svg b/resources/icons/src/zoom.content.svg new file mode 100644 index 000000000..9e0780afd --- /dev/null +++ b/resources/icons/src/zoom.content.svg @@ -0,0 +1,324 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/src/zoom.direction.column.svg b/resources/icons/src/zoom.direction.column.svg new file mode 100644 index 000000000..d6d491fb4 --- /dev/null +++ b/resources/icons/src/zoom.direction.column.svg @@ -0,0 +1,266 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/src/zoom.direction.row.svg b/resources/icons/src/zoom.direction.row.svg new file mode 100644 index 000000000..5429b62e5 --- /dev/null +++ b/resources/icons/src/zoom.direction.row.svg @@ -0,0 +1,266 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/src/zoom.manual.svg b/resources/icons/src/zoom.manual.svg new file mode 100644 index 000000000..31d8bdd19 --- /dev/null +++ b/resources/icons/src/zoom.manual.svg @@ -0,0 +1,178 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/src/zoom.page.svg b/resources/icons/src/zoom.page.svg new file mode 100644 index 000000000..f0f2bda26 --- /dev/null +++ b/resources/icons/src/zoom.page.svg @@ -0,0 +1,319 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/zoom.content.png b/resources/icons/zoom.content.png new file mode 100644 index 000000000..4cf2c303b Binary files /dev/null and b/resources/icons/zoom.content.png differ diff --git a/resources/icons/zoom.direction.column.png b/resources/icons/zoom.direction.column.png new file mode 100644 index 000000000..9d9d3eab2 Binary files /dev/null and b/resources/icons/zoom.direction.column.png differ diff --git a/resources/icons/zoom.direction.row.png b/resources/icons/zoom.direction.row.png new file mode 100644 index 000000000..3da7263fe Binary files /dev/null and b/resources/icons/zoom.direction.row.png differ diff --git a/resources/icons/zoom.manual.png b/resources/icons/zoom.manual.png new file mode 100644 index 000000000..5a8dd30b6 Binary files /dev/null and b/resources/icons/zoom.manual.png differ diff --git a/resources/icons/zoom.page.png b/resources/icons/zoom.page.png new file mode 100644 index 000000000..5b6249348 Binary files /dev/null and b/resources/icons/zoom.page.png differ