mirror of https://github.com/fltk/fltk
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:
parent
f54863db44
commit
7e94d7650b
|
@ -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$".
|
||||
//
|
||||
|
|
Loading…
Reference in New Issue