diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f5cbc3e6c..68e78a8c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ app. ## [Unreleased] - Added support for parsing ざるを得ない endings, e.g. 闘わざるをえなかった. -- Added support for parsing ~ないで, e.g. 払わないですんだ。 +- Added support for parsing ~ないで, e.g. 払わないですんだ. - Added an option for higher contrast pitch accent markings. - (Chrome, Edge) Fixed the browser icon getting stuck at 100% ([#1003](https://github.com/birchill/10ten-ja-reader/issues/1003)). @@ -18,6 +18,10 @@ app. - Made the popup show for sites that make nested contents fullscreen such as Crunchyroll ([#1015](https://github.com/birchill/10ten-ja-reader/issues/1015)). +- Made the touchscreen puck trigger showing text boxes on + [mokuro](https://github.com/kha-white/mokuro) that normally only show on + hover + ([#1009](https://github.com/birchill/10ten-ja-reader/issues/1009)). - Fixed lookup of Japanese inside `display: contents` containers. - Made the copy overlay not show if the user has selected text in the popup. - Made shogi shorthand matches not show up when there is a longer word match. diff --git a/src/content/content.ts b/src/content/content.ts index 6b545e151b..358ce5a50d 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -880,7 +880,7 @@ export class ContentHandler { // If the user pressed the hold-to-show key combination, show the popup // if possible. // - // It's important we only do this whehn the popup is not visible, however, + // It's important we only do this when the popup is not visible, however, // since these keys may overlap with the keys we've defined for pinning the // popup--which only apply when the popup is visible. const matchedHoldToShowKeys = this.isHoldToShowKeyStroke(event); diff --git a/src/content/puck.ts b/src/content/puck.ts index fe2f1d15a3..f3afe9b7cc 100644 --- a/src/content/puck.ts +++ b/src/content/puck.ts @@ -82,10 +82,16 @@ function clearClickTimeout(clickState: ClickState) { // furthermore looks up words. export type PuckEnabledState = 'disabled' | 'inactive' | 'active'; +type RestoreContentParams = { root: Element; restore: () => void }; + export class LookupPuck { public static id = 'tenten-ja-puck'; private puck: HTMLDivElement | undefined; private enabledState: PuckEnabledState = 'disabled'; + + private clickState: ClickState = { kind: 'idle' }; + private static readonly clickHysteresis = 300; + private puckX: number; private puckY: number; private earthWidth: number; @@ -118,6 +124,11 @@ export class LookupPuck { // present yet. private hasBuggyPositionFixed: boolean | undefined; + // We sometimes temporarily modify the content so we can look it up. In such + // a case we register a `restore` function to return the content back + // to its original state when we have finished with it. + private contentToRestore: RestoreContentParams | undefined; + constructor( private safeAreaProvider: SafeAreaProvider, private onLookupDisabled: () => void @@ -392,9 +403,31 @@ export class LookupPuck { this.targetOffset.y - viewportOffsetTop; + // See what we are pointing at + let target = document.elementFromPoint(targetX, targetY); + + // Check if we need to adjust the content to look it up. + // + // But first check we aren't pointing at the same content as we adjusted + // last time (or one of its descendents). + if (!this.contentToRestore?.root.contains(target)) { + // Restore any content we previously adjusted. + this.restoreContent(); + + // Look for hidden textboxes on mokuro reader pages + const mokuroResult = LookupPuck.uncoverMokuroText( + target, + targetX, + targetY + ); + if (mokuroResult) { + target = mokuroResult.newTarget; + this.contentToRestore = mokuroResult.contentToRestore; + } + } + // Make sure the target is an actual element since the mousemove handler // expects that. - const target = document.elementFromPoint(targetX, targetY); if (!target) { return; } @@ -453,6 +486,76 @@ export class LookupPuck { target.dispatchEvent(mouseEvent); }; + private restoreContent() { + this.contentToRestore?.restore(); + this.contentToRestore = undefined; + } + + // Look for textBox elements generated by mokuro reader + // (https://github.com/kha-white/mokuro) since they have hidden paragraph + // elements that are only shown on hover. + private static uncoverMokuroText( + target: Element | null, + targetX: number, + targetY: number + ): { newTarget: Element; contentToRestore: RestoreContentParams } | null { + // Check for a suitable suspect + if ( + !(target instanceof HTMLElement) || + !target.classList.contains('textBox') + ) { + return null; + } + + // Set the paragraphs to display: table to match the hover style's from + // mokuro's stylesheet: + // + // https://github.com/kha-white/mokuro/blob/43c59a3c49100db522db088563297dc609afa031/mokuro/styles.css#L70-L72 + // + // We also record the previous setting on the inline style attribute so we + // can faithfully restore it when we're done. + const paragraphs = target.querySelectorAll('p'); + const toRestore: Array<[HTMLParagraphElement, string | null]> = []; + for (const p of paragraphs) { + if (getComputedStyle(p).display === 'none') { + toRestore.push([p, p.style.display || null]); + p.style.display = 'table'; + } + } + + // Check if we found any paragraphs to adjust + if (!toRestore.length) { + return null; + } + + // Setup a function to restore the content + const restore = () => { + // If we selected part of the content we uncovered we need to clear + // selection or else we'll be unable to select anything more. + const selection = window.getSelection(); + if (target && toRestore.some(([p]) => selection?.containsNode(p, true))) { + selection!.removeAllRanges(); + } + + // Restore the inline style display + for (const [p, display] of toRestore) { + if (display) { + p.style.display = display; + } else { + p.style.removeProperty('display'); + } + } + }; + + const newTarget = document.elementFromPoint(targetX, targetY); + if (!newTarget) { + restore(); + return null; + } + + return { newTarget, contentToRestore: { root: target, restore } }; + } + private readonly checkForBuggyPositionFixed = () => { // Check if we've already run this check if (typeof this.hasBuggyPositionFixed !== 'undefined') { @@ -517,9 +620,6 @@ export class LookupPuck { ); }; - private clickState: ClickState = { kind: 'idle' }; - private static readonly clickHysteresis = 300; - private readonly onPuckPointerDown = (event: PointerEvent) => { if (this.enabledState === 'disabled' || !this.puck) { return; @@ -924,6 +1024,7 @@ export class LookupPuck { } unmount(): void { + this.restoreContent(); removePuck(); window.visualViewport?.removeEventListener( 'resize',