From 29d83b67d3abf54f197e9c245275779a075f4794 Mon Sep 17 00:00:00 2001 From: poire-z Date: Wed, 26 Aug 2020 20:44:58 +0200 Subject: [PATCH] Text/HTML widgets: allow scrolling with the scrollbar By tapping on or panning the scrollbar. --- frontend/ui/widget/scrollhtmlwidget.lua | 9 ++- frontend/ui/widget/scrolltextwidget.lua | 13 +++- frontend/ui/widget/textboxwidget.lua | 27 +++++++-- frontend/ui/widget/verticalscrollbar.lua | 77 +++++++++++++++++++++++- 4 files changed, 116 insertions(+), 10 deletions(-) diff --git a/frontend/ui/widget/scrollhtmlwidget.lua b/frontend/ui/widget/scrollhtmlwidget.lua index 1cd2869f6..1e25999fe 100644 --- a/frontend/ui/widget/scrollhtmlwidget.lua +++ b/frontend/ui/widget/scrollhtmlwidget.lua @@ -12,7 +12,6 @@ local HorizontalSpan = require("ui/widget/horizontalspan") local InputContainer = require("ui/widget/container/inputcontainer") local UIManager = require("ui/uimanager") local VerticalScrollBar = require("ui/widget/verticalscrollbar") -local Math = require("optmath") local Input = Device.input local Screen = Device.screen @@ -46,6 +45,9 @@ function ScrollHtmlWidget:init() enable = self.htmlbox_widget.page_count > 1, width = self.scroll_bar_width, height = self.height, + scroll_callback = function(ratio) + self:scrollToRatio(ratio) + end } self.v_scroll_bar:set((self.htmlbox_widget.page_number-1) / self.htmlbox_widget.page_count, self.htmlbox_widget.page_number / self.htmlbox_widget.page_count) @@ -89,7 +91,10 @@ end function ScrollHtmlWidget:scrollToRatio(ratio) ratio = math.max(0, math.min(1, ratio)) -- ensure ratio is between 0 and 1 (100%) - local page_num = 1 + Math.round((self.htmlbox_widget.page_count - 1) * ratio) + local page_num = 1 + math.floor((self.htmlbox_widget.page_count) * ratio) + if page_num > self.htmlbox_widget.page_count then + page_num = self.htmlbox_widget.page_count + end if page_num == self.htmlbox_widget.page_number then return end diff --git a/frontend/ui/widget/scrolltextwidget.lua b/frontend/ui/widget/scrolltextwidget.lua index 2f49089ea..d1056f03b 100644 --- a/frontend/ui/widget/scrolltextwidget.lua +++ b/frontend/ui/widget/scrolltextwidget.lua @@ -71,6 +71,9 @@ function ScrollTextWidget:init() high = visible_line_count / total_line_count, width = self.scroll_bar_width, height = self.text_widget:getTextHeight(), + scroll_callback = function(ratio) + self:scrollToRatio(ratio, false) + end } self:updateScrollBar() local horizontal_group = HorizontalGroup:new{ align = "top" } @@ -219,8 +222,14 @@ function ScrollTextWidget:scrollText(direction) self:updateScrollBar(true) end -function ScrollTextWidget:scrollToRatio(ratio) - self.text_widget:scrollToRatio(ratio) +function ScrollTextWidget:scrollToRatio(ratio, force_to_page) + if force_to_page == nil then + -- default to force to page, for consistency with + -- ScrollHtmlWidget that always forces to page (for + -- DictQuickLookup when going back to previous dict) + force_to_page = true + end + self.text_widget:scrollToRatio(ratio, force_to_page) self:updateScrollBar(true) end diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index c05fd5f73..db9a6bcfb 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -87,6 +87,7 @@ local TextBoxWidget = InputContainer:new{ image_padding_bottom = Screen:scaleBySize(3), image_alt_face = Font:getFace("xx_smallinfofont"), image_alt_fgcolor = Blitbuffer.COLOR_BLACK, + scroll_force_to_page = false, -- will be forced to true if images -- Additional properties only used when using xtext use_xtext = G_reader_settings:nilOrTrue("use_xtext"), @@ -249,6 +250,11 @@ function TextBoxWidget:_splitToLines() local ln = 1 local offset, end_offset, cur_line_width + if self.images and #self.images > 0 then + -- Force scrolling to align to top of pages, as we + -- expect to draw images only at top of view + self.scroll_force_to_page = true + end local image_num = 0 local targeted_width = self.width local image_lines_remaining = 0 @@ -1227,12 +1233,25 @@ function TextBoxWidget:scrollToBottom() end -function TextBoxWidget:scrollToRatio(ratio) +function TextBoxWidget:scrollToRatio(ratio, force_to_page) self.image_show_alt_text = nil + local line_num ratio = math.max(0, math.min(1, ratio)) -- ensure ratio is between 0 and 1 (100%) - local page_count = 1 + math.floor((#self.vertical_string_list - 1) / self.lines_per_page) - local page_num = 1 + Math.round((page_count - 1) * ratio) - local line_num = 1 + (page_num - 1) * self.lines_per_page + if force_to_page or self.scroll_force_to_page then + -- We want scroll to align to original pages + local page_count = 1 + math.floor((#self.vertical_string_list - 1) / self.lines_per_page) + local page_num = 1 + Math.round((page_count - 1) * ratio) + line_num = 1 + (page_num - 1) * self.lines_per_page + else + -- We want the middle of page to show at ratio, so remove self.lines_per_page/2 + line_num = 1 + math.floor(ratio * #self.vertical_string_list - self.lines_per_page/2) + if line_num + self.lines_per_page > #self.vertical_string_list then + line_num = #self.vertical_string_list - self.lines_per_page + 1 + end + if line_num < 1 then + line_num = 1 + end + end if line_num ~= self.virtual_line_num then self:free(false) self.virtual_line_num = line_num diff --git a/frontend/ui/widget/verticalscrollbar.lua b/frontend/ui/widget/verticalscrollbar.lua index 120e3c0c7..5ac76eabb 100644 --- a/frontend/ui/widget/verticalscrollbar.lua +++ b/frontend/ui/widget/verticalscrollbar.lua @@ -1,9 +1,12 @@ local Blitbuffer = require("ffi/blitbuffer") +local Device = require("device") local Geom = require("ui/geometry") +local GestureRange = require("ui/gesturerange") +local InputContainer = require("ui/widget/container/inputcontainer") local Size = require("ui/size") -local Widget = require("ui/widget/widget") +local Screen = require("device").screen -local VerticalScrollBar = Widget:new{ +local VerticalScrollBar = InputContainer:new{ enable = true, low = 0, high = 1, @@ -16,8 +19,72 @@ local VerticalScrollBar = Widget:new{ -- minimal height of the thumb/knob/grip (usually showing the current -- view size and position relative to the whole scrollable height): min_thumb_size = Size.line.thick, + scroll_callback = nil, + -- extra touchable width (for scrolling with pan) can be larger than + -- the provided width (this is added on each side) + extra_touch_on_side_width_ratio = 1, -- make it 3 x width } +function VerticalScrollBar:init() + self.extra_touch_on_side = math.ceil( self.extra_touch_on_side_width_ratio * self.width ) + if Device:isTouchDevice() then + local pan_rate = Screen.low_pan_rate and 2.0 or 5.0 + self.ges_events = { + TapScroll = { + GestureRange:new{ + ges = "tap", + range = function() return self.touch_dimen end, + }, + }, + HoldScroll = { + GestureRange:new{ + ges = "hold", + range = function() return self.touch_dimen end, + }, + }, + HoldPanScroll = { + GestureRange:new{ + ges = "hold_pan", + rate = pan_rate, + range = function() return self.touch_dimen end, + }, + }, + HoldReleaseScroll = { + GestureRange:new{ + ges = "hold_release", + range = function() return self.touch_dimen end, + }, + }, + PanScroll = { + GestureRange:new{ + ges = "pan", + rate = pan_rate, + range = function() return self.touch_dimen end, + }, + }, + PanScrollRelease = { + GestureRange:new{ + ges = "pan_release", + range = function() return self.touch_dimen end, + }, + } + } + end +end + +function VerticalScrollBar:onTapScroll(arg, ges) + if self.scroll_callback then + local ratio = (ges.pos.y - self.touch_dimen.y) / self.height + self.scroll_callback(ratio) + return true + end +end +VerticalScrollBar.onHoldScroll = VerticalScrollBar.onTapScroll +VerticalScrollBar.onHoldPanScroll = VerticalScrollBar.onTapScroll +VerticalScrollBar.onHoldReleaseScroll = VerticalScrollBar.onTapScroll +VerticalScrollBar.onPanScroll = VerticalScrollBar.onTapScroll +VerticalScrollBar.onPanScrollRelease = VerticalScrollBar.onTapScroll + function VerticalScrollBar:getSize() return Geom:new{ w = self.width, @@ -32,6 +99,12 @@ end function VerticalScrollBar:paintTo(bb, x, y) if not self.enable then return end + self.touch_dimen = Geom:new{ + x = x - self.extra_touch_on_side, + y = y, + w = self.width + 2 * self.extra_touch_on_side, + h = self.height, + } bb:paintBorder(x, y, self.width, self.height, self.bordersize, self.bordercolor, self.radius) bb:paintRect(x + self.bordersize, y + self.bordersize + self.low * self.height,