diff --git a/src/dlangui/core/editable.d b/src/dlangui/core/editable.d index accfc9eb..8b56c789 100644 --- a/src/dlangui/core/editable.d +++ b/src/dlangui/core/editable.d @@ -868,6 +868,40 @@ class EditableContent { return (pos + tabSize) / tabSize * tabSize; } + /// to return information about line space positions + static struct LineWhiteSpace { + int firstNonSpaceIndex = -1; + int firstNonSpaceColumn = -1; + int lastNonSpaceIndex = -1; + int lastNonSpaceColumn = -1; + @property bool empty() { return firstNonSpaceColumn < 0; } + } + + LineWhiteSpace getLineWhiteSpace(int lineIndex) { + LineWhiteSpace res; + if (lineIndex < 0 || lineIndex >= _lines.length) + return res; + dstring s = _lines[lineIndex]; + int x = 0; + for (int i = 0; i < s.length; i++) { + dchar ch = s[i]; + if (ch == '\t') { + x = (x + _tabSize) / _tabSize * _tabSize; + } else if (ch == ' ') { + x++; + } else { + if (res.firstNonSpaceIndex < 0) { + res.firstNonSpaceIndex = i; + res.firstNonSpaceColumn = x; + } + res.lastNonSpaceIndex = i; + res.lastNonSpaceColumn = x; + x++; + } + } + return res; + } + /// returns spaces/tabs for filling from the beginning of line to specified position dstring fillSpace(int pos) { dchar[] buf; diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d index 06a6eb9e..7efc721a 100644 --- a/src/dlangui/graphics/drawbuf.d +++ b/src/dlangui/graphics/drawbuf.d @@ -34,6 +34,11 @@ struct NinePatch { Rect padding; } +enum PatternType : int { + solid, + dotted, +} + static if (ENABLE_OPENGL) { /// non thread safe private __gshared uint drawBufIdGenerator = 0; @@ -278,6 +283,11 @@ class DrawBuf : RefCountedObject { abstract void fill(uint color); /// fill rectangle with solid color (clipping is applied) abstract void fillRect(Rect rc, uint color); + /// fill rectangle with solid color and pattern (clipping is applied) 0=solid fill, 1 = dotted + void fillRectPattern(Rect rc, uint color, int pattern) { + // default implementation: does not support patterns + fillRect(rc, color); + } /// draw pixel at (x, y) with specified color abstract void drawPixel(int x, int y, uint color); /// draw 8bit alpha image - usually font glyph using specified color (clipping is applied) @@ -701,10 +711,10 @@ class ColorDrawBufBase : DrawBuf { } override void fillRect(Rect rc, uint color) { + uint alpha = color >> 24; if (applyClipping(rc)) { foreach(y; rc.top .. rc.bottom) { uint * row = scanLine(y); - uint alpha = color >> 24; if (!alpha) { row[rc.left .. rc.right] = color; } else if (alpha < 254) { @@ -717,6 +727,34 @@ class ColorDrawBufBase : DrawBuf { } } + /// fill rectangle with solid color and pattern (clipping is applied) 0=solid fill, 1 = dotted + override void fillRectPattern(Rect rc, uint color, int pattern) { + uint alpha = color >> 24; + if (alpha == 255) // fully transparent + return; + if (applyClipping(rc)) { + foreach(y; rc.top .. rc.bottom) { + uint * row = scanLine(y); + if (!alpha) { + if (pattern == 1) { + foreach(x; rc.left .. rc.right) { + if ((x ^ y) & 1) + row[x] = color; + } + } else { + row[rc.left .. rc.right] = color; + } + } else if (alpha < 254) { + foreach(x; rc.left .. rc.right) { + // apply blending + if (pattern != 1 || ((x ^ y) & 1) != 0) + row[x] = blendARGB(row[x], color, alpha); + } + } + } + } + } + /// draw pixel at (x, y) with specified color override void drawPixel(int x, int y, uint color) { if (!_clipRect.isPointInside(x, y)) diff --git a/src/dlangui/graphics/gldrawbuf.d b/src/dlangui/graphics/gldrawbuf.d index 71df709a..f55c545a 100644 --- a/src/dlangui/graphics/gldrawbuf.d +++ b/src/dlangui/graphics/gldrawbuf.d @@ -112,6 +112,19 @@ class GLDrawBuf : DrawBuf, GLConfigCallback { if (!isFullyTransparentColor(color) && applyClipping(rc)) _scene.add(new SolidRectSceneItem(rc, color)); } + + /// fill rectangle with solid color and pattern (clipping is applied) 0=solid fill, 1 = dotted + override void fillRectPattern(Rect rc, uint color, int pattern) { + if (pattern == PatternType.solid) + fillRect(rc, color); + else { + assert(_scene !is null); + color = applyAlpha(color); + if (!isFullyTransparentColor(color) && applyClipping(rc)) + _scene.add(new PatternRectSceneItem(rc, color, pattern)); + } + } + /// draw pixel at (x, y) with specified color override void drawPixel(int x, int y, uint color) { assert(_scene !is null); @@ -718,6 +731,30 @@ public: } } +private class PatternRectSceneItem : SceneItem { +private: + Rect _rc; + uint _color; + int _pattern; + +public: + this(Rect rc, uint color, int pattern) { + _rc = rc; + _color = color; + _pattern = pattern; + } + override void draw() { + // TODO: support patterns + // TODO: optimize + for (int y = _rc.top; y < _rc.bottom; y++) { + for (int x = _rc.left; x < _rc.right; x++) + if ((x ^ y) & 1) { + glSupport.drawSolidFillRect(Rect(x, y, x + 1, y + 1), _color, _color, _color, _color); + } + } + } +} + private class TextureSceneItem : SceneItem { private: uint objectId; diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 94e9d60d..1b9bc3f3 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -252,9 +252,9 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction protected uint _selectionColorNormal = 0xD060A0FF; protected uint _leftPaneBackgroundColor = 0xF4F4F4; protected uint _leftPaneBackgroundColor2 = 0xFFFFFF; - protected uint _leftPaneBackgroundColor3 = 0xE0E0E0; + protected uint _leftPaneBackgroundColor3 = 0xF8F8F8; protected uint _leftPaneLineNumberColor = 0x4060D0; - protected uint _leftPaneLineNumberBackgroundColor = 0xF0F0F0; + protected uint _leftPaneLineNumberBackgroundColor = 0xF4F4F4; protected uint _colorIconBreakpoint = 0xFF0000; protected uint _colorIconBookmark = 0x0000FF; protected uint _colorIconError = 0x80FF0000; @@ -278,7 +278,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction @property bool showTabPositionMarks() { return _showTabPositionMarks; } @property EditWidgetBase showTabPositionMarks(bool flg) { if (flg != _showTabPositionMarks) { - _showTabPositionMarks = flg; + _showTabPositionMarks = flg; invalidate(); } return this; @@ -2680,6 +2680,47 @@ class EditBox : EditWidgetBase { } } + /// find max tab mark column position for line + protected int findMaxTabMarkColumn(int lineIndex) { + if (lineIndex < 0 || lineIndex >= content.length) + return -1; + int maxSpace = -1; + auto space = content.getLineWhiteSpace(lineIndex); + maxSpace = space.firstNonSpaceColumn; + if (maxSpace >= 0) + return maxSpace; + for(int i = lineIndex - 1; i >= 0; i--) { + space = content.getLineWhiteSpace(i); + if (!space.empty) { + maxSpace = space.firstNonSpaceColumn; + break; + } + } + for(int i = lineIndex + 1; i < content.length; i++) { + space = content.getLineWhiteSpace(i); + if (!space.empty) { + if (maxSpace < 0 || maxSpace < space.firstNonSpaceColumn) + maxSpace = space.firstNonSpaceColumn; + break; + } + } + return maxSpace; + } + + void drawTabPositionMarks(DrawBuf buf, ref FontRef font, int lineIndex, Rect lineRect) { + int maxCol = findMaxTabMarkColumn(lineIndex); + if (maxCol > 0) { + int spaceWidth = font.charWidth(' '); + Rect rc = lineRect; + uint color = addAlpha(textColor, 0xC0); + for (int i = 0; i < maxCol; i += tabSize) { + rc.left = lineRect.left + i * spaceWidth; + rc.right = rc.left + 1; + buf.fillRectPattern(rc, color, PatternType.dotted); + } + } + } + void drawWhiteSpaceMarks(DrawBuf buf, ref FontRef font, dstring txt, int tabSize, Rect lineRect, Rect visibleRect) { // _showTabPositionMarks // _showWhiteSpaceMarks @@ -2715,42 +2756,30 @@ class EditBox : EditWidgetBase { rc.right = lineRect.left + textSizeBuffer[i]; int h = rc.height; if (rc.intersects(visibleRect)) { - if (_showTabPositionMarks && i < firstNonSpace) { - if (spaceIndex % ts == 0) { - // draw mark - buf.fillRect(Rect(rc.left, rc.top, rc.left + 1, rc.bottom), color); - } - if (ch == ' ') - spaceIndex++; - else if (ch == '\t') - spaceIndex = (spaceIndex + ts) / ts * ts; - } - if (_showWhiteSpaceMarks) { - // draw space mark - if (ch == ' ') { - // space - int sz = h / 6; - if (sz < 1) - sz = 1; - rc.top += h / 2 - sz / 2; - rc.bottom = rc.top + sz; - rc.left += rc.width / 2 - sz / 2; - rc.right = rc.left + sz; - buf.fillRect(rc, color); - } else if (ch == '\t') { - // tab - Point p1 = Point(rc.left + 1, rc.top + h / 2); - Point p2 = p1; - p2.x = rc.right - 1; - int sz = h / 4; - if (sz < 2) - sz = 2; - if (sz > p2.x - p1.x) - sz = p2.x - p1.x; - buf.drawLine(p1, p2, color); - buf.drawLine(p2, Point(p2.x - sz, p2.y - sz), color); - buf.drawLine(p2, Point(p2.x - sz, p2.y + sz), color); - } + // draw space mark + if (ch == ' ') { + // space + int sz = h / 6; + if (sz < 1) + sz = 1; + rc.top += h / 2 - sz / 2; + rc.bottom = rc.top + sz; + rc.left += rc.width / 2 - sz / 2; + rc.right = rc.left + sz; + buf.fillRect(rc, color); + } else if (ch == '\t') { + // tab + Point p1 = Point(rc.left + 1, rc.top + h / 2); + Point p2 = p1; + p2.x = rc.right - 1; + int sz = h / 4; + if (sz < 2) + sz = 2; + if (sz > p2.x - p1.x) + sz = p2.x - p1.x; + buf.drawLine(p1, p2, color); + buf.drawLine(p2, Point(p2.x - sz, p2.y - sz), color); + buf.drawLine(p2, Point(p2.x - sz, p2.y + sz), color); } } } @@ -2778,9 +2807,11 @@ class EditBox : EditWidgetBase { visibleRect.left = _clientRect.left; visibleRect.right = _clientRect.right; drawLineBackground(buf, _firstVisibleLine + i, lineRect, visibleRect); + if (_showTabPositionMarks) + drawTabPositionMarks(buf, font, _firstVisibleLine + i, lineRect); if (!txt.length) continue; - if (_showWhiteSpaceMarks || _showTabPositionMarks) + if (_showWhiteSpaceMarks) drawWhiteSpaceMarks(buf, font, txt, tabSize, lineRect, visibleRect); if (_leftPaneWidth > 0) { Rect leftPaneRect = visibleRect; diff --git a/views/res/theme_default.xml b/views/res/theme_default.xml index c266e32d..44b2b700 100644 --- a/views/res/theme_default.xml +++ b/views/res/theme_default.xml @@ -14,9 +14,9 @@ - + - +