Skip to content

Commit

Permalink
Scroll to search result
Browse files Browse the repository at this point in the history
If the currently selected search result is outside the visible view,
this result will be scroll into that view.
  • Loading branch information
c3er committed Jun 30, 2023
1 parent 6ec46ae commit 668f6c3
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 31 deletions.
52 changes: 26 additions & 26 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,6 @@ function chooseTheme(isDark) {
return isDark ? common.DARK_THEME : common.LIGHT_THEME
}

function scrollTo(position) {
renderer.contentElement().scrollTop = position
}

function reload(isFileModification, encoding) {
ipc.send(
ipc.messages.reloadPrepared,
Expand Down Expand Up @@ -256,7 +252,6 @@ ipc.listen(ipc.messages.fileOpen, async file => {

renderer.contentElement().innerHTML = documentRendering.renderContent(content)
renderer.rawTextElement().innerHTML = documentRendering.renderRawText(content)
search.highlightTerm()
populateToc(content, "toc")

// Alter local references to be relativ to the document
Expand Down Expand Up @@ -289,30 +284,31 @@ ipc.listen(ipc.messages.fileOpen, async file => {
}
})

search.highlightTerm()

const scrollPosition = file.scrollPosition
const internalTarget = file.internalTarget
let titlePrefix = filePath
if (scrollPosition) {
scrollTo(scrollPosition)
}
if (internalTarget) {
const targetElement = document.getElementById(internalTarget.replace("#", ""))
if (targetElement) {
if (!scrollPosition) {
const containerElement = renderer.contentElement().children[0]
scrollTo(
targetElement.getBoundingClientRect().top -
(containerElement.getBoundingClientRect().top -
Number(containerElement.style.paddingTop.replace("px", ""))),
)
if (search.isActive()) {
search.scrollToResult()
} else {
if (scrollPosition) {
renderer.scrollTo(scrollPosition)
}
if (internalTarget) {
const targetElement = document.getElementById(internalTarget.replace("#", ""))
if (targetElement) {
if (!scrollPosition) {
renderer.scrollTo(renderer.elementYPosition(targetElement))
}
titlePrefix += internalTarget
} else {
titlePrefix += ` ("${internalTarget}" not found)`
}
titlePrefix += internalTarget
} else {
titlePrefix += ` ("${internalTarget}" not found)`
}
}
if (!scrollPosition && !internalTarget) {
scrollTo(0)
if (!scrollPosition && !internalTarget) {
renderer.scrollTo(0)
}
}
document.title = `${titlePrefix} - ${TITLE} ${remote.app.getVersion()}`

Expand All @@ -329,7 +325,11 @@ ipc.listen(ipc.messages.fileOpen, async file => {

ipc.listen(ipc.messages.prepareReload, reload)

ipc.listen(ipc.messages.restorePosition, scrollTo)
ipc.listen(ipc.messages.restorePosition, position => {
if (!search.isActive()) {
renderer.scrollTo(position)
}
})

ipc.listen(ipc.messages.changeZoom, zoomFactor => electron.webFrame.setZoomFactor(zoomFactor))

Expand All @@ -352,5 +352,5 @@ ipc.listen(ipc.messages.print, () => {
toc.setVisibility(true)
}

scrollTo(scrollPosition)
renderer.scrollTo(scrollPosition)
})
17 changes: 16 additions & 1 deletion app/lib/renderer/common.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
let _document

function contentElement() {
return _document.getElementById("content-body")
}

exports.init = document => (_document = document)

exports.contentElement = () => _document.getElementById("content-body")
exports.contentElement = contentElement

exports.rawTextElement = () => _document.getElementById("raw-text")

exports.scrollTo = position => (contentElement().scrollTop = position)

exports.elementYPosition = element => {
const containerElement = renderer.contentElement().children[0]
return (
element.getBoundingClientRect().top -
(containerElement.getBoundingClientRect().top -
Number(containerElement.style.paddingTop.replace("px", "")))
)
}
27 changes: 23 additions & 4 deletions app/lib/search/searchRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@ let _searchIndex = 0
let _searchResultCount = 0

// A String.prototype.replaceAll() alternative, that is case insensitive at the input
// ("pattern parameter") but preserves the case during replacing.
// ("pattern parameter") but preserves the upper/lower case during replacing.
function replaceAll(text, pattern, replacement) {
const output = []
let lastIndex = text.length - 1

// Based on https://stackoverflow.com/a/1499916 (Remove HTML Tags in Javascript with Regex)
const tagMatches = [...text.matchAll(/(<([^>]+)>)/g)]

const matches = [...text.matchAll(pattern)]
for (const match of matches.toReversed()) {
for (const match of [...text.matchAll(pattern)].toReversed()) {
const term = match[0]
if (
tagMatches.some(tagMatch => {
Expand All @@ -47,7 +46,7 @@ function replaceAll(text, pattern, replacement) {
lastIndex = match.index
}
output.push(text.substring(0, lastIndex))
return output.reverse().join("")
return output.toReversed().join("")
}

function deactivate() {
Expand Down Expand Up @@ -131,4 +130,24 @@ exports.highlightTerm = () => {
}
}

exports.scrollToResult = () => {
if (!_isActive) {
return
}

const resultElement = _document.getElementById(SELECTED_SEARCH_RESULT_ID)
const resultElementPosition = renderer.elementYPosition(resultElement)

const contentElement = renderer.contentElement()
const scrollPosition = contentElement.scrollTop

if (
resultElementPosition < scrollPosition ||
resultElementPosition + resultElement.clientHeight >
scrollPosition + contentElement.clientHeight
) {
renderer.scrollTo(resultElementPosition)
}
}

exports.deactivate = deactivate

0 comments on commit 668f6c3

Please sign in to comment.