Add double-click zoom toggle to the fx renderer

Desktop counterpart of the existing touch double-tap: at fit scale a
double-click zooms to 2.5x at the clicked location; zoomed or panned it
resets to 100% centered. Page iframes forward dblclick with converted
coordinates and real targets; PDF text-layer spans and annotation links
keep native double-click (word selection) in select/text modes. Applies
to all fixed-layout content (comics, manga, PDFs, fixed EPUBs).
This commit is contained in:
2026-09-17 14:17:51 -04:00
parent 734ce41a50
commit e16530ad86
+54
View File
@@ -145,6 +145,11 @@ export class FixedLayout extends HTMLElement {
this.addEventListener("touchcancel", this.#onTouchCancel.bind(this), {
passive: false,
});
// Desktop double-click zoom toggle, mirroring the touch double-tap.
// Catches events on the host itself (letterbox gutters); page iframes
// forward their dblclick separately (see #attachEventListenersToIframe).
this.addEventListener("dblclick", this.#handleDoubleClick.bind(this));
}
attributeChangedCallback(name, _, value) {
@@ -622,6 +627,34 @@ export class FixedLayout extends HTMLElement {
}
}
// Desktop counterpart of the touch double-tap toggle: at fit scale a
// double-click zooms in at the clicked location; zoomed or panned it
// resets to 100% centered. PDF text interactions keep native
// double-click (word selection, links) in select/text modes — zoom only
// when the target isn't text/annotation content. Returns true when the
// event was consumed (callers preventDefault then).
#handleDoubleClick(event) {
if (this.hasAttribute("panel-mode")) return false;
if (this.#isPDF && this.#interactionMode !== "pan") {
const t = event.realTarget ?? event.target;
if (
t?.closest?.(".textLayer span") ||
t?.closest?.(".annotationLayer a")
)
return false;
}
const rect = this.getBoundingClientRect();
const cx = event.clientX - rect.left;
const cy = event.clientY - rect.top;
if (this.#atFitScale()) {
this.#zoomByRatio(cx, cy, 2.5);
} else {
this.resetZoom();
}
event.preventDefault();
return true;
}
// ----- touch gestures -----
#atFitScale() {
@@ -1132,6 +1165,27 @@ export class FixedLayout extends HTMLElement {
this.#handleMouseUp(mouseEvent);
event.preventDefault();
});
// Double-click zoom: forwarded with converted coordinates and the
// iframe's real target (for PDF text-layer hit-testing).
doc.addEventListener("dblclick", (event) => {
const { clientX, clientY } = convertCoords(event);
const mouseEvent = new MouseEvent("dblclick", {
button: event.button,
clientX,
clientY,
ctrlKey: event.ctrlKey,
metaKey: event.metaKey,
shiftKey: event.shiftKey,
altKey: event.altKey,
bubbles: true,
cancelable: true,
});
mouseEvent.sourceIframe = frameId;
mouseEvent.sourceFrame = frame;
mouseEvent.realTarget = event.target;
if (this.#handleDoubleClick(mouseEvent)) event.preventDefault();
});
}
#render(side = this.#side) {