Merge pull request #12777 from Snuffleupagus/currentOutlineItem
Add support for finding/highlighting the outlineItem, corresponding to the currently visible page, in the sidebar (issue 7557, bug 1253820, bug 1499050)
This commit is contained in:
commit
7f199e7017
@ -147,6 +147,8 @@ layers.title=Show Layers (double-click to reset all layers to the default state)
|
|||||||
layers_label=Layers
|
layers_label=Layers
|
||||||
thumbs.title=Show Thumbnails
|
thumbs.title=Show Thumbnails
|
||||||
thumbs_label=Thumbnails
|
thumbs_label=Thumbnails
|
||||||
|
current_outline_item.title=Find Current Outline Item
|
||||||
|
current_outline_item_label=Current Outline Item
|
||||||
findbar.title=Find in Document
|
findbar.title=Find in Document
|
||||||
findbar_label=Find
|
findbar_label=Find
|
||||||
|
|
||||||
|
@ -148,6 +148,8 @@ layers.title=Lagen tonen (dubbelklik om alle lagen naar de standaardstatus terug
|
|||||||
layers_label=Lagen
|
layers_label=Lagen
|
||||||
thumbs.title=Miniaturen tonen
|
thumbs.title=Miniaturen tonen
|
||||||
thumbs_label=Miniaturen
|
thumbs_label=Miniaturen
|
||||||
|
current_outline_item.title=Huidige positie in documentoverzicht selecteren
|
||||||
|
current_outline_item_label=Huidige positie in documentoverzicht
|
||||||
findbar.title=Zoeken in document
|
findbar.title=Zoeken in document
|
||||||
findbar_label=Zoeken
|
findbar_label=Zoeken
|
||||||
|
|
||||||
|
@ -148,6 +148,8 @@ layers.title=Visa lager (dubbelklicka för att återställa alla lager till stan
|
|||||||
layers_label=Lager
|
layers_label=Lager
|
||||||
thumbs.title=Visa miniatyrer
|
thumbs.title=Visa miniatyrer
|
||||||
thumbs_label=Miniatyrer
|
thumbs_label=Miniatyrer
|
||||||
|
current_outline_item.title=Hitta aktuell position i dokumentdispositionen
|
||||||
|
current_outline_item_label=Aktuell position i dokumentdisposition
|
||||||
findbar.title=Sök i dokument
|
findbar.title=Sök i dokument
|
||||||
findbar_label=Sök
|
findbar_label=Sök
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import {
|
|||||||
ProgressBar,
|
ProgressBar,
|
||||||
RendererType,
|
RendererType,
|
||||||
ScrollMode,
|
ScrollMode,
|
||||||
|
SidebarView,
|
||||||
SpreadMode,
|
SpreadMode,
|
||||||
TextLayerMode,
|
TextLayerMode,
|
||||||
} from "./ui_utils.js";
|
} from "./ui_utils.js";
|
||||||
@ -56,7 +57,6 @@ import {
|
|||||||
} from "pdfjs-lib";
|
} from "pdfjs-lib";
|
||||||
import { CursorTool, PDFCursorTools } from "./pdf_cursor_tools.js";
|
import { CursorTool, PDFCursorTools } from "./pdf_cursor_tools.js";
|
||||||
import { PDFRenderingQueue, RenderingStates } from "./pdf_rendering_queue.js";
|
import { PDFRenderingQueue, RenderingStates } from "./pdf_rendering_queue.js";
|
||||||
import { PDFSidebar, SidebarView } from "./pdf_sidebar.js";
|
|
||||||
import { OverlayManager } from "./overlay_manager.js";
|
import { OverlayManager } from "./overlay_manager.js";
|
||||||
import { PasswordPrompt } from "./password_prompt.js";
|
import { PasswordPrompt } from "./password_prompt.js";
|
||||||
import { PDFAttachmentViewer } from "./pdf_attachment_viewer.js";
|
import { PDFAttachmentViewer } from "./pdf_attachment_viewer.js";
|
||||||
@ -68,6 +68,7 @@ import { PDFLayerViewer } from "./pdf_layer_viewer.js";
|
|||||||
import { PDFLinkService } from "./pdf_link_service.js";
|
import { PDFLinkService } from "./pdf_link_service.js";
|
||||||
import { PDFOutlineViewer } from "./pdf_outline_viewer.js";
|
import { PDFOutlineViewer } from "./pdf_outline_viewer.js";
|
||||||
import { PDFPresentationMode } from "./pdf_presentation_mode.js";
|
import { PDFPresentationMode } from "./pdf_presentation_mode.js";
|
||||||
|
import { PDFSidebar } from "./pdf_sidebar.js";
|
||||||
import { PDFSidebarResizer } from "./pdf_sidebar_resizer.js";
|
import { PDFSidebarResizer } from "./pdf_sidebar_resizer.js";
|
||||||
import { PDFThumbnailViewer } from "./pdf_thumbnail_viewer.js";
|
import { PDFThumbnailViewer } from "./pdf_thumbnail_viewer.js";
|
||||||
import { PDFViewer } from "./pdf_viewer.js";
|
import { PDFViewer } from "./pdf_viewer.js";
|
||||||
@ -1419,7 +1420,7 @@ const PDFViewerApplication = {
|
|||||||
|
|
||||||
onePageRendered.then(() => {
|
onePageRendered.then(() => {
|
||||||
pdfDocument.getOutline().then(outline => {
|
pdfDocument.getOutline().then(outline => {
|
||||||
this.pdfOutlineViewer.render({ outline });
|
this.pdfOutlineViewer.render({ outline, pdfDocument });
|
||||||
});
|
});
|
||||||
pdfDocument.getAttachments().then(attachments => {
|
pdfDocument.getAttachments().then(attachments => {
|
||||||
this.pdfAttachmentViewer.render({ attachments });
|
this.pdfAttachmentViewer.render({ attachments });
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
|
|
||||||
import { removeNullCharacters } from "pdfjs-lib";
|
import { removeNullCharacters } from "pdfjs-lib";
|
||||||
|
|
||||||
|
const TREEITEM_OFFSET_TOP = -100; // px
|
||||||
|
const TREEITEM_SELECTED_CLASS = "selected";
|
||||||
|
|
||||||
class BaseTreeViewer {
|
class BaseTreeViewer {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
if (this.constructor === BaseTreeViewer) {
|
if (this.constructor === BaseTreeViewer) {
|
||||||
@ -27,7 +30,9 @@ class BaseTreeViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
|
this._pdfDocument = null;
|
||||||
this._lastToggleIsShow = true;
|
this._lastToggleIsShow = true;
|
||||||
|
this._currentTreeItem = null;
|
||||||
|
|
||||||
// Remove the tree from the DOM.
|
// Remove the tree from the DOM.
|
||||||
this.container.textContent = "";
|
this.container.textContent = "";
|
||||||
@ -120,6 +125,46 @@ class BaseTreeViewer {
|
|||||||
render(params) {
|
render(params) {
|
||||||
throw new Error("Not implemented: render");
|
throw new Error("Not implemented: render");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_updateCurrentTreeItem(treeItem = null) {
|
||||||
|
if (this._currentTreeItem) {
|
||||||
|
// Ensure that the current treeItem-selection is always removed.
|
||||||
|
this._currentTreeItem.classList.remove(TREEITEM_SELECTED_CLASS);
|
||||||
|
this._currentTreeItem = null;
|
||||||
|
}
|
||||||
|
if (treeItem) {
|
||||||
|
treeItem.classList.add(TREEITEM_SELECTED_CLASS);
|
||||||
|
this._currentTreeItem = treeItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_scrollToCurrentTreeItem(treeItem) {
|
||||||
|
if (!treeItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Ensure that the treeItem is *fully* expanded, such that it will first of
|
||||||
|
// all be visible and secondly that scrolling it into view works correctly.
|
||||||
|
let currentNode = treeItem.parentNode;
|
||||||
|
while (currentNode && currentNode !== this.container) {
|
||||||
|
if (currentNode.classList.contains("treeItem")) {
|
||||||
|
const toggler = currentNode.firstElementChild;
|
||||||
|
toggler?.classList.remove("treeItemsHidden");
|
||||||
|
}
|
||||||
|
currentNode = currentNode.parentNode;
|
||||||
|
}
|
||||||
|
this._updateCurrentTreeItem(treeItem);
|
||||||
|
|
||||||
|
this.container.scrollTo(
|
||||||
|
treeItem.offsetLeft,
|
||||||
|
treeItem.offsetTop + TREEITEM_OFFSET_TOP
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { BaseTreeViewer };
|
export { BaseTreeViewer };
|
||||||
|
1
web/images/toolbarButton-currentOutlineItem.svg
Normal file
1
web/images/toolbarButton-currentOutlineItem.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="m14 9h-6c-1.3 0-1.3 2 0 2h6c1.3 0 1.3-2 0-2zm-5.2-8h-3.8c-1.3 0-1.3 2 0 2h1.7zm-6.8 0c-1 0-1.3 1-0.7 1.7 0.7 0.6 1.7 0.3 1.7-0.7 0-0.5-0.4-1-1-1zm3 8c-1 0-1.3 1-0.7 1.7 0.6 0.6 1.7 0.2 1.7-0.7 0-0.5-0.4-1-1-1zm0.3-4h-0.3c-1.4 0-1.4 2 0 2h2.3zm-3.3 0c-0.9 0-1.4 1-0.7 1.7 0.7 0.6 1.7 0.2 1.7-0.7 0-0.6-0.5-1-1-1zm12 8h-9c-1.3 0-1.3 2 0 2h9c1.3 0 1.3-2 0-2zm-12 0c-1 0-1.3 1-0.7 1.7 0.7 0.6 1.7 0.2 1.7-0.712 0-0.5-0.4-1-1-1z"/><path d="m7.37 4.838 3.93-3.911v2.138h3.629v3.546h-3.629v2.138l-3.93-3.911"/></svg>
|
After Width: | Height: | Size: 581 B |
@ -41,7 +41,6 @@ class PDFLayerViewer extends BaseTreeViewer {
|
|||||||
reset() {
|
reset() {
|
||||||
super.reset();
|
super.reset();
|
||||||
this._optionalContentConfig = null;
|
this._optionalContentConfig = null;
|
||||||
this._pdfDocument = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,8 +13,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addLinkAttributes, LinkTarget } from "pdfjs-lib";
|
import {
|
||||||
|
addLinkAttributes,
|
||||||
|
createPromiseCapability,
|
||||||
|
LinkTarget,
|
||||||
|
} from "pdfjs-lib";
|
||||||
import { BaseTreeViewer } from "./base_tree_viewer.js";
|
import { BaseTreeViewer } from "./base_tree_viewer.js";
|
||||||
|
import { SidebarView } from "./ui_utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} PDFOutlineViewerOptions
|
* @typedef {Object} PDFOutlineViewerOptions
|
||||||
@ -26,6 +31,7 @@ import { BaseTreeViewer } from "./base_tree_viewer.js";
|
|||||||
/**
|
/**
|
||||||
* @typedef {Object} PDFOutlineViewerRenderParameters
|
* @typedef {Object} PDFOutlineViewerRenderParameters
|
||||||
* @property {Array|null} outline - An array of outline objects.
|
* @property {Array|null} outline - An array of outline objects.
|
||||||
|
* @property {PDFDocument} pdfDocument - A {PDFDocument} instance.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class PDFOutlineViewer extends BaseTreeViewer {
|
class PDFOutlineViewer extends BaseTreeViewer {
|
||||||
@ -37,11 +43,29 @@ class PDFOutlineViewer extends BaseTreeViewer {
|
|||||||
this.linkService = options.linkService;
|
this.linkService = options.linkService;
|
||||||
|
|
||||||
this.eventBus._on("toggleoutlinetree", this._toggleAllTreeItems.bind(this));
|
this.eventBus._on("toggleoutlinetree", this._toggleAllTreeItems.bind(this));
|
||||||
|
this.eventBus._on(
|
||||||
|
"currentoutlineitem",
|
||||||
|
this._currentOutlineItem.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.eventBus._on("pagechanging", evt => {
|
||||||
|
this._currentPageNumber = evt.pageNumber;
|
||||||
|
});
|
||||||
|
this.eventBus._on("pagesloaded", evt => {
|
||||||
|
this._isPagesLoaded = !!evt.pagesCount;
|
||||||
|
});
|
||||||
|
this.eventBus._on("sidebarviewchanged", evt => {
|
||||||
|
this._sidebarView = evt.view;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
super.reset();
|
super.reset();
|
||||||
this._outline = null;
|
this._outline = null;
|
||||||
|
|
||||||
|
this._pageNumberToDestHashCapability = null;
|
||||||
|
this._currentPageNumber = 1;
|
||||||
|
this._isPagesLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,6 +75,8 @@ class PDFOutlineViewer extends BaseTreeViewer {
|
|||||||
this.eventBus.dispatch("outlineloaded", {
|
this.eventBus.dispatch("outlineloaded", {
|
||||||
source: this,
|
source: this,
|
||||||
outlineCount,
|
outlineCount,
|
||||||
|
enableCurrentOutlineItemButton:
|
||||||
|
outlineCount > 0 && !this._pdfDocument?.loadingParams.disableAutoFetch,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +97,9 @@ class PDFOutlineViewer extends BaseTreeViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
element.href = linkService.getDestinationHash(dest);
|
element.href = linkService.getDestinationHash(dest);
|
||||||
element.onclick = () => {
|
element.onclick = evt => {
|
||||||
|
this._updateCurrentTreeItem(evt.target.parentNode);
|
||||||
|
|
||||||
if (dest) {
|
if (dest) {
|
||||||
linkService.goToDestination(dest);
|
linkService.goToDestination(dest);
|
||||||
}
|
}
|
||||||
@ -128,11 +156,12 @@ class PDFOutlineViewer extends BaseTreeViewer {
|
|||||||
/**
|
/**
|
||||||
* @param {PDFOutlineViewerRenderParameters} params
|
* @param {PDFOutlineViewerRenderParameters} params
|
||||||
*/
|
*/
|
||||||
render({ outline }) {
|
render({ outline, pdfDocument }) {
|
||||||
if (this._outline) {
|
if (this._outline) {
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
this._outline = outline || null;
|
this._outline = outline || null;
|
||||||
|
this._pdfDocument = pdfDocument || null;
|
||||||
|
|
||||||
if (!outline) {
|
if (!outline) {
|
||||||
this._dispatchEvent(/* outlineCount = */ 0);
|
this._dispatchEvent(/* outlineCount = */ 0);
|
||||||
@ -174,6 +203,120 @@ class PDFOutlineViewer extends BaseTreeViewer {
|
|||||||
|
|
||||||
this._finishRendering(fragment, outlineCount, hasAnyNesting);
|
this._finishRendering(fragment, outlineCount, hasAnyNesting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find/highlight the current outline item, corresponding to the active page.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _currentOutlineItem() {
|
||||||
|
if (!this._isPagesLoaded) {
|
||||||
|
throw new Error("_currentOutlineItem: All pages have not been loaded.");
|
||||||
|
}
|
||||||
|
if (!this._outline || !this._pdfDocument) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageNumberToDestHash = await this._getPageNumberToDestHash(
|
||||||
|
this._pdfDocument
|
||||||
|
);
|
||||||
|
if (!pageNumberToDestHash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._updateCurrentTreeItem(/* treeItem = */ null);
|
||||||
|
|
||||||
|
if (this._sidebarView !== SidebarView.OUTLINE) {
|
||||||
|
return; // The outline view is no longer visible, hence do nothing.
|
||||||
|
}
|
||||||
|
// When there is no destination on the current page, always check the
|
||||||
|
// previous ones in (reverse) order.
|
||||||
|
for (let i = this._currentPageNumber; i > 0; i--) {
|
||||||
|
const destHash = pageNumberToDestHash.get(i);
|
||||||
|
if (!destHash) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const linkElement = this.container.querySelector(`a[href="${destHash}"]`);
|
||||||
|
if (!linkElement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this._scrollToCurrentTreeItem(linkElement.parentNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To (significantly) simplify the overall implementation, we will only
|
||||||
|
* consider *one* destination per page when finding/highlighting the current
|
||||||
|
* outline item (similar to e.g. Adobe Reader); more specifically, we choose
|
||||||
|
* the *first* outline item at the *lowest* level of the outline tree.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _getPageNumberToDestHash(pdfDocument) {
|
||||||
|
if (this._pageNumberToDestHashCapability) {
|
||||||
|
return this._pageNumberToDestHashCapability.promise;
|
||||||
|
}
|
||||||
|
this._pageNumberToDestHashCapability = createPromiseCapability();
|
||||||
|
|
||||||
|
const pageNumberToDestHash = new Map(),
|
||||||
|
pageNumberNesting = new Map();
|
||||||
|
const queue = [{ nesting: 0, items: this._outline }];
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const levelData = queue.shift(),
|
||||||
|
currentNesting = levelData.nesting;
|
||||||
|
for (const { dest, items } of levelData.items) {
|
||||||
|
let explicitDest, pageNumber;
|
||||||
|
if (typeof dest === "string") {
|
||||||
|
explicitDest = await pdfDocument.getDestination(dest);
|
||||||
|
|
||||||
|
if (pdfDocument !== this._pdfDocument) {
|
||||||
|
return null; // The document was closed while the data resolved.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
explicitDest = dest;
|
||||||
|
}
|
||||||
|
if (Array.isArray(explicitDest)) {
|
||||||
|
const [destRef] = explicitDest;
|
||||||
|
|
||||||
|
if (typeof destRef === "object") {
|
||||||
|
pageNumber = this.linkService._cachedPageNumber(destRef);
|
||||||
|
|
||||||
|
if (!pageNumber) {
|
||||||
|
try {
|
||||||
|
pageNumber = (await pdfDocument.getPageIndex(destRef)) + 1;
|
||||||
|
|
||||||
|
if (pdfDocument !== this._pdfDocument) {
|
||||||
|
return null; // The document was closed while the data resolved.
|
||||||
|
}
|
||||||
|
this.linkService.cachePageRef(pageNumber, destRef);
|
||||||
|
} catch (ex) {
|
||||||
|
// Invalid page reference, ignore it and continue parsing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (Number.isInteger(destRef)) {
|
||||||
|
pageNumber = destRef + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isInteger(pageNumber) &&
|
||||||
|
(!pageNumberToDestHash.has(pageNumber) ||
|
||||||
|
currentNesting > pageNumberNesting.get(pageNumber))
|
||||||
|
) {
|
||||||
|
const destHash = this.linkService.getDestinationHash(dest);
|
||||||
|
pageNumberToDestHash.set(pageNumber, destHash);
|
||||||
|
pageNumberNesting.set(pageNumber, currentNesting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
queue.push({ nesting: currentNesting + 1, items });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pageNumberToDestHashCapability.resolve(
|
||||||
|
pageNumberToDestHash.size > 0 ? pageNumberToDestHash : null
|
||||||
|
);
|
||||||
|
return this._pageNumberToDestHashCapability.promise;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { PDFOutlineViewer };
|
export { PDFOutlineViewer };
|
||||||
|
@ -13,20 +13,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NullL10n, PresentationModeState } from "./ui_utils.js";
|
import { NullL10n, PresentationModeState, SidebarView } from "./ui_utils.js";
|
||||||
import { RenderingStates } from "./pdf_rendering_queue.js";
|
import { RenderingStates } from "./pdf_rendering_queue.js";
|
||||||
|
|
||||||
const UI_NOTIFICATION_CLASS = "pdfSidebarNotification";
|
const UI_NOTIFICATION_CLASS = "pdfSidebarNotification";
|
||||||
|
|
||||||
const SidebarView = {
|
|
||||||
UNKNOWN: -1,
|
|
||||||
NONE: 0,
|
|
||||||
THUMBS: 1, // Default value.
|
|
||||||
OUTLINE: 2,
|
|
||||||
ATTACHMENTS: 3,
|
|
||||||
LAYERS: 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} PDFSidebarOptions
|
* @typedef {Object} PDFSidebarOptions
|
||||||
* @property {PDFSidebarElements} elements - The DOM elements.
|
* @property {PDFSidebarElements} elements - The DOM elements.
|
||||||
@ -62,6 +53,10 @@ const SidebarView = {
|
|||||||
* the attachments are placed.
|
* the attachments are placed.
|
||||||
* @property {HTMLDivElement} layersView - The container in which
|
* @property {HTMLDivElement} layersView - The container in which
|
||||||
* the layers are placed.
|
* the layers are placed.
|
||||||
|
* @property {HTMLDivElement} outlineOptionsContainer - The container in which
|
||||||
|
* the outline view-specific option button(s) are placed.
|
||||||
|
* @property {HTMLButtonElement} currentOutlineItemButton - The button used to
|
||||||
|
* find the current outline item.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class PDFSidebar {
|
class PDFSidebar {
|
||||||
@ -103,6 +98,9 @@ class PDFSidebar {
|
|||||||
this.attachmentsView = elements.attachmentsView;
|
this.attachmentsView = elements.attachmentsView;
|
||||||
this.layersView = elements.layersView;
|
this.layersView = elements.layersView;
|
||||||
|
|
||||||
|
this._outlineOptionsContainer = elements.outlineOptionsContainer;
|
||||||
|
this._currentOutlineItemButton = elements.currentOutlineItemButton;
|
||||||
|
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.l10n = l10n;
|
this.l10n = l10n;
|
||||||
this._disableNotification = disableNotification;
|
this._disableNotification = disableNotification;
|
||||||
@ -119,6 +117,7 @@ class PDFSidebar {
|
|||||||
this.outlineButton.disabled = false;
|
this.outlineButton.disabled = false;
|
||||||
this.attachmentsButton.disabled = false;
|
this.attachmentsButton.disabled = false;
|
||||||
this.layersButton.disabled = false;
|
this.layersButton.disabled = false;
|
||||||
|
this._currentOutlineItemButton.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -243,6 +242,12 @@ class PDFSidebar {
|
|||||||
);
|
);
|
||||||
this.layersView.classList.toggle("hidden", view !== SidebarView.LAYERS);
|
this.layersView.classList.toggle("hidden", view !== SidebarView.LAYERS);
|
||||||
|
|
||||||
|
// Finally, update view-specific CSS classes.
|
||||||
|
this._outlineOptionsContainer.classList.toggle(
|
||||||
|
"hidden",
|
||||||
|
view !== SidebarView.OUTLINE
|
||||||
|
);
|
||||||
|
|
||||||
if (forceOpen && !this.isOpen) {
|
if (forceOpen && !this.isOpen) {
|
||||||
this.open();
|
this.open();
|
||||||
return true; // Opening will trigger rendering and dispatch the event.
|
return true; // Opening will trigger rendering and dispatch the event.
|
||||||
@ -460,6 +465,11 @@ class PDFSidebar {
|
|||||||
this.eventBus.dispatch("resetlayers", { source: this });
|
this.eventBus.dispatch("resetlayers", { source: this });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Buttons for view-specific options.
|
||||||
|
this._currentOutlineItemButton.addEventListener("click", () => {
|
||||||
|
this.eventBus.dispatch("currentoutlineitem", { source: this });
|
||||||
|
});
|
||||||
|
|
||||||
// Disable/enable views.
|
// Disable/enable views.
|
||||||
const onTreeLoaded = (count, button, view) => {
|
const onTreeLoaded = (count, button, view) => {
|
||||||
button.disabled = !count;
|
button.disabled = !count;
|
||||||
@ -475,6 +485,12 @@ class PDFSidebar {
|
|||||||
|
|
||||||
this.eventBus._on("outlineloaded", evt => {
|
this.eventBus._on("outlineloaded", evt => {
|
||||||
onTreeLoaded(evt.outlineCount, this.outlineButton, SidebarView.OUTLINE);
|
onTreeLoaded(evt.outlineCount, this.outlineButton, SidebarView.OUTLINE);
|
||||||
|
|
||||||
|
if (evt.enableCurrentOutlineItemButton) {
|
||||||
|
this.pdfViewer.pagesPromise.then(() => {
|
||||||
|
this._currentOutlineItemButton.disabled = !this.isInitialViewSet;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.eventBus._on("attachmentsloaded", evt => {
|
this.eventBus._on("attachmentsloaded", evt => {
|
||||||
|
@ -32,6 +32,15 @@ const PresentationModeState = {
|
|||||||
FULLSCREEN: 3,
|
FULLSCREEN: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SidebarView = {
|
||||||
|
UNKNOWN: -1,
|
||||||
|
NONE: 0,
|
||||||
|
THUMBS: 1, // Default value.
|
||||||
|
OUTLINE: 2,
|
||||||
|
ATTACHMENTS: 3,
|
||||||
|
LAYERS: 4,
|
||||||
|
};
|
||||||
|
|
||||||
const RendererType = {
|
const RendererType = {
|
||||||
CANVAS: "canvas",
|
CANVAS: "canvas",
|
||||||
SVG: "svg",
|
SVG: "svg",
|
||||||
@ -1037,6 +1046,7 @@ export {
|
|||||||
isValidSpreadMode,
|
isValidSpreadMode,
|
||||||
isPortraitOrientation,
|
isPortraitOrientation,
|
||||||
PresentationModeState,
|
PresentationModeState,
|
||||||
|
SidebarView,
|
||||||
RendererType,
|
RendererType,
|
||||||
TextLayerMode,
|
TextLayerMode,
|
||||||
ScrollMode,
|
ScrollMode,
|
||||||
|
@ -48,8 +48,8 @@
|
|||||||
--findbar-nextprevious-btn-bg-color: rgba(227, 228, 230, 1);
|
--findbar-nextprevious-btn-bg-color: rgba(227, 228, 230, 1);
|
||||||
--treeitem-color: rgba(0, 0, 0, 0.8);
|
--treeitem-color: rgba(0, 0, 0, 0.8);
|
||||||
--treeitem-hover-color: rgba(0, 0, 0, 0.9);
|
--treeitem-hover-color: rgba(0, 0, 0, 0.9);
|
||||||
--treeitem-active-color: rgba(0, 0, 0, 0.08);
|
--treeitem-selected-color: rgba(0, 0, 0, 0.9);
|
||||||
--treeitem-active-bg-color: rgba(0, 0, 0, 1);
|
--treeitem-selected-bg-color: rgba(0, 0, 0, 0.25);
|
||||||
--sidebaritem-bg-color: rgba(0, 0, 0, 0.15);
|
--sidebaritem-bg-color: rgba(0, 0, 0, 0.15);
|
||||||
--doorhanger-bg-color: rgba(255, 255, 255, 1);
|
--doorhanger-bg-color: rgba(255, 255, 255, 1);
|
||||||
--doorhanger-border-color: rgba(12, 12, 13, 0.2);
|
--doorhanger-border-color: rgba(12, 12, 13, 0.2);
|
||||||
@ -77,6 +77,7 @@
|
|||||||
--toolbarButton-viewOutline-icon: url(images/toolbarButton-viewOutline.svg);
|
--toolbarButton-viewOutline-icon: url(images/toolbarButton-viewOutline.svg);
|
||||||
--toolbarButton-viewAttachments-icon: url(images/toolbarButton-viewAttachments.svg);
|
--toolbarButton-viewAttachments-icon: url(images/toolbarButton-viewAttachments.svg);
|
||||||
--toolbarButton-viewLayers-icon: url(images/toolbarButton-viewLayers.svg);
|
--toolbarButton-viewLayers-icon: url(images/toolbarButton-viewLayers.svg);
|
||||||
|
--toolbarButton-currentOutlineItem-icon: url(images/toolbarButton-currentOutlineItem.svg);
|
||||||
--toolbarButton-search-icon: url(images/toolbarButton-search.svg);
|
--toolbarButton-search-icon: url(images/toolbarButton-search.svg);
|
||||||
--findbarButton-previous-icon: url(images/findbarButton-previous.svg);
|
--findbarButton-previous-icon: url(images/findbarButton-previous.svg);
|
||||||
--findbarButton-next-icon: url(images/findbarButton-next.svg);
|
--findbarButton-next-icon: url(images/findbarButton-next.svg);
|
||||||
@ -121,8 +122,8 @@
|
|||||||
--findbar-nextprevious-btn-bg-color: rgba(89, 89, 89, 1);
|
--findbar-nextprevious-btn-bg-color: rgba(89, 89, 89, 1);
|
||||||
--treeitem-color: rgba(255, 255, 255, 0.8);
|
--treeitem-color: rgba(255, 255, 255, 0.8);
|
||||||
--treeitem-hover-color: rgba(255, 255, 255, 0.9);
|
--treeitem-hover-color: rgba(255, 255, 255, 0.9);
|
||||||
--treeitem-active-color: rgba(255, 255, 255, 0.08);
|
--treeitem-selected-color: rgba(255, 255, 255, 0.9);
|
||||||
--treeitem-active-bg-color: rgba(255, 255, 255, 1);
|
--treeitem-selected-bg-color: rgba(255, 255, 255, 0.25);
|
||||||
--sidebaritem-bg-color: rgba(255, 255, 255, 0.15);
|
--sidebaritem-bg-color: rgba(255, 255, 255, 0.15);
|
||||||
--doorhanger-bg-color: rgba(74, 74, 79, 1);
|
--doorhanger-bg-color: rgba(74, 74, 79, 1);
|
||||||
--doorhanger-border-color: rgba(39, 39, 43, 1);
|
--doorhanger-border-color: rgba(39, 39, 43, 1);
|
||||||
@ -338,6 +339,13 @@ html[dir="rtl"] #toolbarSidebar .toolbarButton {
|
|||||||
margin-left: 2px !important;
|
margin-left: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[dir="ltr"] #toolbarSidebarRight .toolbarButton {
|
||||||
|
margin-right: 3px !important;
|
||||||
|
}
|
||||||
|
html[dir="rtl"] #toolbarSidebarRight .toolbarButton {
|
||||||
|
margin-left: 3px !important;
|
||||||
|
}
|
||||||
|
|
||||||
#sidebarResizer {
|
#sidebarResizer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -694,16 +702,22 @@ html[dir="ltr"] .doorHangerRight:before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html[dir="ltr"] #toolbarViewerLeft,
|
html[dir="ltr"] #toolbarViewerLeft,
|
||||||
html[dir="rtl"] #toolbarViewerRight {
|
html[dir="rtl"] #toolbarViewerRight,
|
||||||
|
html[dir="ltr"] #toolbarSidebarLeft,
|
||||||
|
html[dir="rtl"] #toolbarSidebarRight {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
html[dir="ltr"] #toolbarViewerRight,
|
html[dir="ltr"] #toolbarViewerRight,
|
||||||
html[dir="rtl"] #toolbarViewerLeft {
|
html[dir="rtl"] #toolbarViewerLeft,
|
||||||
|
html[dir="ltr"] #toolbarSidebarRight,
|
||||||
|
html[dir="rtl"] #toolbarSidebarLeft {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
html[dir="ltr"] #toolbarViewerLeft > *,
|
html[dir="ltr"] #toolbarViewerLeft > *,
|
||||||
html[dir="ltr"] #toolbarViewerMiddle > *,
|
html[dir="ltr"] #toolbarViewerMiddle > *,
|
||||||
html[dir="ltr"] #toolbarViewerRight > *,
|
html[dir="ltr"] #toolbarViewerRight > *,
|
||||||
|
html[dir="ltr"] #toolbarSidebarLeft *,
|
||||||
|
html[dir="ltr"] #toolbarSidebarRight *,
|
||||||
html[dir="ltr"] .findbar * {
|
html[dir="ltr"] .findbar * {
|
||||||
position: relative;
|
position: relative;
|
||||||
float: left;
|
float: left;
|
||||||
@ -711,6 +725,8 @@ html[dir="ltr"] .findbar * {
|
|||||||
html[dir="rtl"] #toolbarViewerLeft > *,
|
html[dir="rtl"] #toolbarViewerLeft > *,
|
||||||
html[dir="rtl"] #toolbarViewerMiddle > *,
|
html[dir="rtl"] #toolbarViewerMiddle > *,
|
||||||
html[dir="rtl"] #toolbarViewerRight > *,
|
html[dir="rtl"] #toolbarViewerRight > *,
|
||||||
|
html[dir="rtl"] #toolbarSidebarLeft *,
|
||||||
|
html[dir="rtl"] #toolbarSidebarRight *,
|
||||||
html[dir="rtl"] .findbar * {
|
html[dir="rtl"] .findbar * {
|
||||||
position: relative;
|
position: relative;
|
||||||
float: right;
|
float: right;
|
||||||
@ -1073,6 +1089,14 @@ html[dir="rtl"] #viewOutline.toolbarButton::before {
|
|||||||
mask-image: var(--toolbarButton-viewLayers-icon);
|
mask-image: var(--toolbarButton-viewLayers-icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#currentOutlineItem.toolbarButton::before {
|
||||||
|
-webkit-mask-image: var(--toolbarButton-currentOutlineItem-icon);
|
||||||
|
mask-image: var(--toolbarButton-currentOutlineItem-icon);
|
||||||
|
}
|
||||||
|
html[dir="rtl"] #currentOutlineItem.toolbarButton::before {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
#viewFind.toolbarButton::before {
|
#viewFind.toolbarButton::before {
|
||||||
-webkit-mask-image: var(--toolbarButton-search-icon);
|
-webkit-mask-image: var(--toolbarButton-search-icon);
|
||||||
mask-image: var(--toolbarButton-search-icon);
|
mask-image: var(--toolbarButton-search-icon);
|
||||||
@ -1448,6 +1472,11 @@ html[dir="rtl"] .treeItemToggler::before {
|
|||||||
left: 4px;
|
left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.treeItem.selected > a {
|
||||||
|
background-color: var(--treeitem-selected-bg-color);
|
||||||
|
color: var(--treeitem-selected-color);
|
||||||
|
}
|
||||||
|
|
||||||
.treeItemToggler:hover,
|
.treeItemToggler:hover,
|
||||||
.treeItemToggler:hover + a,
|
.treeItemToggler:hover + a,
|
||||||
.treeItemToggler:hover ~ .treeItems,
|
.treeItemToggler:hover ~ .treeItems,
|
||||||
@ -1458,12 +1487,6 @@ html[dir="rtl"] .treeItemToggler::before {
|
|||||||
color: var(--treeitem-hover-color);
|
color: var(--treeitem-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.treeItem.selected {
|
|
||||||
background-color: var(--treeitem-active-bg-color);
|
|
||||||
background-clip: padding-box;
|
|
||||||
color: var(--treeitem-active-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: file FF bug to support ::-moz-selection:window-inactive
|
/* TODO: file FF bug to support ::-moz-selection:window-inactive
|
||||||
so we can override the opaque grey background when the window is inactive;
|
so we can override the opaque grey background when the window is inactive;
|
||||||
see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
|
see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
|
||||||
|
@ -76,6 +76,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||||||
|
|
||||||
<div id="sidebarContainer">
|
<div id="sidebarContainer">
|
||||||
<div id="toolbarSidebar">
|
<div id="toolbarSidebar">
|
||||||
|
<div id="toolbarSidebarLeft">
|
||||||
<div class="splitToolbarButton toggled">
|
<div class="splitToolbarButton toggled">
|
||||||
<button id="viewThumbnail" class="toolbarButton toggled" title="Show Thumbnails" tabindex="2" data-l10n-id="thumbs">
|
<button id="viewThumbnail" class="toolbarButton toggled" title="Show Thumbnails" tabindex="2" data-l10n-id="thumbs">
|
||||||
<span data-l10n-id="thumbs_label">Thumbnails</span>
|
<span data-l10n-id="thumbs_label">Thumbnails</span>
|
||||||
@ -91,6 +92,17 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="toolbarSidebarRight">
|
||||||
|
<div id="outlineOptionsContainer" class="hidden">
|
||||||
|
<div class="verticalToolbarSeparator"></div>
|
||||||
|
|
||||||
|
<button id="currentOutlineItem" class="toolbarButton" disabled="disabled" title="Find Current Outline Item" tabindex="6" data-l10n-id="current_outline_item">
|
||||||
|
<span data-l10n-id="current_outline_item_label">Current Outline Item</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="sidebarContent">
|
<div id="sidebarContent">
|
||||||
<div id="thumbnailView">
|
<div id="thumbnailView">
|
||||||
</div>
|
</div>
|
||||||
|
@ -132,6 +132,11 @@ function getViewerConfiguration() {
|
|||||||
outlineView: document.getElementById("outlineView"),
|
outlineView: document.getElementById("outlineView"),
|
||||||
attachmentsView: document.getElementById("attachmentsView"),
|
attachmentsView: document.getElementById("attachmentsView"),
|
||||||
layersView: document.getElementById("layersView"),
|
layersView: document.getElementById("layersView"),
|
||||||
|
// View-specific options
|
||||||
|
outlineOptionsContainer: document.getElementById(
|
||||||
|
"outlineOptionsContainer"
|
||||||
|
),
|
||||||
|
currentOutlineItemButton: document.getElementById("currentOutlineItem"),
|
||||||
},
|
},
|
||||||
sidebarResizer: {
|
sidebarResizer: {
|
||||||
outerContainer: document.getElementById("outerContainer"),
|
outerContainer: document.getElementById("outerContainer"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user