Fix Fl_Text_Display line numbers and scrolling (STR #3272).

Fix slow scrolling, line number display, and scrollbars (STR #3272).

Optimized wrap mode - removed unnecessary wrap margin calculations.
The speed-up is significant, but there is still room for improvements.

Refactored parts of the code to make the decisions more modular and
(hopefully) obvious.

This commit also fixes some positioning issues with different scrollbar
alignments and the new line number display.

Fix line number display with wrap mode (hiding text behind scrollbars).

Note: same changes as r11202 and r11205 in branch-1.3.



git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3-porting@11208 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Albrecht Schlosser 2016-02-22 17:25:30 +00:00
parent f54863db44
commit 7e94d7650b
1 changed files with 161 additions and 80 deletions

View File

@ -1,7 +1,7 @@
//
// "$Id$"
//
// Copyright 2001-2015 by Bill Spitzak and others.
// Copyright 2001-2016 by Bill Spitzak and others.
// Original code Copyright Mark Edel. Permission to distribute under
// the LGPL for the FLTK library granted by Mark Edel.
//
@ -40,6 +40,19 @@
#undef min
#undef max
// #define DEBUG
// #define DEBUG2
#define LINENUM_LEFT_OF_VSCROLL // uncomment this line ...
// ... if you want the line numbers to be drawn left of the vertical
// scrollbar (only if the vertical scrollbar is aligned left).
// This is the default.
// If not defined and the vertical scrollbar is aligned left, then the
// scrollbar is positioned at the left border and the line numbers are
// drawn between the scrollbar (left) and the text area (right).
// If the vertical scrollbar is aligned right, then the line number
// position is not affected by this definition.
// Text area margins. Left & right margins should be at least 3 so that
// there is some room for the overhanging parts of the cursor!
#define TOP_MARGIN 1
@ -198,7 +211,7 @@ Fl_Text_Display::~Fl_Text_Display() {
/**
Set width of screen area for line numbers.
Use to also enable/disable line numbers.
A value of 0 disables line numbering, values >0 enables them.
A value of 0 disables line numbering, values >0 enable the line number display.
\param width The new width of the area for line numbers to appear, in pixels.
0 disables line numbers (default)
*/
@ -471,55 +484,96 @@ int Fl_Text_Display::longest_vline() const {
\param X, Y, W, H new position and size of this widget
*/
void Fl_Text_Display::resize(int X, int Y, int W, int H) {
#ifdef DEBUG
#ifdef DEBUG2
printf("\n");
printf("Fl_Text_Display::resize(X=%d, Y=%d, W=%d, H=%d)\n", X, Y, W, H);
#endif // DEBUG
const int oldWidth = w();
#ifdef DEBUG
printf(" oldWidth=%d, mContinuousWrap=%d, mWrapMarginPix=%d\n", oldWidth, mContinuousWrap, mWrapMarginPix);
#endif // DEBUG
printf(" current size(x=%d, y=%d, w=%d, h=%d)\n", x(), y(), w(), h());
printf(" box_d* size(x=%d, y=%d, w=%d, h=%d)\n",
Fl::box_dx(box()),Fl::box_dy(box()),Fl::box_dw(box()),Fl::box_dh(box()));
printf(" text_area size(x=%d, y=%d, w=%d, h=%d)\n",
text_area.x, text_area.y, text_area.w, text_area.h);
printf(" mContinuousWrap=%d, mWrapMarginPix=%d\n",
mContinuousWrap, mWrapMarginPix);
fflush(stdout);
#endif // DEBUG2
Fl_Widget::resize(X,Y,W,H);
if (!buffer()) return;
X += Fl::box_dx(box());
Y += Fl::box_dy(box());
W -= Fl::box_dw(box());
H -= Fl::box_dh(box());
text_area.x = X+LEFT_MARGIN;
text_area.y = Y+TOP_MARGIN;
text_area.w = W-LEFT_MARGIN-RIGHT_MARGIN;
text_area.h = H-TOP_MARGIN-BOTTOM_MARGIN;
const int oldTAWidth = text_area.w;
int i;
/* Find the new maximum font height for this text display */
for (i = 0, mMaxsize = fl_height(textfont(), textsize()); i < mNStyles; i++)
mMaxsize = max(mMaxsize, fl_height(mStyleTable[i].font, mStyleTable[i].size));
// did we have scrollbars initially?
unsigned int hscrollbarvisible = mHScrollBar->visible();
unsigned int vscrollbarvisible = mVScrollBar->visible();
int oldTAWidth = text_area.w;
X += Fl::box_dx(box());
Y += Fl::box_dy(box());
W -= Fl::box_dw(box());
H -= Fl::box_dh(box());
text_area.x = X + LEFT_MARGIN + mLineNumWidth;
text_area.y = Y + TOP_MARGIN;
text_area.w = W - LEFT_MARGIN - RIGHT_MARGIN - mLineNumWidth;
text_area.h = H - TOP_MARGIN - BOTTOM_MARGIN;
// Find the new maximum font height for this text display
int i;
for (i = 0, mMaxsize = fl_height(textfont(), textsize()); i < mNStyles; i++)
mMaxsize = max(mMaxsize, fl_height(mStyleTable[i].font, mStyleTable[i].size));
// try without scrollbars first
mVScrollBar->clear_visible();
mHScrollBar->clear_visible();
#if (1) // optimization (experimental - seems to work well)
// Optimization: if the number of lines in the buffer does not fit in
// the display area, then we need a vertical scrollbar regardless of
// word wrapping. If we switch it on here, this saves one line counting
// run in wrap mode in the loop below ("... again ..."). This is important
// for large buffers that suffer from slow calculations of character width
// to determine line wrapping.
if (mContinuousWrap && !mWrapMarginPix) {
int nvlines = (text_area.h + mMaxsize - 1) / mMaxsize;
int nlines = buffer()->count_lines(0,buffer()->length());
if (nvlines < 1) nvlines = 1;
if (nlines >= nvlines-1) {
mVScrollBar->set_visible(); // we need a vertical scrollbar
text_area.w -= scrollbar_width();
}
}
#endif // optimization
for (int again = 1; again;) {
again = 0;
/* In continuous wrap mode, a change in width affects the total number of
lines in the buffer, and can leave the top line number incorrect, and
the top character no longer pointing at a valid line start */
if (mContinuousWrap && !mWrapMarginPix && (W!=oldWidth || text_area.w!=oldTAWidth)) {
#ifdef DEBUG2
printf("*** again ... text_area.w = %d, oldTAWidth = %d, diff = %d\n",
text_area.w, oldTAWidth, text_area.w - oldTAWidth);
#endif // DEBUG2
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;
absolute_top_line_number(oldFirstChar);
#ifdef DEBUG
#ifdef DEBUG2
printf(" mNBufferLines=%d\n", mNBufferLines);
#endif // DEBUG
#endif // DEBUG2
}
oldTAWidth = text_area.w;
/* reallocate and update the line starts array, which may have changed
size and / or contents. */
int nvlines = (text_area.h + mMaxsize - 1) / mMaxsize;
@ -535,25 +589,16 @@ void Fl_Text_Display::resize(int X, int Y, int W, int H) {
// figure the scrollbars
if (scrollbar_width()) {
/* Decide if the vertical scrollbar needs to be visible */
uchar vbvis = mVScrollBar->visible();
if (scrollbar_align() & (FL_ALIGN_LEFT|FL_ALIGN_RIGHT) &&
mNBufferLines >= mNVisibleLines - 1)
if (!mVScrollBar->visible() &&
scrollbar_align() & (FL_ALIGN_LEFT|FL_ALIGN_RIGHT) &&
mNBufferLines >= mNVisibleLines-1)
{
mVScrollBar->set_visible();
if (scrollbar_align() & FL_ALIGN_LEFT) {
text_area.x = X+scrollbar_width()+LEFT_MARGIN;
text_area.w = W-scrollbar_width()-LEFT_MARGIN-RIGHT_MARGIN;
mVScrollBar->resize(X, text_area.y-TOP_MARGIN, scrollbar_width(),
text_area.h+TOP_MARGIN+BOTTOM_MARGIN);
} else {
text_area.x = X+LEFT_MARGIN;
text_area.w = W-scrollbar_width()-LEFT_MARGIN-RIGHT_MARGIN;
mVScrollBar->resize(X+W-scrollbar_width(), text_area.y-TOP_MARGIN,
scrollbar_width(), text_area.h+TOP_MARGIN+BOTTOM_MARGIN);
}
mVScrollBar->set_visible();
text_area.w -= scrollbar_width();
again = 1;
}
if (vbvis != mVScrollBar->visible()) again = 1;
/*
Decide if the horizontal scrollbar needs to be visible. If the text
@ -577,35 +622,66 @@ void Fl_Text_Display::resize(int X, int Y, int W, int H) {
/* WAS: Suggestion: Try turning the horizontal scrollbar on when
you first see a line that is too wide in the window, but then
don't turn it off (ie mix both of your solutions). */
if (scrollbar_align() & (FL_ALIGN_TOP|FL_ALIGN_BOTTOM) &&
if (!mHScrollBar->visible() &&
scrollbar_align() & (FL_ALIGN_TOP|FL_ALIGN_BOTTOM) &&
(mVScrollBar->visible() || longest_vline() > text_area.w))
{
char wrap_at_bounds = mContinuousWrap && (mWrapMarginPix<text_area.w);
if (!mHScrollBar->visible() && !wrap_at_bounds) {
if (!wrap_at_bounds) {
mHScrollBar->set_visible();
text_area.h -= scrollbar_width();
again = 1; // loop again to see if we now need vert. & recalc sizes
}
if (scrollbar_align() & FL_ALIGN_TOP) {
text_area.y = Y + scrollbar_width()+TOP_MARGIN;
text_area.h = H - (wrap_at_bounds?0:scrollbar_width())-TOP_MARGIN-BOTTOM_MARGIN;
mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y,
text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width());
} else {
text_area.y = Y+TOP_MARGIN;
text_area.h = H - (wrap_at_bounds?0:scrollbar_width())-TOP_MARGIN-BOTTOM_MARGIN;
mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y+H-scrollbar_width(),
text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width());
}
}
}
} // (... again ...)
// Calculate text area position, dependent on scrollbars and line numbers.
// Note: width and height have been calculated above.
text_area.x = X + mLineNumWidth + LEFT_MARGIN;
if (mVScrollBar->visible() && scrollbar_align() & FL_ALIGN_LEFT)
text_area.x += scrollbar_width();
text_area.y = Y + TOP_MARGIN;
if (mHScrollBar->visible() &&
scrollbar_align() & FL_ALIGN_TOP)
text_area.y += scrollbar_width();
// position and resize scrollbars
if (mVScrollBar->visible()) {
if (scrollbar_align() & FL_ALIGN_LEFT) {
#ifdef LINENUM_LEFT_OF_VSCROLL
mVScrollBar->resize(text_area.x - LEFT_MARGIN - scrollbar_width(),
#else
mVScrollBar->resize(X,
#endif
text_area.y - TOP_MARGIN,
scrollbar_width(),
text_area.h + TOP_MARGIN + BOTTOM_MARGIN);
} else {
mVScrollBar->resize(X+W-scrollbar_width(),
text_area.y - TOP_MARGIN,
scrollbar_width(),
text_area.h + TOP_MARGIN + BOTTOM_MARGIN);
}
}
// add linenum width to the text area - LZA / STR#2621
if (mLineNumWidth > 0) {
text_area.x += mLineNumWidth;
text_area.w -= mLineNumWidth;
if (mHScrollBar->visible()) {
if (scrollbar_align() & FL_ALIGN_TOP) {
mHScrollBar->resize(text_area.x - LEFT_MARGIN,
Y,
text_area.w + LEFT_MARGIN + RIGHT_MARGIN,
scrollbar_width());
} else {
mHScrollBar->resize(text_area.x - LEFT_MARGIN,
Y + H - scrollbar_width(),
text_area.w + LEFT_MARGIN + RIGHT_MARGIN,
scrollbar_width());
}
}
// user request to change viewport
if (mTopLineNumHint != mTopLineNum || mHorizOffsetHint != mHorizOffset)
scroll_(mTopLineNumHint, mHorizOffsetHint);
@ -1322,10 +1398,10 @@ int Fl_Text_Display::move_down() {
/**
\brief Count the number of lines between two positions.
Same as BufCountLines, but takes into account wrapping if wrapping is
turned on. If the caller knows that \p startPos is at a line start, it
can pass \p startPosIsLineStart as True to make the call more efficient
by avoiding the additional step of scanning back to the last newline.
Same as Fl_Text_Buffer::count_lines(), but takes into account wrapping if
wrapping is turned on. If the caller knows that \p startPos is at a line
start, it can pass \p startPosIsLineStart as True to make the call more
efficient by avoiding the additional step of scanning back to the last newline.
\param startPos index to first character
\param endPos index after last character
@ -1344,7 +1420,7 @@ int Fl_Text_Display::count_lines(int startPos, int endPos,
startPos, endPos, startPosIsLineStart);
#endif // DEBUG
/* If we're not wrapping use simple (and more efficient) BufCountLines */
/* If we're not wrapping use simple (and more efficient) Fl_Text_Buffer::count_lines() */
if (!mContinuousWrap)
return buffer()->count_lines(startPos, endPos);
@ -2802,7 +2878,7 @@ int Fl_Text_Display::scroll_(int topLineNum, int horizOffset) {
\brief Update vertical scrollbar.
Update the minimum, maximum, slider size, page increment, and value
for vertical scrollbar.
for the vertical scrollbar.
*/
void Fl_Text_Display::update_v_scrollbar() {
/* The vertical scrollbar value and slider size directly represent the top
@ -2821,7 +2897,7 @@ void Fl_Text_Display::update_v_scrollbar() {
/**
\brief Update vertical scrollbar.
\brief Update horizontal scrollbar.
Update the minimum, maximum, slider size, page increment, and value
for the horizontal scrollbar.
@ -2834,7 +2910,7 @@ void Fl_Text_Display::update_h_scrollbar() {
/**
\brief Callbacks for drag or valueChanged on scrollbars.
\brief Callbacks for drag or valueChanged on vertical scrollbar.
*/
void Fl_Text_Display::v_scrollbar_cb(Fl_Scrollbar* b, Fl_Text_Display* textD) {
if (b->value() == textD->mTopLineNum) return;
@ -2844,7 +2920,7 @@ void Fl_Text_Display::v_scrollbar_cb(Fl_Scrollbar* b, Fl_Text_Display* textD) {
/**
\brief Callbacks for drag or valueChanged on scrollbars.
\brief Callbacks for drag or valueChanged on horizontal scrollbar.
*/
void Fl_Text_Display::h_scrollbar_cb(Fl_Scrollbar* b, Fl_Text_Display* textD) {
if (b->value() == textD->mHorizOffset) return;
@ -2876,22 +2952,29 @@ void Fl_Text_Display::draw_line_numbers(bool /*clearAll*/) {
if (mLineNumWidth <= 0 || !visible_r())
return;
// Make sure we reset clipping range for line number's GC;
// it may be shared (e.g. if line numbers and text have same color)
// and therefore clipping ranges may be invalid.
int xoff = Fl::box_dx(box());
// Make sure we set the correct clipping range for line numbers.
// Take scrollbars and positions into account.
int hscroll_h = mHScrollBar->visible() ? mHScrollBar->h() : 0;
int vscroll_w = mVScrollBar->visible() ? mVScrollBar->w() : 0;
int xoff = Fl::box_dx(box());
int yoff = Fl::box_dy(box()) + ((scrollbar_align()&FL_ALIGN_TOP)?hscroll_h:0);
#ifndef LINENUM_LEFT_OF_VSCROLL
if (scrollbar_align()&FL_ALIGN_LEFT)
xoff += vscroll_w;
#endif
Fl_Color fgcolor = isactive ? linenumber_fgcolor() : fl_inactive(linenumber_fgcolor());
Fl_Color bgcolor = isactive ? linenumber_bgcolor() : fl_inactive(linenumber_bgcolor());
fl_push_clip(x() + xoff,
y() + Fl::box_dy(box()),
mLineNumWidth - xoff,
h() - Fl::box_dh(box()) - hscroll_h);
y() + yoff,
mLineNumWidth,
h() - Fl::box_dw(box()) - hscroll_h);
{
// Set background color for line number area -- LZA / STR# 2621
// Erase background
fl_color(bgcolor);
fl_rectf(x(), y(), mLineNumWidth, h());
fl_rectf(x()+xoff, y(), mLineNumWidth, h());
// Draw separator line
//fl_color(180,180,180);
@ -2900,7 +2983,7 @@ void Fl_Text_Display::draw_line_numbers(bool /*clearAll*/) {
// Draw line number text
fl_font(linenumber_font(), linenumber_size());
Y = y();
Y = y() + yoff;
line = get_absolute_top_line_number();
// set font color for line numbers
@ -2911,7 +2994,7 @@ void Fl_Text_Display::draw_line_numbers(bool /*clearAll*/) {
sprintf(lineNumString, linenumber_format(), line);
int xx = x() + xoff + 3,
yy = Y + 3,
ww = mLineNumWidth - xoff - (3*2),
ww = mLineNumWidth - (3*2),
hh = lineHeight;
fl_draw(lineNumString, xx, yy, ww, hh, linenumber_align(), 0, 0);
//DEBUG fl_rect(xx, yy, ww, hh);
@ -4052,8 +4135,6 @@ double Fl_Text_Display::col_to_x(double col) const
}
//
// End of "$Id$".
//