From 3b147205baaf77fe7c6163a63e4a0d42cc3ac2b0 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Fri, 7 Apr 2023 10:28:53 +0200 Subject: [PATCH] [GeckoView] Add a basic toolbar with a download button for GV (bug 1823164) --- gulpfile.js | 5 ++- web/app.js | 15 +++++++- web/toolbar-geckoview.js | 79 +++++++++++++++++++++++++++++++++++++++ web/viewer-geckoview.css | 76 +++++++++++++++++++++++++++++++++++++ web/viewer-geckoview.html | 8 +++- web/viewer-geckoview.js | 8 +++- 6 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 web/toolbar-geckoview.js diff --git a/gulpfile.js b/gulpfile.js index 243cceb7f..b8d384940 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -264,8 +264,11 @@ function createWebpackConfig( viewerAlias["web-print_service"] = "web/pdf_print_service.js"; } else if (bundleDefines.MOZCENTRAL) { if (bundleDefines.GECKOVIEW) { + const gvAlias = { + "web-toolbar": "web/toolbar-geckoview.js", + }; for (const key in viewerAlias) { - viewerAlias[key] = "web/stubs-geckoview.js"; + viewerAlias[key] = gvAlias[key] || "web/stubs-geckoview.js"; } } else { viewerAlias["web-print_service"] = "web/firefox_print_service.js"; diff --git a/web/app.js b/web/app.js index ea1cca5b5..085d13b0f 100644 --- a/web/app.js +++ b/web/app.js @@ -2234,7 +2234,7 @@ function webViewerInitialized() { } if (!PDFViewerApplication.supportsPrinting) { - appConfig.toolbar?.print.classList.add("hidden"); + appConfig.toolbar?.print?.classList.add("hidden"); appConfig.secondaryToolbar?.printButton.classList.add("hidden"); } @@ -2243,7 +2243,7 @@ function webViewerInitialized() { } if (PDFViewerApplication.supportsIntegratedFind) { - appConfig.toolbar?.viewFind.classList.add("hidden"); + appConfig.toolbar?.viewFind?.classList.add("hidden"); } appConfig.mainContainer.addEventListener( @@ -2917,6 +2917,17 @@ function webViewerTouchEnd(evt) { } function webViewerClick(evt) { + if ( + typeof PDFJSDev === "undefined" + ? window.isGECKOVIEW + : PDFJSDev.test("GECKOVIEW") + ) { + if ( + document.activeElement === PDFViewerApplication.appConfig.mainContainer + ) { + PDFViewerApplication.toolbar?.toggle(); + } + } if (!PDFViewerApplication.secondaryToolbar?.isOpen) { return; } diff --git a/web/toolbar-geckoview.js b/web/toolbar-geckoview.js new file mode 100644 index 000000000..adfa8f644 --- /dev/null +++ b/web/toolbar-geckoview.js @@ -0,0 +1,79 @@ +/* Copyright 2023 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @typedef {Object} ToolbarOptions + * @property {HTMLDivElement} mainContainer - Main container. + * @property {HTMLDivElement} container - Container for the toolbar. + * @property {HTMLButtonElement} download - Button to download the document. + */ + +class Toolbar { + #buttons; + + #eventBus; + + #toolbar; + + #mainContainer; + + #toggleBound = this.toggle.bind(this); + + /** + * @param {ToolbarOptions} options + * @param {EventBus} eventBus + * @param {IL10n} _l10n - Localization service. + */ + constructor(options, eventBus, _l10n) { + this.#toolbar = options.container; + this.#mainContainer = options.mainContainer; + this.#eventBus = eventBus; + this.#buttons = [{ element: options.download, eventName: "download" }]; + + // Bind the event listeners for click and various other actions. + this.#bindListeners(options); + } + + setPageNumber(pageNumber, pageLabel) {} + + setPagesCount(pagesCount, hasPageLabels) {} + + setPageScale(pageScaleValue, pageScale) {} + + reset() {} + + #bindListeners(options) { + // The buttons within the toolbar. + for (const { element, eventName, eventDetails } of this.#buttons) { + element.addEventListener("click", evt => { + if (eventName !== null) { + this.#eventBus.dispatch(eventName, { source: this, ...eventDetails }); + } + }); + } + } + + updateLoadingIndicatorState(loading = false) {} + + toggle() { + if (this.#toolbar.classList.toggle("show")) { + this.#mainContainer.addEventListener("scroll", this.#toggleBound); + } else { + this.#mainContainer.removeEventListener("scroll", this.#toggleBound); + } + } +} + +export { Toolbar }; diff --git a/web/viewer-geckoview.css b/web/viewer-geckoview.css index cc8d14381..6a37002fa 100644 --- a/web/viewer-geckoview.css +++ b/web/viewer-geckoview.css @@ -29,6 +29,8 @@ --dialog-button-border: none; --dialog-button-bg-color: rgba(12, 12, 13, 0.1); --dialog-button-hover-bg-color: rgba(12, 12, 13, 0.3); + + --toolbarButton-download-icon: url(images/toolbarButton-download.svg); } :root:dir(rtl) { @@ -149,6 +151,80 @@ body { border-color: #0a84ff; } +#floatingToolbar { + position: absolute; + width: 40px; + height: auto; + bottom: 5%; + right: 5%; + background-color: transparent; + z-index: 100000; +} + +#floatingToolbar.show { + display: block; +} + +#floatingToolbar:not(show) { + display: none; +} + +.toolbarButton { + margin: 2px; + padding: 8px; + border-style: solid; + border-width: 1px; + border-color: transparent; + border-radius: 19px; + user-select: none; + box-sizing: border-box; + background-color: transparent; + backdrop-filter: blur(20px) contrast(100%) invert(100%); + width: 38px; + height: 38px; + outline: none; + position: relative; +} + +.toolbarButton > span { + display: inline-block; + width: 0; + height: 0; + overflow: hidden; +} + +.toolbarButton[disabled], +.dialogButton[disabled] { + opacity: 0.5; +} + +.toolbarButton:hover, +.toolbarButton:focus-visible { + backdrop-filter: blur(20px) contrast(200%) invert(100%); +} + +.toolbarButton::before { + display: inline-block; + width: 100%; + height: 100%; + content: ""; + background-color: transparent; + backdrop-filter: invert(100%); + mask-size: cover; +} + +.toolbarButton::before { + opacity: var(--toolbar-icon-opacity); +} + +.toolbarButton:hover::before { + backdrop-filter: invert(60%); +} + +#download::before { + mask-image: var(--toolbarButton-download-icon); +} + .dialogButton { width: auto; margin: 3px 4px 2px !important; diff --git a/web/viewer-geckoview.html b/web/viewer-geckoview.html index 03f3f64b0..a13ba8195 100644 --- a/web/viewer-geckoview.html +++ b/web/viewer-geckoview.html @@ -64,7 +64,7 @@ See https://github.com/adobe-type-tools/cmap-resources "web-pdf_thumbnail_viewer": "./stubs-geckoview.js", "web-print_service": "./stubs-geckoview.js", "web-secondary_toolbar": "./stubs-geckoview.js", - "web-toolbar": "./stubs-geckoview.js" + "web-toolbar": "./toolbar-geckoview.js" } } @@ -103,6 +103,12 @@ See https://github.com/adobe-type-tools/cmap-resources +
+ +
+ diff --git a/web/viewer-geckoview.js b/web/viewer-geckoview.js index 536e37a9d..15a2d2d04 100644 --- a/web/viewer-geckoview.js +++ b/web/viewer-geckoview.js @@ -36,10 +36,16 @@ window.PDFViewerApplicationConstants = AppConstants; window.PDFViewerApplicationOptions = AppOptions; function getViewerConfiguration() { + const mainContainer = document.getElementById("viewerContainer"); return { appContainer: document.body, - mainContainer: document.getElementById("viewerContainer"), + mainContainer, viewerContainer: document.getElementById("viewer"), + toolbar: { + mainContainer, + container: document.getElementById("floatingToolbar"), + download: document.getElementById("download"), + }, passwordOverlay: { dialog: document.getElementById("passwordDialog"),