diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index 65cca2fdf..355b7004b 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -2012,7 +2012,10 @@ function TextBoxWidget:onHoldReleaseText(callback, ges) logger.dbg("onHoldReleaseText (duration:", time.format_time(hold_duration), ") :", sel_start_idx, ">", sel_end_idx, "=", selected_text) - callback(selected_text, hold_duration) + -- We give index in the charlist (unicode chars), and provide a function + -- to convert these indices as in the utf8 text, to be used by caller + -- only if needed, as it may be expensive. + callback(selected_text, hold_duration, sel_start_idx, sel_end_idx, function(idx) return self:getSourceIndex(idx) end) return true end @@ -2029,7 +2032,7 @@ function TextBoxWidget:onHoldReleaseText(callback, ges) local selected_text = table.concat(self.charlist, "", sel_start_idx, sel_end_idx) logger.dbg("onHoldReleaseText (duration:", time.format_time(hold_duration), ") :", sel_start_idx, ">", sel_end_idx, "=", selected_text) - callback(selected_text, hold_duration) + callback(selected_text, hold_duration, sel_start_idx, sel_end_idx, function(idx) return self:getSourceIndex(idx) end) return true end @@ -2085,4 +2088,14 @@ function TextBoxWidget:_findWordEdge(x, y, side) return edge_idx end +function TextBoxWidget:getSourceIndex(char_idx) + if self._xtext then + local utf8 = self._xtext:getText(1, char_idx) + return #utf8 + else + local utf8 = table.concat(self.charlist, "", 1, char_idx) + return #utf8 + end +end + return TextBoxWidget diff --git a/frontend/ui/widget/textviewer.lua b/frontend/ui/widget/textviewer.lua index fa7055287..d89238f3d 100644 --- a/frontend/ui/widget/textviewer.lua +++ b/frontend/ui/widget/textviewer.lua @@ -86,37 +86,57 @@ function TextViewer:init() end if Device:isTouchDevice() then + local range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } self.ges_events = { TapClose = { GestureRange:new{ ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } + range = range, }, }, Swipe = { GestureRange:new{ ges = "swipe", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } + range = range, }, }, MultiSwipe = { GestureRange:new{ ges = "multiswipe", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } + range = range, + }, + }, + -- Allow selection of one or more words (see textboxwidget.lua): + HoldStartText = { + GestureRange:new{ + ges = "hold", + range = range, + }, + }, + HoldPanText = { + GestureRange:new{ + ges = "hold", + range = range, + }, + }, + HoldReleaseText = { + GestureRange:new{ + ges = "hold_release", + range = range, }, + -- callback function when HoldReleaseText is handled as args + args = function(text, hold_duration, start_idx, end_idx, to_source_index_func) + self:handleTextSelection(text, hold_duration, start_idx, end_idx, to_source_index_func) + end }, + -- These will be forwarded to MovableContainer after some checks + ForwardingTouch = { GestureRange:new{ ges = "touch", range = range, }, }, + ForwardingPan = { GestureRange:new{ ges = "pan", range = range, }, }, + ForwardingPanRelease = { GestureRange:new{ ges = "pan_release", range = range, }, }, } end @@ -271,7 +291,17 @@ function TextViewer:init() } } self.movable = MovableContainer:new{ - ignore_events = {"swipe"}, + -- We'll handle these events ourselves, and call appropriate + -- MovableContainer's methods when we didn't process the event + ignore_events = { + -- These have effects over the text widget, and may + -- or may not be processed by it + "swipe", "hold", "hold_release", "hold_pan", + -- These do not have direct effect over the text widget, + -- but may happen while selecting text: we need to check + -- a few things before forwarding them + "touch", "pan", "pan_release", + }, self.frame, } self[1] = WidgetContainer:new{ @@ -338,6 +368,58 @@ function TextViewer:onSwipe(arg, ges) return self.movable:onMovableSwipe(arg, ges) end +-- The following handlers are similar to the ones in DictQuickLookup: +-- we just forward to our MoveableContainer the events that our +-- TextBoxWidget has not handled with text selection. +function TextViewer:onHoldStartText(_, ges) + -- Forward Hold events not processed by TextBoxWidget event handler + -- to our MovableContainer + return self.movable:onMovableHold(_, ges) +end + +function TextViewer:onHoldPanText(_, ges) + -- Forward Hold events not processed by TextBoxWidget event handler + -- to our MovableContainer + -- We only forward it if we did forward the Touch + if self.movable._touch_pre_pan_was_inside then + return self.movable:onMovableHoldPan(arg, ges) + end +end + +function TextViewer:onHoldReleaseText(_, ges) + -- Forward Hold events not processed by TextBoxWidget event handler + -- to our MovableContainer + return self.movable:onMovableHoldRelease(_, ges) +end + +-- These 3 event processors are just used to forward these events +-- to our MovableContainer, under certain conditions, to avoid +-- unwanted moves of the window while we are selecting text in +-- the definition widget. +function TextViewer:onForwardingTouch(arg, ges) + -- This Touch may be used as the Hold we don't get (for example, + -- when we start our Hold on the bottom buttons) + if not ges.pos:intersectWith(self.textw.dimen) then + return self.movable:onMovableTouch(arg, ges) + else + -- Ensure this is unset, so we can use it to not forward HoldPan + self.movable._touch_pre_pan_was_inside = false + end +end + +function TextViewer:onForwardingPan(arg, ges) + -- We only forward it if we did forward the Touch or are currently moving + if self.movable._touch_pre_pan_was_inside or self.movable._moving then + return self.movable:onMovablePan(arg, ges) + end +end + +function TextViewer:onForwardingPanRelease(arg, ges) + -- We can forward onMovablePanRelease() does enough checks + return self.movable:onMovablePanRelease(arg, ges) +end + + function TextViewer:findDialog() local input_dialog input_dialog = InputDialog:new{ @@ -422,4 +504,18 @@ function TextViewer:findCallback(input_dialog) end end +function TextViewer:handleTextSelection(text, hold_duration, start_idx, end_idx, to_source_index_func) + if self.text_selection_callback then + self.text_selection_callback(text, hold_duration, start_idx, end_idx, to_source_index_func) + return + end + if Device:hasClipboard() then + Device.input.setClipboardText(text) + UIManager:show(Notification:new{ + text = start_idx == end_idx and _("Word copied to clipboard.") + or _("Selection copied to clipboard."), + }) + end +end + return TextViewer