messageController.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. import { alert } from '../../../base/browser/ui/aria/aria.js';
  15. import { TimeoutTimer } from '../../../base/common/async.js';
  16. import { DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js';
  17. import './messageController.css';
  18. import { EditorCommand, registerEditorCommand, registerEditorContribution } from '../../browser/editorExtensions.js';
  19. import { Range } from '../../common/core/range.js';
  20. import * as nls from '../../../nls.js';
  21. import { IContextKeyService, RawContextKey } from '../../../platform/contextkey/common/contextkey.js';
  22. let MessageController = class MessageController {
  23. constructor(editor, contextKeyService) {
  24. this._messageWidget = new MutableDisposable();
  25. this._messageListeners = new DisposableStore();
  26. this._editor = editor;
  27. this._visible = MessageController.MESSAGE_VISIBLE.bindTo(contextKeyService);
  28. this._editorListener = this._editor.onDidAttemptReadOnlyEdit(() => this._onDidAttemptReadOnlyEdit());
  29. }
  30. static get(editor) {
  31. return editor.getContribution(MessageController.ID);
  32. }
  33. dispose() {
  34. this._editorListener.dispose();
  35. this._messageListeners.dispose();
  36. this._messageWidget.dispose();
  37. this._visible.reset();
  38. }
  39. showMessage(message, position) {
  40. alert(message);
  41. this._visible.set(true);
  42. this._messageWidget.clear();
  43. this._messageListeners.clear();
  44. this._messageWidget.value = new MessageWidget(this._editor, position, message);
  45. // close on blur, cursor, model change, dispose
  46. this._messageListeners.add(this._editor.onDidBlurEditorText(() => this.closeMessage()));
  47. this._messageListeners.add(this._editor.onDidChangeCursorPosition(() => this.closeMessage()));
  48. this._messageListeners.add(this._editor.onDidDispose(() => this.closeMessage()));
  49. this._messageListeners.add(this._editor.onDidChangeModel(() => this.closeMessage()));
  50. // 3sec
  51. this._messageListeners.add(new TimeoutTimer(() => this.closeMessage(), 3000));
  52. // close on mouse move
  53. let bounds;
  54. this._messageListeners.add(this._editor.onMouseMove(e => {
  55. // outside the text area
  56. if (!e.target.position) {
  57. return;
  58. }
  59. if (!bounds) {
  60. // define bounding box around position and first mouse occurance
  61. bounds = new Range(position.lineNumber - 3, 1, e.target.position.lineNumber + 3, 1);
  62. }
  63. else if (!bounds.containsPosition(e.target.position)) {
  64. // check if position is still in bounds
  65. this.closeMessage();
  66. }
  67. }));
  68. }
  69. closeMessage() {
  70. this._visible.reset();
  71. this._messageListeners.clear();
  72. if (this._messageWidget.value) {
  73. this._messageListeners.add(MessageWidget.fadeOut(this._messageWidget.value));
  74. }
  75. }
  76. _onDidAttemptReadOnlyEdit() {
  77. if (this._editor.hasModel()) {
  78. this.showMessage(nls.localize('editor.readonly', "Cannot edit in read-only editor"), this._editor.getPosition());
  79. }
  80. }
  81. };
  82. MessageController.ID = 'editor.contrib.messageController';
  83. MessageController.MESSAGE_VISIBLE = new RawContextKey('messageVisible', false, nls.localize('messageVisible', 'Whether the editor is currently showing an inline message'));
  84. MessageController = __decorate([
  85. __param(1, IContextKeyService)
  86. ], MessageController);
  87. export { MessageController };
  88. const MessageCommand = EditorCommand.bindToContribution(MessageController.get);
  89. registerEditorCommand(new MessageCommand({
  90. id: 'leaveEditorMessage',
  91. precondition: MessageController.MESSAGE_VISIBLE,
  92. handler: c => c.closeMessage(),
  93. kbOpts: {
  94. weight: 100 /* EditorContrib */ + 30,
  95. primary: 9 /* Escape */
  96. }
  97. }));
  98. class MessageWidget {
  99. constructor(editor, { lineNumber, column }, text) {
  100. // Editor.IContentWidget.allowEditorOverflow
  101. this.allowEditorOverflow = true;
  102. this.suppressMouseDown = false;
  103. this._editor = editor;
  104. this._editor.revealLinesInCenterIfOutsideViewport(lineNumber, lineNumber, 0 /* Smooth */);
  105. this._position = { lineNumber, column: column - 1 };
  106. this._domNode = document.createElement('div');
  107. this._domNode.classList.add('monaco-editor-overlaymessage');
  108. const anchorTop = document.createElement('div');
  109. anchorTop.classList.add('anchor', 'top');
  110. this._domNode.appendChild(anchorTop);
  111. const message = document.createElement('div');
  112. message.classList.add('message');
  113. message.textContent = text;
  114. this._domNode.appendChild(message);
  115. const anchorBottom = document.createElement('div');
  116. anchorBottom.classList.add('anchor', 'below');
  117. this._domNode.appendChild(anchorBottom);
  118. this._editor.addContentWidget(this);
  119. this._domNode.classList.add('fadeIn');
  120. }
  121. static fadeOut(messageWidget) {
  122. let handle;
  123. const dispose = () => {
  124. messageWidget.dispose();
  125. clearTimeout(handle);
  126. messageWidget.getDomNode().removeEventListener('animationend', dispose);
  127. };
  128. handle = setTimeout(dispose, 110);
  129. messageWidget.getDomNode().addEventListener('animationend', dispose);
  130. messageWidget.getDomNode().classList.add('fadeOut');
  131. return { dispose };
  132. }
  133. dispose() {
  134. this._editor.removeContentWidget(this);
  135. }
  136. getId() {
  137. return 'messageoverlay';
  138. }
  139. getDomNode() {
  140. return this._domNode;
  141. }
  142. getPosition() {
  143. return { position: this._position, preference: [1 /* ABOVE */, 2 /* BELOW */] };
  144. }
  145. afterRender(position) {
  146. this._domNode.classList.toggle('below', position === 2 /* BELOW */);
  147. }
  148. }
  149. registerEditorContribution(MessageController.ID, MessageController);