mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Non-touch: highlight support (#8877)
readerhighlight: non-touch support focusmanager: fix same type container share same selected field radiobuttonwidget: non touch support sortwidget: non touch support openwithdialog: fix layout contains textinput, checkboxes added to layout twice
This commit is contained in:
@@ -13,6 +13,7 @@ local UIManager = require("ui/uimanager")
|
||||
local dbg = require("dbg")
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
local Size = require("ui/size")
|
||||
local ffiUtil = require("ffi/util")
|
||||
local _ = require("gettext")
|
||||
local C_ = _.pgettext
|
||||
@@ -47,6 +48,28 @@ end
|
||||
|
||||
function ReaderHighlight:init()
|
||||
self.select_mode = false -- extended highlighting
|
||||
self._start_indicator_highlight = false
|
||||
self._current_indicator_pos = nil
|
||||
self._previous_indicator_pos = nil
|
||||
|
||||
if Device:hasDPad() then
|
||||
-- Used for text selection with dpad/keys
|
||||
local QUICK_INDICTOR_MOVE = true
|
||||
self.key_events.StopHighlightIndicator = { {Device.input.group.Back}, doc = "Stop non-touch highlight", args = true } -- true: clear highlight selection
|
||||
self.key_events.UpHighlightIndicator = { {"Up"}, doc = "move indicator up", event = "MoveHighlightIndicator", args = {0, -1} }
|
||||
self.key_events.DownHighlightIndicator = { {"Down"}, doc = "move indicator down", event = "MoveHighlightIndicator", args = {0, 1} }
|
||||
-- let FewKeys device can move indicator left
|
||||
self.key_events.LeftHighlightIndicator = { {"Left"}, doc = "move indicator left", event = "MoveHighlightIndicator", args = {-1, 0} }
|
||||
self.key_events.RightHighlightIndicator = { {"Right"}, doc = "move indicator right", event = "MoveHighlightIndicator", args = {1, 0} }
|
||||
self.key_events.HighlightPress = { {"Press"}, doc = "highlight start or end" }
|
||||
if Device:hasKeys() then
|
||||
self.key_events.QuicklyUpHighlightIndicator = { {"Shift", "Up"}, doc = "quick move indicator up", event = "MoveHighlightIndicator", args = {0, -1, QUICK_INDICTOR_MOVE} }
|
||||
self.key_events.QuicklyDownHighlightIndicator = { {"Shift", "Down"}, doc = "quick move indicator down", event = "MoveHighlightIndicator", args = {0, 1, QUICK_INDICTOR_MOVE} }
|
||||
self.key_events.QuicklyLeftHighlightIndicator = { {"Shift", "Left"}, doc = "quick move indicator left", event = "MoveHighlightIndicator", args = {-1, 0, QUICK_INDICTOR_MOVE} }
|
||||
self.key_events.QuicklyRightHighlightIndicator = { {"Shift", "Right"}, doc = "quick move indicator right", event = "MoveHighlightIndicator", args = {1, 0, QUICK_INDICTOR_MOVE} }
|
||||
self.key_events.StartHighlightIndicator = { {"H"}, doc = "start non-touch highlight" }
|
||||
end
|
||||
end
|
||||
|
||||
self._highlight_buttons = {
|
||||
-- highlight and add_note are for the document itself,
|
||||
@@ -295,6 +318,14 @@ local long_press_action = {
|
||||
|
||||
function ReaderHighlight:addToMainMenu(menu_items)
|
||||
-- insert table to main reader menu
|
||||
if Device:hasDPad() then
|
||||
menu_items.start_content_selection = {
|
||||
text = _("Start content selection"),
|
||||
callback = function()
|
||||
self:onStartHighlightIndicator()
|
||||
end,
|
||||
}
|
||||
end
|
||||
menu_items.highlight_options = {
|
||||
text = _("Highlight style"),
|
||||
sub_item_table = {},
|
||||
@@ -361,14 +392,35 @@ function ReaderHighlight:addToMainMenu(menu_items)
|
||||
end
|
||||
menu_items.translation_settings = Translator:genSettingsMenu()
|
||||
|
||||
if not Device:isTouchDevice() then
|
||||
-- Menu items below aren't needed.
|
||||
return
|
||||
end
|
||||
|
||||
menu_items.long_press = {
|
||||
text = _("Long-press on text"),
|
||||
sub_item_table = {
|
||||
{
|
||||
text = _("Highlight long-press interval"),
|
||||
keep_menu_open = true,
|
||||
callback = function()
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local items = SpinWidget:new{
|
||||
title_text = _("Highlight long-press interval"),
|
||||
info_text = _([[
|
||||
If a touch is not released in this interval, it is considered a long-press. On document text, single word selection will not be triggered.
|
||||
|
||||
The interval value is in seconds and can range from 3 to 20 seconds.]]),
|
||||
width = math.floor(Screen:getWidth() * 0.75),
|
||||
value = G_reader_settings:readSetting("highlight_long_hold_threshold", 3),
|
||||
value_min = 3,
|
||||
value_max = 20,
|
||||
value_step = 1,
|
||||
value_hold_step = 5,
|
||||
ok_text = _("Set interval"),
|
||||
default_value = 3,
|
||||
callback = function(spin)
|
||||
G_reader_settings:saveSetting("highlight_long_hold_threshold", spin.value)
|
||||
end
|
||||
}
|
||||
UIManager:show(items)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Dictionary on single word selection"),
|
||||
checked_func = function()
|
||||
@@ -397,6 +449,12 @@ function ReaderHighlight:addToMainMenu(menu_items)
|
||||
end,
|
||||
})
|
||||
end
|
||||
-- long_press menu is under taps_and_gestures menu which is not available for non touch device
|
||||
-- Clone long_press menu and change label making much meaning for non touch devices
|
||||
if Device:hasDPad() then
|
||||
menu_items.selection_text = util.tableDeepCopy(menu_items.long_press)
|
||||
menu_items.selection_text.text = _("Select on text")
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderHighlight:genPanelZoomMenu()
|
||||
@@ -907,6 +965,7 @@ function ReaderHighlight:onHold(arg, ges)
|
||||
fullscreen = true,
|
||||
}
|
||||
UIManager:show(imgviewer)
|
||||
self:onStopHighlightIndicator()
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -1365,7 +1424,8 @@ function ReaderHighlight:onHoldRelease()
|
||||
local long_final_hold = false
|
||||
if self.hold_last_tv then
|
||||
local hold_duration = TimeVal:now() - self.hold_last_tv
|
||||
if hold_duration > TimeVal:new{ sec = 3, usec = 0 } then
|
||||
local long_hold_threshold = G_reader_settings:readSetting("highlight_long_hold_threshold", 3)
|
||||
if hold_duration > TimeVal:new{ sec = long_hold_threshold, usec = 0 } then
|
||||
-- We stayed 3 seconds before release without updating selection
|
||||
long_final_hold = true
|
||||
end
|
||||
@@ -1806,4 +1866,125 @@ function ReaderHighlight:onClose()
|
||||
self:clear()
|
||||
end
|
||||
|
||||
function ReaderHighlight:onHighlightPress()
|
||||
if self._current_indicator_pos then
|
||||
if not self._start_indicator_highlight then
|
||||
-- try a tap at current indicator position to open any existing highlight
|
||||
if not self:onTap(nil, self:_createHighlightGesture("tap")) then
|
||||
-- no existing highlight at current indicator position: start hold
|
||||
self._start_indicator_highlight = true
|
||||
self:onHold(nil, self:_createHighlightGesture("hold"))
|
||||
if self.selected_text and self.selected_text.sboxes and #self.selected_text.sboxes then
|
||||
local pos = self.selected_text.sboxes[1]
|
||||
-- set hold_pos to center of selected_test to make center selection more stable, not jitted at edge
|
||||
self.hold_pos = self.view:screenToPageTransform({
|
||||
x = pos.x + pos.w / 2,
|
||||
y = pos.y + pos.h / 2
|
||||
})
|
||||
-- move indicator to center selected text making succeed same row selection much accurate.
|
||||
UIManager:setDirty(self.dialog, "ui", self._current_indicator_pos)
|
||||
self._current_indicator_pos.x = pos.x + pos.w / 2 - self._current_indicator_pos.w / 2
|
||||
self._current_indicator_pos.y = pos.y + pos.h / 2 - self._current_indicator_pos.h / 2
|
||||
UIManager:setDirty(self.dialog, "ui", self._current_indicator_pos)
|
||||
end
|
||||
else
|
||||
self:onStopHighlightIndicator(true) -- need_clear_selection=true
|
||||
end
|
||||
else
|
||||
self:onHoldRelease(nil, self:_createHighlightGesture("hold_release"))
|
||||
self:onStopHighlightIndicator()
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ReaderHighlight:onStartHighlightIndicator()
|
||||
if self.view.visible_area and not self._current_indicator_pos then
|
||||
-- set start position to centor of page
|
||||
local rect = self._previous_indicator_pos
|
||||
if not rect then
|
||||
rect = Geom:new()
|
||||
rect.x = self.view.visible_area.w / 2
|
||||
rect.y = self.view.visible_area.h / 2
|
||||
rect.w = Size.item.height_default
|
||||
rect.h = rect.w
|
||||
end
|
||||
self._current_indicator_pos = rect
|
||||
self.view.highlight.indicator = rect
|
||||
UIManager:setDirty(self.dialog, "ui", rect)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ReaderHighlight:onStopHighlightIndicator(need_clear_selection)
|
||||
if self._current_indicator_pos then
|
||||
local rect = self._current_indicator_pos
|
||||
self._previous_indicator_pos = rect
|
||||
self._start_indicator_highlight = false
|
||||
self._current_indicator_pos = nil
|
||||
self.view.highlight.indicator = nil
|
||||
UIManager:setDirty(self.dialog, "ui", rect)
|
||||
if need_clear_selection then
|
||||
self:clear()
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ReaderHighlight:onMoveHighlightIndicator(args)
|
||||
if self.view.visible_area and self._current_indicator_pos then
|
||||
local dx, dy, quick_move = unpack(args)
|
||||
local step_distance = self.view.visible_area.w / 5 -- quick move distance: fifth of visible_area
|
||||
local y_step_distance = self.view.visible_area.h / 5
|
||||
if step_distance > y_step_distance then
|
||||
-- take the smaller, make all direction move distance much predictable
|
||||
step_distance = y_step_distance
|
||||
end
|
||||
if not quick_move then
|
||||
step_distance = step_distance / 4 -- twentieth of visible_area
|
||||
end
|
||||
local rect = self._current_indicator_pos:copy()
|
||||
rect.x = rect.x + step_distance * dx
|
||||
rect.y = rect.y + step_distance * dy
|
||||
if rect.x < 0 then
|
||||
rect.x = 0
|
||||
end
|
||||
if rect.x + rect.w > self.view.visible_area.w then
|
||||
rect.x = self.view.visible_area.w - rect.w
|
||||
end
|
||||
if rect.y < 0 then
|
||||
rect.y = 0
|
||||
end
|
||||
if rect.y + rect.h > self.view.visible_area.h then
|
||||
rect.y = self.view.visible_area.h - rect.h
|
||||
end
|
||||
UIManager:setDirty(self.dialog, "ui", self._current_indicator_pos)
|
||||
self._current_indicator_pos = rect
|
||||
self.view.highlight.indicator = rect
|
||||
UIManager:setDirty(self.dialog, "ui", rect)
|
||||
if self._start_indicator_highlight then
|
||||
self:onHoldPan(nil, self:_createHighlightGesture("hold_pan"))
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ReaderHighlight:_createHighlightGesture(gesture)
|
||||
local point = self._current_indicator_pos:copy()
|
||||
point.x = point.x + point.w / 2
|
||||
point.y = point.y + point.h / 2
|
||||
point.w = 0
|
||||
point.h = 0
|
||||
return {
|
||||
ges = gesture,
|
||||
pos = point,
|
||||
time = TimeVal:realtime(),
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
return ReaderHighlight
|
||||
|
||||
Reference in New Issue
Block a user