Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit c376320

Browse files
committed
Merge pull request #7988 from adobe/randy/issue-6538-2
Defer most Quick View processing until after delay
2 parents 6cd94c3 + e9363a8 commit c376320

File tree

1 file changed

+136
-97
lines changed
  • src/extensions/default/QuickView

1 file changed

+136
-97
lines changed

src/extensions/default/QuickView/main.js

Lines changed: 136 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ define(function (require, exports, module) {
4545
prefs = null, // Preferences
4646
$previewContainer, // Preview container
4747
$previewContent, // Preview content holder
48-
lastPos; // Last line/ch pos processed by handleMouseMove
48+
lastMousePos, // Last mouse position
49+
animationRequest; // Request for animation frame
4950

5051
// Constants
5152
var CMD_ENABLE_QUICK_VIEW = "view.enableQuickView",
@@ -146,13 +147,13 @@ define(function (require, exports, module) {
146147
.addClass("active");
147148
}
148149

149-
function divContainsMouse($div, event) {
150+
function divContainsMouse($div, mousePos) {
150151
var offset = $div.offset();
151152

152-
return (event.clientX >= offset.left &&
153-
event.clientX <= offset.left + $div.width() &&
154-
event.clientY >= offset.top &&
155-
event.clientY <= offset.top + $div.height());
153+
return (mousePos.clientX >= offset.left &&
154+
mousePos.clientX <= offset.left + $div.width() &&
155+
mousePos.clientY >= offset.top &&
156+
mousePos.clientY <= offset.top + $div.height());
156157
}
157158

158159

@@ -515,20 +516,74 @@ define(function (require, exports, module) {
515516
return null;
516517
}
517518

519+
function getHoveredEditor(mousePos) {
520+
// Figure out which editor we are over
521+
var fullEditor = EditorManager.getCurrentFullEditor();
522+
523+
if (!fullEditor || !mousePos) {
524+
return;
525+
}
526+
527+
// Check for inline Editor instances first
528+
var inlines = fullEditor.getInlineWidgets(),
529+
i,
530+
editor;
531+
532+
for (i = 0; i < inlines.length; i++) {
533+
var $inlineEditorRoot = inlines[i].editor && $(inlines[i].editor.getRootElement()), // see MultiRangeInlineEditor
534+
$otherDiv = inlines[i].$htmlContent;
535+
536+
if ($inlineEditorRoot && divContainsMouse($inlineEditorRoot, mousePos)) {
537+
editor = inlines[i].editor;
538+
break;
539+
} else if ($otherDiv && divContainsMouse($otherDiv, mousePos)) {
540+
// Mouse inside unsupported inline editor like Quick Docs or Color Editor
541+
return;
542+
}
543+
}
544+
545+
// Check main editor
546+
if (!editor) {
547+
if (divContainsMouse($(fullEditor.getRootElement()), mousePos)) {
548+
editor = fullEditor;
549+
}
550+
}
551+
552+
return editor;
553+
}
554+
518555
/**
519556
* Changes the current hidden popoverState to visible, showing it in the UI and highlighting
520557
* its matching text in the editor.
521558
*/
522559
function showPreview(editor, popover) {
523-
var token,
524-
cm = editor._codeMirror;
560+
var token, cm;
561+
562+
// Figure out which editor we are over
563+
if (!editor) {
564+
editor = getHoveredEditor(lastMousePos);
565+
}
566+
567+
if (!editor || !editor._codeMirror) {
568+
return;
569+
}
570+
571+
cm = editor._codeMirror;
572+
573+
// Find char mouse is over
574+
var pos = cm.coordsChar({left: lastMousePos.clientX, top: lastMousePos.clientY});
575+
576+
// No preview if mouse is past last char on line
577+
if (pos.ch >= editor.document.getLine(pos.line).length) {
578+
return;
579+
}
525580

526581
if (popover) {
527582
popoverState = popover;
528583
} else {
529584
// Query providers and append to popoverState
530-
token = cm.getTokenAt(lastPos, true);
531-
popoverState = $.extend({}, popoverState, queryPreviewProviders(editor, lastPos, token));
585+
token = cm.getTokenAt(pos, true);
586+
popoverState = $.extend({}, popoverState, queryPreviewProviders(editor, pos, token));
532587
}
533588

534589
if (popoverState && popoverState.start && popoverState.end) {
@@ -550,100 +605,78 @@ define(function (require, exports, module) {
550605
}
551606
}
552607
}
608+
609+
function processMouseMove() {
610+
animationRequest = null;
611+
612+
if (!lastMousePos) {
613+
return; // should never get here, but safety first!
614+
}
615+
616+
var showImmediately = false,
617+
editor = null;
618+
619+
if (popoverState && popoverState.visible) {
620+
// Only figure out which editor we are over when there is already a popover
621+
// showing (otherwise wait until after delay to minimize processing)
622+
editor = getHoveredEditor(lastMousePos);
623+
if (editor && editor._codeMirror) {
624+
// Find char mouse is over
625+
var cm = editor._codeMirror,
626+
pos = cm.coordsChar({left: lastMousePos.clientX, top: lastMousePos.clientY});
627+
628+
if (popoverState.start && popoverState.end &&
629+
editor.posWithinRange(pos, popoverState.start, popoverState.end, true) &&
630+
(pos.ch < editor.document.getLine(pos.line).length)) {
631+
632+
// That one's still relevant - nothing more to do
633+
// Note: posWithinRange() includes mouse past end of line, so need to check for that case
634+
return;
635+
}
636+
}
637+
638+
// That one doesn't cover this pos - hide it and start anew
639+
showImmediately = true;
640+
}
641+
642+
// Initialize popoverState
643+
hidePreview();
644+
popoverState = {};
645+
646+
// Set timer to scan and show. This will get cancelled (in hidePreview())
647+
// if mouse movement rendered this popover inapplicable before timer fires.
648+
// When showing "immediately", still use setTimeout() to make this async
649+
// so we return from this mousemove event handler ASAP.
650+
popoverState.hoverTimer = window.setTimeout(function () {
651+
showPreview(editor);
652+
}, showImmediately ? 0 : HOVER_DELAY);
653+
}
553654

554655
function handleMouseMove(event) {
656+
lastMousePos = null;
657+
555658
if (!enabled) {
556659
return;
557660
}
558-
661+
559662
if (event.which) {
560663
// Button is down - don't show popovers while dragging
561664
hidePreview();
562665
return;
563666
}
564-
565-
// Figure out which editor we are over
566-
var fullEditor = EditorManager.getCurrentFullEditor();
567-
568-
if (!fullEditor) {
569-
hidePreview();
570-
return;
571-
}
572-
573-
// Check for inline Editor instances first
574-
var inlines = fullEditor.getInlineWidgets(),
575-
i,
576-
editor;
577-
578-
for (i = 0; i < inlines.length; i++) {
579-
var $inlineEditorRoot = inlines[i].editor && $(inlines[i].editor.getRootElement()), // see MultiRangeInlineEditor
580-
$otherDiv = inlines[i].$htmlContent;
581-
582-
if ($inlineEditorRoot && divContainsMouse($inlineEditorRoot, event)) {
583-
editor = inlines[i].editor;
584-
break;
585-
} else if ($otherDiv && divContainsMouse($otherDiv, event)) {
586-
// Mouse inside unsupported inline editor like Quick Docs or Color Editor
587-
hidePreview();
588-
return;
589-
}
590-
}
591-
592-
// Check main editor
593-
if (!editor) {
594-
if (divContainsMouse($(fullEditor.getRootElement()), event)) {
595-
editor = fullEditor;
596-
}
597-
}
598-
599-
if (editor && editor._codeMirror) {
600-
// Find char mouse is over
601-
var cm = editor._codeMirror,
602-
pos = cm.coordsChar({left: event.clientX, top: event.clientY}),
603-
showImmediately = false;
604-
605-
// Bail if mouse is on same char as last event
606-
if (lastPos && lastPos.line === pos.line && lastPos.ch === pos.ch) {
607-
return;
608-
}
609-
lastPos = pos;
610-
611-
// No preview if mouse is past last char on line
612-
if (pos.ch >= editor.document.getLine(pos.line).length) {
613-
hidePreview();
614-
return;
615-
}
616-
617-
// Is there already a popover provider and range?
618-
if (popoverState) {
619-
if (popoverState.start && popoverState.end &&
620-
editor.posWithinRange(pos, popoverState.start, popoverState.end, 1)) {
621-
// That one's still relevant - nothing more to do
622-
return;
623-
} else {
624-
// That one doesn't cover this pos - hide it and start anew
625-
showImmediately = popoverState.visible;
626-
hidePreview();
627-
}
628-
}
629-
630-
// Initialize popoverState
631-
popoverState = {};
632-
633-
// Set timer to scan and show. This will get cancelled (in hidePreview())
634-
// if mouse movement rendered this popover inapplicable before timer fires.
635-
// When showing "immediately", still use setTimeout() to make this async
636-
// so we return from this mousemove event handler ASAP.
637-
popoverState.hoverTimer = window.setTimeout(function () {
638-
showPreview(editor, null);
639-
}, showImmediately ? 0 : HOVER_DELAY);
640-
641-
} else {
642-
// Mouse not over any Editor - immediately hide popover
643-
hidePreview();
667+
668+
// Keep track of last mouse position
669+
lastMousePos = {
670+
clientX: event.clientX,
671+
clientY: event.clientY
672+
};
673+
674+
// Prevent duplicate animation frame requests
675+
if (!animationRequest) {
676+
animationRequest = window.requestAnimationFrame(processMouseMove);
644677
}
645678
}
646-
679+
647680
function onActiveEditorChange(event, current, previous) {
648681
// Hide preview when editor changes
649682
hidePreview();
@@ -700,7 +733,16 @@ define(function (require, exports, module) {
700733
function toggleEnableQuickView() {
701734
setEnabled(!enabled);
702735
}
703-
736+
737+
function _forceShow(popover) {
738+
hidePreview();
739+
lastMousePos = {
740+
clientX: popover.xpos,
741+
clientY: Math.floor((popover.ybot + popover.ytop) / 2)
742+
};
743+
showPreview(popover.editor, popover);
744+
}
745+
704746
// Create the preview container
705747
$previewContainer = $(previewContainerHTML).appendTo($("body"));
706748
$previewContent = $previewContainer.find(".preview-content");
@@ -726,8 +768,5 @@ define(function (require, exports, module) {
726768

727769
// For unit testing
728770
exports._queryPreviewProviders = queryPreviewProviders;
729-
exports._forceShow = function (popover) {
730-
hidePreview();
731-
showPreview(popover.editor, popover);
732-
};
771+
exports._forceShow = _forceShow;
733772
});

0 commit comments

Comments
 (0)