[HtmlBoxWidget, DictQuickLookup] add capability to search within dictionary definitions#15001
Conversation
|
Suggest calling it "record", "article", or "body". There are usually multiple definitions, sample sentences, etymology, etc., per returned record. |
| function ScrollHtmlWidget:onTapScrollText(arg, ges) | ||
| if self.dialog and self.dialog.in_definition_search then return false end |
There was a problem hiding this comment.
Can't do that, it's an ugly hack: ScrollHtmlWidget is generic and does not have to know about what happens in its users. Remember your principles (reaching across the dinner table and grabbing someone else's wine).
You'll have to find another idea.
There was a problem hiding this comment.
On mobile right now, we can talk to our friends not to strangers though. I'm grabbing your wine, but that's fine because we're friends. Self.dialog is our friend too, the other idea would be to add Scroll class variable that DictQuick flips ... better?
There was a problem hiding this comment.
Yes, even a method, ScrollHtmlWidget:enableScrollByTap(toggle) (or some other name/param).
| self.key_events.ReadPrevResult = { { Input.group.PgBack } } | ||
| self.key_events.ReadNextResult = { { Input.group.PgFwd } } | ||
| self.key_events.Close = { { Input.group.Back } } | ||
| self.key_events.CloseWithButton = { { Input.group.Back } } |
There was a problem hiding this comment.
Why "CloseWithButton", if it's only associated to closing with... keys ? :)
There was a problem hiding this comment.
Back is a button, esc is a key. I suppose is a matter of preference at this point
There was a problem hiding this comment.
A mysterious entity once used Key so I'd rather it be consistent. ;p
672be80
There was a problem hiding this comment.
For most of us, a Button would be a ButtonWidget.
|
|
||
| function DictQuickLookup:findInDefinitionNextOrPreviousPage(direction) | ||
| if not self.in_definition_search then return false end | ||
| local scroll_widget, inner_widget = self:_getScrollAndInnerWidget() |
There was a problem hiding this comment.
Maybe more specific naming than "inner" ? And plural if it returns 2 widgets :)
local scroll_widget, content_widget = self:_getScrollAndContentWidgets()
Maybe now that many stuff access these widgets (I remember some hw/tw by @NiLuJe ), we could have the changeDictionary method, that creates/swaps/reuses them, just set them as properties on self.
self.cur_scroll_widget, self.cur_content_widget ?
| function DictQuickLookup:findInDefinitionNextOrPreviousPage(direction) | ||
| if not self.in_definition_search then return false end | ||
| local scroll_widget, inner_widget = self:_getScrollAndInnerWidget() | ||
| if inner_widget and inner_widget.findTextNextPage then |
There was a problem hiding this comment.
I guess this check and maybe some other convolutions can go once you have done it for TextWidget.
There was a problem hiding this comment.
Some of these are there to stop text from crashing yes
| if not self.in_definition_search then return false end | ||
| local scroll_widget, inner_widget = self:_getScrollAndInnerWidget() | ||
| if inner_widget and inner_widget.findTextNextPage then | ||
| inner_widget:findTextNextPage(direction) |
There was a problem hiding this comment.
You were always using the word "pages" in your description of this feature, which feels a bit odd to me. Not sure what a better word could be, "view" ?
(There is no occurence of the word "page" in DictQuickLookup (except for Wikipedia full page) and neither in TextWidget, haven't reread it if we have a common word for the slice of the... article/definition/...page :))
There was a problem hiding this comment.
It's MuPDF the method is getPages or something like that
| UIManager:show(InfoMessage:new{ | ||
| text = T(_("No matches for '%1' were found."), text), | ||
| timeout = 2, | ||
| }) | ||
| if self.in_definition_search then | ||
| inner_widget:clearSearch(true) | ||
| end |
There was a problem hiding this comment.
Can this happen ? When you do a 2nd seach while the results for a first search are still active?
There was a problem hiding this comment.
You just perfectly described when it can happen ;)
| function DictQuickLookup:onLookupInputWord(hint) | ||
| if self.allow_key_text_selection and self.nt_text_selector_indicator then | ||
| self:onStopTextSelectorIndicator(true) | ||
| end | ||
| self:lookupInputWord(hint) | ||
| end | ||
|
|
||
| function DictQuickLookup:lookupInputWord(hint) |
There was a problem hiding this comment.
Why this?
Looks like there is no other use of :lookupInputWord(), so why this wrapper?
There was a problem hiding this comment.
To not sully what clearly is meant to be a declaration of a widget and nothing more. Besides, principle dictates event handlers trigger methods
There was a problem hiding this comment.
That's not really how most handlers do. Most of them do the full work. We even call these self:onSomething() directly as methods of the object in the linear code. We even renamed them from :doSomething to :onDoSomething just because they may now be called as an action/event.
We can have these 2 names (and :onDoSomething calling :doSomething) when there are actually some work in :onDoSomething that are event-related, but have nothing to do in the :doSomething(), which may be called in other contexts than events.
Here, your split is gratuitous (it wasn't there before, it does nothing more, it is not required.)
There was a problem hiding this comment.
but it is doing something extra
if self.allow_key_text_selection and self.nt_text_selector_indicator then
self:onStopTextSelectorIndicator(true)
endThere was a problem hiding this comment.
Extra to what? To what other use that doesn't need that extra?
(I won't fight on this one if you like it like that - but seeing it like this, I, or little Timmy when he'll be older, would go look for some other call site that would call lookupInputWord() directly - because it it has been splitted, there must be a reason and something that needed it without the need for onStopTextSelectorIndicator() - and find nothing, and wonder.)
There was a problem hiding this comment.
the idea of the extra is simple, this mode and text selection are natural enemies, so we can't have both coexist at the same time. otherwise the shift+left/right won't work properly
91250a5 to
694523e
Compare
|
Pinging @TnS-hun for some quick review about the html widgets. |
| function ScrollHtmlWidget:disableTapScrollText() | ||
| return self.ignore_taps | ||
| function ScrollHtmlWidget:disableTapScrollText(force_block) | ||
| -- If an argument is provided, update the external block state | ||
| if force_block ~= nil then | ||
| self._blocked_by_parent = force_block | ||
| end |
There was a problem hiding this comment.
Sorry to insist on this little thing.
scroll_widget:disableTapScrollText(false) : it's a bit hard to get that the double negative will actually enable taps.
No matter the initial state, you can just use a positive one:
scroll_widget:enableTapScrollText(true)
And scroll_widget:enableTapScrollText(false) will read easier that it does not enable it = it disables it.
Also, inside the function:
self._blocked_by_parent = force_block
Why mention it is blocked by parent ? In here, you don't know who is calling you.
Be simple and generic, don't assume the current use case is the single one (it might be your parent, tomorrow your cousing, next month your neighbour, next year me!), just use self.ignore_taps as initially.
tight coupling, don't listen to strangers
There was a problem hiding this comment.
we should make it (for real men)
function ScrollHtmlWidget:is_not_un_un_enabled(isnt_not_un_disabled)
-- If the parent provides a value, we store the inverse of the negation
if isnt_not_un_disabled ~= nil then
self.isnt_un_dis_ignored = not isnt_not_un_disabled
end
return not (not (not self.isnt_un_dis_ignored))
endThere was a problem hiding this comment.
-if isnt_not_un_disabled ~= nil then
+if isnt_not_un_disabled ~= innit thendf909b5 to
ac0b356
Compare
|
I did not check the code, just the functionality. Looks like you cannot cancel the find in definition functionality without switching to the next dictionary (which you might not have) or cancel the dictionary lookup? What if you use the find in definition functionality, the result is in the last line, but you want to read the next line too? Would not be better to show a temporary row of buttons (for example: previous result, cancel find, next result) to control the find in definition functionality when using it? And keep the tap for turning pages. (I don't like the main search functionality for the exact same reason, it "breaks" page turning, I cannot look around the results.) |
this doesn't break anything. all answers to your questions can be found in #3022. but in short, it was deemed undesirable to use the
that is kind of the point, could you have a look at that please! |
da8a8d9 to
bcf524d
Compare
It hijacks taps. They no longer do the same page turning that they always do. Just like when using the regular search. Why repeat the same mistake here? |
There is no mistake, you can still swipe up/down to move through the definition as normal. |
| self.ignore_taps = not enabled | ||
| end | ||
| return not self.ignore_taps | ||
| end |
There was a problem hiding this comment.
Nitpick: I would keep this just a setter and either add a simple getter or just use ignore_taps directly.
There was a problem hiding this comment.
setter and getter does seem more appropriate
There was a problem hiding this comment.
If you don't use your getter externally, it's not needed, just use ignore_taps directly.
There was a problem hiding this comment.
please lets not play this game again, that is doing a full 360 to what I was doing originally (kind of)
There was a problem hiding this comment.
I don't care for this one, but I'd like you to get that: we don't have to have getter and setter for the beauty (or kludgility) of it.
It's good when it's actually called from the outside, to not expose the internals - but it's useless for a class itself which can use its own internals (unless the check for the internal is complex and a function avoids code duplication).
There was a problem hiding this comment.
Do you have any other comments?
Normally I would never use swipes though. :)
The changes looks good to me. (But it has been a while since I last worked on this code.) |
|
@TnS-hun incidentally you might remember #13276 (comment), interestingly these coordinates are actually correct... not sure if that helps fix that yet to be fixed problem |
f59b37a to
d3af7f4
Compare
| highlight_text_selection = true, | ||
| on_clear_search = function() | ||
| self.in_definition_search = false | ||
| self.stw_widget:setTapScrollEnabled(true) |
There was a problem hiding this comment.
Does this one exist on the scroll text widget ?
(Something tells me you have done the scroll/search on non-HTML dicts, and you forgot to push these changes ! :))
There was a problem hiding this comment.
It will exist in textbox. Essentially all the stuff is ready on dictquick for both, but textbox won't be ready for this PR (as i already mentioned) so when it is ready it'll be a matter of removing the enabled_func and PR'ing the textbox side.
As for the current state of textbox... coming soon ;) it currently "works" but it is not ready
There was a problem hiding this comment.
But currently, there is no ScrollTextWidget:setTapScrollEnabled.
I guess that on_clear_search can't currently be called when on a text dict.
But it feels dirty (even as an interim) to have references to stuff that doesn't exist.
I would comment it or leave it out (you'll anyway have to edit dictquicklookup when you'll implement search for text dicts.
(I also wouldn't merge this without its text dict search counterpart in a stable.)
There was a problem hiding this comment.
That sounds a lot like coercion "do my bidding or else" ;)
There was a problem hiding this comment.
okay let's look closer at what you are saying...
(I also wouldn't merge this without its text dict search counterpart in a stable.)
two options available to us here, option 1 this PR won't be merged until there is a second one with TextBox, or option 2, it can be merged but there won't be a stable release ever again until TextBox is merged.
of the two, only one is enforceable and realistic, so we are left with option 1 only, which begs the question, why comment anything out? also, I don't know how much longer it will take for TextBox to be ready, so will this sit here for months? (assuming it took months for it to be ready)
so what are you saying? because it does sound like coercion, a lot or extortion ;)
There was a problem hiding this comment.
There's option 3: wait until after next stable (which is promised next week, since weeks :) I know) to merge this.
There's no hurry to have it merged, it doesn't fix any issue, it's a feature, next stable can be without it.
There was a problem hiding this comment.
why comment anything out?
I would say the same if anybody introduced:
if False then UIManager:blowItOut() end
blowItOut doesn't exist, it's not because it's never called that we have to have it in the code.
There was a problem hiding this comment.
But that still assumes that textbox will be ready before 2026.05 (or whatever) which i am not happy to guarantee, it will be ready when its ready, so we are back to option 1… i am happy to delay until after 2026.02 just not under thinly veiled threats ;), it goes live in 26.05 with or without textbox… agreed?
There was a problem hiding this comment.
It's not a threat.
You bring a feature, half done because you just need it for your Oxford HTML dict.
And somehow promise you'll bring it for TEXT dicts (but without pressure because you don't need it).
Some kind of engagement that you will deliver for v2026.05 (with that pressure) would be welcome, and would somehow reassure me that I won't be the one that will have to finish it.
There was a problem hiding this comment.
but without pressure because you don't need it
only, this isn’t true.
Is not half finished, is not my fault one has to double the work here, nowhere else this is the case (i might regret saying that)
You could have simply said, let’s delay till next release, and that would have been fine. Instead you went, well give me the other or else… see the difference?
|
There is one little issue we are currently not dealing with, straddling matches, is it worth pursuing potential matches that span across different pages? |
|
What's the problem precisely? The highlights? |
|
MuPDF doesn't find them since we give it the text on a per page basis. So it can only match full strings within that page. Searching for 'set apart' where set is in page 3 and apart on page 4, fails to match. |
|
Oh right, in that case I wouldn't worry about it too much. I'd check if they don't have a whole document search. |
👍. I ignorantly ass/u/me-d that Wiki would use HtmlBoxWidget. |
|
Merging, unless you think is still |
|
Should be fine. Please squash it and clean up the commit message. |
|
and you feel like the best time to mention it is, exactly 5 minutes after it was merged? ;) weren't you the one who wanted "Search with preset" in the middle row? I honestly can't remember, so apologies if that is not the case. |
|
Well, it's when it is merged that we can easily test it. (Btw, how do you reach the pencil icon with keys to trigger this dialog on NT ? Some key combination?) |
"that is not what the development branch is for" - The poet (2024)
on K4, screenkb + back, on |
These closes the dict window for me. |
|
What do you mean they close it? Like you added a patch so it does that? What you describe makes absolutely no sense to me, i've never encountered such behaviour |
|
No, but I have I disabled it to check I got your dialog on backspace, and I did. Re-enabled and I still got it. Restarted, ensure it is checked, got it. Disabled and re-enabled, and I got my behaviour to close it working again... Feels like it may depend on some random ordering of events? (ie iterating an unordered hash table, where the iteration order may change - we had this with gestures, and we had to add the |
|
Probably because: koreader/frontend/device/input.lua Lines 303 to 305 in be68f1c koreader/frontend/ui/widget/dictquicklookup.lua Lines 796 to 812 in be68f1c and we have Backspace associated to CloseWithKeys and LookupInputWord. A solution could be to check for if G_reader_settings:isTrue("backspace_as_back") on line 812 to not include Backspace ?Do you remember if you associated Backspace to other actions elsewhere? |
I believe that is the only one
I don't know, maybe... incidentally, that backspace event on the emulator propagates to the next widget (i.e., it deletes the added space), but when you use the alphabet keys, those don't propagate on the emulator (they do on kindle)... why does it do it on one and not the others? |
I don't understand what you mean (by "next widget", and "propagate"). May be describe precisely one case. |
|
Try both those events from dictquick, the alpha one and the backspace one 809 and 812. The backspace passes the backspace press forward and the whitespace is deleted. With alphabetical keys, those don't get handed down, that original press is ignored. |
|
To that inputtext, don't you see? It should be |
|
Oh, I see, you appended a Well, if doesn't propage if you add a What is strange is that when onLookupInputWord() is done - and has created that new dialog, I would expect for it to propagate to lower widgets in the stack, and not to be fed to the newly created dialog on top of it ! koreader/frontend/ui/uimanager.lua Lines 921 to 957 in be68f1c |
|
But my beef isn't with the delete event propagating there. My real issue is that the alphabet key ones don't 😭 |
|
That's for the first "a": That's for the next "a" typed: Non-special keys are handled by I would say that because you haven't yet done |
|
couldn't we somehow capture that press and pass it as we do with |
|
we could do function DictQuickLookup:onLookupInputWord(hint, ev)
if self.allow_key_text_selection and self.nt_text_selector_indicator then
self:onStopTextSelectorIndicator(true)
end
if not hint and ev and ev.key then
local k = tostring(ev.key)
local is_shift = ev.modifiers and ev.modifiers.Shift
hint = is_shift and k or k:lower()
end
self:lookupInputWord(hint)
end? |
|
That's a bit ugly (but ok if you don't find anything cleaner I guess). |
function DictQuickLookup:onLookupInputWord(hint, ev)
if self.allow_key_text_selection and self.nt_text_selector_indicator then
self:onStopTextSelectorIndicator(true)
end
-- Key-event path: open dialog now, then inject one TextInput next tick.
if Device:isSDL() and ev and ev.key then
local text = tostring(ev.key)
if not (ev.modifiers and ev.modifiers.Shift) then
text = text:lower()
end
self:lookupInputWord()
UIManager:nextTick(function()
if self.input_dialog then
UIManager:sendEvent(Event:new("TextInput", text))
end
end)
return true
end
self:lookupInputWord(hint)
end? |
|
If the ev can't be used as-is and you need your modifiers checks and :lower(), I prefer your shorted first idea. |
|
well the idea is that both shift + A and A, can directly call the thing |
…ionary definitions (koreader#15001) Adds in-definition search to DictQuickLookup relaying on MuPDF's word-matching implementation, uses tap handling for navigation by allowing temporary disable/restore of Tap events.



what's new
Find-in-definition feature:
Added a new
in_definition_searchstate flag toDictQuickLookup, and implemented methods to search for text within a definition, highlight matches, and navigate between them (searchInDefinition,findInDefinitionNextOrPreviousPage, and UI integration).Updated the lookup input dialog to include a "Find in definition" button, which triggers the search within the current definition.
Added search state and methods to
HtmlBoxWidgetto find text across pages, highlight matches, and navigate between matched pages (findText,findTextNextPage,_highlightSearchOnCurrentPage,clearSearch).Ensured search highlights are updated when changing pages and cleared when starting text selection or when appropriate.
Refactored key event handling to distinguish between closing with keys and normal closing, and to allow navigation between search matches using keyboard shortcuts.
Added
onFindInTextOrMoveSelectorIndicatorto handle navigation between matches or text selection movement, depending on context.Consolidated widget access with a new
_getScrollAndContentWidgetshelper, replacing the previous_getSelectionWidgetfunction for more consistent access to scroll/content widgets.Updated scroll and tap handling in
ScrollHtmlWidgetto respect the newignore_tapsflag, disabling tap-to-scroll when in search mode.These changes collectively enable a robust "find in definition" experience for users, with integrated UI, keyboard, and touch navigation.
screenshots
related issues
bump me! ;)
This change is