From db1b28207d5b8b799d7202cf47bb9ece1c0afb3c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 10 Jan 2013 11:44:05 +0100 Subject: [PATCH] Make line widget handles objects with methods. Handle widget height better Closes #1147 Widget handles now have .clear() and .changed() methods. The second informs CodeMirror that the widget changed, and causes it to recompute the line height for the widget's line. Fixes some sloppiness in the tracking of widget heights. --- doc/manual.html | 15 +++++---- lib/codemirror.js | 80 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 8f9574b871..150ef05353 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -978,12 +978,15 @@

Programming API

CodeMirror-specific CSS classes, and those classes might in some cases affect it. This method returns an object that represents the widget placement. It'll have a line property - pointing at the line handle that it is associated with, and it - can be passed to removeLineWidget to remove the - widget. - -
removeLineWidget(widget)
-
Removes the given line widget.
+ pointing at the line handle that it is associated with, and the following methods: +
+
clear()
Removes the widget.
+
changed()
Call + this if you made some change to the widget's DOM node that + might affect its height. It'll force CodeMirror to update + the height of the line that contains the widget.
+
+
posFromIndex(index) → object
Calculates and returns a {line, ch} object for a diff --git a/lib/codemirror.js b/lib/codemirror.js index 9cce59d93f..975c8d23b9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -497,8 +497,12 @@ window.CodeMirror = (function() { } var diff = node.lineObj.height - height; if (height < 2) height = textHeight(display); - if (diff > .001 || diff < -.001) + if (diff > .001 || diff < -.001) { updateLineHeight(node.lineObj, height); + var widgets = node.lineObj.widgets; + if (widgets) for (var i = 0; i < widgets.length; ++i) + widgets[i].height = widgets[i].node.offsetHeight; + } } display.viewOffset = heightAtLine(cm, getLine(doc, from)); // Position the mover div to align with the current virtual scroll position @@ -978,7 +982,7 @@ window.CodeMirror = (function() { // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" function intoCoordSystem(cm, lineObj, rect, context) { if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { - var size = lineObj.widgets[i].node.offsetHeight; + var size = widgetHeight(lineObj.widgets[i]); rect.top += size; rect.bottom += size; } if (context == "line") return rect; @@ -2597,25 +2601,10 @@ window.CodeMirror = (function() { }), addLineWidget: operation(null, function(handle, node, options) { - var widget = options || {}; - widget.node = node; - if (widget.noHScroll) this.display.alignWidgets = true; - changeLine(this, handle, function(line) { - (line.widgets || (line.widgets = [])).push(widget); - widget.line = line; - return true; - }); - return widget; + return addLineWidget(this, handle, node, options); }), - removeLineWidget: operation(null, function(widget) { - var ws = widget.line.widgets, no = lineNo(widget.line); - if (no == null || !ws) return; - for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1); - var newHeight = widget.node.offsetHeight ? widget.line.height - widget.node.offsetHeight : textHeight(this.display); - updateLineHeight(widget.line, newHeight); - regChange(this, no, no + 1); - }), + removeLineWidget: function(widget) { widget.remove(); }, lineInfo: function(line) { if (typeof line == "number") { @@ -3587,6 +3576,59 @@ window.CodeMirror = (function() { line.markedSpans = spans; } + // LINE WIDGETS + + var LineWidget = CodeMirror.LineWidget = function(cm, node, options) { + for (var opt in options) if (options.hasOwnProperty(opt)) + this[opt] = options[opt]; + this.cm = cm; + this.node = node; + }; + function widgetOperation(f) { + return function() { + startOperation(this.cm); + try {var result = f.apply(this, arguments);} + finally {endOperation(this.cm);} + return result; + }; + } + LineWidget.prototype.clear = widgetOperation(function() { + var ws = this.line.widgets, no = lineNo(this.line); + if (no == null || !ws) return; + for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); + updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this))); + regChange(this.cm, no, no + 1); + }); + LineWidget.prototype.changed = widgetOperation(function() { + var oldH = this.height; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) return; + updateLineHeight(this.line, this.line.height + diff); + var no = lineNo(this.line); + regChange(this.cm, no, no + 1); + }); + + function widgetHeight(widget) { + if (widget.height != null) return widget.height; + if (!widget.node.parentNode) + removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative")); + return widget.height = widget.node.offsetHeight; + } + + function addLineWidget(cm, handle, node, options) { + var widget = new LineWidget(cm, node, options); + if (widget.noHScroll) cm.display.alignWidgets = true; + changeLine(cm, handle, function(line) { + (line.widgets || (line.widgets = [])).push(widget); + widget.line = line; + if (!lineIsHidden(line) || widget.showIfHidden) + updateLineHeight(line, line.height + widgetHeight(widget)); + return true; + }); + return widget; + } + // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including