pdf_cursor_tools.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /* Copyright 2017 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. import { AnnotationEditorType } from "pdfjs-lib";
  16. import { GrabToPan } from "./grab_to_pan.js";
  17. import { PresentationModeState } from "./ui_utils.js";
  18. const CursorTool = {
  19. SELECT: 0, // The default value.
  20. HAND: 1,
  21. ZOOM: 2,
  22. };
  23. /**
  24. * @typedef {Object} PDFCursorToolsOptions
  25. * @property {HTMLDivElement} container - The document container.
  26. * @property {EventBus} eventBus - The application event bus.
  27. * @property {number} [cursorToolOnLoad] - The cursor tool that will be enabled
  28. * on load; the constants from {CursorTool} should be used. The default value
  29. * is `CursorTool.SELECT`.
  30. */
  31. class PDFCursorTools {
  32. /**
  33. * @param {PDFCursorToolsOptions} options
  34. */
  35. constructor({ container, eventBus, cursorToolOnLoad = CursorTool.SELECT }) {
  36. this.container = container;
  37. this.eventBus = eventBus;
  38. this.active = CursorTool.SELECT;
  39. this.previouslyActive = null;
  40. this.handTool = new GrabToPan({
  41. element: this.container,
  42. });
  43. this.#addEventListeners();
  44. // Defer the initial `switchTool` call, to give other viewer components
  45. // time to initialize *and* register 'cursortoolchanged' event listeners.
  46. Promise.resolve().then(() => {
  47. this.switchTool(cursorToolOnLoad);
  48. });
  49. }
  50. /**
  51. * @type {number} One of the values in {CursorTool}.
  52. */
  53. get activeTool() {
  54. return this.active;
  55. }
  56. /**
  57. * @param {number} tool - The cursor mode that should be switched to,
  58. * must be one of the values in {CursorTool}.
  59. */
  60. switchTool(tool) {
  61. if (this.previouslyActive !== null) {
  62. // Cursor tools cannot be used in PresentationMode/AnnotationEditor.
  63. return;
  64. }
  65. if (tool === this.active) {
  66. return; // The requested tool is already active.
  67. }
  68. const disableActiveTool = () => {
  69. switch (this.active) {
  70. case CursorTool.SELECT:
  71. break;
  72. case CursorTool.HAND:
  73. this.handTool.deactivate();
  74. break;
  75. case CursorTool.ZOOM:
  76. /* falls through */
  77. }
  78. };
  79. // Enable the new cursor tool.
  80. switch (tool) {
  81. case CursorTool.SELECT:
  82. disableActiveTool();
  83. break;
  84. case CursorTool.HAND:
  85. disableActiveTool();
  86. this.handTool.activate();
  87. break;
  88. case CursorTool.ZOOM:
  89. /* falls through */
  90. default:
  91. console.error(`switchTool: "${tool}" is an unsupported value.`);
  92. return;
  93. }
  94. // Update the active tool *after* it has been validated above,
  95. // in order to prevent setting it to an invalid state.
  96. this.active = tool;
  97. this.#dispatchEvent();
  98. }
  99. #dispatchEvent() {
  100. this.eventBus.dispatch("cursortoolchanged", {
  101. source: this,
  102. tool: this.active,
  103. });
  104. }
  105. #addEventListeners() {
  106. this.eventBus._on("switchcursortool", evt => {
  107. this.switchTool(evt.tool);
  108. });
  109. let annotationEditorMode = AnnotationEditorType.NONE,
  110. presentationModeState = PresentationModeState.NORMAL;
  111. const disableActive = () => {
  112. const previouslyActive = this.active;
  113. this.switchTool(CursorTool.SELECT);
  114. this.previouslyActive ??= previouslyActive; // Keep track of the first one.
  115. };
  116. const enableActive = () => {
  117. const previouslyActive = this.previouslyActive;
  118. if (
  119. previouslyActive !== null &&
  120. annotationEditorMode === AnnotationEditorType.NONE &&
  121. presentationModeState === PresentationModeState.NORMAL
  122. ) {
  123. this.previouslyActive = null;
  124. this.switchTool(previouslyActive);
  125. }
  126. };
  127. this.eventBus._on("secondarytoolbarreset", evt => {
  128. if (this.previouslyActive !== null) {
  129. annotationEditorMode = AnnotationEditorType.NONE;
  130. presentationModeState = PresentationModeState.NORMAL;
  131. enableActive();
  132. }
  133. });
  134. this.eventBus._on("annotationeditormodechanged", ({ mode }) => {
  135. annotationEditorMode = mode;
  136. if (mode === AnnotationEditorType.NONE) {
  137. enableActive();
  138. } else {
  139. disableActive();
  140. }
  141. });
  142. this.eventBus._on("presentationmodechanged", ({ state }) => {
  143. presentationModeState = state;
  144. if (state === PresentationModeState.NORMAL) {
  145. enableActive();
  146. } else if (state === PresentationModeState.FULLSCREEN) {
  147. disableActive();
  148. }
  149. });
  150. }
  151. }
  152. export { CursorTool, PDFCursorTools };