From 77accf65469d3014a44129b2162786c1b5f16618 Mon Sep 17 00:00:00 2001 From: Philip Chan Date: Thu, 14 Apr 2022 14:59:36 +0800 Subject: [PATCH] Non-touch: highlight movement improvement and enable page crop (#8947) --- .../apps/reader/modules/readercropping.lua | 12 +- .../apps/reader/modules/readerhighlight.lua | 46 ++++-- frontend/ui/data/koptoptions.lua | 2 +- frontend/ui/widget/bboxwidget.lua | 136 +++++++++++++++++- 4 files changed, 174 insertions(+), 22 deletions(-) diff --git a/frontend/apps/reader/modules/readercropping.lua b/frontend/apps/reader/modules/readercropping.lua index 6499e2084..e8469ca28 100644 --- a/frontend/apps/reader/modules/readercropping.lua +++ b/frontend/apps/reader/modules/readercropping.lua @@ -9,7 +9,8 @@ local InputContainer = require("ui/widget/container/inputcontainer") local Math = require("optmath") local UIManager = require("ui/uimanager") local VerticalGroup = require("ui/widget/verticalgroup") -local Screen = require("device").screen +local Device = require("device") +local Screen = Device.screen local _ = require("gettext") local ReaderCropping = InputContainer:new{} @@ -85,7 +86,11 @@ function ReaderCropping:onPageCrop(mode) } } -- height available for page - local page_container_h = Screen:getHeight() - button_table:getSize().h + local page_container_h = Screen:getHeight() + if Device:isTouchDevice() then + -- non-touch devices do not need cancel and apply buttons + page_container_h = page_container_h - button_table:getSize().h + end local page_dimen = Geom:new{ w = Screen:getWidth(), h = page_container_h, @@ -102,9 +107,8 @@ function ReaderCropping:onPageCrop(mode) self.crop_dialog = VerticalGroup:new{ align = "left", self.bbox_widget, - button_container, + (Device:isTouchDevice() and button_container) or nil, -- button bar only availble for touch devices } - UIManager:show(self.crop_dialog) return true end diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index 45e006250..8ccd30e67 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -51,6 +51,7 @@ function ReaderHighlight:init() self._start_indicator_highlight = false self._current_indicator_pos = nil self._previous_indicator_pos = nil + self._last_indicator_move_args = {dx = 0, dy = 0, distance = 0, time = TimeVal:now()} if Device:hasDPad() then -- Used for text selection with dpad/keys @@ -63,10 +64,10 @@ function ReaderHighlight:init() 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.QuickUpHighlightIndicator = { {"Shift", "Up"}, doc = "quick move indicator up", event = "MoveHighlightIndicator", args = {0, -1, QUICK_INDICTOR_MOVE} } + self.key_events.QuickDownHighlightIndicator = { {"Shift", "Down"}, doc = "quick move indicator down", event = "MoveHighlightIndicator", args = {0, 1, QUICK_INDICTOR_MOVE} } + self.key_events.QuickLeftHighlightIndicator = { {"Shift", "Left"}, doc = "quick move indicator left", event = "MoveHighlightIndicator", args = {-1, 0, QUICK_INDICTOR_MOVE} } + self.key_events.QuickRightHighlightIndicator = { {"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 @@ -1938,18 +1939,33 @@ 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 quick_move_distance_dx = self.view.visible_area.w / 5 -- quick move distance: fifth of visible_area + local quick_move_distance_dy = self.view.visible_area.h / 5 + -- single move distance, small and capable to move on word with small font size and narrow line height + local move_distance = Size.item.height_default / 4 local rect = self._current_indicator_pos:copy() - rect.x = rect.x + step_distance * dx - rect.y = rect.y + step_distance * dy + if quick_move then + rect.x = rect.x + quick_move_distance_dx * dx + rect.y = rect.y + quick_move_distance_dy * dy + else + local now = TimeVal:now() + if dx == self._last_indicator_move_args.dx and dy == self._last_indicator_move_args.dy then + local diff = now - self._last_indicator_move_args.time + -- if press same arrow key in 1 second, speed up + -- double press: 4 single move distances, usually move to next word or line + -- triple press: 16 single distances, usually skip several words or lines + -- quadruple press: 54 single distances, almost move to screen edge + if diff < TimeVal:new{ sec = 1, usec = 0 } then + move_distance = self._last_indicator_move_args.distance * 4 + end + end + rect.x = rect.x + move_distance * dx + rect.y = rect.y + move_distance * dy + self._last_indicator_move_args.distance = move_distance + self._last_indicator_move_args.dx = dx + self._last_indicator_move_args.dy = dy + self._last_indicator_move_args.time = now + end if rect.x < 0 then rect.x = 0 end diff --git a/frontend/ui/data/koptoptions.lua b/frontend/ui/data/koptoptions.lua index a6dd67389..bbcb46f19 100644 --- a/frontend/ui/data/koptoptions.lua +++ b/frontend/ui/data/koptoptions.lua @@ -87,7 +87,7 @@ local KoptOptions = { alternate = false, values = {3, 1, 2, 0}, default_value = DKOPTREADER_CONFIG_TRIM_PAGE, - enabled_func = Device.isTouchDevice, + enabled_func = function() return Device:isTouchDevice() or Device:hasDPad() end, event = "PageCrop", args = {"none", "auto", "semi-auto", "manual"}, name_text_hold_callback = optionsutil.showValues, diff --git a/frontend/ui/widget/bboxwidget.lua b/frontend/ui/widget/bboxwidget.lua index c8bfe2aa5..ef72e672b 100644 --- a/frontend/ui/widget/bboxwidget.lua +++ b/frontend/ui/widget/bboxwidget.lua @@ -8,6 +8,7 @@ local Event = require("ui/event") local Geom = require("ui/geometry") local GestureRange = require("ui/gesturerange") local Math = require("optmath") +local Screen = Device.screen local Size = require("ui/size") local UIManager = require("ui/uimanager") @@ -48,6 +49,12 @@ function BBoxWidget:init() } } } + else + self._confirm_stage = 1 -- 1 for left-top, 2 for right-bottom + self.key_events.MoveIndicatorUp = { { "Up" }, doc="Move indicator up", event="MoveIndicator", args = { 0, -1 } } + self.key_events.MoveIndicatorDown = { { "Down" }, doc="Move indicator down", event="MoveIndicator", args = { 0, 1 } } + self.key_events.MoveIndicatorLeft = { { "Left" }, doc="Move indicator left", event="MoveIndicator", args = { -1, 0 } } + self.key_events.MoveIndicatorRight = { { "Right" }, doc="Move indicator right", event="MoveIndicator", args = { 1, 0 } } end if Device:hasKeys() then self.key_events.Close = { {Device.input.group.Back}, doc = "close windows" } @@ -74,6 +81,35 @@ function BBoxWidget:paintTo(bb, x, y) bb:invertRect(bbox.x0, bbox.y0, self.linesize, bbox.y1 - bbox.y0 + self.linesize) -- right edge bb:invertRect(bbox.x1, bbox.y0 + self.linesize, self.linesize, bbox.y1 - bbox.y0) + if self._confirm_stage == 1 then + -- left top indicator + self:_drawIndicator(bb, bbox.x0, bbox.y0) + elseif self._confirm_stage == 2 then + -- right bottom indicator + self:_drawIndicator(bb, bbox.x1, bbox.y1) + end +end + +function BBoxWidget:_drawIndicator(bb, x, y) + local rect = Geom:new({ + x = x - Size.item.height_default / 2, + y = y - Size.item.height_default / 2, + w = Size.item.height_default, + h = Size.item.height_default, + }) + -- paint big cross line + + bb:invertRect( + rect.x, + rect.y + rect.h / 2 - Size.border.thick / 2, + rect.w, + Size.border.thick + ) + bb:invertRect( + rect.x + rect.w / 2 - Size.border.thick / 2, + rect.y, + Size.border.thick, + rect.h + ) end -- transform page bbox to screen bbox @@ -194,7 +230,82 @@ function BBoxWidget:adjustScreenBBox(ges, relative) y1 = Math.round(bottom_right.y) } - UIManager:setDirty("all") + UIManager:setDirty(self.ui, "ui") +end + +function BBoxWidget:onMoveIndicator(args) + local dx, dy = unpack(args) + local bbox = self.screen_bbox + local move_distance = Size.item.height_default / 4 + local half_indicator_size = move_distance * 2 + -- mark edges dirty to redraw + -- top edge + UIManager:setDirty(self.ui, "ui", Geom:new{ + x = bbox.x0 - move_distance, + y = bbox.y0 - move_distance, + w = bbox.x1 - bbox.x0 + move_distance, + h = move_distance * 2 + }) + -- left edge + UIManager:setDirty(self.ui, "ui", Geom:new{ + x = bbox.x0 - move_distance, + y = bbox.y0 - move_distance, + w = move_distance * 2, + h = bbox.y1 - bbox.y0 + move_distance, + }) + -- right edge + UIManager:setDirty(self.ui, "ui", Geom:new{ + x = bbox.x1 - move_distance, + y = bbox.y0 - move_distance, + w = move_distance * 2, + h = bbox.y1 - bbox.y0 + move_distance, + }) + -- bottom edge + UIManager:setDirty(self.ui, "ui", Geom:new{ + x = bbox.x0 - move_distance, + y = bbox.y1 - move_distance, + w = bbox.x1 - bbox.x0 + move_distance, + h = move_distance * 2, + }) + -- left top indicator + UIManager:setDirty(self.ui, "ui", Geom:new{ + x = bbox.x0 - half_indicator_size - move_distance, + y = bbox.y0 - half_indicator_size - move_distance, + w = half_indicator_size * 2 + move_distance, + h = half_indicator_size * 2 + move_distance + }) + -- bottom right indicator + UIManager:setDirty(self.ui, "ui", Geom:new{ + x = bbox.x1 - half_indicator_size, + y = bbox.y1 - half_indicator_size, + w = half_indicator_size * 2 + move_distance, + h = half_indicator_size * 2 + move_distance + }) + if self._confirm_stage == 1 then + local x = self.screen_bbox.x0 + dx * Size.item.height_default / 4 + local y = self.screen_bbox.y0 + dy * Size.item.height_default / 4 + local max_x = self.screen_bbox.x1 - Size.item.height_default + local max_y = self.screen_bbox.y1 - Size.item.height_default + x = (x > 0 and x) or 0 + x = (x < max_x and x) or max_x + y = (y > 0 and y) or 0 + y = (y < max_y and y) or max_y + self.screen_bbox.x0 = Math.round(x) + self.screen_bbox.y0 = Math.round(y) + return true + elseif self._confirm_stage == 2 then + local x = self.screen_bbox.x1 + dx * Size.item.height_default / 4 + local y = self.screen_bbox.y1 + dy * Size.item.height_default / 4 + local min_x = self.screen_bbox.x0 + Size.item.height_default + local min_y = self.screen_bbox.y0 + Size.item.height_default + x = (x > min_x and x) or min_x + x = (x < Screen:getWidth() and x) or Screen:getWidth() + y = (y > min_y and y) or min_y + y = (y < Screen:getHeight() and y) or Screen:getHeight() + self.screen_bbox.x1 = Math.round(x) + self.screen_bbox.y1 = Math.round(y) + return true + end end function BBoxWidget:getModifiedPageBBox() @@ -228,10 +339,31 @@ end function BBoxWidget:onClose() self.ui:handleEvent(Event:new("CancelPageCrop")) + return true end function BBoxWidget:onSelect() - self.ui:handleEvent(Event:new("ConfirmPageCrop")) + if not self._confirm_stage or self._confirm_stage == 2 then + self.ui:handleEvent(Event:new("ConfirmPageCrop")) + else + local bbox = self.screen_bbox + self._confirm_stage = self._confirm_stage + 1 + -- left top indicator + UIManager:setDirty(self.ui, "ui", Geom:new{ + x = bbox.x0 - Size.item.height_default / 2, + y = bbox.y0 - Size.item.height_default / 2, + w = Size.item.height_default, + h = Size.item.height_default, + }) + -- right bottom indicator + UIManager:setDirty(self.ui, "ui", Geom:new{ + x = bbox.x1 - Size.item.height_default / 2, + y = bbox.y1 - Size.item.height_default / 2, + w = Size.item.height_default, + h = Size.item.height_default, + }) + end + return true end