ImageViewer: Followup to #9529 (#9544)

* ImageViewer: Minor code cleanups
* GestureDetector: Fix the `distance` field of `two_finger_pan` & `two_finger_swipe` gestures so that it's no longer the double of the actual distance traveled. Get rid of existing workarounds throughout the codebase that had to deal with this quirk.
reviewable/pr9550/r1
NiLuJe 2 years ago committed by GitHub
parent ab4b4b31bd
commit b0d8919399
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -110,7 +110,6 @@ read_globals = {
"DCREREADER_CONFIG_WORD_EXPANSION_MORE",
"DMINIBAR_CONTAINER_HEIGHT",
"DGESDETECT_DISABLE_DOUBLE_TAP",
"FRONTLIGHT_SENSITIVITY_DECREASE",
"DALPHA_SORT_CASE_INSENSITIVE",
"KOBO_LIGHT_ON_START",
"NETWORK_PROXY",

@ -215,10 +215,6 @@ DMINIBAR_CONTAINER_HEIGHT = 14 -- Larger means more padding at the bottom, at t
-- no longer needed
--DDICT_FONT_SIZE = 20
-- Frontlight decrease of sensitivity for two-fingered pan gesture,
-- e.g. 2 changes the sensitivity by 1/2, 3 by 1/3 etc.
FRONTLIGHT_SENSITIVITY_DECREASE = 2
-- Normally, KOReader will present file lists sorted in case insensitive manner
-- when presenting an alphatically sorted list. So the Order is "A, b, C, d".
-- You can switch to a case sensitive sort ("A", "C", "b", "d") by disabling

@ -26,7 +26,6 @@ local ReaderFont = InputContainer:new{
-- default gamma from crengine's lvfntman.cpp
gamma_index = nil,
steps = {0,1,1,1,1,1,2,2,2,3,3,3,4,4,5},
gestureScale = Screen:getWidth() * FRONTLIGHT_SENSITIVITY_DECREASE,
}
function ReaderFont:init()
@ -369,11 +368,24 @@ function ReaderFont:addToMainMenu(menu_items)
end
function ReaderFont:gesToFontSize(ges)
-- Dispatcher feeds us a number, not a gesture
if type(ges) ~= "table" then return ges end
if ges.distance == nil then
ges.distance = 1
end
local step = math.ceil(2 * #self.steps * ges.distance / self.gestureScale)
-- Compute the scaling based on the gesture's direction (for pinch/spread)
local step
if ges.direction and ges.direction == "vertical" then
step = math.ceil(2 * #self.steps * ges.distance / Screen:getHeight())
elseif ges.direction and ges.direction == "horizontal" then
step = math.ceil(2 * #self.steps * ges.distance / Screen:getWidth())
elseif ges.direction and ges.direction == "diagonal" then
local screen_diagonal = math.sqrt(Screen:getWidth()^2 + Screen:getHeight()^2)
step = math.ceil(2 * #self.steps * ges.distance / screen_diagonal)
else
step = math.ceil(2 * #self.steps * ges.distance / math.min(Screen:getWidth(), Screen:getHeight()))
end
local delta_int = self.steps[step] or self.steps[#self.steps]
return delta_int
end

@ -83,10 +83,7 @@ if Device:hasFrontlight() then
end
local gestureScale
local scale_multiplier
if ges.ges == "two_finger_swipe" then
-- for backward compatibility
scale_multiplier = FRONTLIGHT_SENSITIVITY_DECREASE * 0.8
elseif ges.ges == "swipe" then
if ges.ges == "two_finger_swipe" or ges.ges == "swipe" then
scale_multiplier = 0.8
else
scale_multiplier = 1

@ -1061,13 +1061,18 @@ function Contact:handleTwoFingerPan(buddy_contact)
w = 0,
h = 0,
}
-- Use midpoint of tstart and rstart as swipe start point
local start_point = tstart_pos:midpoint(rstart_pos)
local end_point = tend_pos:midpoint(rend_pos)
-- Compute the distance based on the start & end midpoints
local avg_distance = start_point:distance(end_point)
-- We'll also want to remember the span between both contacts on start & end for some gestures
local start_distance = tstart_pos:distance(rstart_pos)
local end_distance = tend_pos:distance(rend_pos)
local ges_ev = {
ges = "two_finger_pan",
-- Use midpoint of tstart and rstart as swipe start point
pos = tstart_pos:midpoint(rstart_pos),
distance = tpan_dis + rpan_dis,
pos = start_point,
distance = avg_distance,
direction = tpan_dir,
time = self.current_tev.timev,
}
@ -1078,6 +1083,11 @@ function Contact:handleTwoFingerPan(buddy_contact)
ges_ev.ges = "outward_pan"
end
ges_ev.direction = gesture_detector.DIRECTION_TABLE[tpan_dir]
-- Use the the sum of both contacts' travel for the distance
ges_ev.distance = tpan_dis + rpan_dis
-- Some handlers might also want to know the distance between the two contacts on lift & down.
ges_ev.span = end_distance
ges_ev.start_span = start_distance
elseif self.state == Contact.holdState then
ges_ev.ges = "two_finger_hold_pan"
-- Flag 'em for holdState to discriminate with two_finger_hold_release

@ -367,7 +367,7 @@ Returns the Euclidean distance between two geoms.
@tparam Geom rect_b
]]
function Geom:distance(geom)
return math.sqrt(math.pow(self.x - geom.x, 2) + math.pow(self.y - geom.y, 2))
return math.sqrt((self.x - geom.x)^2 + (self.y - geom.y)^2)
end
--[[--

@ -103,7 +103,7 @@ function ImageViewer:init()
w = Screen:getWidth(),
h = Screen:getHeight(),
}
local diagonal = math.sqrt( math.pow(Screen:getWidth(), 2) + math.pow(Screen:getHeight(), 2) )
local diagonal = math.sqrt(Screen:getWidth()^2 + Screen:getHeight()^2)
self.ges_events = {
Tap = { GestureRange:new{ ges = "tap", range = range } },
-- Zoom in/out (Pinch & Spread are not triggered if user is too
@ -636,48 +636,57 @@ function ImageViewer:onPanRelease(_, ges)
end
-- Zoom events
function ImageViewer:onZoomIn(inc)
function ImageViewer:_refreshScaleFactor()
if self.scale_factor == 0 then
-- Get the scale_factor made out for best fit
self.scale_factor = self._scale_factor_0 or self._image_wg:getScaleFactor()
end
end
if not inc then
-- default for key zoom event
inc = 0.2
end
-- Compute new scale factor for rescaled image dimensions
local new_factor = self.scale_factor * (1 + inc)
function ImageViewer:_applyNewScaleFactor(new_factor)
-- Make sure self.scale_factor is up-to-date
self:_refreshScaleFactor()
-- We destroy ImageWidget on update, so only request this the first time,
-- in order to avoid jitter in the results given differing memory consumption at different zoom levels...
if not self._max_scale_factor then
if not self._min_scale_factor or not self._max_scale_factor then
self._min_scale_factor, self._max_scale_factor = self._image_wg:getScaleFactorExtrema()
end
-- Clamp to sane values
new_factor = math.min(new_factor, self._max_scale_factor)
new_factor = math.max(new_factor, self._min_scale_factor)
if new_factor ~= self.scale_factor then
self.scale_factor = new_factor
self:update()
else
if self.scale_factor == self._max_scale_factor then
logger.dbg("ImageViewer:onZoomIn: Hit the max scaling factor:", self.scale_factor)
if self.scale_factor == self._min_scale_factor then
logger.dbg("ImageViewer: Hit the min scaling factor:", self.scale_factor)
elseif self.scale_factor == self._max_scale_factor then
logger.dbg("ImageViewer: Hit the max scaling factor:", self.scale_factor)
else
logger.dbg("ImageViewer:onZoomIn: No change in scaling factor:", self.scale_factor)
logger.dbg("ImageViewer: No change in scaling factor:", self.scale_factor)
end
end
end
function ImageViewer:onZoomIn(inc)
self:_refreshScaleFactor()
if not inc then
-- default for key zoom event
inc = 0.2
end
-- Compute new scale factor for rescaled image dimensions
local new_factor = self.scale_factor * (1 + inc)
self:_applyNewScaleFactor(new_factor)
return true
end
function ImageViewer:onZoomOut(dec)
if self.scale_factor == 0 then
-- Get the scale_factor made out for best fit
self.scale_factor = self._scale_factor_0 or self._image_wg:getScaleFactor()
end
self:_refreshScaleFactor()
if not dec then
-- default for key zoom event
dec = 0.2
elseif dec >= 0.75 then
-- Larger reductions tend to be fairly jarring, so limit to 75%.
@ -685,70 +694,90 @@ function ImageViewer:onZoomOut(dec)
dec = 0.75
end
-- Compute new scale factor for rescaled image dimensions
local new_factor = self.scale_factor * (1 - dec)
self:_applyNewScaleFactor(new_factor)
return true
end
if not self._min_scale_factor then
self._min_scale_factor, self._max_scale_factor = self._image_wg:getScaleFactorExtrema()
end
-- Clamp to sane values
new_factor = math.max(new_factor, self._min_scale_factor)
if new_factor ~= self.scale_factor then
self.scale_factor = new_factor
self:update()
else
if self.scale_factor == self._min_scale_factor then
logger.dbg("ImageViewer:onZoomOut: Hit the min scaling factor:", self.scale_factor)
else
logger.dbg("ImageViewer:onZoomOut: No change in scaling factor:", self.scale_factor)
end
end
--[[
function ImageViewer:onZoomToHeight(height)
local new_factor = height / self._image_wg:getOriginalHeight()
self:_applyNewScaleFactor(new_factor)
return true
end
function ImageViewer:onZoomToWidth(width)
local new_factor = width / self._image_wg:getOriginalWidth()
self:_applyNewScaleFactor(new_factor)
return true
end
function ImageViewer:onZoomToDiagonal(d)
-- It's trigonometry time!
-- c.f., https://math.stackexchange.com/a/3369637
local r = self._image_wg:getOriginalWidth() / self._image_wg:getOriginalHeight()
local h = math.sqrt(d^2 / (r^2 + 1))
local w = h * r
-- Matches ImageWidget's best-fit computation in _render
local new_factor = math.min(w / self._image_wg:getOriginalWidth(), h / self._image_wg:getOriginalHeight())
self:_applyNewScaleFactor(new_factor)
return true
end
--]]
function ImageViewer:onSpread(_, ges)
if not self._image_wg then
return
end
-- We get the position where spread was done
-- First, get center ratio we would have had if we did a pan to there,
-- so we can have the zoom centered on there
if self._image_wg then
self._center_x_ratio, self._center_y_ratio = self._image_wg:getPanByCenterRatio(ges.pos.x - Screen:getWidth()/2, ges.pos.y - Screen:getHeight()/2)
end
-- Set some zoom increase value from pinch distance.
-- Making it relative to the smallest dimension between the currently scaled image or the Screen makes it less annoying
-- when approaching both very small scale factors (where the image dimensions are many times smaller than the screen),
self._center_x_ratio, self._center_y_ratio = self._image_wg:getPanByCenterRatio(ges.pos.x - Screen:getWidth()/2, ges.pos.y - Screen:getHeight()/2)
-- We compute a scaling percentage (which will *modify* the current scaling factor),
-- based on the gesture distance (it's the sum of the travel of both fingers).
-- Making this distance relative to the smallest dimension between
-- the currently scaled image or the Screen makes it less annoying when approaching both very small scale factors
-- (where the image dimensions are many times smaller than the screen),
-- meaning using the image dimensions here takes less zoom steps to get it back to a sensible size;
-- *and* large scale factors (where the image dimensions are larger than the screen),
-- meaning using the screen dimensions here makes zoom steps, again, slightly more potent.
local inc
if ges.direction == "vertical" then
inc = ges.distance / math.min(Screen:getHeight(), self._image_wg:getCurrentHeight())
local img_h = self._image_wg:getCurrentHeight()
local screen_h = Screen:getHeight()
self:onZoomIn(ges.distance / math.min(screen_h, img_h))
elseif ges.direction == "horizontal" then
inc = ges.distance / math.min(Screen:getWidth(), self._image_wg:getCurrentWidth())
local img_w = self._image_wg:getCurrentWidth()
local screen_w = Screen:getWidth()
self:onZoomIn(ges.distance / math.min(screen_w, img_w))
else
local tl = Geom:new{ x = 0, y = 0 }
local br = Geom:new{ x = Screen:getWidth() - 1, y = Screen:getHeight() - 1}
local screen_diag = tl:distance(br)
inc = ges.distance / math.min(screen_diag, self._image_wg:getCurrentDiagonal())
local img_d = self._image_wg:getCurrentDiagonal()
local screen_d = math.sqrt(Screen:getWidth()^2 + Screen:getHeight()^2)
self:onZoomIn(ges.distance / math.min(screen_d, img_d))
end
self:onZoomIn(inc)
return true
end
function ImageViewer:onPinch(_, ges)
if not self._image_wg then
return
end
-- With Pinch, unlike Spread, it feels more natural if we keep the same center point.
-- Set some zoom decrease value from pinch distance
local dec
if ges.direction == "vertical" then
dec = ges.distance / math.min(Screen:getHeight(), self._image_wg:getCurrentHeight())
local img_h = self._image_wg:getCurrentHeight()
local screen_h = Screen:getHeight()
self:onZoomOut(ges.distance / math.min(screen_h, img_h))
elseif ges.direction == "horizontal" then
dec = ges.distance / math.min(Screen:getWidth(), self._image_wg:getCurrentWidth())
local img_w = self._image_wg:getCurrentWidth()
local screen_w = Screen:getWidth()
self:onZoomOut(ges.distance / math.min(screen_w, img_w))
else
local tl = Geom:new{ x = 0, y = 0 }
local br = Geom:new{ x = Screen:getWidth() - 1, y = Screen:getHeight() - 1}
local screen_diag = tl:distance(br)
dec = ges.distance / math.min(screen_diag, self._image_wg:getCurrentDiagonal())
local img_d = self._image_wg:getCurrentDiagonal()
local screen_d = math.sqrt(Screen:getWidth()^2 + Screen:getHeight()^2)
self:onZoomOut(ges.distance / math.min(screen_d, img_d))
end
self:onZoomOut(dec)
return true
end

@ -456,9 +456,20 @@ function ImageWidget:getCurrentHeight()
end
function ImageWidget:getCurrentDiagonal()
local tl = Geom:new{ x = 0, y = 0 }
local br = Geom:new{ x = self._bb:getWidth() - 1, y = self._bb:getHeight() - 1}
return tl:distance(br)
return math.sqrt(self._bb:getWidth()^2 + self._bb:getHeight()^2)
end
-- And now, getters for the original, unscaled dimensions.
function ImageWidget:getOriginalWidth()
return self._img_w
end
function ImageWidget:getOriginalHeight()
return self._img_h
end
function ImageWidget:getOriginalDiagonal()
return math.sqrt(self._img_w^2 + self._img_h^2)
end
function ImageWidget:getPanByCenterRatio(x, y)

@ -14,10 +14,7 @@ local Screenshoter = InputContainer:new{
}
function Screenshoter:init()
local diagonal = math.sqrt(
math.pow(Screen:getWidth(), 2) +
math.pow(Screen:getHeight(), 2)
)
local diagonal = math.sqrt(Screen:getWidth()^2 + Screen:getHeight()^2)
self.ges_events = {
TapDiagonal = {
GestureRange:new{

@ -8,7 +8,7 @@ describe("defaults module", function()
it("should load all defaults from defaults.lua", function()
Defaults:init()
assert.is_same(99, #Defaults.defaults_name)
assert.is_same(98, #Defaults.defaults_name)
end)
it("should save changes to defaults.persistent.lua", function()
@ -16,7 +16,7 @@ describe("defaults module", function()
os.remove(persistent_filename)
-- To see indices and help updating this when new settings are added:
-- for i=1, 99 do print(i.." ".. Defaults.defaults_name[i]) end
-- for i=1, 98 do print(i.." ".. Defaults.defaults_name[i]) end
-- not in persistent but checked in defaults
Defaults.changed[18] = true
@ -24,7 +24,7 @@ describe("defaults module", function()
Defaults.changed[54] = true
Defaults.changed[83] = true
Defaults:saveSettings()
assert.is_same(99, #Defaults.defaults_name)
assert.is_same(98, #Defaults.defaults_name)
assert.is_same("DTAP_ZONE_BACKWARD", Defaults.defaults_name[84])
assert.is_same("DCREREADER_CONFIG_WORD_SPACING_LARGE", Defaults.defaults_name[48])
assert.is_same("DCREREADER_CONFIG_H_MARGIN_SIZES_XXX_LARGE", Defaults.defaults_name[18])

Loading…
Cancel
Save