Skip to content

Commit

Permalink
Merge pull request #4423 from James-Yu/flicker-free-refresh
Browse files Browse the repository at this point in the history
Flicker free refresh
  • Loading branch information
James-Yu authored Oct 5, 2024
2 parents 374e6fd + 90f09d9 commit c6cbb8d
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 87 deletions.
14 changes: 1 addition & 13 deletions dev/editviewer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import re
import argparse

parser = argparse.ArgumentParser()
Expand All @@ -22,9 +21,6 @@
with open(args.viewer + '/viewer.mjs', 'wt', encoding='utf-8') as fout:
currentClass = ''
for line in fin:
r = re.match(r'class (.*?) \{', line)
if r:
currentClass = r[1]
line = line.replace('''const MATCH_SCROLL_OFFSET_TOP = -50;''', '''const MATCH_SCROLL_OFFSET_TOP = -100;''') \
.replace('''this.switchView(view, true);''', '''this.switchView(view, false);''') \
.replace('''console.warn(`[fluent] Missing translations in ${locale}: ${ids}`);''', '''// console.warn(`[fluent] Missing translations in ${locale}: ${ids}`);''') \
Expand All @@ -46,15 +42,7 @@
.replace('''(this.container.clientHeight - vPadding) / currentPage.height * currentPage.scale;''', '''(this.container.clientHeight - vPadding) / Math.max(...this._pages.map(p => p.height)) * currentPage.scale * (1 / (1 - (viewerTrim ?? 0) / 100));''') \
.replace('''setRotation(this.initialRotation);''', '''// setRotation(this.initialRotation);''') \
.replace('''this.pdfLinkService.setHash(this.initialBookmark);''', '''// this.pdfLinkService.setHash(this.initialBookmark);''') \
.replace('''.then(([firstPdfPage, permissions])''', '''.then(async ([firstPdfPage, permissions])''') \
.replace('''const scale = this.currentScale;''', '''this._currentScale = oldScale; const scale = oldScale ? oldScale : this.currentScale;''') \
.replace('''this._pages[0]?.setPdfPage(firstPdfPage);''', '''this._pages[0]?.setPdfPage(firstPdfPage);\n await lwRenderSync(this, pdfDocument, pagesCount);''') \
.replace('''this._currentScaleValue = null;''', '''// this._currentScaleValue = null;''') \
.replace('''this.viewer.textContent = "";''', '''// this.viewer.textContent = "";''')

if currentClass == 'PDFViewer':
line = line.replace('''setDocument(pdfDocument) {''', '''setDocument(pdfDocument) {\n const oldScale = lwRecordRender(this);''')

.replace('''hPadding = vPadding = 0;''', '''if (this._scrollMode === ScrollMode.HORIZONTAL || this._spreadMode === SpreadMode.NONE) { hPadding = vPadding = 0; } else { hPadding = 10; vPadding = 0; }''') \
# .replace('''parent.document.dispatchEvent(event);''', '''parent.document.dispatchEvent(event); \n document.dispatchEvent(event);''')
fout.write(line)

Expand Down
2 changes: 1 addition & 1 deletion src/compile/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ async function buildLoop() {
}
}
isBuilding = false
setTimeout(() => lw.compile.compiledPDFWriting--, vscode.workspace.getConfiguration('latex-workshop').get('latex.watch.pdf.delay') as number)
setTimeout(() => lw.compile.compiledPDFWriting--, vscode.workspace.getConfiguration('latex-workshop').get('latex.watch.pdf.delay') as number * 2)
}

/**
Expand Down
2 changes: 1 addition & 1 deletion test/units/08_compile_build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
await new Promise((resolve) =>
setTimeout(
resolve,
(vscode.workspace.getConfiguration('latex-workshop').get('latex.watch.pdf.delay') as number) + 100
(vscode.workspace.getConfiguration('latex-workshop').get('latex.watch.pdf.delay') as number) * 2 + 100
)
)

Expand Down
40 changes: 27 additions & 13 deletions viewer/components/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type PdfjsEventName
= 'documentloaded'
| 'pagesinit'
| 'pagesloaded'
| 'pagerendered'
| 'scalechanged'
| 'zoomin'
| 'zoomout'
Expand All @@ -11,6 +12,30 @@ export type PdfjsEventName
| 'pagenumberchanged'
| 'rotationchanging'

type PDFViewerPage = {
viewport: {
rawDims: {
pageHeight: number,
pageWidth: number,
pageX: number,
pageY: number
},
rotation: number,
convertToViewportPoint(x: number, y: number): [number, number]
},
canvas: HTMLCanvasElement | undefined,
div: HTMLDivElement,
getPagePoint(x: number, y: number): [number, number],
get renderingState(): RenderingStates
}

export enum RenderingStates {
INITIAL = 0,
RUNNING = 1,
PAUSED = 2,
FINISHED = 3,
}

export type PDFViewerApplicationType = {
eventBus: {
on: (eventName: PdfjsEventName, listener: () => void) => void,
Expand All @@ -25,19 +50,8 @@ export type PDFViewerApplicationType = {
isViewerEmbedded: boolean,
pdfViewer: {
_currentScale: number,
_pages: {
viewport: {
rawDims: {
pageHeight: number,
pageWidth: number,
pageX: number,
pageY: number
},
rotation: number,
convertToViewportPoint(x: number, y: number): [number, number]
},
getPagePoint(x: number, y: number): [number, number]
}[],
_getVisiblePages(): { first: number, last: number, views: { id: number, x: number, y: number, view: PDFViewerPage, percent: number }[], ids: Set<number> },
_pages: PDFViewerPage[],
currentPageNumber: number,
currentScaleValue: string,
scrollMode: number,
Expand Down
134 changes: 92 additions & 42 deletions viewer/components/refresh.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as utils from './utils.js'
import { setTrimValue } from './trimming.js'
import { getTrimValue, setTrimValue } from './trimming.js'
import { sendLog } from './connection.js'
import { viewerState, viewerStatePromise } from './state.js'
import type { PDFViewerApplicationType, PDFViewerApplicationOptionsType } from './interface'
import { type PDFViewerApplicationType, type PDFViewerApplicationOptionsType, RenderingStates } from './interface.js'
import type { PdfViewerParams } from '../../types/latex-workshop-protocol-types/index.js'

declare const pdfjsLib: any
Expand All @@ -19,6 +19,9 @@ export function toggleAutoRefresh() {
}

let prevState: {
page: number,
trim: number,
scale: string,
scrollMode: number,
sidebarView: number,
spreadMode: number,
Expand Down Expand Up @@ -56,6 +59,9 @@ export async function refresh() {

// Fail-safe. For unknown reasons, the pack may have null values #4076
const currentState = {
page: PDFViewerApplication.pdfViewer.currentPageNumber ?? prevState?.page,
trim: getTrimValue(),
scale: PDFViewerApplication.pdfViewer.currentScaleValue ?? prevState?.scale,
scrollMode: PDFViewerApplication.pdfViewer.scrollMode ?? prevState?.scrollMode,
sidebarView: PDFViewerApplication.pdfSidebar.visibleView ?? prevState?.sidebarView,
spreadMode: PDFViewerApplication.pdfViewer.spreadMode ?? prevState?.spreadMode,
Expand All @@ -74,6 +80,16 @@ export async function refresh() {
PDFViewerApplicationOptions.set('spreadModeOnLoad', prevState.spreadMode)
}

const masks = addMasks()
const cb = () => {
if (!allPagesRendered()) {
return
}
removeMasks(masks)
PDFViewerApplication.eventBus.off('pagerendered', cb)
}
PDFViewerApplication.eventBus.on('pagerendered', cb)

const { encodedPath, docTitle } = utils.parseURL()
/* eslint-disable */
const doc = await pdfjsLib.getDocument({
Expand All @@ -92,6 +108,15 @@ export async function restoreState() {
return
}

if (prevState.page !== undefined) {
PDFViewerApplication.pdfViewer.currentPageNumber = prevState.page
}
if (prevState.trim !== undefined) {
setTrimValue(prevState.trim)
}
if (prevState.scale !== undefined) {
PDFViewerApplication.pdfViewer.currentScaleValue = prevState.scale
}
if (prevState.sidebarView) {
PDFViewerApplication.pdfSidebar.switchView(prevState.sidebarView)
}
Expand Down Expand Up @@ -143,46 +168,71 @@ async function restoreDefault() {
}
}

let oldVisiblePages: number[]
let oldScrollHeight: number
let oldPageCount: number
export function patchViewerRefresh() {
/* eslint-disable */
;(globalThis as any).lwRecordRender = (pdfViewer: any) => {
oldVisiblePages = pdfViewer._getVisiblePages().ids
oldPageCount = pdfViewer.pdfDocument?.numPages ?? 0
let oldScale = pdfViewer.currentScale
oldScrollHeight = pdfViewer.pdfDocument ? document.getElementById('viewerContainer')!.scrollHeight : 0
return oldScale
}
;(globalThis as any).lwRenderSync = async (pdfViewer: any, pdfDocument: any, pagesCount: number) => {
// Only do flicker-free refresh when spread is off #4415
if (pdfViewer.spreadMode === 0) {
await Array.from(oldVisiblePages)
.filter(pageNum => pageNum <= pagesCount)
.map(pageNum => pdfDocument.getPage(pageNum)
.then((pdfPage: [number, any]) => [pageNum, pdfPage])
)
.reduce((accPromise, currPromise) => accPromise.then(() =>
// This forces all visible pages to be rendered synchronously rather than asynchronously to avoid race condition involving this.renderingQueue.highestPriorityPage
currPromise.then(([pageNum, pdfPage]: [number, any]) => {
const pageView = pdfViewer._pages[pageNum - 1]
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage)
}
pdfViewer.renderingQueue.highestPriorityPage = pageView.renderingId
return pdfViewer._pages[pageNum - 1].draw().finally(() => {
pdfViewer.renderingQueue.renderHighestPriority()
})
})), Promise.resolve()
)
}
document.getElementById('viewerContainer')!.scrollTop += oldScrollHeight
if (pdfViewer.spreadMode === 0) {
for (let i = 1; i <= oldPageCount; i++) {
pdfViewer.viewer.removeChild(pdfViewer.viewer.firstChild)
}
function addMasks() {
const viewerDom = document.getElementById('viewer')!
const viewerContainer = document.getElementById('viewerContainer')!
const masks: HTMLDivElement[] = []
if (!viewerContainer || !viewerDom) {
return masks
}

const visiblePages = PDFViewerApplication.pdfViewer._getVisiblePages()
for (const visiblePage of visiblePages.views) {
const canvas = visiblePage.view.canvas
if (!canvas) {
continue
}

const viewerBound = viewerDom.getBoundingClientRect()
const pageBound = visiblePage.view.div.getBoundingClientRect()
const canvasBound = canvas.getBoundingClientRect()

const div = document.createElement('div')
div.className = 'page-loading-mask'
masks.push(div)
div.style.display = 'none'
div.style.left = pageBound.x - viewerBound.x + 'px'
div.style.top = pageBound.y - viewerBound.y + 'px'
div.style.width = pageBound.width + 'px'
div.style.height = pageBound.height + 'px'

const img = new Image()
img.src = canvas.toDataURL() ?? ''
img.style.left = canvasBound.x - pageBound.x + 'px'
img.style.top = canvasBound.y - pageBound.y + 'px'
img.style.width = canvasBound.width + 'px'
img.style.height = canvasBound.height + 'px'

div.appendChild(img)
viewerContainer.appendChild(div)
div.style.display = 'inherit'

// This workaround is for the last page of the document.
const anchor = document.createElement('div')
anchor.className = 'page-loading-anchor'
masks.push(anchor)
anchor.style.display = 'inherit'
anchor.style.position = 'absolute'
anchor.style.left = pageBound.x - viewerBound.x + 'px'
anchor.style.top = pageBound.y - viewerBound.y + pageBound.height + 'px'
anchor.style.width = '10px'
anchor.style.height = '10px'
viewerContainer.appendChild(anchor)
}
return masks
}

function allPagesRendered() {
return PDFViewerApplication.pdfViewer._getVisiblePages().views.every(view => view.view.renderingState === RenderingStates.FINISHED)
}

function removeMasks(masks: HTMLDivElement[]) {
for (const mask of masks) {
mask.classList.add('remove')
}
/* eslint-enable */
setTimeout(() => {
for (const mask of masks) {
mask.remove()
}
}, 1000)
}
5 changes: 4 additions & 1 deletion viewer/components/synctex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ function callSynctex(e: MouseEvent, page: number, pageDom: HTMLElement, viewerCo
const canvas = document.getElementsByClassName('canvasWrapper')[0] as HTMLElement
const left = e.pageX - pageDom.offsetLeft + viewerContainer.scrollLeft - canvas.offsetLeft
const top = e.pageY - pageDom.offsetTop + viewerContainer.scrollTop - canvas.offsetTop
const pos = PDFViewerApplication.pdfViewer._pages[page-1].getPagePoint(left, canvasDom.offsetHeight - top)
const pos = PDFViewerApplication.pdfViewer._pages[page-1]?.getPagePoint(left, canvasDom.offsetHeight - top)
if (pos === undefined) {
return
}
void send({ type: 'reverse_synctex', pdfFileUri: utils.parseURL().pdfFileUri, pos, page, textBeforeSelection, textAfterSelection })
}

Expand Down
55 changes: 49 additions & 6 deletions viewer/latexworkshop.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ html[dir='rtl'] .findbar {

#synctex-indicator {
position: absolute;
top: 0;
left: 0;
z-index: 100000;
border: 0.2em solid red;
border-radius: 50%;
Expand Down Expand Up @@ -103,19 +105,38 @@ html[dir='rtl'] .findbar {
mask-image:var(--secondaryToolbarButton-spreadNone-icon);
}

.spread {
margin-inline: 0 !important;
}

.pdfViewer.removePageBorders .page {
border: none;
overflow: hidden;
box-shadow: 0px 0px 0px 1px lightgrey;
}

.pdfViewer .page.loadingIcon::after{
z-index: -100000 !important;
.pdfViewer.removePageBorders .spread .page:first-of-type {
margin-inline-start: 0px;
margin-inline-end: 5px;
}

.pdfViewer .page:not(.loading)::after{
transition-property: auto !important;
display:block !important;
.pdfViewer.removePageBorders .spread .page:last-of-type {
margin-inline-start: 5px;
margin-inline-end: 0px;
}

.pdfViewer.removePageBorders.scrollHorizontal .page {
margin-bottom: 0px;
}

.pdfViewer.removePageBorders.scrollHorizontal .page:first-of-type {
margin-inline-start: 0px;
margin-inline-end: 5px;
}

.pdfViewer.removePageBorders.scrollHorizontal .page:last-of-type {
margin-inline-start: 5px;
margin-inline-end: 0px;
}

.notransition {
Expand Down Expand Up @@ -193,4 +214,26 @@ html[dir='rtl'] .findbar {

.annotationLayer .popup {
margin: 0 calc(5px * var(--scale-factor));
}
}

.page-loading-mask {
border: none;
box-shadow: 0px 0px 0px 1px lightgrey;
outline: none;
position: absolute;
overflow: hidden;
z-index: 10;
}

.page-loading-mask img {
padding: 0;
margin: 0;
border: none;
outline: none;
position: relative;
}

.page-loading-mask.remove {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
3 changes: 1 addition & 2 deletions viewer/latexworkshop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as utils from './components/utils.js'
import type { PdfjsEventName, PDFViewerApplicationType, PDFViewerApplicationOptionsType } from './components/interface.js'
import type { PdfViewerParams } from '../types/latex-workshop-protocol-types/index'
import { initTrim, setTrimCSS } from './components/trimming.js'
import { doneRefresh, patchViewerRefresh, restoreState } from './components/refresh.js'
import { doneRefresh, restoreState } from './components/refresh.js'
import { initUploadState, setParams, uploadState } from './components/state.js'
import { initConnect, send } from './components/connection.js'
import { registerSyncTeX } from './components/synctex.js'
Expand Down Expand Up @@ -68,7 +68,6 @@ async function initialization() {

initConnect()
patchViewerUI()
patchViewerRefresh()
registerKeyBind()
}

Expand Down
Loading

0 comments on commit c6cbb8d

Please sign in to comment.