Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions res/icons/20/reading-mode-enabled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions res/icons/20/reading-mode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 9 additions & 26 deletions src/common/components/modal-popup/appearance-popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { ReaderContext } from '../../reader';
import { DEFAULT_THEMES } from '../../defines';
import TickedRangeInput from "../common/ticked-range-input";

function ReflowableAppearanceSection({ params, enablePageWidth, onChange, indent }) {
function ReflowableAppearanceSection({ params, enablePageWidth, onChange }) {
const { l10n } = useLocalization();

const { type } = useContext(ReaderContext);
Expand All @@ -52,7 +52,7 @@ function ReflowableAppearanceSection({ params, enablePageWidth, onChange, indent
}

return (
<div className={cx('reflowable-appearance', { indent })}>
<div className="reflowable-appearance">
<div className="row">
<label htmlFor="line-height">{l10n.getString('reader-epub-appearance-line-height')}</label>
<TickedRangeInput
Expand Down Expand Up @@ -375,31 +375,14 @@ function AppearancePopup(props) {
</div>
</div>
</div>
{(type === 'epub' || type === 'snapshot') && (
{(type === 'epub' || type === 'snapshot' && props.viewStats.readingModeEnabled) && (
<div className="group">
{type === 'snapshot' && (
<div className="option">
<label htmlFor="focus-mode-enabled">{l10n.getString('reader-focus-mode')}</label>
<input
data-tabstop={1}
tabIndex={-1}
className="switch"
type="checkbox"
id="focus-mode-enabled"
checked={props.viewStats.focusModeEnabled}
onChange={e => props.onChangeFocusModeEnabled(e.target.checked)}
/>
</div>
)}
{(type === 'epub' || props.viewStats.focusModeEnabled) && (
<ReflowableAppearanceSection
params={props.viewStats.appearance}
enablePageWidth={type === 'snapshot'
|| props.viewStats.flowMode !== 'paginated' || props.viewStats.spreadMode === 0}
onChange={props.onChangeAppearance}
indent={type === 'snapshot'}
/>
)}
<ReflowableAppearanceSection
params={props.viewStats.appearance}
enablePageWidth={type === 'snapshot'
|| props.viewStats.flowMode !== 'paginated' || props.viewStats.spreadMode === 0}
onChange={props.onChangeAppearance}
/>
</div>
)}
<div className="group">
Expand Down
3 changes: 2 additions & 1 deletion src/common/components/reader-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ const ReaderUI = React.forwardRef((props, ref) => {
enableNavigateBack={viewStats.canNavigateBack}
enableNavigateToPreviousPage={viewStats.canNavigateToPreviousPage}
enableNavigateToNextPage={viewStats.canNavigateToNextPage}
readingModeEnabled={viewStats.readingModeEnabled}
appearancePopup={state.appearancePopup}
findPopupOpen={findState.popupOpen}
themes={state.themes}
Expand All @@ -150,6 +151,7 @@ const ReaderUI = React.forwardRef((props, ref) => {
onChangePageNumber={props.onChangePageNumber}
onChangeTool={props.onChangeTool}
onOpenColorContextMenu={props.onOpenColorContextMenu}
onToggleReadingMode={props.onToggleReadingMode}
onToggleAppearancePopup={props.onToggleAppearancePopup}
onToggleFind={props.onToggleFind}
onToggleContextPane={props.onToggleContextPane}
Expand Down Expand Up @@ -234,7 +236,6 @@ const ReaderUI = React.forwardRef((props, ref) => {
onChangeSpreadMode={props.onChangeSpreadMode}
onChangeFlowMode={props.onChangeFlowMode}
onChangeAppearance={props.onChangeAppearance}
onChangeFocusModeEnabled={props.onChangeFocusModeEnabled}
onAddTheme={props.onAddTheme}
onChangeTheme={props.onChangeTheme}
onOpenThemeContextMenu={props.onOpenThemeContextMenu}
Expand Down
13 changes: 12 additions & 1 deletion src/common/components/toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import IconAutoWidth from '../../../res/icons/20/auto-width.svg';
import IconChevronLeft from '../../../res/icons/20/chevron-left.svg';
import IconChevronUp from '../../../res/icons/20/chevron-up.svg';
import IconChevronDown from '../../../res/icons/20/chevron-down.svg';
import IconReadingModeEnabled from '../../../res/icons/20/reading-mode-enabled.svg';
import IconReadingMode from '../../../res/icons/20/reading-mode.svg';
import IconFormatText from '../../../res/icons/20/format-text.svg';
import IconHighlight from '../../../res/icons/20/annotate-highlight.svg';
import IconUnderline from '../../../res/icons/20/annotate-underline.svg';
Expand Down Expand Up @@ -106,12 +108,21 @@ function Toolbar(props) {
disabled={!props.enableZoomReset}
onClick={props.onZoomReset}
><IconAutoWidth/></button>
{props.type === 'snapshot' && (
<button
id="reading-mode"
className={cx('toolbar-button', { active: props.readingModeEnabled })}
title={l10n.getString('reader-reading-mode')}
tabIndex={-1}
onClick={() => props.onToggleReadingMode()}
>{props.readingModeEnabled ? <IconReadingModeEnabled/> : <IconReadingMode/>}</button>
)}
<button
id="appearance"
className={cx('toolbar-button', { active: props.appearancePopup })}
title={l10n.getString('reader-appearance')}
tabIndex={-1}
onClick={props.onToggleAppearancePopup}
onClick={() => props.onToggleAppearancePopup()}
><IconFormatText/></button>
<div className="divider"/>
<button
Expand Down
13 changes: 8 additions & 5 deletions src/common/reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,8 @@ class Reader {
onRenderThumbnails={(pageIndexes) => this._primaryView._pdfThumbnails.render(pageIndexes)}
onSetDataTransferAnnotations={this._handleSetDataTransferAnnotations.bind(this)}
onOpenLink={this._onOpenLink}
onToggleReadingMode={this._onToggleReadingMode.bind(this)}
onChangeAppearance={this._handleAppearanceChange.bind(this)}
onChangeFocusModeEnabled={this._handleFocusModeEnabledChange.bind(this)}
onChangeFindState={this._handleFindStateChange.bind(this)}
onFindNext={this.findNext.bind(this)}
onFindPrevious={this.findPrevious.bind(this)}
Expand Down Expand Up @@ -747,15 +747,18 @@ class Reader {
this._secondaryView?.setAppearance(params);
}

_handleFocusModeEnabledChange(enabled) {
_onToggleReadingMode(enabled) {
this._ensureType('snapshot');
if (enabled === undefined) {
enabled = !this._state.primaryViewStats.readingModeEnabled;
}
try {
this._primaryView?.setFocusModeEnabled(enabled);
this._secondaryView?.setFocusModeEnabled(enabled);
this._primaryView?.setReadingModeEnabled(enabled);
this._secondaryView?.setReadingModeEnabled(enabled);
}
catch (e) {
console.error(e);
this.setErrorMessage(this._getString('reader-focus-mode-not-supported'));
this.setErrorMessage(this._getString('reader-reading-mode-not-supported'));
setTimeout(() => {
this.setErrorMessage(null);
}, 5000);
Expand Down
5 changes: 0 additions & 5 deletions src/common/stylesheets/components/_modal-popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -394,11 +394,6 @@
flex-direction: column;
gap: 12px;

&.indent {
margin-top: 5px;
margin-inline-start: 10px;
}

.row {
display: grid;
grid-template-columns: 1fr minmax(3.2em, max-content) max-content;
Expand Down
8 changes: 2 additions & 6 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export type ViewStats = {
appearance?: Partial<ReflowableAppearance>;
fontFamily?: string;
outlinePath?: number[];
focusModeEnabled?: boolean;
readingModeEnabled?: boolean;
};

export type AnnotationPopupParams<A extends Annotation = Annotation> = {
Expand Down Expand Up @@ -147,11 +147,7 @@ type ImagePopupParams = {
rect: ArrayRect;
}

type HiddenInFocusModeParams = {
type: 'hiddenInFocusMode';
}

export type OverlayPopupParams = FootnotePopupParams | LinkPopupParams | ImagePopupParams | HiddenInFocusModeParams;
export type OverlayPopupParams = FootnotePopupParams | LinkPopupParams | ImagePopupParams;

export type ArrayRect = [left: number, top: number, right: number, bottom: number];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import focusSCSS from '../stylesheets/focus.scss';
import readingModeSCSS from '../stylesheets/reading-mode.scss';
import { Readability } from "@abejellinek/readability-keep-nodes";
import { iterateWalker } from "../../common/lib/nodes";
import { enumerate } from "../../common/lib/collection";
import { NodeMapping } from "./node-mapping";

export class FocusMode {
export class ReadingMode {
private readonly _doc: Document;

private readonly _mapping = new NodeMapping();
Expand All @@ -13,15 +13,15 @@ export class FocusMode {

private readonly _originalStyleSheets = new Map<CSSStyleSheet, Element | ProcessingInstruction | null>;

private readonly _focusStyle: HTMLStyleElement;
private readonly _style: HTMLStyleElement;

private _enabled = false;

constructor(doc: Document) {
this._doc = doc;
this._fragment = doc.createDocumentFragment();
this._focusStyle = doc.createElement('style');
this._focusStyle.textContent = focusSCSS;
this._style = doc.createElement('style');
this._style.textContent = readingModeSCSS;

for (let styleSheet of [...this._doc.styleSheets, ...this._doc.adoptedStyleSheets]) {
if (styleSheet.disabled) {
Expand Down Expand Up @@ -153,7 +153,7 @@ export class FocusMode {
styleSheet.disabled = true;
ownerNode?.remove();
}
this._doc.head.append(this._focusStyle);
this._doc.head.append(this._style);
}

private _disable() {
Expand All @@ -168,7 +168,7 @@ export class FocusMode {
this._doc.head.append(ownerNode);
}
}
this._focusStyle.remove();
this._style.remove();
this._mapping.clear();
this._fragment.replaceChildren();
}
Expand Down
36 changes: 18 additions & 18 deletions src/dom/snapshot/snapshot-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ import { isPageRectVisible } from "../common/lib/rect";
import { debounceUntilScrollFinishes } from "../../common/lib/utilities";
import { scrollIntoView } from "../common/lib/scroll-into-view";
import { SORT_INDEX_LENGTH, SORT_INDEX_LENGTH_OLD } from "./defines";
import { FocusMode } from "./focus-mode";
import { ReadingMode } from "./reading-mode";

class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
protected _find: DefaultFindProcessor | null = null;

private _isDynamicThemeSupported = true;

protected _focusMode!: FocusMode;
protected _readingMode!: ReadingMode;

private get _searchContext() {
let searchContext = createSearchContext(getVisibleTextNodes(this._iframeDocument.body));
Expand Down Expand Up @@ -137,7 +137,7 @@ class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
}
}

this._focusMode = new FocusMode(this._iframeDocument);
this._readingMode = new ReadingMode(this._iframeDocument);

this._iframeDocument.addEventListener('visibilitychange', this._handleVisibilityChange.bind(this));

Expand Down Expand Up @@ -292,10 +292,10 @@ class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
};

let count: number;
if (this._focusMode.enabled) {
let newRange = this._focusMode.mapRangeFromFocus(range);
if (this._readingMode.enabled) {
let newRange = this._readingMode.mapRangeFromFocus(range);
if (newRange) {
count = getCount(this._focusMode.originalRoot, newRange.startContainer, newRange.startOffset);
count = getCount(this._readingMode.originalRoot, newRange.startContainer, newRange.startOffset);
}
else {
count = 0;
Expand All @@ -312,8 +312,8 @@ class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
}

toSelector(range: Range): Selector | null {
if (this._focusMode.enabled) {
let newRange = this._focusMode.mapRangeFromFocus(range);
if (this._readingMode.enabled) {
let newRange = this._readingMode.mapRangeFromFocus(range);
if (!newRange) {
return null;
}
Expand Down Expand Up @@ -362,10 +362,10 @@ class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
if (selector.refinedBy && selector.refinedBy.type != 'TextPositionSelector') {
throw new Error('CssSelectors can only be refined by TextPositionSelectors');
}
let root = (this._focusMode.enabled ? this._focusMode.originalRoot : this._iframeDocument)
let root = (this._readingMode.enabled ? this._readingMode.originalRoot : this._iframeDocument)
.querySelector(selector.value);
if (!root) {
console.error(`Unable to locate selector root for selector '${selector.value}' (focus mode: ${this._focusMode.enabled})`);
console.error(`Unable to locate selector root for selector '${selector.value}' (reading mode: ${this._readingMode.enabled})`);
return null;
}
let range;
Expand All @@ -376,8 +376,8 @@ class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
range = this._iframeDocument.createRange();
range.selectNodeContents(root);
}
if (this._focusMode.enabled) {
let newRange = this._focusMode.mapRangeToFocus(range);
if (this._readingMode.enabled) {
let newRange = this._readingMode.mapRangeToFocus(range);
if (!newRange) {
return null;
}
Expand Down Expand Up @@ -411,8 +411,8 @@ class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
protected _navigateToSelector(selector: Selector, options: NavigateOptions = {}) {
let range = this.toDisplayedRange(selector);
if (!range) {
// Suppress log when failure is likely just due to focus mode
if (!this._focusMode.enabled) {
// Suppress log when failure is likely just due to reading mode
if (!this._readingMode.enabled) {
console.warn('Unable to resolve selector to range', selector);
}
return;
Expand Down Expand Up @@ -463,14 +463,14 @@ class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
canNavigateBack: this._history.canNavigateBack,
canNavigateForward: this._history.canNavigateForward,
appearance: this.appearance,
focusModeEnabled: this._focusMode.enabled,
readingModeEnabled: this._readingMode.enabled,
};
this._options.onChangeViewStats(viewStats);
}

protected override _updateColorScheme() {
super._updateColorScheme();
if (this._isDynamicThemeSupported || this._focusMode.enabled) {
if (this._isDynamicThemeSupported || this._readingMode.enabled) {
// Pages with a reasonable amount of CSS: Use Dark Reader
this._iframeDocument.body.classList.remove('force-static-theme');
if (!('DarkReader' in this._iframeWindow)) {
Expand Down Expand Up @@ -673,8 +673,8 @@ class SnapshotView extends DOMView<SnapshotViewState, SnapshotViewData> {
// Ignore
}

setFocusModeEnabled(enabled: boolean) {
this._focusMode.enabled = enabled;
setReadingModeEnabled(enabled: boolean) {
this._readingMode.enabled = enabled;
// Hide inaccessible annotations
if (enabled) {
this._options.onSetHiddenAnnotations(
Expand Down