Improved checkCursorMenu and checkCursorTag performance and fully rewrite its position method for UX and verified

This commit is contained in:
Cheng-Han, Wu 2016-03-15 11:12:45 +08:00
parent ea703145c5
commit a55ece023a

View file

@ -54,7 +54,7 @@ var defaultExtraKeys = {
var idleTime = 300000; //5 mins var idleTime = 300000; //5 mins
var updateViewDebounce = 200; var updateViewDebounce = 200;
var cursorMenuThrottle = 100; var cursorMenuThrottle = 50;
var cursorActivityDebounce = 50; var cursorActivityDebounce = 50;
var cursorAnimatePeriod = 100; var cursorAnimatePeriod = 100;
var supportContainers = ['success', 'info', 'warning', 'danger']; var supportContainers = ['success', 'info', 'warning', 'danger'];
@ -531,6 +531,8 @@ var ui = {
view: $(".ui-view-area"), view: $(".ui-view-area"),
codemirror: $(".ui-edit-area .CodeMirror"), codemirror: $(".ui-edit-area .CodeMirror"),
codemirrorScroll: $(".ui-edit-area .CodeMirror .CodeMirror-scroll"), codemirrorScroll: $(".ui-edit-area .CodeMirror .CodeMirror-scroll"),
codemirrorSizer: $(".ui-edit-area .CodeMirror .CodeMirror-sizer"),
codemirrorSizerInner: $(".ui-edit-area .CodeMirror .CodeMirror-sizer > div"),
markdown: $(".ui-view-area .markdown-body") markdown: $(".ui-view-area .markdown-body")
} }
}; };
@ -1897,33 +1899,41 @@ function emitUserStatus(force) {
} }
function checkCursorTag(coord, ele) { function checkCursorTag(coord, ele) {
if (!ele) return; if (!ele) return; // return if element not exists
var curosrtagMargin = 60; // set margin
var cursor = editor.getCursor(); var tagRightMargin = 0;
//var viewport = editor.getViewport(); var tagBottomMargin = 2;
//var viewportHeight = (viewport.to - viewport.from) * editor.defaultTextHeight(); // use sizer to get the real doc size (won't count status bar and gutters)
var docWidth = ui.area.codemirrorSizer.width();
var docHeight = ui.area.codemirrorSizer.height();
// get editor size (status bar not count in)
var editorWidth = ui.area.codemirror.width(); var editorWidth = ui.area.codemirror.width();
var editorHeight = ui.area.codemirror.height(); var editorHeight = ui.area.codemirror.height();
var width = ele.width(); // get element size
var height = ele.height(); var width = ele.outerWidth();
if (!lineHeightMap) var height = ele.outerHeight();
buildMapInner(); var padding = (ele.outerWidth() - ele.width()) / 2;
// get coord position
var left = coord.left; var left = coord.left;
var top = lineHeightMap[cursor.line] * defaultTextHeight; //coord.top; var top = coord.top;
top -= ele.closest('.CodeMirror-sizer > *').position().top; // get doc top offset (to workaround with viewport)
var docTopOffset = ui.area.codemirrorSizerInner.position().top;
// set offset
var offsetLeft = -3; var offsetLeft = -3;
var offsetTop = defaultTextHeight; var offsetTop = defaultTextHeight;
var statusBarHeight = 0; // only do when have width and height
if (statusBar)
statusBarHeight = statusBar.outerHeight();
if (width > 0 && height > 0) { if (width > 0 && height > 0) {
if (left + width + offsetLeft > editorWidth - curosrtagMargin) { // flip x when element right bound larger than doc width
offsetLeft = -(width + 10); if (left + width + offsetLeft + tagRightMargin > docWidth) {
offsetLeft = -(width + tagRightMargin) + padding + offsetLeft;
} }
if (top + height + offsetTop > Math.max(editor.doc.height, editorHeight) + curosrtagMargin - statusBarHeight * 2 && top - height > curosrtagMargin) { // flip y when element bottom bound larger than doc height
offsetTop = -(height + 4); // and element top position is larger than element height
if (top + docTopOffset + height + offsetTop + tagBottomMargin > Math.max(editor.doc.height, editorHeight) && top + docTopOffset > height + tagBottomMargin) {
offsetTop = -(height);
} }
} }
// set position
ele[0].style.left = offsetLeft + 'px'; ele[0].style.left = offsetLeft + 'px';
ele[0].style.top = offsetTop + 'px'; ele[0].style.top = offsetTop + 'px';
} }
@ -2464,56 +2474,80 @@ if ($('.cursor-menu').length <= 0) {
$("<div class='cursor-menu'>").insertAfter('.CodeMirror-cursors'); $("<div class='cursor-menu'>").insertAfter('.CodeMirror-cursors');
} }
function reverseSortCursorMenu(dropdown) {
var items = dropdown.find('.textcomplete-item');
items.sort(function (a, b) {
return $(b).attr('data-index') - $(a).attr('data-index');
});
return items;
}
var lastUpSideDown = false;
var upSideDown = false; var upSideDown = false;
var checkCursorMenu = _.throttle(checkCursorMenuInner, cursorMenuThrottle); var checkCursorMenu = _.throttle(checkCursorMenuInner, cursorMenuThrottle);
function checkCursorMenuInner() { function checkCursorMenuInner() {
var menuMargin = 60; // get element
var dropdown = $('.cursor-menu .dropdown-menu'); var dropdown = $('.cursor-menu > .dropdown-menu');
// return if not exists
if (dropdown.length <= 0) return; if (dropdown.length <= 0) return;
// set margin
var menuRightMargin = 10;
var menuBottomMargin = 4;
// use sizer to get the real doc size (won't count status bar and gutters)
var docWidth = ui.area.codemirrorSizer.width();
var docHeight = ui.area.codemirrorSizer.height();
// get editor size (status bar not count in)
var editorWidth = ui.area.codemirror.width();
var editorHeight = ui.area.codemirror.height();
// get element size
var width = dropdown.outerWidth();
var height = dropdown.outerHeight();
// get cursor
var cursor = editor.getCursor(); var cursor = editor.getCursor();
var scrollInfo = editor.getScrollInfo(); // set element cursor data
if (!dropdown.hasClass('other-cursor')) if (!dropdown.hasClass('other-cursor'))
dropdown.addClass('other-cursor'); dropdown.addClass('other-cursor');
dropdown.attr('data-line', cursor.line); dropdown.attr('data-line', cursor.line);
dropdown.attr('data-ch', cursor.ch); dropdown.attr('data-ch', cursor.ch);
// get coord position
var coord = editor.charCoords({ var coord = editor.charCoords({
line: cursor.line, line: cursor.line,
ch: cursor.ch ch: cursor.ch
}, 'windows'); }, 'windows');
//var viewport = editor.getViewport();
//var viewportHeight = (viewport.to - viewport.from) * editor.defaultTextHeight();
var editorWidth = ui.area.codemirror.width();
var editorHeight = ui.area.codemirror.height();
var width = dropdown.outerWidth();
var height = dropdown.outerHeight();
if (!lineHeightMap)
buildMapInner();
var left = coord.left; var left = coord.left;
var top = lineHeightMap[cursor.line] * defaultTextHeight; //coord.top; var top = coord.top;
top -= dropdown.closest('.CodeMirror-sizer > *').position().top; // get doc top offset (to workaround with viewport)
var docTopOffset = ui.area.codemirrorSizerInner.position().top;
// set offset
var offsetLeft = 0; var offsetLeft = 0;
var offsetTop = defaultTextHeight; var offsetTop = defaultTextHeight;
var statusBarHeight = 0; // only do when have width and height
if (statusBar) if (width > 0 && height > 0) {
statusBarHeight = statusBar.outerHeight(); // make element right bound not larger than doc width
if (left + width + offsetLeft > editorWidth - menuMargin) if (left + width + offsetLeft + menuRightMargin > docWidth)
offsetLeft = -(left + width - editorWidth + menuMargin); offsetLeft = -(left + width - docWidth + menuRightMargin);
if (top + height + offsetTop > Math.max(editor.doc.height, editorHeight) + menuMargin - statusBarHeight * 2 && top - height > menuMargin) { // flip y when element bottom bound larger than doc height
offsetTop = -(height + 4); // and element top position is larger than element height
upSideDown = true; if (top + docTopOffset + height + offsetTop + menuBottomMargin > Math.max(editor.doc.height, editorHeight) && top + docTopOffset > height + menuBottomMargin) {
var items = dropdown.find('.textcomplete-item'); offsetTop = -(height + menuBottomMargin);
items.sort(function (a, b) { // reverse sort menu because upSideDown
return $(b).attr('data-index') - $(a).attr('data-index'); dropdown.html(reverseSortCursorMenu(dropdown));
}); lastUpSideDown = upSideDown;
dropdown.html(items); upSideDown = true;
dropdown.scrollTop(dropdown[0].scrollHeight); } else {
} else { lastUpSideDown = upSideDown;
upSideDown = false; upSideDown = false;
}
} }
// make menu scroll top only if upSideDown changed
if (upSideDown !== lastUpSideDown)
dropdown.scrollTop(dropdown[0].scrollHeight);
// set element offset data
dropdown.attr('data-offset-left', offsetLeft); dropdown.attr('data-offset-left', offsetLeft);
dropdown.attr('data-offset-top', offsetTop); dropdown.attr('data-offset-top', offsetTop);
// set position
dropdown[0].style.left = left + offsetLeft + 'px'; dropdown[0].style.left = left + offsetLeft + 'px';
dropdown[0].style.top = top + offsetTop + 'px'; dropdown[0].style.top = top + offsetTop + 'px';
} }