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", "DCREREADER_CONFIG_WORD_EXPANSION_MORE",
"DMINIBAR_CONTAINER_HEIGHT", "DMINIBAR_CONTAINER_HEIGHT",
"DGESDETECT_DISABLE_DOUBLE_TAP", "DGESDETECT_DISABLE_DOUBLE_TAP",
"FRONTLIGHT_SENSITIVITY_DECREASE",
"DALPHA_SORT_CASE_INSENSITIVE", "DALPHA_SORT_CASE_INSENSITIVE",
"KOBO_LIGHT_ON_START", "KOBO_LIGHT_ON_START",
"NETWORK_PROXY", "NETWORK_PROXY",

@ -215,10 +215,6 @@ DMINIBAR_CONTAINER_HEIGHT = 14 -- Larger means more padding at the bottom, at t
-- no longer needed -- no longer needed
--DDICT_FONT_SIZE = 20 --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 -- 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". -- 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 -- 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 -- default gamma from crengine's lvfntman.cpp
gamma_index = nil, gamma_index = nil,
steps = {0,1,1,1,1,1,2,2,2,3,3,3,4,4,5}, 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() function ReaderFont:init()
@ -369,11 +368,24 @@ function ReaderFont:addToMainMenu(menu_items)
end end
function ReaderFont:gesToFontSize(ges) function ReaderFont:gesToFontSize(ges)
-- Dispatcher feeds us a number, not a gesture
if type(ges) ~= "table" then return ges end if type(ges) ~= "table" then return ges end
if ges.distance == nil then if ges.distance == nil then
ges.distance = 1 ges.distance = 1
end 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] local delta_int = self.steps[step] or self.steps[#self.steps]
return delta_int return delta_int
end end

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

@ -1061,13 +1061,18 @@ function Contact:handleTwoFingerPan(buddy_contact)
w = 0, w = 0,
h = 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 start_distance = tstart_pos:distance(rstart_pos)
local end_distance = tend_pos:distance(rend_pos) local end_distance = tend_pos:distance(rend_pos)
local ges_ev = { local ges_ev = {
ges = "two_finger_pan", ges = "two_finger_pan",
-- Use midpoint of tstart and rstart as swipe start point pos = start_point,
pos = tstart_pos:midpoint(rstart_pos), distance = avg_distance,
distance = tpan_dis + rpan_dis,
direction = tpan_dir, direction = tpan_dir,
time = self.current_tev.timev, time = self.current_tev.timev,
} }
@ -1078,6 +1083,11 @@ function Contact:handleTwoFingerPan(buddy_contact)
ges_ev.ges = "outward_pan" ges_ev.ges = "outward_pan"
end end
ges_ev.direction = gesture_detector.DIRECTION_TABLE[tpan_dir] 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 elseif self.state == Contact.holdState then
ges_ev.ges = "two_finger_hold_pan" ges_ev.ges = "two_finger_hold_pan"
-- Flag 'em for holdState to discriminate with two_finger_hold_release -- 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 @tparam Geom rect_b
]] ]]
function Geom:distance(geom) 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 end
--[[-- --[[--

@ -103,7 +103,7 @@ function ImageViewer:init()
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), 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 = { self.ges_events = {
Tap = { GestureRange:new{ ges = "tap", range = range } }, Tap = { GestureRange:new{ ges = "tap", range = range } },
-- Zoom in/out (Pinch & Spread are not triggered if user is too -- Zoom in/out (Pinch & Spread are not triggered if user is too
@ -636,48 +636,57 @@ function ImageViewer:onPanRelease(_, ges)
end end
-- Zoom events -- Zoom events
function ImageViewer:onZoomIn(inc) function ImageViewer:_refreshScaleFactor()
if self.scale_factor == 0 then if self.scale_factor == 0 then
-- Get the scale_factor made out for best fit -- Get the scale_factor made out for best fit
self.scale_factor = self._scale_factor_0 or self._image_wg:getScaleFactor() self.scale_factor = self._scale_factor_0 or self._image_wg:getScaleFactor()
end end
end
if not inc then function ImageViewer:_applyNewScaleFactor(new_factor)
-- default for key zoom event -- Make sure self.scale_factor is up-to-date
inc = 0.2 self:_refreshScaleFactor()
end
-- Compute new scale factor for rescaled image dimensions
local new_factor = self.scale_factor * (1 + inc)
-- We destroy ImageWidget on update, so only request this the first time, -- 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... -- 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() self._min_scale_factor, self._max_scale_factor = self._image_wg:getScaleFactorExtrema()
end end
-- Clamp to sane values -- Clamp to sane values
new_factor = math.min(new_factor, self._max_scale_factor) 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 if new_factor ~= self.scale_factor then
self.scale_factor = new_factor self.scale_factor = new_factor
self:update() self:update()
else else
if self.scale_factor == self._max_scale_factor then if self.scale_factor == self._min_scale_factor then
logger.dbg("ImageViewer:onZoomIn: Hit the max scaling factor:", self.scale_factor) 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 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 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 return true
end end
function ImageViewer:onZoomOut(dec) function ImageViewer:onZoomOut(dec)
if self.scale_factor == 0 then self:_refreshScaleFactor()
-- Get the scale_factor made out for best fit
self.scale_factor = self._scale_factor_0 or self._image_wg:getScaleFactor()
end
if not dec then if not dec then
-- default for key zoom event
dec = 0.2 dec = 0.2
elseif dec >= 0.75 then elseif dec >= 0.75 then
-- Larger reductions tend to be fairly jarring, so limit to 75%. -- Larger reductions tend to be fairly jarring, so limit to 75%.
@ -685,70 +694,90 @@ function ImageViewer:onZoomOut(dec)
dec = 0.75 dec = 0.75
end end
-- Compute new scale factor for rescaled image dimensions
local new_factor = self.scale_factor * (1 - dec) 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() function ImageViewer:onZoomToHeight(height)
end local new_factor = height / self._image_wg:getOriginalHeight()
-- Clamp to sane values self:_applyNewScaleFactor(new_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._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
return true return true
end 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) function ImageViewer:onSpread(_, ges)
if not self._image_wg then
return
end
-- We get the position where spread was done -- We get the position where spread was done
-- First, get center ratio we would have had if we did a pan to there, -- First, get center ratio we would have had if we did a pan to there,
-- so we can have the zoom centered on 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)
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),
end -- based on the gesture distance (it's the sum of the travel of both fingers).
-- Set some zoom increase value from pinch distance. -- Making this distance relative to the smallest dimension between
-- Making it relative to the smallest dimension between the currently scaled image or the Screen makes it less annoying -- the currently scaled image or the Screen makes it less annoying when approaching both very small scale factors
-- when approaching both very small scale factors (where the image dimensions are many times smaller than the screen), -- (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; -- 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), -- *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. -- meaning using the screen dimensions here makes zoom steps, again, slightly more potent.
local inc
if ges.direction == "vertical" then 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 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 else
local tl = Geom:new{ x = 0, y = 0 } local img_d = self._image_wg:getCurrentDiagonal()
local br = Geom:new{ x = Screen:getWidth() - 1, y = Screen:getHeight() - 1} local screen_d = math.sqrt(Screen:getWidth()^2 + Screen:getHeight()^2)
local screen_diag = tl:distance(br) self:onZoomIn(ges.distance / math.min(screen_d, img_d))
inc = ges.distance / math.min(screen_diag, self._image_wg:getCurrentDiagonal())
end end
self:onZoomIn(inc)
return true return true
end end
function ImageViewer:onPinch(_, ges) 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. -- 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 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 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 else
local tl = Geom:new{ x = 0, y = 0 } local img_d = self._image_wg:getCurrentDiagonal()
local br = Geom:new{ x = Screen:getWidth() - 1, y = Screen:getHeight() - 1} local screen_d = math.sqrt(Screen:getWidth()^2 + Screen:getHeight()^2)
local screen_diag = tl:distance(br) self:onZoomOut(ges.distance / math.min(screen_d, img_d))
dec = ges.distance / math.min(screen_diag, self._image_wg:getCurrentDiagonal())
end end
self:onZoomOut(dec)
return true return true
end end

@ -456,9 +456,20 @@ function ImageWidget:getCurrentHeight()
end end
function ImageWidget:getCurrentDiagonal() function ImageWidget:getCurrentDiagonal()
local tl = Geom:new{ x = 0, y = 0 } return math.sqrt(self._bb:getWidth()^2 + self._bb:getHeight()^2)
local br = Geom:new{ x = self._bb:getWidth() - 1, y = self._bb:getHeight() - 1} end
return tl:distance(br)
-- 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 end
function ImageWidget:getPanByCenterRatio(x, y) function ImageWidget:getPanByCenterRatio(x, y)

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

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

Loading…
Cancel
Save