diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Fl_Text_Buffer.cxx | 42 | ||||
| -rw-r--r-- | src/Fl_Text_Display.cxx | 66 |
2 files changed, 100 insertions, 8 deletions
diff --git a/src/Fl_Text_Buffer.cxx b/src/Fl_Text_Buffer.cxx index 3bb8a10f0..f26afb39d 100644 --- a/src/Fl_Text_Buffer.cxx +++ b/src/Fl_Text_Buffer.cxx @@ -1166,6 +1166,48 @@ int Fl_Text_Buffer::count_lines(int startPos, int endPos) const { return lineCount; } +/** + Estimate the number of newlines between \p startPos and \p endPos in buffer. + This call takes line wrapping into account. It assumes a line break at every + `lineLen` characters after the beginning of a line. + */ +int Fl_Text_Buffer::estimate_lines(int startPos, int endPos, int lineLen) const +{ + IS_UTF8_ALIGNED2(this, (startPos)) + IS_UTF8_ALIGNED2(this, (endPos)) + + int gapLen = mGapEnd - mGapStart; + int lineCount = 0; + int softLineBreaks = 0, softLineBreakCount = lineLen; + + int pos = startPos; + while (pos < mGapStart) + { + if (pos == endPos) + return lineCount + softLineBreaks; + if (mBuf[pos++] == '\n') { + softLineBreakCount = lineLen; + lineCount++; + } + if (--softLineBreakCount == 0) { + softLineBreakCount = lineLen; + softLineBreaks++; + } + } + while (pos < mLength) { + if (pos == endPos) + return lineCount + softLineBreaks; + if (mBuf[pos++ + gapLen] == '\n') { + softLineBreakCount = lineLen; + lineCount++; + } + if (--softLineBreakCount == 0) { + softLineBreakCount = lineLen; + softLineBreaks++; + } + } + return lineCount + softLineBreaks; +} /* Skip to the first character, n lines ahead. diff --git a/src/Fl_Text_Display.cxx b/src/Fl_Text_Display.cxx index c91c98a79..84571af47 100644 --- a/src/Fl_Text_Display.cxx +++ b/src/Fl_Text_Display.cxx @@ -560,9 +560,9 @@ void Fl_Text_Display::recalc_display() { if (mContinuousWrap && !mWrapMarginPix && text_area.w != oldTAWidth) { int oldFirstChar = mFirstChar; - mNBufferLines = count_lines(0, buffer()->length(), true); mFirstChar = line_start(mFirstChar); mTopLineNum = count_lines(0, mFirstChar, true)+1; + mNBufferLines = mTopLineNum-1 + count_lines(mFirstChar, buffer()->length(), true); absolute_top_line_number(oldFirstChar); #ifdef DEBUG2 printf(" mNBufferLines=%d\n", mNBufferLines); @@ -1436,16 +1436,66 @@ int Fl_Text_Display::count_lines(int startPos, int endPos, if (!mContinuousWrap) return buffer()->count_lines(startPos, endPos); - wrapped_line_counter(buffer(), startPos, endPos, INT_MAX, - startPosIsLineStart, 0, &retPos, &retLines, &retLineStart, - &retLineEnd); + /* + Correctly counting wrapped lines is very slow. We have to query the length + of every segment of text for every line change and style change and find + potential soft line breaks. + + Most of the resulting information is needed for calculating the vertical + scroll bar size. After a certain text length, the scroll bar size is no + longer very precise anyway, so we optimize line count for all lines but + the visible ones (plus minus a few lines for rounding). + + The optimized code is several magnitudes faster and makes scrolling and + window resizing of long texts quite responsive. There is a slight but IMHO + tollerable drawback: when walking huge files using arrow up and down, the + text display sometimes jumps 2 or 3 lines instead of 1, but the overall + buffer stays intact as well as the scroll position. + */ + if (buffer()->length() > 16384) { + // Optimized line counting + int nLines = 0; + int firstVisibleChar = buffer()->rewind_lines(mFirstChar, 3); + int lastVisibleChar = buffer()->skip_lines(mLastChar, 3); + // Calculate the averga number of characters up to a soft line break + if (mColumnScale==0.0) x_to_col(1.0); + int avgCharsPerLine = mWrapMarginPix; + if (!avgCharsPerLine) avgCharsPerLine = text_area.w; + avgCharsPerLine = (int)(avgCharsPerLine / mColumnScale) + 1; + + // first segment, lines up to display, count fast + if (startPos < firstVisibleChar) { + int tmpEnd = endPos<firstVisibleChar ? endPos : firstVisibleChar; + nLines += buffer()->estimate_lines(startPos, tmpEnd, avgCharsPerLine); + startPos = tmpEnd; + } + // second segement, count displayed liens + if (startPos < endPos && startPos < mLastChar) { + // Precisse line counting only for visible text: + int tmpEnd = endPos<lastVisibleChar ? endPos : lastVisibleChar; + wrapped_line_counter(buffer(), startPos, tmpEnd, INT_MAX, + startPosIsLineStart, 0, &retPos, &retLines, &retLineStart, + &retLineEnd); + nLines += retLines; + startPos = tmpEnd; + } + // third segement is everything after displayed lines + if (startPos < endPos && startPos >= lastVisibleChar) { + nLines += buffer()->estimate_lines(startPos, endPos, avgCharsPerLine); + } + return nLines; + } else { + // Precise line counting only for small text buffer sizes: + wrapped_line_counter(buffer(), startPos, endPos, INT_MAX, + startPosIsLineStart, 0, &retPos, &retLines, &retLineStart, + &retLineEnd); #ifdef DEBUG - printf(" # after WLC: retPos=%d, retLines=%d, retLineStart=%d, retLineEnd=%d\n", - retPos, retLines, retLineStart, retLineEnd); + printf(" # after WLC: retPos=%d, retLines=%d, retLineStart=%d, retLineEnd=%d\n", + retPos, retLines, retLineStart, retLineEnd); #endif // DEBUG - - return retLines; + return retLines; + } } |
