Changeset 53052 in webkit


Ignore:
Timestamp:
Jan 10, 2010 11:15:04 AM (14 years ago)
Author:
pfeldman@chromium.org
Message:

2010-01-10 Pavel Feldman <pfeldman@chromium.org>

Reviewed by Timothy Hatcher.

Web Inspector: Introduce support for flexible line height in the text editor.

https://bugs.webkit.org/show_bug.cgi?id=33431

Location:
trunk/WebCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r53051 r53052  
     12010-01-10  Pavel Feldman  <pfeldman@chromium.org>
     2
     3        Reviewed by Timothy Hatcher.
     4
     5        Web Inspector: Introduce support for flexible line height in the text editor.
     6
     7        https://bugs.webkit.org/show_bug.cgi?id=33431
     8
     9        * inspector/front-end/TextEditor.js:
     10        (WebInspector.TextEditor):
     11        (WebInspector.TextEditor.prototype._offsetToLineNumber):
     12        (WebInspector.TextEditor.prototype._lineNumberToOffset):
     13        (WebInspector.TextEditor.prototype._lineHeight):
     14        (WebInspector.TextEditor.prototype.reveal):
     15        (WebInspector.TextEditor.prototype._textChanged):
     16        (WebInspector.TextEditor.prototype._selectionChanged):
     17        (WebInspector.TextEditor.prototype._updateSize):
     18        (WebInspector.TextEditor.prototype._paintLines):
     19        (WebInspector.TextEditor.prototype._paintLinesContinuation):
     20        (WebInspector.TextEditor.prototype._paintLineNumbers):
     21        (WebInspector.TextEditor.prototype._paintCurrentLine):
     22        (WebInspector.TextEditor.prototype._scroll):
     23        (WebInspector.TextEditor.prototype._repaintOnScroll):
     24        (WebInspector.TextEditor.prototype._caretForMouseEvent):
     25        (WebInspector.TextEditor.prototype._handleNavigationKey):
     26        (WebInspector.TextEditor.prototype._updateCursor):
     27        (WebInspector.TextEditor.prototype._invalidateHighlight):
     28        (WebInspector.TextEditor.prototype._paintSelection):
     29        (WebInspector.TextEditor.prototype._initFont):
     30        (WebInspector.TextCursor.prototype.setTextLineHeight):
     31
    1322010-01-10  Pavel Feldman  <pfeldman@chromium.org>
    233
  • trunk/WebCore/inspector/front-end/TextEditor.js

    r53051 r53052  
    9090    this._highlightingEnabled = true;
    9191    this._debugMode = false;
    92     this._lineAlignmentOffset = 0;
    9392
    9493    this._textWidth = 0;
    9594    this._longestLineNumber = 0;
     95
     96    this._lineOffsetsCache = [0];
    9697}
    9798
     
    112113    },
    113114
     115    _offsetToLineNumber: function(offset)
     116    {
     117        if (offset > this._lineOffsetsCache[this._lineOffsetsCache.length - 1]) {
     118            // Seeking outside cached area. Fill the cache.
     119            var lineNumber = this._lineOffsetsCache.length;
     120            while (this._lineNumberToOffset(lineNumber) < offset)
     121                lineNumber++;
     122            return lineNumber;
     123        }
     124
     125        // Bisect.
     126        var from = 0;
     127        var to = this._lineOffsetsCache.length;
     128        while (to > from + 1) {
     129            var mid = Math.floor((from + to) / 2);
     130            if (this._lineOffsetsCache[mid] > offset)
     131                to = mid;
     132            else
     133                from = mid;
     134        }
     135        return to;
     136    },
     137
     138    _lineNumberToOffset: function(lineNumber)
     139    {
     140        var offset = this._lineOffsetsCache[lineNumber];
     141        if (offset)
     142            return offset;
     143        for (var line = lineNumber; line > 0; --line) {
     144            if (this._lineOffsetsCache[line])
     145                break;
     146        }
     147        offset = this._lineOffsetsCache[line];
     148        for (var i = line + 1; i <= lineNumber; ++i) {
     149            offset += this._lineHeight(i - 1);
     150            this._lineOffsetsCache[i] = offset;
     151        }
     152        return offset;
     153    },
     154
     155    _lineHeight: function(lineNumber)
     156    {
     157        return this._debugMode ? this._textLineHeight * (1 + lineNumber % 3) : this._textLineHeight;
     158    },
     159
    114160    reveal: function(line, column) {
    115         var firstLine = this._scrollTop / this._lineHeight;
    116         var visibleLines = this._canvas.height / this._lineHeight;
    117         var minTop = (line - visibleLines + 1) * this._lineHeight;
    118         var maxTop = line * this._lineHeight;
    119         if (this._scrollTop < minTop) {
    120             this._lineAlignmentOffset = this._canvas.height % this._lineHeight;
    121             this._container.scrollTop = minTop;
    122         } else if (this._scrollTop > maxTop) {
    123             this._lineAlignmentOffset = 0;
    124             this._container.scrollTop = maxTop;
    125         }
     161        var maxScrollTop = this._lineNumberToOffset(line);
     162        var minScrollTop = maxScrollTop + this._lineHeight(line) - this._canvas.height;
     163        if (this._scrollTop > maxScrollTop)
     164            this._container.scrollTop = maxScrollTop;
     165        else if (this._scrollTop < minScrollTop)
     166            this._container.scrollTop = minScrollTop;
    126167
    127168        var firstColumn = this._columnForOffset(line, this._scrollLeft);
    128         var maxLeft = this._columnToOffset(line, column);
    129         var minLeft = maxLeft - this._container.clientWidth + this._lineNumberOffset;
    130         if (this._scrollLeft < minLeft)
    131             this._container.scrollLeft = minLeft + 100;
    132         if (this._scrollLeft > maxLeft)
    133             this._container.scrollLeft = maxLeft;
     169        var maxScrollLeft = this._columnToOffset(line, column);
     170        var minScrollLeft = maxScrollLeft - this._container.clientWidth + this._lineNumberOffset;
     171        if (this._scrollLeft < minScrollLeft)
     172            this._container.scrollLeft = minScrollLeft + 100;
     173        if (this._scrollLeft > maxScrollLeft)
     174            this._container.scrollLeft = maxScrollLeft;
    134175    },
    135176
     
    145186        this._invalidateHighlight(newRange.startLine);
    146187        this._updateSize(newRange.startLine, Math.max(newRange.endLine, oldRange.endLine));
    147         if (oldRange.linesCount !== newRange.linesCount)
     188        if (oldRange.linesCount !== newRange.linesCount) {
     189            // Invalidate offset cache.
     190            this._lineOffsetsCache.length = oldRange.startLine + 1;
     191            // Force linenumber cache to be continuous.
     192            this._lineNumberToOffset(oldRange.startLine);
    148193            this._paintLineNumbers();
     194        }
    149195        this._paint();
    150196    },
     
    157203            return;
    158204        }
     205
    159206        this._invalidateLines(oldRange.startLine, oldRange.endLine + 1);
    160207        this._invalidateLines(newRange.startLine, newRange.endLine + 1);
     
    191238
    192239        this._sheet.style.width = this._textWidth + this._lineNumberOffset + "px";
    193         this._sheet.style.height = this._lineHeight * this._textModel.linesCount + "px";
     240        this._sheet.style.height = this._lineNumberToOffset(this._textModel.linesCount) + "px";
    194241
    195242        if (this._canvas.width !== this._container.clientWidth || this._canvas.height !== this._container.clientHeight || newLineNumberDigits !== this._lineNumberDigits) {
     
    240287        this._ctx.font = this._font;
    241288        this._ctx.textBaseline = "bottom";
    242 
    243         firstLine = Math.max(firstLine, Math.floor(this._scrollTop / this._lineHeight) - 1);
    244         lastLine = Math.min(lastLine, Math.ceil((this._scrollTop + this._canvas.height) / this._lineHeight) + 1);
     289       
     290        firstLine = Math.max(firstLine, this._offsetToLineNumber(this._scrollTop) - 1);
     291        lastLine = Math.min(lastLine, this._offsetToLineNumber(this._scrollTop + this._canvas.height) + 1);
    245292        if (firstLine > lastLine)
    246293            return;
     
    249296            WebInspector.log("Repaint %d:%d", firstLine, lastLine);
    250297            this._ctx.fillStyle = "rgb(255,255,0)";
    251             var height = (lastLine - firstLine) * this._lineHeight;
    252             this._ctx.fillRect(this._lineNumberOffset - 1, this._lineHeight * firstLine - this._scrollTop, this._canvas.width - this._lineNumberOffset + 1, height);
     298            var fromOffset = this._lineNumberToOffset(firstLine);
     299            var toOffset = this._lineNumberToOffset(lastLine);
     300            this._ctx.fillRect(this._lineNumberOffset - 1, fromOffset - this._scrollTop, this._canvas.width - this._lineNumberOffset + 1, toOffset - fromOffset);
    253301            setTimeout(this._paintLinesContinuation.bind(this, firstLine, lastLine), 100);
    254302        } else
     
    264312
    265313        // First clear the region, then update last line to fit model (this clears removed lines from the end of the document).
    266         var height = (lastLine - firstLine) * this._lineHeight;
     314        var fromOffset = this._lineNumberToOffset(firstLine);
     315        var toOffset = lastLine < this._textModel.linesCount ? this._lineNumberToOffset(lastLine) : this._canvas.height + this._scrollTop;
    267316        // Do not clear region when paintCurrentLine is likely to do all the necessary work.
    268317        if (firstLine + 1 != lastLine || this._selection.endLine != firstLine) {
    269318            this._ctx.fillStyle = "rgb(255,255,255)";
    270             this._ctx.fillRect(0, this._lineHeight * firstLine - this._scrollTop, this._canvas.width, height);
     319            this._ctx.fillRect(0, fromOffset - this._scrollTop, this._canvas.width, toOffset - fromOffset);
    271320        }
    272321        lastLine = Math.min(lastLine, this._textModel.linesCount);
     
    282331        for (var i = firstLine; i < lastLine; ++i) {
    283332            var line = this._textModel.line(i);
     333            var lineOffset = this._lineNumberToOffset(i) + this._textLineHeight - this._scrollTop;
     334
    284335            if (!this._highlightingEnabled) {
    285                 this._ctx.fillText(line, this._lineNumberOffset - this._scrollLeft, this._lineHeight * (i + 1) - this._scrollTop);
     336                this._ctx.fillStyle = "rgb(0,0,0)";
     337                this._ctx.fillText(line, this._lineNumberOffset - this._scrollLeft, lineOffset);
    286338                continue;
    287339            }
     
    298350                    if (plainTextStart !== -1) {
    299351                        this._ctx.fillStyle = "rgb(0,0,0)";
    300                         this._ctx.fillText(line.substring(plainTextStart, j), this._lineNumberOffset - this._scrollLeft + this._columnToOffset(i, plainTextStart), this._lineHeight * (i + 1) - this._scrollTop);
     352                        this._ctx.fillText(line.substring(plainTextStart, j), this._lineNumberOffset - this._scrollLeft + this._columnToOffset(i, plainTextStart), lineOffset);
    301353                        plainTextStart = -1;
    302354                    }
    303355                    this._ctx.fillStyle = attribute.style;
    304                     this._ctx.fillText(line.substring(j, j + attribute.length), this._lineNumberOffset - this._scrollLeft + this._columnToOffset(i, j), this._lineHeight * (i + 1) - this._scrollTop);
     356                    this._ctx.fillText(line.substring(j, j + attribute.length), this._lineNumberOffset - this._scrollLeft + this._columnToOffset(i, j), lineOffset);
    305357                    j += attribute.length;
    306358                }
     
    308360            if (plainTextStart !== -1) {
    309361                this._ctx.fillStyle = "rgb(0,0,0)";
    310                 this._ctx.fillText(line.substring(plainTextStart, line.length), this._lineNumberOffset - this._scrollLeft + this._columnToOffset(i, plainTextStart), this._lineHeight * (i + 1) - this._scrollTop);
     362                this._ctx.fillText(line.substring(plainTextStart, line.length), this._lineNumberOffset - this._scrollLeft + this._columnToOffset(i, plainTextStart), lineOffset);
    311363            }
    312364        }
     
    320372
    321373        this._ctx.fillStyle = "rgb(255,255,255)";
    322         this._ctx.fillRect(0, 0, this._lineNumberOffset - 3, this._canvas.height);
     374        this._ctx.fillRect(0, 0, this._lineNumberOffset - 2, this._canvas.height);
    323375
    324376        this._ctx.fillStyle = "rgb(235,235,235)";
    325377        this._ctx.fillRect(this._lineNumberOffset - 2, 0, 1, this._canvas.height);
    326378
    327         var firstLine = Math.min(this._textModel.linesCount, Math.floor(this._scrollTop / this._lineHeight) - 1);
    328         var lastLine = Math.min(this._textModel.linesCount, Math.ceil((this._scrollTop + this._canvas.height) / this._lineHeight) + 1);
     379        var firstLine = Math.max(0, this._offsetToLineNumber(this._scrollTop) - 1);
     380        var lastLine = Math.min(this._textModel.linesCount, this._offsetToLineNumber(this._scrollTop + this._canvas.height) + 1);
    329381
    330382        this._ctx.fillStyle = "rgb(155,155,155)";
    331383        for (var i = firstLine; i < lastLine; ++i)
    332            this._ctx.fillText(i + 1, (this._lineNumberDigits - this._decimalDigits(i + 1) + 1) * this._digitWidth, this._lineHeight * (i + 1) - this._scrollTop);
     384           this._ctx.fillText(i + 1, (this._lineNumberDigits - this._decimalDigits(i + 1) + 1) * this._digitWidth, this._lineNumberToOffset(i) + this._textLineHeight - this._scrollTop);
    333385    },
    334386
     
    336388    {
    337389        this._ctx.fillStyle = "rgb(232, 242, 254)";
    338         this._ctx.fillRect(0, this._lineHeight * line - this._scrollTop, this._canvas.width, this._lineHeight);
     390        this._ctx.fillRect(0, this._lineNumberToOffset(line) - this._scrollTop, this._canvas.width, this._lineHeight(line));
    339391        this._ctx.fillStyle = "rgb(0, 0, 0)";
    340392    },
     
    342394    _scroll: function(e)
    343395    {
    344         if (this._muteScroll)
    345             return;
    346 
    347         if (this._container.scrollTop === 0)
    348             this._lineAlignmentOffset = 0;
    349         else if (this._container.scrollTop + this._container.clientHeight === this._textModel.linesCount * this._lineHeight)
    350             this._lineAlignmentOffset = this._container.scrollTop % this._lineHeight;
    351 
    352         // Enforce line alignment.
    353         if (this._container.scrollTop % this._lineHeight !== this._lineAlignmentOffset) {
    354             var linesOffset = Math.floor(this._container.scrollTop / this._lineHeight);
    355             this._muteScroll = true;
    356             this._container.scrollTop = linesOffset * this._lineHeight + this._lineAlignmentOffset;
    357             delete this._muteScroll;
    358         }
    359 
    360396        // Hide div-based cursor first.
    361397        this._cursor._cursorElement.style.display = "none";
     
    365401    _repaintOnScroll: function()
    366402    {
    367         var linesOffset = Math.floor(this._container.scrollTop / this._lineHeight);
    368403        if (this._scrollTop !== this._container.scrollTop || this._scrollLeft !== this._container.scrollLeft) {
    369404            this._scrollTop = this._container.scrollTop;
     
    411446    _caretForMouseEvent: function(e)
    412447    {
    413         var lineNumber = Math.floor((e.y + this._scrollTop - 4) / this._lineHeight);
     448        var lineNumber = Math.max(0, this._offsetToLineNumber(e.y + this._scrollTop) - 1);
    414449        var line = this._textModel.line(lineNumber);
    415450        var offset = e.x + this._scrollLeft - this._lineNumberOffset - this._digitWidth;
     
    485520                    arrowAction.call(this, 0, 0, true);
    486521                else if (e.ctrlKey)
    487                     this._container.scrollTop -= this._lineHeight;
     522                    this._container.scrollTop -= this._lineHeight(caretLine);
    488523                else {
    489                     var jump = e.keyCode === keyCodes.Up ? 1 : Math.floor(this._canvas.height / this._lineHeight);
    490                     arrowAction.call(this, caretLine - jump, this._desiredCaretColumn, true);
     524                    if (e.keyCode === keyCodes.Up)
     525                        arrowAction.call(this, caretLine - 1, this._desiredCaretColumn, true);
     526                    else {
     527                        var offset = Math.max(0, this._lineNumberToOffset(caretLine) - this._canvas.height);
     528                        arrowAction.call(this, this._offsetToLineNumber(offset), this._desiredCaretColumn, true);
     529                    }
    491530                }
    492531                break;
     
    496535                    arrowAction.call(this, this._textModel.linesCount - 1, this._textModel.lineLength(this._textModel.linesCount - 1), true);
    497536                else if (e.ctrlKey)
    498                     this._container.scrollTop += this._lineHeight;
     537                    this._container.scrollTop += this._lineHeight(caretLine);
    499538                else {
    500                     var jump = e.keyCode === keyCodes.Down ? 1 : Math.floor(this._canvas.height / this._lineHeight);
    501                     arrowAction.call(this, caretLine + jump, this._desiredCaretColumn, true);
     539                    if (e.keyCode === keyCodes.Down)
     540                        arrowAction.call(this, caretLine + 1, this._desiredCaretColumn, true);
     541                    else {
     542                        var offset = this._lineNumberToOffset(caretLine) + this._canvas.height;
     543                        arrowAction.call(this, this._offsetToLineNumber(offset), this._desiredCaretColumn, true);
     544                    }
    502545                }
    503546                break;
     
    575618        var offset = this._columnToOffset(line, column);
    576619        if (offset >= this._container.scrollLeft)
    577             this._cursor.setLocation(this._lineNumberOffset + offset - 1, line * this._lineHeight);
     620            this._cursor.setLocation(this._lineNumberOffset + offset - 1, this._lineNumberToOffset(line));
    578621        else
    579622            this._cursor.setLocation(0, 0);
     
    592635        if (!this._highlightingEnabled)
    593636            return;
    594         var firstVisibleLine = Math.max(0, Math.floor(this._scrollTop / this._lineHeight) - 1);
    595         var lastVisibleLine = Math.min(this._textModel.linesCount, Math.ceil(firstVisibleLine + this._canvas.height / this._lineHeight + 1));
     637        var firstVisibleLine = Math.max(0, this._offsetToLineNumber(this._scrollTop) - 1);
     638        var lastVisibleLine = Math.min(this._textModel.linesCount, this._offsetToLineNumber(this._scrollTop + this._canvas.height) + 1);
    596639
    597640        var damage = this._highlighter.highlight(startLine, lastVisibleLine);
     
    626669                to = this._canvas.width;
    627670
    628             this._ctx.fillRect(from, this._lineHeight * i - this._scrollTop, to - from, this._lineHeight);
     671            this._ctx.fillRect(from, this._lineNumberToOffset(i) - this._scrollTop, to - from, this._textLineHeight);
    629672        }
    630673        this._ctx.fillStyle = "rgb(0, 0, 0)";
     
    723766        this._digitWidth = this._ctx.measureText("0").width;
    724767
    725         this._lineHeight = Math.floor(this._fontSize * 1.4);
    726         this._cursor.setLineHeight(this._lineHeight);
     768        this._textLineHeight = Math.floor(this._fontSize * 1.4);
     769        this._cursor.setTextLineHeight(this._textLineHeight);
    727770    },
    728771
     
    914957    },
    915958
    916     setLineHeight: function(lineHeight)
    917     {
    918         this._cursorElement.style.height = lineHeight + "px";
     959    setTextLineHeight: function(textLineHeight)
     960    {
     961        this._cursorElement.style.height = textLineHeight + "px";
    919962    },
    920963
Note: See TracChangeset for help on using the changeset viewer.