diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index f0d31f10..ac64e002 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -237,6 +237,21 @@ cursor: move; } +.ace_gutter_tooltip { + background-color: #FFFFD5; + border: 1px solid gray; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4); + color: black; + display: inline-block; + padding: 4px; + position: absolute; + z-index: 300; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + cursor: default; +} + .ace_folding-enabled > .ace_gutter-cell { padding-right: 13px; } diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 144f2aff..6559df98 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -72,6 +72,7 @@ var SearchHighlight = require("./search_highlight").SearchHighlight; var EditSession = function(text, mode) { this.$modified = true; this.$breakpoints = []; + this.$decorations = []; this.$frontMarkers = {}; this.$backMarkers = {}; this.$markerId = 1; @@ -490,6 +491,32 @@ var EditSession = function(text, mode) { this.setOverwrite(!this.$overwrite); }; + /** + * EditSession.addGutterDecoration(row, className) -> Void + * - row (Number): The row number + * - className (String): The class to add + * + * Adds `className` to the `row`, to be used for CSS stylings and whatnot. + **/ + this.addGutterDecoration = function(row, className) { + if (!this.$decorations[row]) + this.$decorations[row] = ""; + this.$decorations[row] += " " + className; + this._emit("changeBreakpoint", {}); + }; + + /** + * EditSession.removeGutterDecoration(row, className)-> Void + * - row (Number): The row number + * - className (String): The class to add + * + * Removes `className` from the `row`. + **/ + this.removeGutterDecoration = function(row, className) { + this.$decorations[row] = (this.$decorations[row] || "").replace(" " + className, ""); + this._emit("changeBreakpoint", {}); + }; + /** * EditSession.getBreakpoints() -> Array * @@ -509,7 +536,7 @@ var EditSession = function(text, mode) { this.setBreakpoints = function(rows) { this.$breakpoints = []; for (var i=0; i foldStart) { @@ -124,12 +123,12 @@ var Gutter = function(parentEl) { break; var annotation = this.$annotations[i] || emptyAnno; - html.push("
", (i)); + html.push( + "
", + i + 1 + ); if (foldWidgets) { var c = foldWidgets[i]; diff --git a/lib/ace/mouse/default_gutter_handler.js b/lib/ace/mouse/default_gutter_handler.js index 0fbadfb7..813f68e9 100644 --- a/lib/ace/mouse/default_gutter_handler.js +++ b/lib/ace/mouse/default_gutter_handler.js @@ -39,14 +39,16 @@ define(function(require, exports, module) { "use strict"; var dom = require("../lib/dom"); +var event = require("../lib/event"); function GutterHandler(mouseHandler) { var editor = mouseHandler.editor; + var gutter = editor.renderer.$gutterLayer; mouseHandler.editor.setDefaultHandler("guttermousedown", function(e) { if (!editor.isFocused()) return; - var gutterRegion = editor.renderer.$gutterLayer.getRegion(e); + var gutterRegion = gutter.getRegion(e); if (gutterRegion) return; @@ -62,6 +64,98 @@ function GutterHandler(mouseHandler) { mouseHandler.captureMouse(e, "selectByLines"); return e.preventDefault(); }); + + + var tooltipTimeout, mouseEvent, tooltip, tooltipAnnotation; + function createTooltip() { + tooltip = dom.createElement("div"); + tooltip.className = "ace_gutter_tooltip"; + tooltip.style.maxWidth = "500px"; + tooltip.style.display = "none"; + editor.container.appendChild(tooltip); + } + + function showTooltip() { + if (!tooltip) { + createTooltip(); + } + var row = mouseEvent.getDocumentPosition().row; + var annotation = gutter.$annotations[row]; + if (!annotation) + return hideTooltip(); + + var maxRow = editor.session.getLength(); + if (row == maxRow) { + var screenRow = editor.renderer.pixelToScreenCoordinates(0, mouseEvent.y).row; + var pos = mouseEvent.$pos; + if (screenRow > editor.session.documentToScreenRow(pos.row, pos.column)) + return hideTooltip(); + } + + if (tooltipAnnotation == annotation) + return; + tooltipAnnotation = annotation.text.join("\n"); + + tooltip.style.display = "block"; + tooltip.innerHTML = tooltipAnnotation; + editor.on("mousewheel", hideTooltip); + + moveTooltip(mouseEvent); + } + + function hideTooltip() { + if (tooltipTimeout) + tooltipTimeout = clearTimeout(tooltipTimeout); + if (tooltipAnnotation) { + tooltip.style.display = "none"; + tooltipAnnotation = null; + editor.removeEventListener("mousewheel", hideTooltip); + } + } + + function moveTooltip(e) { + var rect = editor.renderer.$gutter.getBoundingClientRect(); + tooltip.style.left = e.x - rect.left + 15 + "px"; + if (e.y + 3 * editor.renderer.lineHeight + 15 < rect.bottom) { + tooltip.style.bottom = ""; + tooltip.style.top = e.y - rect.top + 15 + "px"; + } else { + tooltip.style.top = ""; + tooltip.style.bottom = rect.bottom - e.y + 5 + "px"; + } + } + + mouseHandler.editor.setDefaultHandler("guttermousemove", function(e) { + var target = e.domEvent.target || e.domEvent.srcElement; + if (dom.hasCssClass(target, "ace_fold-widget")) + return hideTooltip(); + + if (tooltipAnnotation) + moveTooltip(e); + + mouseEvent = e; + if (tooltipTimeout) + return; + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + if (mouseEvent) + showTooltip(); + else + hideTooltip(); + }, 50); + }); + + event.addListener(editor.renderer.$gutter, "mouseout", function(e) { + mouseEvent = null; + if (!tooltipAnnotation || tooltipTimeout) + return; + + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + hideTooltip(); + }, 50); + }); + } exports.GutterHandler = GutterHandler; diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index b8e992a3..bf708ee2 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -69,7 +69,7 @@ var MouseHandler = function(editor) { event.addListener(gutterEl, "mousedown", this.onMouseEvent.bind(this, "guttermousedown")); event.addListener(gutterEl, "click", this.onMouseEvent.bind(this, "gutterclick")); event.addListener(gutterEl, "dblclick", this.onMouseEvent.bind(this, "gutterdblclick")); - event.addListener(gutterEl, "mousemove", this.onMouseMove.bind(this, "gutter")); + event.addListener(gutterEl, "mousemove", this.onMouseEvent.bind(this, "guttermousemove")); }; (function() { diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 03555bee..252fed11 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -890,26 +890,20 @@ var VirtualRenderer = function(container, theme) { /** * VirtualRenderer.addGutterDecoration(row, className) -> Void - * - row (Number): The row number - * - className (String): The class to add * - * Adds `className` to the `row`, to be used for CSS stylings and whatnot. + * Deprecated (moved to EditSession) **/ this.addGutterDecoration = function(row, className){ this.$gutterLayer.addGutterDecoration(row, className); - this.$loop.schedule(this.CHANGE_GUTTER); }; /** * VirtualRenderer.removeGutterDecoration(row, className)-> Void - * - row (Number): The row number - * - className (String): The class to add * - * Removes `className` from the `row`. + * Deprecated (moved to EditSession) **/ this.removeGutterDecoration = function(row, className){ this.$gutterLayer.removeGutterDecoration(row, className); - this.$loop.schedule(this.CHANGE_GUTTER); }; /**