-
Notifications
You must be signed in to change notification settings - Fork 10.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clear all find highlights when the findbar is closed (issue 7468) #10100
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,6 +56,9 @@ class TextLayerBuilder { | |
this.textLayerRenderTask = null; | ||
this.enhanceTextSelection = enhanceTextSelection; | ||
|
||
this._boundEvents = Object.create(null); | ||
this._bindEvents(); | ||
|
||
this._bindMouse(); | ||
} | ||
|
||
|
@@ -314,7 +317,7 @@ class TextLayerBuilder { | |
clearedUntilDivIdx = match.end.divIdx + 1; | ||
} | ||
|
||
if (this.findController === null || !this.findController.active) { | ||
if (!this.findController || !this.findController.highlightMatches) { | ||
return; | ||
} | ||
|
||
|
@@ -331,6 +334,40 @@ class TextLayerBuilder { | |
this.renderMatches(this.matches); | ||
} | ||
|
||
/** | ||
* @private | ||
*/ | ||
_bindEvents() { | ||
const { eventBus, _boundEvents, } = this; | ||
|
||
_boundEvents.pageCancelled = (evt) => { | ||
if (evt.pageNumber !== this.pageNumber) { | ||
return; | ||
} | ||
if (this.textLayerRenderTask) { | ||
console.error('TextLayerBuilder._bindEvents: `this.cancel()` should ' + | ||
'have been called when the page was reset, or rendering cancelled.'); | ||
return; | ||
} | ||
// Ensure that all event listeners are cleaned up when the page is reset, | ||
// since re-rendering will create new `TextLayerBuilder` instances and the | ||
// number of (stale) event listeners would otherwise grow without bound. | ||
for (const name in _boundEvents) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a comment here about why this is necessary? Basically a few lines of summary from the discussion in the other PR so we don't forget the reason why we added this and don't accidentally remove this later without a better alternative. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point; please check that the added comment is clear enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks really good! |
||
eventBus.off(name.toLowerCase(), _boundEvents[name]); | ||
delete _boundEvents[name]; | ||
} | ||
}; | ||
_boundEvents.updateTextLayerMatches = (evt) => { | ||
if (evt.pageIndex !== -1) { | ||
return; | ||
} | ||
this.updateMatches(); | ||
}; | ||
|
||
eventBus.on('pagecancelled', _boundEvents.pageCancelled); | ||
eventBus.on('updatetextlayermatches', _boundEvents.updateTextLayerMatches); | ||
} | ||
|
||
/** | ||
* Improves text selection by adding an additional div where the mouse was | ||
* clicked. This reduces flickering of the content if the mouse is slowly | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can implement this feature without tracking any state in the find controller. We should be able to get rid of this
active
member variable if the text layer builder listens to the event on the event bus instead and tracks that member variable internally. After all it's a text layer state (whether or not to highlight the matches) and not find controller state (which only produces the matches and leaves the representation to the text layer builder).In short, can we try removing all changes in this file and introducing
this._highlightMatches = true;
in the constructor of the text layer builder, with a listener for thefindbarclose
event that toggles this value, and then check this value at https://github.com/mozilla/pdf.js/pull/10100/files#diff-6a8d3c6754c77cfa0876314f0f54bed7R319?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I cannot reasonably disagree with any of the above, given that
TextLayerBuilder
already have access toPDFFindController
the current solution just seemed like the simplest possible one :-)There's a slight snag with this suggestion, which is why I went with the current solution:
With the suggested changes there wouldn't be anything resetting
this._highlightMatches
for subsequent find operations, thus permanently breaking highlighting of matches once the findbar has been closed just one time.In order for this to work, you'd basically need to start dispatching an event instead at line
pdf.js/web/pdf_find_controller.js
Line 356 in 54d6c24
TextLayerBuilder
instances listen for it in order to setthis._highlightMatches = true
. That feels slightly more involved, and there's also the question of what a good event name would be;updatetextlayerhighlight
perhaps?That should probably work too, although I've not tested it, but at the expense of a bit more event dispatching though. How do you think that we should proceed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having (quickly) tried using events instead, it unfortunately seems that the solution will need to become a bit more complex than current one for things to work out.
Most likely this can all be addressed in a good way, but it's not as easy as one would hope. Besides, there's also the issue described in #10099 (comment) which affects this PR as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My idea was indeed that in the places where we manipulate the
this.active
member variable we'd use events instead, so where we setthis.active = true/false;
(doing a search sets it totrue
and closing the findbar sets it tofalse
) we dispatch an event instead (updatetextlayerhighlight
seems like a good name to me) and the text layer builder sets this value for thethis._highlightMatches
member variable that it tracks to display the highlighted matches or not. I didn't implement it, so it may indeed be a bit more complicated than I can foresee now, but I think that should work so we don't have to do anything in the find controller.If this does turn out to be more complicated than what I described, can we at least rename
active
tohighlightMatches
or something similar? I find that a much more descriptive name and then we don't need the extra comment behind its declaration in thereset
method.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having thought about all the various cases, the conclusion is unfortunately that we still need to track this state in
PDFFindController
.To summarize: There's simply no good way to relay the correct highlight state to all
TextLayerBuilder
instances, since they are created lazily; please read on for for additional details.The important point here is that you need one source of truth, and not multiple, for whether or not find highlights should be displayed. There's only ever going to be one
PDFFindController
instance around, but an arbitrary number ofTextLayerBuilder
ones (i.e. one per page).In particular, even attempting to track this in
TextLayerBuilder
is basically impossible when you consider all of the cases:Assume that you set
this.highlightMatches = true
in theTextLayerBuilder
constructor.That means that for currently active pages, i.e. ones in the
PDFPageViewBuffer
cache, you'll receive the event to disable the highlights when closing the findbar. However when new pages are scrolled into view, and thus newTextLayerBuilder
instances are created, those will incorrectly have highlights enable since they didn't exist at the point when the events (regardless if those are 'findbarclose' or 'updatetextlayerhighlight') were dispatched.Assume instead that
this.highlightMatches = false
is set in theTextLayerBuilder
constructor, and that an event is dispatched whenever searching starts.Similar to the above, for new
TextLayerBuilder
instances created after searching started, no events are received and thus highlights will be permanently disabled in this case.Furthermore please note that I choose to piggy-back on the 'updatetextlayermatches' event from your PR, for two reasons:
First of all, there's fewer events overall that way (and the name fits nicely here as well). Note that this PR alone means that each
TextLayerBuilder
instance registers two event listeners, and with your PR that would have otherwise increased to a total of three events.Second of all, by having only
PDFFindController
listen for 'findbarclose' events, there's no longer necessary to worry about the order in which the 'findbarclose' listeners are registered and run, thus guaranteeing thathighlightMatches
has been correctly set.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the lazy loading is a really good point which indeed I didn't foresee in my idea. In that case the current code structure seems fine to me!