readerhighlight: remove selected_word and use selected_text everywhere

There were two ways of specifing selected text for a highlight depending
on whether it was a "single word" or text selected using hold-and-pan.
In addition to being a bit more complicated than is necessary, with the
addition of the language support plugin system (where the "single word"
selected might be expanded), it makes more sense to simply use the same
logic and table structure for both cases.

The dictionary lookup special case (hold-without-pan triggering a
dictionary lookup by default) still works as before.

In addition, this patch fixes a minor inefficiency during dictionary
quick lookup -- before this patch, the highlight would be re-selected
because the quick lookup window is run concurrently and tries to fetch
ReaderHighlight.selected_text but this is set to nil immediately after
triggering the lookup. This is unnecessary because :clear() will be
called anyway when the quick pop-up closes, and so clearing this can be
left until then.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
Aleksa Sarai
2021-10-23 21:12:56 +11:00
committed by Frans de Jonge
parent 3ffb4c1692
commit 7a0e3d5e68
3 changed files with 83 additions and 49 deletions

View File

@@ -2,6 +2,7 @@ local BD = require("ui/bidi")
local ButtonDialog = require("ui/widget/buttondialog")
local Device = require("device")
local Event = require("ui/event")
local Geom = require("ui/geometry")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local Notification = require("ui/widget/notification")
@@ -436,6 +437,7 @@ function ReaderHighlight:clear(clear_id)
self.restore_page_mode_func()
self.restore_page_mode_func = nil
end
self.is_word_selection = false
self.selected_text_start_xpointer = nil
if self.hold_pos then
self.hold_pos = nil
@@ -844,7 +846,16 @@ function ReaderHighlight:onHold(arg, ges)
local ok, word = pcall(self.ui.document.getWordFromPosition, self.ui.document, self.hold_pos)
if ok and word then
logger.dbg("selected word:", word)
self.selected_word = word
-- Convert "word selection" table to "text selection" table because we
-- use text selections throughout readerhighlight.
self.is_word_selection = true
self.selected_text = {
text = word.word or "",
pos0 = word.pos0 or word.pos,
pos1 = word.pos1 or word.pos,
sboxes = word.sbox and { word.sbox },
pboxes = word.pbox and { word.pbox },
}
local link = self.ui.link:getLinkFromGes(ges)
self.selected_link = nil
if link then
@@ -852,15 +863,13 @@ function ReaderHighlight:onHold(arg, ges)
self.selected_link = link
end
if self.ui.document.info.has_pages then
local boxes = {}
table.insert(boxes, self.selected_word.sbox)
self.view.highlight.temp[self.hold_pos.page] = boxes
self.view.highlight.temp[self.hold_pos.page] = self.selected_text.sboxes
-- Unfortunately, getWordFromPosition() may not return good coordinates,
-- so refresh the whole page
UIManager:setDirty(self.dialog, "ui")
else
-- With crengine, getWordFromPosition() does return good coordinates
UIManager:setDirty(self.dialog, "ui", self.selected_word.sbox)
-- With crengine, getWordFromPosition() does return good coordinates.
UIManager:setDirty(self.dialog, "ui", Geom.boundingBox(self.selected_text.sboxes))
end
self:_resetHoldTimer()
if word.pos0 then
@@ -992,6 +1001,7 @@ function ReaderHighlight:onHoldPan(_, ges)
local old_text = self.selected_text and self.selected_text.text
self.selected_text = self.ui.document:getTextFromPositions(self.hold_pos, self.holdpan_pos)
self.is_word_selection = false
if self.selected_text and self.selected_text.pos0 then
if not self.selected_text_start_xpointer then
@@ -1015,13 +1025,6 @@ function ReaderHighlight:onHoldPan(_, ges)
logger.dbg("selected text:", self.selected_text)
if self.selected_text then
self.view.highlight.temp[self.hold_pos.page] = self.selected_text.sboxes
-- remove selected word if hold moves out of word box
if not self.selected_text.sboxes or #self.selected_text.sboxes == 0 then
self.selected_word = nil
elseif self.selected_word and not self.selected_word.sbox:contains(self.selected_text.sboxes[1]) or
#self.selected_text.sboxes > 1 then
self.selected_word = nil
end
end
UIManager:setDirty(self.dialog, "ui")
end
@@ -1035,18 +1038,36 @@ You can download language data files for version 3.04 from https://tesseract-ocr
Copy the language data files for Tesseract 3.04 (e.g., eng.traineddata for English and spa.traineddata for Spanish) into koreader/data/tessdata]])
function ReaderHighlight:lookup(selected_word, selected_link)
function ReaderHighlight:lookup(selected_text, selected_link)
-- convert sboxes to word boxes
local word_boxes = {}
for i, sbox in ipairs(selected_text.sboxes) do
word_boxes[i] = self.view:pageToScreenTransform(self.hold_pos.page, sbox)
end
-- if we extracted text directly
if selected_word.word and self.hold_pos then
local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox)
self.ui:handleEvent(Event:new("LookupWord", selected_word.word, false, word_box, self, selected_link))
if selected_text.text and self.hold_pos then
self.ui:handleEvent(Event:new("LookupWord", selected_text.text, false, word_boxes, self, selected_link))
-- or we will do OCR
elseif selected_word.sbox and self.hold_pos then
local word = self.ui.document:getOCRWord(self.hold_pos.page, selected_word)
logger.dbg("OCRed word:", word)
if word and word ~= "" then
local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox)
self.ui:handleEvent(Event:new("LookupWord", word, false, word_box, self, selected_link))
elseif selected_text.sboxes and self.hold_pos then
local text = self.ui.document:getOCRText(self.hold_pos.page, selected_text.sboxes)
if not text then
-- getOCRText is not implemented in some document backends, but
-- getOCRWord is implemented everywhere. As such, fall back to
-- getOCRWord.
text = ""
for _, sbox in ipairs(selected_text.sboxes) do
local word = self.ui.document:getOCRWord(self.hold_pos.page, { sbox = sbox })
logger.dbg("OCRed word:", word)
-- @fixme This might produce incorrect results on RTL text.
if word and word ~= "" then
text = text .. word
end
end
end
logger.dbg("OCRed text:", text)
if text and text ~= "" then
self.ui:handleEvent(Event:new("LookupWord", text, false, word_boxes, self, selected_link))
else
UIManager:show(InfoMessage:new{
text = info_message_ocr_text,
@@ -1227,15 +1248,15 @@ function ReaderHighlight:onHoldRelease()
end
self.hold_last_tv = nil
end
if self.selected_word then -- single-word selection
if self.is_word_selection then -- single-word selection
if long_final_hold or G_reader_settings:isTrue("highlight_action_on_single_word") then
-- Force a 0-distance pan to have a self.selected_text with this word,
-- which will enable the highlight menu or action instead of dict lookup
self:onHoldPan(nil, {pos=self.hold_ges_pos})
self.is_word_selection = false
end
end
if self.selected_text then
if self.is_word_selection then
self:lookup(self.selected_text, self.selected_link)
else
local default_highlight_action = G_reader_settings:readSetting("default_highlight_action", "ask")
if long_final_hold or default_highlight_action == "ask" then
-- bypass default action and show popup if long final hold
@@ -1257,9 +1278,6 @@ function ReaderHighlight:onHoldRelease()
-- No self:onClose() to not remove the selected text
-- which will have been the first search result
end
elseif self.selected_word then
self:lookup(self.selected_word, self.selected_link)
self.selected_word = nil
end
return true
end