tracker.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { IInstanceTracker, InstanceTracker } from '@jupyterlab/apputils';
  4. import { Cell } from '@jupyterlab/cells';
  5. import { Token } from '@phosphor/coreutils';
  6. import { ISignal, Signal } from '@phosphor/signaling';
  7. import { NotebookPanel, Notebook } from './';
  8. /**
  9. * An object that tracks notebook widgets.
  10. */
  11. export interface INotebookTracker extends IInstanceTracker<NotebookPanel> {
  12. /**
  13. * The currently focused cell.
  14. *
  15. * #### Notes
  16. * If there is no cell with the focus, then this value is `null`.
  17. */
  18. readonly activeCell: Cell;
  19. /**
  20. * A signal emitted when the current active cell changes.
  21. *
  22. * #### Notes
  23. * If there is no cell with the focus, then `null` will be emitted.
  24. */
  25. readonly activeCellChanged: ISignal<this, Cell>;
  26. /**
  27. * A signal emitted when the selection state changes.
  28. */
  29. readonly selectionChanged: ISignal<this, void>;
  30. }
  31. /* tslint:disable */
  32. /**
  33. * The notebook tracker token.
  34. */
  35. export const INotebookTracker = new Token<INotebookTracker>(
  36. '@jupyterlab/notebook:INotebookTracker'
  37. );
  38. /* tslint:enable */
  39. export class NotebookTracker extends InstanceTracker<NotebookPanel>
  40. implements INotebookTracker {
  41. /**
  42. * The currently focused cell.
  43. *
  44. * #### Notes
  45. * This is a read-only property. If there is no cell with the focus, then this
  46. * value is `null`.
  47. */
  48. get activeCell(): Cell {
  49. let widget = this.currentWidget;
  50. if (!widget) {
  51. return null;
  52. }
  53. return widget.content.activeCell || null;
  54. }
  55. /**
  56. * A signal emitted when the current active cell changes.
  57. *
  58. * #### Notes
  59. * If there is no cell with the focus, then `null` will be emitted.
  60. */
  61. get activeCellChanged(): ISignal<this, Cell> {
  62. return this._activeCellChanged;
  63. }
  64. /**
  65. * A signal emitted when the selection state changes.
  66. */
  67. get selectionChanged(): ISignal<this, void> {
  68. return this._selectionChanged;
  69. }
  70. /**
  71. * Add a new notebook panel to the tracker.
  72. *
  73. * @param panel - The notebook panel being added.
  74. */
  75. add(panel: NotebookPanel): Promise<void> {
  76. const promise = super.add(panel);
  77. panel.content.activeCellChanged.connect(
  78. this._onActiveCellChanged,
  79. this
  80. );
  81. panel.content.selectionChanged.connect(
  82. this._onSelectionChanged,
  83. this
  84. );
  85. return promise;
  86. }
  87. /**
  88. * Dispose of the resources held by the tracker.
  89. */
  90. dispose(): void {
  91. this._activeCell = null;
  92. super.dispose();
  93. }
  94. /**
  95. * Handle the current change event.
  96. */
  97. protected onCurrentChanged(widget: NotebookPanel): void {
  98. // Store an internal reference to active cell to prevent false positives.
  99. let activeCell = this.activeCell;
  100. if (activeCell && activeCell === this._activeCell) {
  101. return;
  102. }
  103. this._activeCell = activeCell;
  104. if (!widget) {
  105. return;
  106. }
  107. // Since the notebook has changed, immediately signal an active cell change
  108. this._activeCellChanged.emit(widget.content.activeCell || null);
  109. }
  110. private _onActiveCellChanged(sender: Notebook, cell: Cell): void {
  111. // Check if the active cell change happened for the current notebook.
  112. if (this.currentWidget && this.currentWidget.content === sender) {
  113. this._activeCell = cell || null;
  114. this._activeCellChanged.emit(this._activeCell);
  115. }
  116. }
  117. private _onSelectionChanged(sender: Notebook): void {
  118. // Check if the selection change happened for the current notebook.
  119. if (this.currentWidget && this.currentWidget.content === sender) {
  120. this._selectionChanged.emit(void 0);
  121. }
  122. }
  123. private _activeCell: Cell | null = null;
  124. private _activeCellChanged = new Signal<this, Cell>(this);
  125. private _selectionChanged = new Signal<this, void>(this);
  126. }