Skip to content

Commit

Permalink
fix: keyboard focus inversion of control, Thorium now pushes requests…
Browse files Browse the repository at this point in the history
… to navigator (removed body-injected underscore / jump link)
  • Loading branch information
danielweck committed Dec 30, 2024
1 parent fcffd1d commit 0926c7b
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 109 deletions.
211 changes: 128 additions & 83 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@
"pdf.js": "github:edrlab/pdf.js#build",
"proxy-agent": "^6.5.0",
"r2-lcp-js": "^1.0.41",
"r2-navigator-js": "^1.16.5",
"r2-navigator-js": "^1.16.6",
"r2-opds-js": "^1.0.44",
"r2-shared-js": "^1.0.77",
"r2-streamer-js": "^1.0.48",
Expand Down Expand Up @@ -368,12 +368,12 @@
"@types/yargs": "^17.0.33",
"@types/yauzl": "^2.10.3",
"@types/yazl": "^2.4.5",
"@typescript-eslint/eslint-plugin": "^8.18.2",
"@typescript-eslint/parser": "^8.18.2",
"@typescript-eslint/eslint-plugin": "^8.19.0",
"@typescript-eslint/parser": "^8.19.0",
"babel-loader": "^9.2.1",
"babel-plugin-macros": "^3.1.0",
"chromium-pickle-js": "^0.2.0",
"concurrently": "^9.1.0",
"concurrently": "^9.1.1",
"copy-webpack-plugin": "^12.0.2",
"cross-env": "^7.0.3",
"css-hot-loader": "^1.4.4",
Expand Down
7 changes: 7 additions & 0 deletions src/common/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ const _defaults_ = Object.freeze({
shift: false,
key: "F10",
}),
FocusMainDeep: Object.freeze<TKeyboardShortcut>({
alt: false,
control: true,
shift: true,
key: "F10",
}),

FocusToolbar: Object.freeze<TKeyboardShortcut>({
alt: false,
control: true,
Expand Down
60 changes: 43 additions & 17 deletions src/renderer/reader/components/Reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ import {
setReadingLocationSaver, ttsClickEnable, ttsNext, ttsOverlayEnable, ttsPause,
ttsPlay, ttsPlaybackRate, ttsPrevious, ttsResume, ttsSkippabilityEnable, ttsSentenceDetectionEnable, TTSStateEnum,
ttsStop, ttsVoice, highlightsClickListen,
stealFocusDisable,
// stealFocusDisable,
keyboardFocusRequest,
} from "@r2-navigator-js/electron/renderer/index";
import { Locator as R2Locator } from "@r2-navigator-js/electron/common/locator";

Expand Down Expand Up @@ -269,6 +270,7 @@ class Reader extends React.Component<IProps, IState> {
this.onKeyboardSpineNavigationPrevious = this.onKeyboardSpineNavigationPrevious.bind(this);
this.onKeyboardSpineNavigationNext = this.onKeyboardSpineNavigationNext.bind(this);
this.onKeyboardFocusMain = this.onKeyboardFocusMain.bind(this);
this.onKeyboardFocusMainDeep = this.onKeyboardFocusMainDeep.bind(this);
this.onKeyboardFocusToolbar = this.onKeyboardFocusToolbar.bind(this);
this.onKeyboardFullScreen = this.onKeyboardFullScreen.bind(this);
this.onKeyboardBookmark = this.onKeyboardBookmark.bind(this);
Expand Down Expand Up @@ -1042,6 +1044,10 @@ class Reader extends React.Component<IProps, IState> {
true, // listen for key up (not key down)
this.props.keyboardShortcuts.FocusMain,
this.onKeyboardFocusMain);
registerKeyboardListener(
true, // listen for key up (not key down)
this.props.keyboardShortcuts.FocusMainDeep,
this.onKeyboardFocusMainDeep);

registerKeyboardListener(
true, // listen for key up (not key down)
Expand Down Expand Up @@ -1145,6 +1151,7 @@ class Reader extends React.Component<IProps, IState> {
unregisterKeyboardListener(this.onKeyboardSpineNavigationPrevious);
unregisterKeyboardListener(this.onKeyboardSpineNavigationNext);
unregisterKeyboardListener(this.onKeyboardFocusMain);
unregisterKeyboardListener(this.onKeyboardFocusMainDeep);
unregisterKeyboardListener(this.onKeyboardFocusToolbar);
unregisterKeyboardListener(this.onKeyboardFullScreen);
unregisterKeyboardListener(this.onKeyboardBookmark);
Expand Down Expand Up @@ -1577,10 +1584,10 @@ class Reader extends React.Component<IProps, IState> {
}

// lock focus outside webview for 400ms
if (this.props.readerConfig.readerDockingMode !== "full") {
stealFocusDisable(true);
setTimeout(() => stealFocusDisable(false), 400);
}
// if (this.props.readerConfig.readerDockingMode !== "full") {
// stealFocusDisable(true);
// setTimeout(() => stealFocusDisable(false), 400);
// }

this.handleMenuButtonClick(true, this.state.openedSectionMenu, true);
};
Expand Down Expand Up @@ -1612,7 +1619,21 @@ class Reader extends React.Component<IProps, IState> {
return;
}

this.focusMainArea();
this.focusMainArea(false, true);
// if (this.fastLinkRef?.current) {
// console.log("€€€€€ FOCUS READER MAIN");
// this.fastLinkRef.current.focus();
// }
};
private onKeyboardFocusMainDeep = () => {
if (!this.state.shortcutEnable) {
if (DEBUG_KEYBOARD) {
console.log("!shortcutEnable (onKeyboardFocusMainDeep)");
}
return;
}

this.focusMainArea(true, true);
// if (this.fastLinkRef?.current) {
// console.log("€€€€€ FOCUS READER MAIN");
// this.fastLinkRef.current.focus();
Expand Down Expand Up @@ -2228,6 +2249,7 @@ class Reader extends React.Component<IProps, IState> {
this.props.winId,
computeReadiumCssJsonMessage(this.props.readerConfig),
);
// stealFocusDisable(true);

windowHistory._length = 1;
// console.log("#+$%".repeat(5) + " installNavigatorDOM => window history replaceState() ...", JSON.stringify(locator), JSON.stringify(window.history.state), window.history.length, windowHistory._length, JSON.stringify(document.location), JSON.stringify(window.location));
Expand Down Expand Up @@ -2256,10 +2278,10 @@ class Reader extends React.Component<IProps, IState> {
}

// lock focus outside webview for 400ms
if (this.props.readerConfig.readerDockingMode !== "full") {
stealFocusDisable(true);
setTimeout(() => stealFocusDisable(false), 400);
}
// if (this.props.readerConfig.readerDockingMode !== "full") {
// stealFocusDisable(true);
// setTimeout(() => stealFocusDisable(false), 400);
// }

this.handleMenuButtonClick(true, "tab-toc");

Expand Down Expand Up @@ -2366,11 +2388,15 @@ class Reader extends React.Component<IProps, IState> {
return visibleBookmarkList;
}

private focusMainArea() {
private focusMainArea(deep: boolean, immediate: boolean) {
if (this.fastLinkRef?.current) {
console.log("€€€€€ FOCUS READER MAIN");
this.fastLinkRef.current.focus();
}

setTimeout(() => {
keyboardFocusRequest(deep);
}, immediate ? 0 : 800); // time for document load + render/paginate + reading location setter
}

private closeMenu() {
Expand All @@ -2380,7 +2406,7 @@ class Reader extends React.Component<IProps, IState> {
}
}

private focusMainAreaLandmarkAndCloseMenu() {
private focusMainAreaLandmarkAndCloseMenu(deep: boolean) {

// if (this.state.menuOpen) {
// this.handleMenuButtonClick(false);
Expand All @@ -2392,7 +2418,7 @@ class Reader extends React.Component<IProps, IState> {
// }

this.closeMenu();
this.focusMainArea();
this.focusMainArea(deep, false);
// if (this.fastLinkRef?.current) {
// // shortcutEnable must be true (see handleMenuButtonClick() above, and this.state.menuOpen))
// console.log("@@@@@@@@@@@@@@@");
Expand Down Expand Up @@ -2445,9 +2471,9 @@ class Reader extends React.Component<IProps, IState> {

private goToLocator(locator: R2Locator, closeNavPanel = true, isFromOnPopState = false) {

if (closeNavPanel) {
if (closeNavPanel && !isFromOnPopState) {
// this.closeMenu();
this.focusMainAreaLandmarkAndCloseMenu();
this.focusMainAreaLandmarkAndCloseMenu(true);
}

if (this.props.isPdf) {
Expand Down Expand Up @@ -2488,8 +2514,8 @@ class Reader extends React.Component<IProps, IState> {
return;
}

if (closeNavPanel) {
this.focusMainAreaLandmarkAndCloseMenu();
if (closeNavPanel && !isFromOnPopState) {
this.focusMainAreaLandmarkAndCloseMenu(true);
}

if (this.props.isPdf) {
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/reader/components/ReaderMenuSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { readerLocalActionSearch } from "../redux/actions";

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface IBaseProps {
focusMainAreaLandmarkAndCloseMenu: () => void;
focusMainAreaLandmarkAndCloseMenu: (deep: boolean) => void;
dockedMode: boolean;
}
// IProps may typically extend:
Expand Down Expand Up @@ -442,7 +442,7 @@ const handleSearchClickFunc = (
e.preventDefault();

if (closeNavPanel) {
thiz.props.focusMainAreaLandmarkAndCloseMenu();
thiz.props.focusMainAreaLandmarkAndCloseMenu(true);
}

thiz.props.searchFocusCurrent(href); // search uuid
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/reader/components/options-values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export interface IReaderMenuProps {
handleLinkClick: (event: TMouseEventOnSpan | TMouseEventOnAnchor | TKeyboardEventOnAnchor | undefined, url: string, closeNavPanel?: boolean) => void;
goToLocator: (locator: R2Locator, closeNavPanel?: boolean) => void;
toggleMenu: () => void;
focusMainAreaLandmarkAndCloseMenu: () => void;
focusMainAreaLandmarkAndCloseMenu: (deep: boolean) => void;
pdfToc: TToc;
isPdf: boolean;
pdfNumberOfPages: number;
Expand Down
9 changes: 7 additions & 2 deletions src/utils/search/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { ContentType } from "../contentType";
import { ISearchDocument, ISearchResult } from "./search.interface";
import { searchDocDomSeek } from "./searchWithDomSeek";

import { ENABLE_SKIP_LINK } from "@r2-navigator-js/electron/common/styles";

export async function search(searchInput: string, data: ISearchDocument): Promise<ISearchResult[]> {

if (!data.xml) {
Expand All @@ -27,10 +29,13 @@ export async function search(searchInput: string, data: ISearchDocument): Promis
// TODO: this is a hack...
// but rendered reflowable documents have a top-level invisible accessible link injected by the navigator
// so we need it here to compute CSS Selectors
let toParse = data.isFixedLayout ? data.xml : data.xml.replace(
// SKIP_LINK_ID === "r2-skip-link"
let toParse = (!ENABLE_SKIP_LINK || data.isFixedLayout) ?
data.xml :
data.xml.replace(
/<body([\s\S]*?)>/gm,
"<body$1><a id=\"r2-skip-link\" href=\"javascript:;\" title=\"__\" aria-label=\"__\" tabindex=\"0\"> </a>",
);
);
// console.log(`===data.isFixedLayout ${data.isFixedLayout}`, data.xml);

const contentType = data.contentType ? (data.contentType as DOMParserSupportedType) : ContentType.Xhtml;
Expand Down

0 comments on commit 0926c7b

Please sign in to comment.