From 0b1630d6fac3d95ad0dc979864393fd47056baba Mon Sep 17 00:00:00 2001 From: DanyaPostfactum Date: Mon, 30 Dec 2013 20:13:00 +1000 Subject: [PATCH] Refactor tooltip to separate class. --- demo/kitchen-sink/token_tooltip.js | 85 +++++---------- lib/ace/css/editor.css | 11 +- lib/ace/mouse/default_gutter_handler.js | 58 ++++++---- lib/ace/tooltip.js | 138 ++++++++++++++++++++++++ 4 files changed, 208 insertions(+), 84 deletions(-) create mode 100644 lib/ace/tooltip.js diff --git a/demo/kitchen-sink/token_tooltip.js b/demo/kitchen-sink/token_tooltip.js index f182824a..9e607f69 100644 --- a/demo/kitchen-sink/token_tooltip.js +++ b/demo/kitchen-sink/token_tooltip.js @@ -32,25 +32,26 @@ define(function(require, exports, module) { "use strict"; var dom = require("ace/lib/dom"); +var oop = require("ace/lib/oop"); var event = require("ace/lib/event"); var Range = require("ace/range").Range; +var Tooltip = require("ace/tooltip").Tooltip; -var tooltipNode; - -var TokenTooltip = function(editor) { +function TokenTooltip (editor) { if (editor.tokenTooltip) return; - editor.tokenTooltip = this; + Tooltip.call(this, editor.container); + editor.tokenTooltip = this; this.editor = editor; - - editor.tooltip = tooltipNode || this.$init(); this.update = this.update.bind(this); this.onMouseMove = this.onMouseMove.bind(this); this.onMouseOut = this.onMouseOut.bind(this); event.addListener(editor.renderer.scroller, "mousemove", this.onMouseMove); event.addListener(editor.renderer.content, "mouseout", this.onMouseOut); -}; +} + +oop.inherits(TokenTooltip, Tooltip); (function(){ this.token = {}; @@ -86,15 +87,10 @@ var TokenTooltip = function(editor) { } if (!token) { session.removeMarker(this.marker); - tooltipNode.style.display = "none"; - this.isOpen = false; + this.hide(); return; } - if (!this.isOpen) { - tooltipNode.style.display = ""; - this.isOpen = true; - } - + var tokenText = token.type; if (token.state) tokenText += "|" + token.state; @@ -102,15 +98,15 @@ var TokenTooltip = function(editor) { tokenText += "\n merge"; if (token.stateTransitions) tokenText += "\n " + token.stateTransitions.join("\n "); - + if (this.tokenText != tokenText) { - tooltipNode.textContent = tokenText; - this.tooltipWidth = tooltipNode.offsetWidth; - this.tooltipHeight = tooltipNode.offsetHeight; + this.setText(tokenText); + this.width = this.getWidth(); + this.height = this.getHeight(); this.tokenText = tokenText; } - - this.updateTooltipPosition(this.x, this.y); + + this.show(null, this.x, this.y); this.token = token; session.removeMarker(this.marker); @@ -123,56 +119,34 @@ var TokenTooltip = function(editor) { this.y = e.clientY; if (this.isOpen) { this.lastT = e.timeStamp; - this.updateTooltipPosition(this.x, this.y); + this.setPosition(this.x, this.y); } if (!this.$timer) this.$timer = setTimeout(this.update, 100); }; - + this.onMouseOut = function(e) { - var t = e && e.relatedTarget; - var ct = e && e.currentTarget; - while(t && (t = t.parentNode)) { - if (t == ct) - return; - } - tooltipNode.style.display = "none"; + if (e && e.currentTarget.contains(e.relatedTarget)) + return; + this.hide(); this.editor.session.removeMarker(this.marker); this.$timer = clearTimeout(this.$timer); - this.isOpen = false; - }; - - this.updateTooltipPosition = function(x, y) { - var st = tooltipNode.style; - if (x + 10 + this.tooltipWidth > this.maxWidth) - x = window.innerWidth - this.tooltipWidth - 10; - if (y > window.innerHeight * 0.75 || y + 20 + this.tooltipHeight > this.maxHeight) - y = y - this.tooltipHeight - 30; - - st.left = x + 10 + "px"; - st.top = y + 20 + "px"; }; - this.$init = function() { - tooltipNode = document.documentElement.appendChild(dom.createElement("div")); - var st = tooltipNode.style; - st.position = "fixed"; - st.display = "none"; - st.background = "lightyellow"; - st.borderRadius = ""; - st.border = "1px solid gray"; - st.padding = "1px"; - st.zIndex = 1000; - st.fontFamily = "monospace"; - st.whiteSpace = "pre-line"; - return tooltipNode; + this.setPosition = function(x, y) { + if (x + 10 + this.width > this.maxWidth) + x = window.innerWidth - this.width - 10; + if (y > window.innerHeight * 0.75 || y + 20 + this.height > this.maxHeight) + y = y - this.height - 30; + + Tooltip.prototype.setPosition.call(this, x + 10, y + 20); }; this.destroy = function() { this.onMouseOut(); event.removeListener(this.editor.renderer.scroller, "mousemove", this.onMouseMove); event.removeListener(this.editor.renderer.content, "mouseout", this.onMouseOut); - delete this.editor.tokenTooltip; + delete this.editor.tokenTooltip; }; }).call(TokenTooltip.prototype); @@ -180,4 +154,3 @@ var TokenTooltip = function(editor) { exports.TokenTooltip = TokenTooltip; }); - diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index c27acfd6..b3a137f3 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -301,7 +301,7 @@ background-position: center center, top left; } -.ace_gutter-tooltip { +.ace_tooltip { background-color: #FFF; background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1)); background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1)); @@ -309,21 +309,22 @@ border-radius: 1px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); color: black; - display: inline-block; - max-width: 500px; - padding: 4px; + display: block; + max-width: 100%; + padding: 3px 4px; position: fixed; z-index: 999999; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; cursor: default; - white-space: pre-line; + white-space: pre; word-wrap: break-word; line-height: normal; font-style: normal; font-weight: normal; letter-spacing: normal; + pointer-events: none; } .ace_folding-enabled > .ace_gutter-cell { diff --git a/lib/ace/mouse/default_gutter_handler.js b/lib/ace/mouse/default_gutter_handler.js index 1ad8119c..42fbb02a 100644 --- a/lib/ace/mouse/default_gutter_handler.js +++ b/lib/ace/mouse/default_gutter_handler.js @@ -31,11 +31,14 @@ define(function(require, exports, module) { "use strict"; var dom = require("../lib/dom"); +var oop = require("../lib/oop"); var event = require("../lib/event"); +var Tooltip = require("../tooltip").Tooltip; function GutterHandler(mouseHandler) { var editor = mouseHandler.editor; var gutter = editor.renderer.$gutterLayer; + var tooltip = new GutterTooltip(editor.container); mouseHandler.editor.setDefaultHandler("guttermousedown", function(e) { if (!editor.isFocused() || e.getButton() != 0) @@ -63,18 +66,9 @@ function GutterHandler(mouseHandler) { }); - var tooltipTimeout, mouseEvent, tooltip, tooltipAnnotation; - function createTooltip() { - tooltip = dom.createElement("div"); - tooltip.className = "ace_gutter-tooltip"; - tooltip.style.display = "none"; - editor.container.appendChild(tooltip); - } + var tooltipTimeout, mouseEvent, tooltipAnnotation; function showTooltip() { - if (!tooltip) { - createTooltip(); - } var row = mouseEvent.getDocumentPosition().row; var annotation = gutter.$annotations[row]; if (!annotation) @@ -92,8 +86,8 @@ function GutterHandler(mouseHandler) { return; tooltipAnnotation = annotation.text.join("
"); - tooltip.style.display = "block"; - tooltip.innerHTML = tooltipAnnotation; + tooltip.setHtml(tooltipAnnotation); + tooltip.show(); editor.on("mousewheel", hideTooltip); moveTooltip(mouseEvent); @@ -103,23 +97,14 @@ function GutterHandler(mouseHandler) { if (tooltipTimeout) tooltipTimeout = clearTimeout(tooltipTimeout); if (tooltipAnnotation) { - tooltip.style.display = "none"; + tooltip.hide(); tooltipAnnotation = null; editor.removeEventListener("mousewheel", hideTooltip); } } function moveTooltip(e) { - var rect = editor.renderer.$gutter.getBoundingClientRect(); - tooltip.style.left = e.x + 15 + "px"; - if (e.y + 3 * editor.renderer.lineHeight + 15 < rect.bottom) { - tooltip.style.bottom = ""; - tooltip.style.top = e.y + 15 + "px"; - } else { - tooltip.style.top = ""; - var innerHeight = window.innerHeight || document.documentElement.clientHeight; - tooltip.style.bottom = innerHeight - e.y + 5 + "px"; - } + tooltip.setPosition(e.x, e.y); } mouseHandler.editor.setDefaultHandler("guttermousemove", function(e) { @@ -156,6 +141,33 @@ function GutterHandler(mouseHandler) { editor.on("changeSession", hideTooltip); } +function GutterTooltip(parentNode) { + Tooltip.call(this, parentNode); +} + +oop.inherits(GutterTooltip, Tooltip); + +(function(){ + this.setPosition = function(x, y) { + var windowWidth = window.innerWidth || document.documentElement.clientWidth; + var windowHeight = window.innerHeight || document.documentElement.clientHeight; + var width = this.getWidth(); + var height = this.getHeight(); + x += 15; + y += 15; + if (x + width > windowWidth) { + x -= (x + width) - windowWidth; + } + if (y + height > windowHeight) { + y -= 20 + height; + } + Tooltip.prototype.setPosition.call(this, x, y); + }; + +}).call(GutterTooltip.prototype); + + + exports.GutterHandler = GutterHandler; }); diff --git a/lib/ace/tooltip.js b/lib/ace/tooltip.js new file mode 100644 index 00000000..1f5502d6 --- /dev/null +++ b/lib/ace/tooltip.js @@ -0,0 +1,138 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var dom = require("./lib/dom"); + +/** + * @class Tooltip + **/ + +/** + * @param {Element} parentNode + * + * @constructor + **/ +function Tooltip (parentNode) { + this.isOpen = false; + this.$element = null; + this.$parentNode = parentNode; +} + +(function() { + this.$init = function() { + this.$element = dom.createElement("div"); + this.$element.className = "ace_tooltip"; + this.$element.style.display = "none"; + this.$parentNode.appendChild(this.$element); + return this.$element; + }; + + /** + * @returns {Element} + **/ + this.getElement = function() { + return this.$element || this.$init(); + }; + + /** + * @param {String} text + **/ + this.setText = function(text) { + dom.setInnerText(this.getElement(), text); + }; + + /** + * @param {String} html + **/ + this.setHtml = function(html) { + this.getElement().innerHTML = html; + }; + + /** + * @param {Number} x + * @param {Number} y + **/ + this.setPosition = function(x, y) { + this.getElement().style.left = x + "px"; + this.getElement().style.top = y + "px"; + }; + + /** + * @param {String} className + **/ + this.setClassName = function(className) { + dom.addCssClass(this.getElement(), className); + }; + + /** + * @param {String} text + * @param {Number} x + * @param {Number} y + **/ + this.show = function(text, x, y) { + if (text != null) + this.setText(text); + if (x != null && y != null) + this.setPosition(x, y); + if (!this.isOpen) { + this.getElement().style.display = "block"; + this.isOpen = true; + } + }; + + this.hide = function() { + if (this.isOpen) { + this.getElement().style.display = "none"; + this.isOpen = false; + } + }; + + /** + * @returns {Number} + **/ + this.getHeight = function() { + return this.getElement().offsetHeight; + }; + + /** + * @returns {Number} + **/ + this.getWidth = function() { + return this.getElement().offsetWidth; + }; + +}).call(Tooltip.prototype); + +exports.Tooltip = Tooltip; +});