From e56d3c4df37f0644754a27ba40d2646a59cd9984 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 08:07:56 -0500
Subject: [PATCH 01/24] Version 2 starting implementation

---
 src/dlangui/core/editable.d   |  9 ++++
 src/dlangui/widgets/editors.d | 83 ++++++++++++++++++++++++++++++++---
 2 files changed, 85 insertions(+), 7 deletions(-)

diff --git a/src/dlangui/core/editable.d b/src/dlangui/core/editable.d
index 3973d65c..3a781b36 100644
--- a/src/dlangui/core/editable.d
+++ b/src/dlangui/core/editable.d
@@ -450,6 +450,15 @@ struct LineSpan {
     int start;
     /// number of lines it spans
     int len;
+    /// the wrapping points
+    WrapPoint[] wrapPoints;
+    
+    dstring[] wrappedContent;
+}
+
+struct WrapPoint {
+    int wrapPos;
+    int wrapWidth;
 }
 
 /// interface for custom syntax highlight, comments toggling, smart indents, and other language dependent features for source code editors
diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 6460dc7e..2693b351 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -391,12 +391,28 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return this;
     }
 
-    void wrapLine(dstring line, int maxWidth) {
-
+    dstring[] wrapLine(dstring line, int maxWidth) {
+        return [];
     }
 
     /// information about line span into several lines - in word wrap mode
     protected LineSpan[] _span;
+    
+    int wrapsUpTo(int line)
+    {
+        if(line < _span.length)
+        {
+            
+            int sum;
+            for(int i = 0; i<line; i++)
+            {
+                sum += _span[i].len - 1;
+            }
+            //Log.d(sum);
+            return sum;
+        }
+        return 0;
+    }
 
     /// override to add custom items on left panel
     protected void updateLeftPaneWidth() {
@@ -3321,6 +3337,15 @@ class EditBox : EditWidgetBase {
         }
     }
 
+    void resetVisibleSpans()
+    {
+        //TODO: Don't erase spans which have not been modified
+        _span = [];
+    }
+    
+    private bool needRewrap;
+    private int lastStartingLine;
+    
     override protected void drawClient(DrawBuf buf) {
         // update matched braces
         if (!content.findMatchedBraces(_caretPos, _matchingBraces)) {
@@ -3329,6 +3354,23 @@ class EditBox : EditWidgetBase {
         }
 
         Rect rc = _clientRect;
+        
+        if (_contentChanged)
+          needRewrap = true;
+          
+        if (rc.width <= 0 && _wordWrap)
+        {
+            return;
+        }
+        
+        bool doRewrap = false;
+        
+        if (needRewrap && _wordWrap)
+        {
+            resetVisibleSpans();
+            needRewrap = false;
+            doRewrap = true;
+        }
 
         FontRef font = font();
         for (int i = 0; i < _visibleLines.length; i++) {
@@ -3344,7 +3386,7 @@ class EditBox : EditWidgetBase {
             drawLineBackground(buf, _firstVisibleLine + i, lineRect, visibleRect);
             if (_showTabPositionMarks)
                 drawTabPositionMarks(buf, font, _firstVisibleLine + i, lineRect);
-            if (!txt.length)
+            if (!txt.length && !_wordWrap)
                 continue;
             if (_showWhiteSpaceMarks)
                 drawWhiteSpaceMarks(buf, font, txt, tabSize, lineRect, visibleRect);
@@ -3354,12 +3396,39 @@ class EditBox : EditWidgetBase {
                 leftPaneRect.left -= _leftPaneWidth;
                 drawLeftPane(buf, leftPaneRect, 0);
             }
-            if (txt.length > 0) {
+            if (txt.length > 0 || _wordWrap) {
                 CustomCharProps[] highlight = _visibleLinesHighlights[i];
-                if (highlight)
-                    font.drawColoredText(buf, rc.left - _scrollPos.x, rc.top + i * _lineHeight, txt, highlight, tabSize);
+                if (_wordWrap)
+                {
+                    dstring[] wrappedLine;
+                    if (doRewrap)
+                        wrappedLine = wrapLine(txt, _firstVisibleLine + i);
+                    else
+                        if (i < _span.length)
+                            wrappedLine = _span[i].wrappedContent;
+                    int accumulativeLength;
+                    CustomCharProps[] wrapProps;
+                    foreach (int q, curWrap; wrappedLine)
+                    {
+                        auto lineOffset = q + i + wrapsUpTo(i);
+                        if (highlight)
+                        {
+                            wrapProps = highlight[accumulativeLength .. $];
+                            accumulativeLength += curWrap.length;
+                            font.drawColoredText(buf, rc.left - _scrollPos.x, rc.top + lineOffset * _lineHeight, curWrap, wrapProps, tabSize);
+                        }
+                        else
+                            font.drawText(buf, rc.left - _scrollPos.x, rc.top + lineOffset * _lineHeight, curWrap, textColor, tabSize);
+
+                    }
+                }
                 else
-                    font.drawText(buf, rc.left - _scrollPos.x, rc.top + i * _lineHeight, txt, textColor, tabSize);
+                {
+                    if (highlight)
+                        font.drawColoredText(buf, rc.left - _scrollPos.x, rc.top + i * _lineHeight, txt, highlight, tabSize);
+                    else
+                        font.drawText(buf, rc.left - _scrollPos.x, rc.top + i * _lineHeight, txt, textColor, tabSize);
+                }
             }
         }
 

From 05a8dd7bdcd8154293dadf8c400a9587d877709e Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 08:20:01 -0500
Subject: [PATCH 02/24] Using WrapPoint[]

---
 src/dlangui/widgets/editors.d | 112 +++++++++++++++++++++++++++++++++-
 1 file changed, 110 insertions(+), 2 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 2693b351..f9e658fd 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -391,13 +391,121 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return this;
     }
 
-    dstring[] wrapLine(dstring line, int maxWidth) {
-        return [];
+    dchar[] splitChars = [' ', '-', '\t'];
+    
+    dstring[] wrapLine(dstring str, int lineNumber) {
+        FontRef font = font();
+        dstring[] words = explode(str, splitChars);
+        int curLineLength = 0;
+        dchar[] buildingStr;
+        dstring[] buildingStrArr;
+        WrapPoint[] wrapPoints;
+        int wrappedLineCount = 0;
+        int curLineWidth = 0;
+        int maxWidth = _clientRect.width;
+        for (int i = 0; i < words.length; i++)
+        {
+            dstring word = words[i];
+            if (curLineWidth + measureWrappedText(word) > maxWidth)
+            {
+                if (curLineWidth > 0)
+                {
+                    buildingStrArr ~= to!dstring(buildingStr);
+                    wrappedLineCount++;
+                    wrapPoints ~= WrapPoint(curLineLength, curLineWidth);
+                    curLineLength = 0;
+                    curLineWidth = 0;
+                    buildingStr = [];
+                }
+                while (measureWrappedText(word) > maxWidth)
+                {
+                    //For when string still too long
+                    int wrapPoint = findWrapPoint(word);
+                    wrapPoints ~= WrapPoint(wrapPoint, measureWrappedText(word[0..wrapPoint]));
+                    buildingStr ~= word[0 .. wrapPoint];
+                    word = word[wrapPoint .. $];
+                    buildingStrArr ~= to!dstring(buildingStr);
+                    buildingStr = [];
+                    wrappedLineCount++;
+                }
+            }
+            buildingStr ~= word;
+            curLineLength += to!int(word.length);
+            curLineWidth += measureWrappedText(word);
+        }
+        wrapPoints ~= WrapPoint(curLineLength, curLineWidth);
+        buildingStrArr ~= to!dstring(buildingStr);
+        _span ~= LineSpan(lineNumber, wrappedLineCount + 1, wrapPoints, buildingStrArr);
+        return buildingStrArr;
     }
 
+    dstring[] explode(dstring str, dchar[] splitChars)
+    {
+        dstring[] parts;
+        int startIndex = 0;
+        import std.string:indexOfAny;
+        while (true)
+        {
+            int index = to!int(str.indexOfAny(splitChars, startIndex));
+        
+            if (index == -1)
+            {
+                parts ~= str[startIndex .. $];
+                //Log.d("Explode output: ", parts);
+                return parts;
+            }
+        
+            dstring word = str[startIndex .. index];
+            dchar nextChar = (str[index .. index + 1])[0];
+        
+            import std.ascii:isWhite;
+            if (isWhite(nextChar))
+            {
+                parts ~= word;
+                parts ~= to!dstring(nextChar);
+            }
+            else
+            {
+                parts ~= word ~ nextChar;
+            }
+            startIndex = index + 1;
+        }
+    }
+    
     /// information about line span into several lines - in word wrap mode
     protected LineSpan[] _span;
     
+    //Finds good visual wrapping point for string
+    int findWrapPoint(dstring text)
+    {
+        int maxWidth = _clientRect.width;
+        int wrapPoint = 0;
+        while (true)
+        {
+            if (measureWrappedText(text[0 .. wrapPoint]) < maxWidth)
+            {
+                wrapPoint++;
+            }
+            else
+            {
+                return wrapPoint;
+            }
+        }
+     }
+    
+    //Calls measureText for word wrap
+    int measureWrappedText(dstring text)
+    {
+        FontRef font = font();
+        int[] measuredWidths;
+        measuredWidths.length = text.length;
+        //DO NOT REMOVE THIS
+        int boggle = font.measureText(text, measuredWidths);
+        if (measuredWidths.length > 0)
+            return measuredWidths[$-1];
+        return 0;
+    }
+    
     int wrapsUpTo(int line)
     {
         if(line < _span.length)

From 8df39f232b9146d0361c15c50c897383dbda7ee9 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 08:23:07 -0500
Subject: [PATCH 03/24] Set need rewrap true by default

---
 src/dlangui/widgets/editors.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index f9e658fd..e383c65c 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -3451,7 +3451,7 @@ class EditBox : EditWidgetBase {
         _span = [];
     }
     
-    private bool needRewrap;
+    private bool needRewrap = true;
     private int lastStartingLine;
     
     override protected void drawClient(DrawBuf buf) {

From 761a7bc821ff0e2477782ac7ae52084678a22e86 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 08:50:59 -0500
Subject: [PATCH 04/24] Sort of working

---
 src/dlangui/widgets/editors.d | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index e383c65c..afc84b2a 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -474,6 +474,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     
     /// information about line span into several lines - in word wrap mode
     protected LineSpan[] _span;
+    protected LineSpan[] _spanCache;
     
     //Finds good visual wrapping point for string
     int findWrapPoint(dstring text)
@@ -508,7 +509,14 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     
     int wrapsUpTo(int line)
     {
-        if(line < _span.length)
+        int sum;
+        lineSpanIterate(delegate(int wantedLine, int wantedWrap)
+        {
+            if (wantedLine < line)
+                sum += _span[wantedLine].len - 1;
+        });
+        return sum;
+        /*if(line < _span.length)
         {
             
             int sum;
@@ -518,8 +526,19 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
             }
             //Log.d(sum);
             return sum;
+        }*/
+        //return 0;
+    }
+    
+    void lineSpanIterate(void delegate(int wantedLine, int wantedWrap) iterator)
+    {
+        for (int i; i<_span.length; i++)
+        {
+            for (int q; q<_span[i].wrapPoints.length; q++)
+            {
+                iterator(i, q);
+            }
         }
-        return 0;
     }
 
     /// override to add custom items on left panel

From 64dab5989e157c7ff9254dc504785a265bb7830e Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 08:55:00 -0500
Subject: [PATCH 05/24] Working now

---
 src/dlangui/widgets/editors.d | 29 ++++++-----------------------
 1 file changed, 6 insertions(+), 23 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index afc84b2a..6dc5fb7e 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -510,35 +510,18 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     int wrapsUpTo(int line)
     {
         int sum;
-        lineSpanIterate(delegate(int wantedLine, int wantedWrap)
+        lineSpanIterate(delegate(LineSpan curSpan)
         {
-            if (wantedLine < line)
-                sum += _span[wantedLine].len - 1;
+            if (curSpan.start < line)
+                sum += curSpan.len - 1;
         });
         return sum;
-        /*if(line < _span.length)
-        {
-            
-            int sum;
-            for(int i = 0; i<line; i++)
-            {
-                sum += _span[i].len - 1;
-            }
-            //Log.d(sum);
-            return sum;
-        }*/
-        //return 0;
     }
     
-    void lineSpanIterate(void delegate(int wantedLine, int wantedWrap) iterator)
+    void lineSpanIterate(void delegate(LineSpan curSpan) iterator)
     {
-        for (int i; i<_span.length; i++)
-        {
-            for (int q; q<_span[i].wrapPoints.length; q++)
-            {
-                iterator(i, q);
-            }
-        }
+        foreach (currentSpan; _span)
+            iterator(currentSpan);
     }
 
     /// override to add custom items on left panel

From ad6d547812bf688ea292ca555d4a26e4b3f66ef3 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 09:19:02 -0500
Subject: [PATCH 06/24] Even better, scrolling

---
 src/dlangui/widgets/editors.d | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 6dc5fb7e..fcc80671 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -3468,6 +3468,12 @@ class EditBox : EditWidgetBase {
         if (_contentChanged)
           needRewrap = true;
           
+        if (lastStartingLine != _firstVisibleLine)
+        {
+            needRewrap = true;
+            lastStartingLine = _firstVisibleLine;
+        }
+          
         if (rc.width <= 0 && _wordWrap)
         {
             return;
@@ -3520,7 +3526,7 @@ class EditBox : EditWidgetBase {
                     CustomCharProps[] wrapProps;
                     foreach (int q, curWrap; wrappedLine)
                     {
-                        auto lineOffset = q + i + wrapsUpTo(i);
+                        auto lineOffset = q + i + wrapsUpTo(i + _firstVisibleLine);
                         if (highlight)
                         {
                             wrapProps = highlight[accumulativeLength .. $];

From 72187063bd3272e227728556c4a8e0aba340990f Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 09:38:35 -0500
Subject: [PATCH 07/24] findWrapLine and getSpan added into the mix

---
 src/dlangui/widgets/editors.d | 38 ++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index fcc80671..44cb04e3 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -518,6 +518,36 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return sum;
     }
     
+    LineSpan getSpan(int lineNumber)
+    {
+        LineSpan lineSpan = LineSpan(lineNumber, 0, [], []);
+        lineSpanIterate(delegate(LineSpan curSpan)
+        {
+            if (curSpan.start == lineNumber)
+                lineSpan = curSpan;
+        });
+        return lineSpan;
+    }
+    
+    int findWrapLine(TextPosition textPos)
+    {
+        int curLine = 0;
+        int curPosition = textPos.pos;
+        int i = 0;
+        while (true)
+        {
+            if (i == getSpan(textPos.line).wrapPoints.length - 1)
+                return curLine;
+            curPosition -= getSpan(textPos.line).wrapPoints[i].wrapPos;
+            if (curPosition < 0)
+            {   
+                return curLine;
+            }
+            curLine++;
+            i++;
+        }
+    }
+    
     void lineSpanIterate(void delegate(LineSpan curSpan) iterator)
     {
         foreach (currentSpan; _span)
@@ -1249,7 +1279,13 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
                 caretRc.right += _spaceWidth;
             }
         }
-        caretRc.offset(_clientRect.left, _clientRect.top);
+        if (_wordWrap)
+            {
+                _scrollPos.x = 0;
+                caretRc.offset(_clientRect.left, _clientRect.top);
+            }
+        else
+            caretRc.offset(_clientRect.left, _clientRect.top);
         return caretRc;
     }
 

From d39fadd1d07e073771960d72d6294cb40cc7bc1f Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 09:59:15 -0500
Subject: [PATCH 08/24] CaretRect in progress

---
 src/dlangui/core/editable.d   | 16 +++++++++++++++-
 src/dlangui/widgets/editors.d | 16 +++++++++++++---
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/src/dlangui/core/editable.d b/src/dlangui/core/editable.d
index 3a781b36..b6f8e4de 100644
--- a/src/dlangui/core/editable.d
+++ b/src/dlangui/core/editable.d
@@ -452,8 +452,22 @@ struct LineSpan {
     int len;
     /// the wrapping points
     WrapPoint[] wrapPoints;
-    
+    /// the wrapped text
     dstring[] wrappedContent;
+    
+    int widthAccumulation(int wrapLine)
+    {
+        int widthTotal;
+        for (int i; i < wrapLine; i++)
+        {
+            if (i < this.wrapPoints.length - 1)
+            {
+                int curWidth = this.wrapPoints[i].wrapWidth;
+                widthTotal += curWidth;
+            }
+        }
+        return widthTotal;
+    }
 }
 
 struct WrapPoint {
diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 44cb04e3..79f73803 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -1264,6 +1264,8 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
             handleAction(ACTION_EDITOR_SELECT_ALL);
         super.handleFocusChange(focused);
     }
+    
+    protected int _firstVisibleLine;
 
     /// returns cursor rectangle
     protected Rect caretRect() {
@@ -1280,10 +1282,18 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
             }
         }
         if (_wordWrap)
+        {
+            _scrollPos.x = 0;
+            int wrapLine = findWrapLine(_caretPos);
+            int xOffset;
+            if (wrapLine > 0)
             {
-                _scrollPos.x = 0;
-                caretRc.offset(_clientRect.left, _clientRect.top);
+                LineSpan curSpan = getSpan(_caretPos.line);
+                xOffset = curSpan.widthAccumulation(wrapLine);
             }
+            auto yOffset = -1 * _lineHeight * (wrapsUpTo(_caretPos.line - _firstVisibleLine) + wrapLine);
+            caretRc.offset(_clientRect.left - xOffset, _clientRect.top - yOffset);
+        }
         else
             caretRc.offset(_clientRect.left, _clientRect.top);
         return caretRc;
@@ -2470,7 +2480,7 @@ class EditBox : EditWidgetBase {
         }
     }
 
-    protected int _firstVisibleLine;
+    //protected int _firstVisibleLine;
 
     protected int _maxLineWidth;
     protected int _numVisibleLines;             // number of lines visible in client area

From e58538de60b5d7fde1f2f1142ad08112f3788cdf Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 10:09:51 -0500
Subject: [PATCH 09/24] Improved caret offset, put placeholder linespan into
 place

---
 src/dlangui/widgets/editors.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 79f73803..1a5d68be 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -520,7 +520,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     
     LineSpan getSpan(int lineNumber)
     {
-        LineSpan lineSpan = LineSpan(lineNumber, 0, [], []);
+        LineSpan lineSpan = LineSpan(lineNumber, 0, [WrapPoint(0,0)], []);
         lineSpanIterate(delegate(LineSpan curSpan)
         {
             if (curSpan.start == lineNumber)
@@ -1291,7 +1291,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
                 LineSpan curSpan = getSpan(_caretPos.line);
                 xOffset = curSpan.widthAccumulation(wrapLine);
             }
-            auto yOffset = -1 * _lineHeight * (wrapsUpTo(_caretPos.line - _firstVisibleLine) + wrapLine);
+            auto yOffset = -1 * _lineHeight * (wrapsUpTo(_caretPos.line) + wrapLine);
             caretRc.offset(_clientRect.left - xOffset, _clientRect.top - yOffset);
         }
         else

From 7c500bd57a819404c34251d90cdf9592d0e3a025 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 11:26:21 -0500
Subject: [PATCH 10/24] drawExtendedArea

---
 src/dlangui/widgets/editors.d | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 1a5d68be..4a6a252c 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -3320,8 +3320,28 @@ class EditBox : EditWidgetBase {
             if (lineRect.top >= _clientRect.bottom)
                 break;
             drawLeftPane(buf, lineRect, i < lc ? i : -1);
-            i++;
             rc.top += _lineHeight;
+            if (_wordWrap)
+            {
+                int currentWrap = 1;
+                for (;;)
+                {
+                    LineSpan curSpan = getSpan(i);
+                    if (currentWrap > curSpan.len - 1)
+                        break;
+                    Rect lineRect2 = rc;
+                    lineRect2.left = _clientRect.left - _leftPaneWidth;
+                    lineRect2.right = _clientRect.left;
+                    lineRect2.bottom = lineRect.top + _lineHeight;
+                    if (lineRect2.top >= _clientRect.bottom)
+                        break;
+                    drawLeftPane(buf, lineRect2, -1);
+                    rc.top += _lineHeight;
+
+                    currentWrap++;
+                }
+            }
+            i++;
         }
     }
 

From ce39c13e316b3eacdeadea7a150c899d2d350930 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 11:36:38 -0500
Subject: [PATCH 11/24] Don't horizontal scroll in word wrap mode

---
 src/dlangui/widgets/editors.d | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 4a6a252c..6384671e 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -2771,7 +2771,8 @@ class EditBox : EditWidgetBase {
             invalidate();
         } else if (rc.left >= _clientRect.width - 10) {
             // scroll right
-            _scrollPos.x += (rc.left - _clientRect.width) + _clientRect.width / 4;
+            if (!_wordWrap)
+                _scrollPos.x += (rc.left - _clientRect.width) + _clientRect.width / 4;
             invalidate();
         }
         updateScrollBars();

From 83a2f2ef5c26b49ab87608dda6010e7d89c189cc Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 11:56:00 -0500
Subject: [PATCH 12/24] Changed accumulation to work for both points and widths

---
 src/dlangui/core/editable.d   | 16 +++++++++++-----
 src/dlangui/widgets/editors.d |  2 +-
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/dlangui/core/editable.d b/src/dlangui/core/editable.d
index b6f8e4de..16de7294 100644
--- a/src/dlangui/core/editable.d
+++ b/src/dlangui/core/editable.d
@@ -455,18 +455,24 @@ struct LineSpan {
     /// the wrapped text
     dstring[] wrappedContent;
     
-    int widthAccumulation(int wrapLine)
+    enum WrapPointInfo : bool {
+        Position,
+        Width,
+    }
+    
+    int accumulation(int wrapLine, bool wrapPointInfo)
     {
-        int widthTotal;
+        int total;
         for (int i; i < wrapLine; i++)
         {
             if (i < this.wrapPoints.length - 1)
             {
-                int curWidth = this.wrapPoints[i].wrapWidth;
-                widthTotal += curWidth;
+                int curVal;
+                curVal = wrapPointInfo ? this.wrapPoints[i].wrapWidth : this.wrapPoints[i].wrapPos;
+                total += curVal;
             }
         }
-        return widthTotal;
+        return total;
     }
 }
 
diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 6384671e..d805b2e6 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -1289,7 +1289,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
             if (wrapLine > 0)
             {
                 LineSpan curSpan = getSpan(_caretPos.line);
-                xOffset = curSpan.widthAccumulation(wrapLine);
+                xOffset = curSpan.accumulation(wrapLine, LineSpan.WrapPointInfo.Width);
             }
             auto yOffset = -1 * _lineHeight * (wrapsUpTo(_caretPos.line) + wrapLine);
             caretRc.offset(_clientRect.left - xOffset, _clientRect.top - yOffset);

From bc40bbf561d7cb5e95d90b468ad368a6d3626b99 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 12:37:20 -0500
Subject: [PATCH 13/24] Removed some redundant parts from findWrapLine

---
 src/dlangui/widgets/editors.d | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index d805b2e6..68358af2 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -531,20 +531,18 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     
     int findWrapLine(TextPosition textPos)
     {
-        int curLine = 0;
+        int curWrapLine = 0;
         int curPosition = textPos.pos;
-        int i = 0;
         while (true)
         {
-            if (i == getSpan(textPos.line).wrapPoints.length - 1)
-                return curLine;
-            curPosition -= getSpan(textPos.line).wrapPoints[i].wrapPos;
+            if (curWrapLine == getSpan(textPos.line).wrapPoints.length - 1)
+                return curWrapLine;
+            curPosition -= getSpan(textPos.line).wrapPoints[curWrapLine].wrapPos;
             if (curPosition < 0)
             {   
-                return curLine;
+                return curWrapLine;
             }
-            curLine++;
-            i++;
+            curWrapLine++;
         }
     }
     

From bd721d590ee22ca58284fb6b3c45a0620b84cf3e Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 12:40:32 -0500
Subject: [PATCH 14/24] Improved efficiency a bit

---
 src/dlangui/widgets/editors.d | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 68358af2..338bfecf 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -533,11 +533,12 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     {
         int curWrapLine = 0;
         int curPosition = textPos.pos;
+        LineSpan curSpan = getSpan(textPos.line);
         while (true)
         {
-            if (curWrapLine == getSpan(textPos.line).wrapPoints.length - 1)
+            if (curWrapLine == curSpan.wrapPoints.length - 1)
                 return curWrapLine;
-            curPosition -= getSpan(textPos.line).wrapPoints[curWrapLine].wrapPos;
+            curPosition -= curSpan.wrapPoints[curWrapLine].wrapPos;
             if (curPosition < 0)
             {   
                 return curWrapLine;

From 422517aff49fdce2cb0ab6001f49de269e42311e Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 13:02:13 -0500
Subject: [PATCH 15/24] Caret movement

---
 src/dlangui/widgets/editors.d | 68 +++++++++++++++++++++++++++++++----
 1 file changed, 62 insertions(+), 6 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 338bfecf..57b91f53 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -2848,17 +2848,73 @@ class EditBox : EditWidgetBase {
                 return true;
             case Up:
             case SelectUp:
-                if (_caretPos.line > 0) {
-                    _caretPos.line--;
-                    correctCaretPos();
-                    updateSelectionAfterCursorMovement(oldCaretPos, (a.id & 1) != 0);
-                    ensureCaretVisible();
+                if ((_caretPos.line > 0) | wordWrap) {
+                    if (_wordWrap)
+                    {
+                        LineSpan curSpan = getSpan(_caretPos.line);
+                        int curWrap = findWrapLine(_caretPos);
+                        if (curWrap > 0)
+                        {
+                            _caretPos.pos-= curSpan.wrapPoints[curWrap - 1].wrapPos;
+                        }
+                        else
+                        {
+                            int previousPos = _caretPos.pos;
+                            curSpan = getSpan(_caretPos.line - 1);
+                            curWrap = curSpan.len - 1;
+                            if (curWrap > 0)
+                            {
+                                int accumulativePoint = curSpan.accumulation(curSpan.len - 1, LineSpan.WrapPointInfo.Position);
+                                _caretPos.line--;
+                                _caretPos.pos = accumulativePoint + previousPos;
+                            }
+                            else
+                            {
+                                _caretPos.line--;
+                            }
+                        }
+                    }
+                    else if(_caretPos.line > 0)
+                        _caretPos.line--;
+                     correctCaretPos();
+                     updateSelectionAfterCursorMovement(oldCaretPos, (a.id & 1) != 0);
+                     ensureCaretVisible();
                 }
                 return true;
             case Down:
             case SelectDown:
                 if (_caretPos.line < _content.length - 1) {
-                    _caretPos.line++;
+                    if (_wordWrap)
+                    {
+                        LineSpan curSpan = getSpan(_caretPos.line);
+                        int curWrap = findWrapLine(_caretPos);
+                        if (curWrap < curSpan.len - 1)
+                        {
+                            int previousPos = _caretPos.pos;
+                            _caretPos.pos+= curSpan.wrapPoints[curWrap].wrapPos;
+                            correctCaretPos();
+                            if (_caretPos.pos == previousPos)
+                            {
+                                _caretPos.pos = 0;
+                                _caretPos.line++;
+                            }
+                        }
+                        else if (curSpan.len > 1)
+                        {
+                            int previousPos = _caretPos.pos;
+                            int previousAccumulatedPosition = curSpan.accumulation(curSpan.len - 1, LineSpan.WrapPointInfo.Position);
+                            _caretPos.line++;
+                            _caretPos.pos = previousPos - previousAccumulatedPosition;
+                        }
+                        else
+                        {
+                            _caretPos.line++;
+                        }
+                    }
+                    else
+                    {
+                        _caretPos.line++;
+                    }
                     correctCaretPos();
                     updateSelectionAfterCursorMovement(oldCaretPos, (a.id & 1) != 0);
                     ensureCaretVisible();

From 71931c8e43f5bd0b3e1710c3b0a19aae9657e624 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 17:41:18 -0500
Subject: [PATCH 16/24] Basic implementation for highlighting text selection in
 word wrap

---
 src/dlangui/widgets/editors.d | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 57b91f53..55d6ca89 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -3336,7 +3336,23 @@ class EditBox : EditWidgetBase {
             Rect rc = lineRect;
             rc.left = startx;
             rc.right = endx;
-            if (!rc.empty) {
+            if (!rc.empty && _wordWrap)
+            {
+                auto limitNumber = (int num, int limit) => num > limit ? limit : num;
+                LineSpan curSpan = getSpan(lineIndex);
+                int yOffset = _lineHeight * (wrapsUpTo(lineIndex));
+                rc.offset(0, yOffset);
+                Rect[] wrappedSelection;
+                wrappedSelection.length = curSpan.len;
+                foreach (int i, wrapLineRect; wrappedSelection)
+                {
+                    wrapLineRect = rc;
+                    wrapLineRect.offset(-1 * curSpan.accumulation(i, LineSpan.WrapPointInfo.Width), i * _lineHeight);
+                    wrapLineRect.right = limitNumber(wrapLineRect.right,(rc.left + curSpan.wrapPoints[i].wrapWidth));
+                    buf.fillRect(wrapLineRect, focused ? _selectionColorFocused : _selectionColorNormal);
+                }
+            }
+            else if (!rc.empty) {
                 // draw selection rect for line
                 buf.fillRect(rc, focused ? _selectionColorFocused : _selectionColorNormal);
             }

From d28eab7e9dc2e2c78b7e558cdc6527f2c14eaf4a Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 18:22:39 -0500
Subject: [PATCH 17/24] highlightLineRange implemented like last

---
 src/dlangui/widgets/editors.d | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 55d6ca89..7fc2ed7f 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -3315,7 +3315,24 @@ class EditBox : EditWidgetBase {
         Rect rc = lineRect;
         rc.left = _clientRect.left + startrc.left;
         rc.right = _clientRect.left + endrc.right;
-        if (!rc.empty) {
+        if (_wordWrap && !rc.empty)
+        {
+            auto limitNumber = (int num, int limit) => num > limit ? limit : num;
+            LineSpan curSpan = getSpan(r.start.line);
+            int yOffset = _lineHeight * (wrapsUpTo(r.start.line));
+            rc.offset(0, yOffset);
+            Rect[] wrappedSelection;
+            wrappedSelection.length = curSpan.len;
+            foreach (int i, wrapLineRect; wrappedSelection)
+            {
+                wrapLineRect = rc;
+                wrapLineRect.offset(-1 * curSpan.accumulation(i, LineSpan.WrapPointInfo.Width), i * _lineHeight);
+                wrapLineRect.right = limitNumber(wrapLineRect.right,(rc.left + curSpan.wrapPoints[i].wrapWidth));
+                //buf.fillRect(wrapLineRect, focused ? _selectionColorFocused : _selectionColorNormal);
+                buf.fillRect(wrapLineRect, color);
+            }
+        }
+        else if (!rc.empty) {
             // draw selection rect for matching bracket
             buf.fillRect(rc, color);
         }

From 3463fec39d8ca18d770438c6bb79dc483acb03a4 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Thu, 11 Jan 2018 19:55:28 -0500
Subject: [PATCH 18/24] Made function for filling rect in word wrap

---
 src/dlangui/widgets/editors.d | 47 +++++++++++++++--------------------
 1 file changed, 20 insertions(+), 27 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 7fc2ed7f..bb1cc046 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -3317,26 +3317,31 @@ class EditBox : EditWidgetBase {
         rc.right = _clientRect.left + endrc.right;
         if (_wordWrap && !rc.empty)
         {
-            auto limitNumber = (int num, int limit) => num > limit ? limit : num;
-            LineSpan curSpan = getSpan(r.start.line);
-            int yOffset = _lineHeight * (wrapsUpTo(r.start.line));
-            rc.offset(0, yOffset);
-            Rect[] wrappedSelection;
-            wrappedSelection.length = curSpan.len;
-            foreach (int i, wrapLineRect; wrappedSelection)
-            {
-                wrapLineRect = rc;
-                wrapLineRect.offset(-1 * curSpan.accumulation(i, LineSpan.WrapPointInfo.Width), i * _lineHeight);
-                wrapLineRect.right = limitNumber(wrapLineRect.right,(rc.left + curSpan.wrapPoints[i].wrapWidth));
-                //buf.fillRect(wrapLineRect, focused ? _selectionColorFocused : _selectionColorNormal);
-                buf.fillRect(wrapLineRect, color);
-            }
+            wordWrapFillRect(buf, r.start.line, rc, color);
         }
         else if (!rc.empty) {
             // draw selection rect for matching bracket
             buf.fillRect(rc, color);
         }
     }
+    
+    void wordWrapFillRect(DrawBuf buf, int line, Rect lineToDivide, uint color)
+    {
+        Rect rc = lineToDivide;
+        auto limitNumber = (int num, int limit) => num > limit ? limit : num;
+        LineSpan curSpan = getSpan(line);
+        int yOffset = _lineHeight * (wrapsUpTo(line));
+        rc.offset(0, yOffset);
+        Rect[] wrappedSelection;
+        wrappedSelection.length = curSpan.len;
+        foreach (int i, wrapLineRect; wrappedSelection)
+        {
+            wrapLineRect = rc;
+            wrapLineRect.offset(-1 * curSpan.accumulation(i, LineSpan.WrapPointInfo.Width), i * _lineHeight);
+            wrapLineRect.right = limitNumber(wrapLineRect.right,(rc.left + curSpan.wrapPoints[i].wrapWidth));
+            buf.fillRect(wrapLineRect, color);
+        }
+    }
 
     /// override to custom highlight of line background
     protected void drawLineBackground(DrawBuf buf, int lineIndex, Rect lineRect, Rect visibleRect) {
@@ -3355,19 +3360,7 @@ class EditBox : EditWidgetBase {
             rc.right = endx;
             if (!rc.empty && _wordWrap)
             {
-                auto limitNumber = (int num, int limit) => num > limit ? limit : num;
-                LineSpan curSpan = getSpan(lineIndex);
-                int yOffset = _lineHeight * (wrapsUpTo(lineIndex));
-                rc.offset(0, yOffset);
-                Rect[] wrappedSelection;
-                wrappedSelection.length = curSpan.len;
-                foreach (int i, wrapLineRect; wrappedSelection)
-                {
-                    wrapLineRect = rc;
-                    wrapLineRect.offset(-1 * curSpan.accumulation(i, LineSpan.WrapPointInfo.Width), i * _lineHeight);
-                    wrapLineRect.right = limitNumber(wrapLineRect.right,(rc.left + curSpan.wrapPoints[i].wrapWidth));
-                    buf.fillRect(wrapLineRect, focused ? _selectionColorFocused : _selectionColorNormal);
-                }
+                wordWrapFillRect(buf, lineIndex, rc, focused ? _selectionColorFocused : _selectionColorNormal);
             }
             else if (!rc.empty) {
                 // draw selection rect for line

From d2caa2a1a783be606276182396d9bd32c57ed6fa Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Fri, 12 Jan 2018 07:17:09 -0500
Subject: [PATCH 19/24] EnsureCaretVisible for word wrap

---
 src/dlangui/widgets/editors.d | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index bb1cc046..a053bd57 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -1266,6 +1266,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     
     protected int _firstVisibleLine;
 
+    protected int caretHeightOffset;
     /// returns cursor rectangle
     protected Rect caretRect() {
         Rect caretRc = textPosToClient(_caretPos);
@@ -1291,6 +1292,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
                 xOffset = curSpan.accumulation(wrapLine, LineSpan.WrapPointInfo.Width);
             }
             auto yOffset = -1 * _lineHeight * (wrapsUpTo(_caretPos.line) + wrapLine);
+            caretHeightOffset = yOffset;
             caretRc.offset(_clientRect.left - xOffset, _clientRect.top - yOffset);
         }
         else
@@ -2743,6 +2745,22 @@ class EditBox : EditWidgetBase {
                 _firstVisibleLine = maxFirstVisibleLine;
             measureVisibleText();
             invalidate();
+        } else if(_wordWrap && !(_firstVisibleLine > maxFirstVisibleLine)) {
+            //For wordwrap mode, move down sooner
+            int offsetLines = -1 * caretHeightOffset / _lineHeight;
+            //Log.d("offsetLines: ", offsetLines);
+            if (_caretPos.line >= _firstVisibleLine + visibleLines - offsetLines)
+            {
+                _firstVisibleLine = _caretPos.line - visibleLines + 1 + offsetLines;
+                if (center)
+                    _firstVisibleLine += visibleLines / 2;
+                if (_firstVisibleLine > maxFirstVisibleLine)
+                    _firstVisibleLine = maxFirstVisibleLine;
+                if (_firstVisibleLine < 0)
+                    _firstVisibleLine = 0;
+                measureVisibleText();
+                invalidate();
+            }
         } else if (_caretPos.line >= _firstVisibleLine + visibleLines) {
             _firstVisibleLine = _caretPos.line - visibleLines + 1;
             if (center)

From 278ecc75ebfa6e88b40c712d3264c3fc52556e1f Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Fri, 12 Jan 2018 08:09:37 -0500
Subject: [PATCH 20/24] Word wrap mouse offset

---
 src/dlangui/widgets/editors.d | 48 ++++++++++++++++++++++++++++++++---
 1 file changed, 45 insertions(+), 3 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index a053bd57..327b1f0a 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -1434,10 +1434,52 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         _textToHighlightOptions = textToHighlightOptions;
         invalidate();
     }
+    
+    protected TextPosition wordWrapMouseOffset(int x, int y)
+    {
+        if(_span.length == 0)
+            return clientToTextPos(Point(x,y));
+        int selectedVisibleLine = y / _lineHeight;
+            
+        LineSpan _curSpan;
+        
+        int wrapLine = 0;
+        int curLine = 0;
+        bool foundWrap = false;
+        int accumulativeWidths = 0;
+        int curWrapOfSpan = 0;
+        
+        lineSpanIterate(delegate(LineSpan curSpan){
+            while (!foundWrap)
+            {
+                if (wrapLine == selectedVisibleLine)
+                {
+                    foundWrap = true;
+                    break;
+                }
+                accumulativeWidths += curSpan.wrapPoints[curWrapOfSpan].wrapWidth;
+                wrapLine++;
+                curWrapOfSpan++;
+                if (curWrapOfSpan >= curSpan.len)
+                {
+                    break;
+                }
+            }
+            if (!foundWrap)
+            {
+                accumulativeWidths = 0;
+                curLine++;
+            }
+            curWrapOfSpan = 0;
+        });
+        
+        int fakeLineHeight = curLine * _lineHeight;
+        return clientToTextPos(Point(x + accumulativeWidths,fakeLineHeight));
+    }
 
     protected void selectWordByMouse(int x, int y) {
         TextPosition oldCaretPos = _caretPos;
-        TextPosition newPos = clientToTextPos(Point(x,y));
+        TextPosition newPos = _wordWrap ? wordWrapMouseOffset(x,y) : clientToTextPos(Point(x,y));
         TextRange r = content.wordBounds(newPos);
         if (r.start < r.end) {
             _selectionRange = r;
@@ -1453,7 +1495,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
 
     protected void selectLineByMouse(int x, int y, bool onSameLineOnly = true) {
         TextPosition oldCaretPos = _caretPos;
-        TextPosition newPos = clientToTextPos(Point(x,y));
+        TextPosition newPos = _wordWrap ? wordWrapMouseOffset(x,y) : clientToTextPos(Point(x,y));
         if (onSameLineOnly && newPos.line != oldCaretPos.line)
             return; // different lines
         TextRange r = content.lineRange(newPos.line);
@@ -1471,7 +1513,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
 
     protected void updateCaretPositionByMouse(int x, int y, bool selecting) {
         TextPosition oldCaretPos = _caretPos;
-        TextPosition newPos = clientToTextPos(Point(x,y));
+        TextPosition newPos = _wordWrap ? wordWrapMouseOffset(x,y) : clientToTextPos(Point(x,y));
         if (newPos != _caretPos) {
             _caretPos = newPos;
             updateSelectionAfterCursorMovement(oldCaretPos, selecting);

From 87f4a8a918fd9d611f7f404a829400e3caa3f8cc Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Fri, 12 Jan 2018 09:10:29 -0500
Subject: [PATCH 21/24] Small additions from previous implementation

---
 src/dlangui/widgets/editors.d | 38 +++++++++++++++++++++++++++++++++--
 1 file changed, 36 insertions(+), 2 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 327b1f0a..588dd7be 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -379,6 +379,10 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return 1;
     }
 
+    //Override for EditBox
+    void wordWrapRefresh(){return;}
+    
+    int previousXScrollPos;
     protected bool _wordWrap;
     /// true if word wrap mode is set
     @property bool wordWrap() {
@@ -387,6 +391,19 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     /// true if word wrap mode is set
     @property EditWidgetBase wordWrap(bool v) {
         _wordWrap = v;
+        //Horizontal scrollbar should not be visible in word wrap mode
+        if (v)
+        {
+            _hscrollbar.visibility(Visibility.Invisible);
+            previousXScrollPos = _scrollPos.x;
+            _scrollPos.x = 0;
+            wordWrapRefresh();
+        }
+        else
+        {
+            _hscrollbar.visibility(Visibility.Visible);
+            _scrollPos.x = previousXScrollPos;
+        }
         invalidate();
         return this;
     }
@@ -2537,6 +2554,17 @@ class EditBox : EditWidgetBase {
     protected dstring _textToSetWidgetSize = "aaaaa/naaaaa"d;
     protected int[] _measuredTextToSetWidgetSizeWidths;
 
+    override void wordWrapRefresh()
+    {
+        needRewrap = true;
+    }
+    
+    override @property int fontSize() const { return super.fontSize(); }
+    override @property Widget fontSize(int size) {
+        needRewrap = true;
+        return super.fontSize(size);
+    }
+    
     override protected int lineCount() {
         return _content.length;
     }
@@ -2590,6 +2618,7 @@ class EditBox : EditWidgetBase {
         super.layout(contentRc);
         if (_contentChanged) {
             measureVisibleText();
+            needRewrap = true;
             _contentChanged = false;
         }
 
@@ -3134,6 +3163,7 @@ class EditBox : EditWidgetBase {
                             Log.i("Font size in editor ", id, " zoomed to ", newFontSize);
                             fontSize = cast(ushort)newFontSize;
                             updateFontProps();
+                            needRewrap = true;
                             measureVisibleText();
                             updateScrollBars();
                             invalidate();
@@ -3396,9 +3426,10 @@ class EditBox : EditWidgetBase {
         wrappedSelection.length = curSpan.len;
         foreach (int i, wrapLineRect; wrappedSelection)
         {
+            int startingDifference = rc.left - _clientRect.left;
             wrapLineRect = rc;
             wrapLineRect.offset(-1 * curSpan.accumulation(i, LineSpan.WrapPointInfo.Width), i * _lineHeight);
-            wrapLineRect.right = limitNumber(wrapLineRect.right,(rc.left + curSpan.wrapPoints[i].wrapWidth));
+            wrapLineRect.right = limitNumber(wrapLineRect.right,(rc.left + curSpan.wrapPoints[i].wrapWidth) - startingDifference);
             buf.fillRect(wrapLineRect, color);
         }
     }
@@ -3441,6 +3472,9 @@ class EditBox : EditWidgetBase {
 
         // frame around current line
         if (focused && lineIndex == _caretPos.line && _selectionRange.singleLine && _selectionRange.start.line == _caretPos.line) {
+            //TODO: Figure out why a little slow to catch up
+            if (_wordWrap)
+                visibleRect.offset(0, -caretHeightOffset);
             buf.drawFrame(visibleRect, 0xA0808080, Rect(1,1,1,1));
         }
 
@@ -3657,7 +3691,7 @@ class EditBox : EditWidgetBase {
 
     void resetVisibleSpans()
     {
-        //TODO: Don't erase spans which have not been modified
+        //TODO: Don't erase spans which have not been modified, cache them
         _span = [];
     }
     

From f40ae806da97ea99b86ab0a0d277f5359a6eb2d9 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Fri, 12 Jan 2018 09:47:43 -0500
Subject: [PATCH 22/24] Whitespace marks

---
 src/dlangui/widgets/editors.d | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 588dd7be..b2e550ac 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -3731,6 +3731,7 @@ class EditBox : EditWidgetBase {
         }
 
         FontRef font = font();
+        int previousWraps;
         for (int i = 0; i < _visibleLines.length; i++) {
             dstring txt = _visibleLines[i];
             Rect lineRect;
@@ -3747,7 +3748,16 @@ class EditBox : EditWidgetBase {
             if (!txt.length && !_wordWrap)
                 continue;
             if (_showWhiteSpaceMarks)
-                drawWhiteSpaceMarks(buf, font, txt, tabSize, lineRect, visibleRect);
+            {
+                Rect whiteSpaceRc = lineRect;
+                Rect whiteSpaceRcVisible = visibleRect;
+                for(int z; z < previousWraps; z++)
+                {
+                    whiteSpaceRc.offset(0, _lineHeight);
+                    whiteSpaceRcVisible.offset(0, _lineHeight);
+                }
+                drawWhiteSpaceMarks(buf, font, txt, tabSize, whiteSpaceRc, whiteSpaceRcVisible);
+            }
             if (_leftPaneWidth > 0) {
                 Rect leftPaneRect = visibleRect;
                 leftPaneRect.right = leftPaneRect.left;
@@ -3779,6 +3789,7 @@ class EditBox : EditWidgetBase {
                             font.drawText(buf, rc.left - _scrollPos.x, rc.top + lineOffset * _lineHeight, curWrap, textColor, tabSize);
 
                     }
+                    previousWraps += to!int(wrappedLine.length - 1);
                 }
                 else
                 {

From 435c10c662438f1c87401eb89ec5b894be3b1101 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Fri, 12 Jan 2018 11:14:57 -0500
Subject: [PATCH 23/24] Added comments, renamed variable

---
 src/dlangui/core/editable.d   |  4 +++
 src/dlangui/widgets/editors.d | 46 +++++++++++++++++++++++------------
 2 files changed, 34 insertions(+), 16 deletions(-)

diff --git a/src/dlangui/core/editable.d b/src/dlangui/core/editable.d
index 16de7294..acbb2ea2 100644
--- a/src/dlangui/core/editable.d
+++ b/src/dlangui/core/editable.d
@@ -460,6 +460,7 @@ struct LineSpan {
         Width,
     }
     
+    ///Adds up either positions or widths to a wrapLine
     int accumulation(int wrapLine, bool wrapPointInfo)
     {
         int total;
@@ -476,8 +477,11 @@ struct LineSpan {
     }
 }
 
+///Holds info about a word wrapping point
 struct WrapPoint {
+    ///The relative wrapping position (related to TextPosition.pos)
     int wrapPos;
+    ///The associated calculated width of the wrapLine
     int wrapWidth;
 }
 
diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index b2e550ac..70d16826 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -379,10 +379,12 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return 1;
     }
 
-    //Override for EditBox
+    /// Override for EditBox
     void wordWrapRefresh(){return;}
     
+    /// To hold _scrollpos.x toggling between normal and word wrap mode
     int previousXScrollPos;
+    
     protected bool _wordWrap;
     /// true if word wrap mode is set
     @property bool wordWrap() {
@@ -408,8 +410,10 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return this;
     }
 
+    /// Characters at which content is split for word wrap mode
     dchar[] splitChars = [' ', '-', '\t'];
     
+    /// Divides up a string for word wrapping, sets info in _span
     dstring[] wrapLine(dstring str, int lineNumber) {
         FontRef font = font();
         dstring[] words = explode(str, splitChars);
@@ -456,6 +460,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return buildingStrArr;
     }
 
+    /// Divide (and conquer) text into words
     dstring[] explode(dstring str, dchar[] splitChars)
     {
         dstring[] parts;
@@ -493,7 +498,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     protected LineSpan[] _span;
     protected LineSpan[] _spanCache;
     
-    //Finds good visual wrapping point for string
+    /// Finds good visual wrapping point for string
     int findWrapPoint(dstring text)
     {
         int maxWidth = _clientRect.width;
@@ -511,7 +516,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         }
      }
     
-    //Calls measureText for word wrap
+    /// Calls measureText for word wrap
     int measureWrappedText(dstring text)
     {
         FontRef font = font();
@@ -524,6 +529,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return 0;
     }
     
+    /// Returns number of visible wraps up to a line (not including the first wrapLines themselves)
     int wrapsUpTo(int line)
     {
         int sum;
@@ -535,6 +541,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return sum;
     }
     
+    /// Returns LineSpan for line based on actual line number
     LineSpan getSpan(int lineNumber)
     {
         LineSpan lineSpan = LineSpan(lineNumber, 0, [WrapPoint(0,0)], []);
@@ -546,6 +553,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         return lineSpan;
     }
     
+    /// Based on a TextPosition, finds which wrapLine it is on for its current line
     int findWrapLine(TextPosition textPos)
     {
         int curWrapLine = 0;
@@ -564,8 +572,10 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         }
     }
     
+    /// Simple way of iterating through _span
     void lineSpanIterate(void delegate(LineSpan curSpan) iterator)
     {
+        //TODO: Rename iterator to iteration?
         foreach (currentSpan; _span)
             iterator(currentSpan);
     }
@@ -1283,7 +1293,9 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
     
     protected int _firstVisibleLine;
 
+    //In word wrap mode, set by caretRect so ensureCaretVisible will know when to scroll
     protected int caretHeightOffset;
+    
     /// returns cursor rectangle
     protected Rect caretRect() {
         Rect caretRc = textPosToClient(_caretPos);
@@ -1452,6 +1464,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
         invalidate();
     }
     
+    /// Used instead of using clientToTextPos for mouse input when in word wrap mode
     protected TextPosition wordWrapMouseOffset(int x, int y)
     {
         if(_span.length == 0)
@@ -2554,14 +2567,16 @@ class EditBox : EditWidgetBase {
     protected dstring _textToSetWidgetSize = "aaaaa/naaaaa"d;
     protected int[] _measuredTextToSetWidgetSizeWidths;
 
+    /// Set _needRewrap to true;
     override void wordWrapRefresh()
     {
-        needRewrap = true;
+        _needRewrap = true;
     }
     
     override @property int fontSize() const { return super.fontSize(); }
     override @property Widget fontSize(int size) {
-        needRewrap = true;
+        // Need to rewrap if fontSize changed
+        _needRewrap = true;
         return super.fontSize(size);
     }
     
@@ -2618,7 +2633,7 @@ class EditBox : EditWidgetBase {
         super.layout(contentRc);
         if (_contentChanged) {
             measureVisibleText();
-            needRewrap = true;
+            _needRewrap = true;
             _contentChanged = false;
         }
 
@@ -3163,7 +3178,7 @@ class EditBox : EditWidgetBase {
                             Log.i("Font size in editor ", id, " zoomed to ", newFontSize);
                             fontSize = cast(ushort)newFontSize;
                             updateFontProps();
-                            needRewrap = true;
+                            _needRewrap = true;
                             measureVisibleText();
                             updateScrollBars();
                             invalidate();
@@ -3415,6 +3430,7 @@ class EditBox : EditWidgetBase {
         }
     }
     
+    /// Used in place of directly calling buf.fillRect in word wrap mode
     void wordWrapFillRect(DrawBuf buf, int line, Rect lineToDivide, uint color)
     {
         Rect rc = lineToDivide;
@@ -3689,13 +3705,14 @@ class EditBox : EditWidgetBase {
         }
     }
 
+    /// Clear _span
     void resetVisibleSpans()
     {
         //TODO: Don't erase spans which have not been modified, cache them
         _span = [];
     }
     
-    private bool needRewrap = true;
+    private bool _needRewrap = true;
     private int lastStartingLine;
     
     override protected void drawClient(DrawBuf buf) {
@@ -3708,25 +3725,22 @@ class EditBox : EditWidgetBase {
         Rect rc = _clientRect;
         
         if (_contentChanged)
-          needRewrap = true;
-          
+          _needRewrap = true;
         if (lastStartingLine != _firstVisibleLine)
         {
-            needRewrap = true;
+            _needRewrap = true;
             lastStartingLine = _firstVisibleLine;
         }
-          
         if (rc.width <= 0 && _wordWrap)
         {
+            //Prevent drawClient from getting stuck in loop
             return;
         }
-        
         bool doRewrap = false;
-        
-        if (needRewrap && _wordWrap)
+        if (_needRewrap && _wordWrap)
         {
             resetVisibleSpans();
-            needRewrap = false;
+            _needRewrap = false;
             doRewrap = true;
         }
 

From cfff2982e8c525528dec6fff5c0e80c4487b3896 Mon Sep 17 00:00:00 2001
From: James Johnson <triplejam@protonmail.com>
Date: Fri, 12 Jan 2018 12:00:38 -0500
Subject: [PATCH 24/24] Moved back to EditBox

---
 src/dlangui/widgets/editors.d | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d
index 70d16826..5236eaf4 100644
--- a/src/dlangui/widgets/editors.d
+++ b/src/dlangui/widgets/editors.d
@@ -1290,8 +1290,6 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
             handleAction(ACTION_EDITOR_SELECT_ALL);
         super.handleFocusChange(focused);
     }
-    
-    protected int _firstVisibleLine;
 
     //In word wrap mode, set by caretRect so ensureCaretVisible will know when to scroll
     protected int caretHeightOffset;
@@ -2553,7 +2551,7 @@ class EditBox : EditWidgetBase {
         }
     }
 
-    //protected int _firstVisibleLine;
+    protected int _firstVisibleLine;
 
     protected int _maxLineWidth;
     protected int _numVisibleLines;             // number of lines visible in client area