diff --git a/FL/Fl_Text_Display.H b/FL/Fl_Text_Display.H index 58d71ea94..375ee4eed 100644 --- a/FL/Fl_Text_Display.H +++ b/FL/Fl_Text_Display.H @@ -142,10 +142,25 @@ public: \see Fl_Text_Display::highlight_data() */ struct Style_Table_Entry { - Fl_Color color; ///< text color - Fl_Font font; ///< text font - Fl_Fontsize size; ///< text font size - unsigned attr; ///< currently unused (this may be changed in the future) + Fl_Color color; ///< text color + Fl_Font font; ///< text font + Fl_Fontsize size; ///< text font size + unsigned attr; ///< further attributes for the text style (see `ATTR_BGCOLOR`, etc.) + Fl_Color bgcolor; ///< text background color if `ATTR_BGCOLOR` or `ATTR_BGCOLOR_EXT` is set + }; + + /** + attribute flags in `Style_Table_Entry.attr` + */ + enum { + ATTR_BGCOLOR = 0x0001, ///< use the background color in the `bgcolor` field + ATTR_BGCOLOR_EXT_ = 0x0002, ///< (internal use) + ATTR_BGCOLOR_EXT = 0x0003, ///< extend background color to the end of the line + ATTR_UNDERLINE = 0x0004, ///< a single underline, underline types are mutually exclusive + ATTR_GRAMMAR = 0x0008, ///< grammar suggestion (blue dotted underline) + ATTR_SPELLING = 0x000C, ///< spelling suggestion (red dotted underline) + ATTR_STRIKE_THROUGH = 0x0010, ///< line through the middle of the text + ATTR_LINES_MASK = 0x001C, ///< the mask for all underline and strike through types }; Fl_Text_Display(int X, int Y, int W, int H, const char *l = 0); diff --git a/src/Fl_Text_Display.cxx b/src/Fl_Text_Display.cxx index 6612595bd..b26311767 100644 --- a/src/Fl_Text_Display.cxx +++ b/src/Fl_Text_Display.cxx @@ -2022,141 +2022,148 @@ int Fl_Text_Display::handle_vline( X = text_area.x - mHorizOffset; } - startX = X; - startIndex = 0; - if (!lineStr) { - // just clear the background - if (mode==DRAW_LINE) { - style = position_style(lineStartPos, lineLen, -1); - draw_string( style|BG_ONLY_MASK, text_area.x, Y, text_area.x+text_area.w, lineStr, lineLen ); + // In DRAW_LINE mode, the first iteration of the loop will draw all + // backgrounds. The second iteration will draw the text, so that text + // overlapping background color changes will not be clipped. + for (int loop=1; loop<=2; loop++) { + int mask = (loop==1) ? BG_ONLY_MASK : TEXT_ONLY_MASK; + startX = X; + startIndex = 0; + if (!lineStr) { + // just clear the background + if (mode==DRAW_LINE) { + style = position_style(lineStartPos, lineLen, -1); + if (loop==1) + draw_string( style|BG_ONLY_MASK, text_area.x, Y, text_area.x+text_area.w, lineStr, lineLen ); + } + if (mode==FIND_INDEX) { + IS_UTF8_ALIGNED2(buffer(), lineStartPos) + return lineStartPos; + } + return 0; } - if (mode==FIND_INDEX) { - IS_UTF8_ALIGNED2(buffer(), lineStartPos) - return lineStartPos; - } - return 0; - } - char currChar = 0, prevChar = 0; - styleX = startX; startStyle = startIndex; - // draw the line - style = position_style(lineStartPos, lineLen, 0); - for (i=0; itab_distance()); - double xAbs = (mode==GET_WIDTH) ? startX : startX+mHorizOffset-text_area.x; - w = ((int(xAbs/tab)+1)*tab) - xAbs; - styleX = startX+w; startStyle = i; - if (mode==DRAW_LINE) - draw_string( style|BG_ONLY_MASK, int(startX), Y, int(startX+w), 0, 0 ); - if (mode==FIND_INDEX && startX+w>rightClip) { - // find x pos inside block - free(lineStr); - if (cursor_pos && (startX+w/2tab_distance()); + double xAbs = (mode==GET_WIDTH) ? startX : startX+mHorizOffset-text_area.x; + w = ((int(xAbs/tab)+1)*tab) - xAbs; + styleX = startX+w; startStyle = i; + if (mode==DRAW_LINE && loop==1) + draw_string( style|BG_ONLY_MASK, int(startX), Y, int(startX+w), 0, 0 ); + if (mode==FIND_INDEX && startX+w>rightClip) { + // find x pos inside block + free(lineStr); + if (cursor_pos && (startX+w/2rightClip) { + // find x pos inside block + int di; + if (startIndex!=startStyle) { + di = find_x(lineStr+startStyle, i-startStyle, style, -int(rightClip-styleX)); // STR #2788 + di = lineStartPos + startStyle + di; + } else { + di = find_x(lineStr+startIndex, i-startIndex, style, -int(rightClip-startX)); // STR #2788 + di = lineStartPos + startIndex + di; + } + free(lineStr); + IS_UTF8_ALIGNED2(buffer(), (lineStartPos+startIndex+di)) + return di; + } + if ( (style&0xff)!=(charStyle&0xff)) { + startStyle = i; + styleX = startX+w; } } - if (mode==FIND_INDEX && startX+w>rightClip) { - // find x pos inside block - int di; - if (startIndex!=startStyle) { - di = find_x(lineStr+startStyle, i-startStyle, style, -int(rightClip-styleX)); // STR #2788 - di = lineStartPos + startStyle + di; - } else { - di = find_x(lineStr+startIndex, i-startIndex, style, -int(rightClip-startX)); // STR #2788 - di = lineStartPos + startIndex + di; - } - free(lineStr); - IS_UTF8_ALIGNED2(buffer(), (lineStartPos+startIndex+di)) - return di; - } - if ( (style&0xff)!=(charStyle&0xff)) { - startStyle = i; - styleX = startX+w; + style = charStyle; + startX += w; + startIndex = i; + } + i += len; + prevChar = currChar; + } + double w = 0; + if (currChar=='\t') { + // draw a single Tab space + double tab = col_to_x(mBuffer->tab_distance()); + double xAbs = (mode==GET_WIDTH) ? startX : startX+mHorizOffset-text_area.x; + w = ((int(xAbs/tab)+1)*tab) - xAbs; + if (mode==DRAW_LINE && loop==1) + draw_string( style|BG_ONLY_MASK, int(startX), Y, int(startX+w), 0, 0 ); + if (mode==FIND_INDEX) { + // find x pos inside block + free(lineStr); + if (cursor_pos) // STR #2788 + return lineStartPos + startIndex + ( rightClip-startX>w/2 ? 1 : 0 ); // STR #2788 + return lineStartPos + startIndex + ( rightClip-startX>w ? 1 : 0 ); + } + } else { + w = string_width( lineStr+startIndex, i-startIndex, style ); + if (mode==DRAW_LINE) { + // STR 2531 + if (startIndex!=startStyle) { + fl_push_clip(int(startX), Y, int(w)+1, mMaxsize); + draw_string( style|mask, int(styleX), Y, int(startX+w), lineStr+startStyle, i-startStyle ); + fl_pop_clip(); + } else { + draw_string( style|mask, int(startX), Y, int(startX+w), lineStr+startIndex, i-startIndex ); } } - style = charStyle; - startX += w; - startIndex = i; - } - i += len; - prevChar = currChar; - } - double w = 0; - if (currChar=='\t') { - // draw a single Tab space - double tab = col_to_x(mBuffer->tab_distance()); - double xAbs = (mode==GET_WIDTH) ? startX : startX+mHorizOffset-text_area.x; - w = ((int(xAbs/tab)+1)*tab) - xAbs; - if (mode==DRAW_LINE) - draw_string( style|BG_ONLY_MASK, int(startX), Y, int(startX+w), 0, 0 ); - if (mode==FIND_INDEX) { - // find x pos inside block - free(lineStr); - if (cursor_pos) // STR #2788 - return lineStartPos + startIndex + ( rightClip-startX>w/2 ? 1 : 0 ); // STR #2788 - return lineStartPos + startIndex + ( rightClip-startX>w ? 1 : 0 ); - } - } else { - w = string_width( lineStr+startIndex, i-startIndex, style ); - if (mode==DRAW_LINE) { - // STR 2531 - if (startIndex!=startStyle) { - fl_push_clip(int(startX), Y, int(w)+1, mMaxsize); - draw_string( style, int(styleX), Y, int(startX+w), lineStr+startStyle, i-startStyle ); - fl_pop_clip(); - } else { - draw_string( style, int(startX), Y, int(startX+w), lineStr+startIndex, i-startIndex ); + if (mode==FIND_INDEX) { + // find x pos inside block + int di; + if (startIndex!=startStyle) { + di = find_x(lineStr+startStyle, i-startStyle, style, -int(rightClip-styleX)); // STR #2788 + di = lineStartPos + startStyle + di; + } else { + di = find_x(lineStr+startIndex, i-startIndex, style, -int(rightClip-startX)); // STR #2788 + di = lineStartPos + startIndex + di; + } + free(lineStr); + IS_UTF8_ALIGNED2(buffer(), (lineStartPos+startIndex+di)) + return di; } } - if (mode==FIND_INDEX) { - // find x pos inside block - int di; - if (startIndex!=startStyle) { - di = find_x(lineStr+startStyle, i-startStyle, style, -int(rightClip-styleX)); // STR #2788 - di = lineStartPos + startStyle + di; - } else { - di = find_x(lineStr+startIndex, i-startIndex, style, -int(rightClip-startX)); // STR #2788 - di = lineStartPos + startIndex + di; - } + if (mode==GET_WIDTH) { free(lineStr); - IS_UTF8_ALIGNED2(buffer(), (lineStartPos+startIndex+di)) - return di; + return int(startX+w); } - } - if (mode==GET_WIDTH) { - free(lineStr); - return int(startX+w); - } - // clear the rest of the line - startX += w; - style = position_style(lineStartPos, lineLen, i); - if (mode==DRAW_LINE) - draw_string( style|BG_ONLY_MASK, int(startX), Y, text_area.x+text_area.w, lineStr, lineLen ); + // clear the rest of the line + startX += w; + style = position_style(lineStartPos, lineLen, i); + if (mode==DRAW_LINE && loop==1) + draw_string( style|BG_ONLY_MASK, int(startX), Y, text_area.x+text_area.w, lineStr, lineLen ); + } free(lineStr); IS_UTF8_ALIGNED2(buffer(), (lineStartPos+lineLen)) @@ -2267,7 +2274,7 @@ void Fl_Text_Display::draw_string(int style, const char *string, int nChars) const { IS_UTF8_ALIGNED(string) - const Style_Table_Entry * styleRec; + const Style_Table_Entry *styleRec = NULL; /* Draw blank area rather than text, if that was the request */ if ( style & FILL_MASK ) { @@ -2284,6 +2291,7 @@ void Fl_Text_Display::draw_string(int style, int fsize = textsize(); Fl_Color foreground; Fl_Color background; + Fl_Color bgbasecolor; if ( style & STYLE_LOOKUP_MASK ) { int si = (style & STYLE_LOOKUP_MASK) - 'A'; @@ -2293,19 +2301,27 @@ void Fl_Text_Display::draw_string(int style, styleRec = mStyleTable + si; font = styleRec->font; fsize = styleRec->size; + bgbasecolor = (styleRec->attr&ATTR_BGCOLOR) ? styleRec->bgcolor : color(); if (style & PRIMARY_MASK) { if (Fl::focus() == (Fl_Widget*)this) { - if (Fl::screen_driver()->has_marked_text() && Fl::compose_state) - background = color();// Mac OS: underline marked text - else + if (Fl::screen_driver()->has_marked_text() && Fl::compose_state) { + background = bgbasecolor; // Mac OS: underline marked text + } else { background = selection_color(); } - else background = fl_color_average(color(), selection_color(), 0.4f); + } else { + background = fl_color_average(bgbasecolor, selection_color(), 0.4f); + } } else if (style & HIGHLIGHT_MASK) { - if (Fl::focus() == (Fl_Widget*)this) background = fl_color_average(color(), selection_color(), 0.5f); - else background = fl_color_average(color(), selection_color(), 0.6f); - } else background = color(); + if (Fl::focus() == (Fl_Widget*)this) { + background = fl_color_average(bgbasecolor, selection_color(), 0.5f); + } else { + background = fl_color_average(bgbasecolor, selection_color(), 0.6f); + } + } else { + background = bgbasecolor; + } foreground = (style & PRIMARY_MASK) ? fl_contrast(styleRec->color, background) : styleRec->color; } else if (style & PRIMARY_MASK) { if (Fl::focus() == (Fl_Widget*)this) background = selection_color(); @@ -2332,11 +2348,44 @@ void Fl_Text_Display::draw_string(int style, if (!(style & BG_ONLY_MASK)) { fl_color( foreground ); fl_font( font, fsize ); + int baseline = Y + mMaxsize - fl_descent(); // Make sure antialiased ÄÖÜ do not leak on line above: // on X11+Xft the antialiased part of characters such as ÄÖÜ leak on the bottom pixel of the line above static int can_leak = Fl::screen_driver()->text_display_can_leak(); - if (can_leak) fl_push_clip(X, Y, toX - X, mMaxsize); - fl_draw( string, nChars, X, Y + mMaxsize - fl_descent()); + // Clip top and bottom only. Add margin to avoid clipping horizontally + if (can_leak) fl_push_clip(x(), Y, w(), mMaxsize); + fl_draw( string, nChars, X, baseline); + if (styleRec) { + if (styleRec->attr & ATTR_LINES_MASK) { + int pitch = fsize/7; + int prevAA = fl_antialias(); + fl_antialias(1); + switch (styleRec->attr & ATTR_LINES_MASK) { + case ATTR_UNDERLINE: + fl_color(foreground); + fl_line_style(FL_SOLID, pitch); + goto DRAW_UNDERLINE; + break; + case ATTR_GRAMMAR: + fl_color(FL_BLUE); + goto DRAW_DOTTED_UNDERLINE; + case ATTR_SPELLING: + fl_color(FL_RED); + DRAW_DOTTED_UNDERLINE: + fl_line_style(FL_DOT, pitch); + DRAW_UNDERLINE: + fl_xyline(X, baseline + fl_descent()/2, toX); + break; + case ATTR_STRIKE_THROUGH: + fl_color(foreground); + fl_line_style(FL_SOLID, pitch); + fl_xyline(X, baseline - (fl_height()-fl_descent())/3, toX); + break; + } + fl_line_style(FL_SOLID, 1); + fl_antialias(prevAA); + } + } if (Fl::screen_driver()->has_marked_text() && Fl::compose_state && (style & PRIMARY_MASK)) { fl_color( fl_color_average(foreground, background, 0.6f) ); fl_line(X, Y + mMaxsize - 1, X + (int)fl_width(string, nChars), Y + mMaxsize - 1); @@ -2379,21 +2428,31 @@ void Fl_Text_Display::clear_rect(int style, if ( width == 0 ) return; + Fl_Color bgbasecolor = color(); + if ( style & STYLE_LOOKUP_MASK ) { + int si = (style & STYLE_LOOKUP_MASK) - 'A'; + if (si < 0) si = 0; + else if (si >= mNStyles) si = mNStyles - 1; + const Style_Table_Entry *styleRec = mStyleTable + si; + if (styleRec->attr&ATTR_BGCOLOR_EXT_) + bgbasecolor = styleRec->bgcolor; + } + Fl_Color c; if (style & PRIMARY_MASK) { if (Fl::focus()==(Fl_Widget*)this) { c = selection_color(); } else { - c = fl_color_average(color(), selection_color(), 0.4f); + c = fl_color_average(bgbasecolor, selection_color(), 0.4f); } } else if (style & HIGHLIGHT_MASK) { if (Fl::focus()==(Fl_Widget*)this) { - c = fl_color_average(color(), selection_color(), 0.5f); + c = fl_color_average(bgbasecolor, selection_color(), 0.5f); } else { - c = fl_color_average(color(), selection_color(), 0.6f); + c = fl_color_average(bgbasecolor, selection_color(), 0.6f); } } else { - c = color(); + c = bgbasecolor; } fl_color(active_r() ? c : fl_inactive(c)); fl_rectf( X, Y, width, height ); @@ -2502,6 +2561,10 @@ void Fl_Text_Display::draw_cursor( int X, int Y ) { Note that style is a somewhat incorrect name, drawing method would be more appropriate. + If lineIndex is pointing to the last character in a line, and the second + to last character has the ATTR_BGCOLOR_EXT set, the background color will + extend into the remaining line. + \param lineStartPos beginning of this line \param lineLen number of bytes in line \param lineIndex position of character within line @@ -2520,9 +2583,21 @@ int Fl_Text_Display::position_style( int lineStartPos, int lineLen, int lineInde pos = lineStartPos + min( lineIndex, lineLen ); - if ( lineIndex >= lineLen ) + if ( styleBuf && lineIndex==lineLen && lineLen>0) { + style = ( unsigned char ) styleBuf->byte_at( pos-1 ); + if (style == mUnfinishedStyle && mUnfinishedHighlightCB) { + (mUnfinishedHighlightCB)( pos, mHighlightCBArg); + style = (unsigned char) styleBuf->byte_at( pos); + } + int si = (style & STYLE_LOOKUP_MASK) - 'A'; + if (si < 0) si = 0; + else if (si >= mNStyles) si = mNStyles - 1; + const Style_Table_Entry *styleRec = mStyleTable + si; + if ((styleRec->attr&ATTR_BGCOLOR_EXT_)==0) + style = FILL_MASK; + } else if ( lineIndex >= lineLen ) { style = FILL_MASK; - else if ( styleBuf != NULL ) { + } else if ( styleBuf != NULL ) { style = ( unsigned char ) styleBuf->byte_at( pos ); if (style == mUnfinishedStyle && mUnfinishedHighlightCB) { /* encountered "unfinished" style, trigger parsing */ diff --git a/test/editor.cxx b/test/editor.cxx index eb45de7aa..478bd3ebe 100644 --- a/test/editor.cxx +++ b/test/editor.cxx @@ -57,6 +57,15 @@ const int line_num_width = 75; Fl_Text_Buffer *stylebuf = 0; Fl_Text_Display::Style_Table_Entry styletable[] = { // Style table +#ifdef TESTING_ATTRIBUTES + { FL_BLACK, FL_COURIER, TS }, // A - Plain + { FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS, Fl_Text_Display::ATTR_BGCOLOR, FL_LIGHT2 }, // B - Line comments + { FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS, Fl_Text_Display::ATTR_BGCOLOR_EXT, FL_LIGHT2 }, // C - Block comments + { FL_BLUE, FL_COURIER, TS, Fl_Text_Display::ATTR_UNDERLINE }, // D - Strings + { FL_DARK_RED, FL_COURIER, TS, Fl_Text_Display::ATTR_GRAMMAR }, // E - Directives + { FL_DARK_RED, FL_COURIER_BOLD, TS, Fl_Text_Display::ATTR_STRIKE_THROUGH }, // F - Types + { FL_BLUE, FL_COURIER_BOLD, TS, Fl_Text_Display::ATTR_SPELLING }, // G - Keywords +#else { FL_BLACK, FL_COURIER, TS }, // A - Plain { FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS }, // B - Line comments { FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS }, // C - Block comments @@ -64,6 +73,7 @@ Fl_Text_Display::Style_Table_Entry { FL_DARK_RED, FL_COURIER, TS }, // E - Directives { FL_DARK_RED, FL_COURIER_BOLD, TS }, // F - Types { FL_BLUE, FL_COURIER_BOLD, TS }, // G - Keywords +#endif }; const char *code_keywords[] = { // List of known C/C++ keywords... "and", @@ -188,7 +198,6 @@ style_parse(const char *text, } else if (strncmp(text, "//", 2) == 0) { current = 'B'; for (; length > 0 && *text != '\n'; length --, text ++) *style++ = 'B'; - if (length == 0) break; } else if (strncmp(text, "/*", 2) == 0) { current = 'C';