From efd0fed4dec904b6d0257dffe5ea3e2939650f44 Mon Sep 17 00:00:00 2001
From: J-P Nurmi <jpnurmi@digia.com>
Date: Tue, 14 May 2013 10:39:43 +0200
Subject: [PATCH] TextArea: fix word wrap layout

Since scrollbars affect the document/viewport size and vice versa, we
must allow the layout loop to recurse twice until the sizes stabilize.
Furthermore, avoid nasty content size binding loops (from ScrollView)
by providing a specialized Flickable item that TextArea has full
control over.

Task-number: QTBUG-30832
Change-Id: I74fe6c5017e75aa411fdbb64087afa18e1221ce9
Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
---
 src/controls/TextArea.qml | 161 +++++++++++++++++++++-----------------
 1 file changed, 88 insertions(+), 73 deletions(-)

diff --git a/src/controls/TextArea.qml b/src/controls/TextArea.qml
index a818779a3..6dab90fd3 100644
--- a/src/controls/TextArea.qml
+++ b/src/controls/TextArea.qml
@@ -609,12 +609,11 @@ ScrollView {
     property alias backgroundColor: colorRect.color
 
     /*! \internal */
-    property int documentMargins: 4
+    property int __documentMargin: 4
 
     width: 280
     height: 120
 
-    flickableItem.contentWidth: edit.paintedWidth + (2 * documentMargins)
     frameVisible: true
 
     activeFocusOnTab: true
@@ -629,84 +628,100 @@ ScrollView {
     */
     property alias textDocument: edit.textDocument
 
-    TextEdit {
-        id: edit
-        focus: true
+    Flickable {
+        id: flickable
 
-        SystemPalette {
-            id: palette
-            colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled
-        }
+        interactive: false
+        anchors.fill: parent
 
-        Rectangle {
-            id: colorRect
-            parent: viewport
-            anchors.fill: parent
-            color: palette.base
-            z: -1
-        }
+        TextEdit {
+            id: edit
+            focus: true
+
+            SystemPalette {
+                id: palette
+                colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled
+            }
+
+            Rectangle {
+                id: colorRect
+                parent: viewport
+                anchors.fill: parent
+                color: palette.base
+                z: -1
+            }
+
+            property int layoutRecursionDepth: 0
+
+            function doLayout() {
+                // scrollbars affect the document/viewport size and vice versa, so we
+                // must allow the layout loop to recurse twice until the sizes stabilize
+                if (layoutRecursionDepth <= 2) {
+                    layoutRecursionDepth++
+
+                    var margins = 2 * __documentMargin
 
-        property bool recursionGuard: false
-
-        function doLayout() {
-            if (!recursionGuard) {
-                recursionGuard = true
-                if (wrapMode == TextEdit.NoWrap) {
-                    __horizontalScrollBar.visible = edit.paintedWidth + (2 * documentMargins) > area.viewport.width
-                    edit.width = edit.paintedWidth + (2 * documentMargins)
-                } else {
-                    __horizontalScrollBar.visible = false
-                    edit.width = area.viewport.width - (2 * documentMargins)
+                    if (wrapMode == TextEdit.NoWrap) {
+                        __horizontalScrollBar.visible = edit.contentWidth > viewport.width - margins
+                        edit.width = Math.max(viewport.width - margins, edit.contentWidth)
+                    } else {
+                        __horizontalScrollBar.visible = false
+                        edit.width = viewport.width - margins
+                    }
+                    edit.height = Math.max(viewport.height - margins, edit.contentHeight)
+
+                    flickable.contentWidth = edit.contentWidth + margins
+                    flickable.contentHeight = edit.contentHeight + margins
+
+                    layoutRecursionDepth--
                 }
-                edit.height = Math.max(area.viewport.height - (2 * documentMargins), paintedHeight + (2 * documentMargins))
-                recursionGuard = false
             }
-        }
 
-        Connections {
-            target: area.viewport
-            onWidthChanged: edit.doLayout()
-            onHeightChanged: edit.doLayout()
-        }
-        onPaintedWidthChanged: edit.doLayout()
-        onPaintedHeightChanged: edit.doLayout()
-        onWrapModeChanged: edit.doLayout()
-
-        renderType: Text.NativeRendering
-
-        color: palette.text
-        selectionColor: palette.highlight
-        selectedTextColor: palette.highlightedText
-        wrapMode: TextEdit.WordWrap
-        x: documentMargins
-        y: documentMargins
-
-        selectByMouse: true
-        readOnly: false
-
-        KeyNavigation.priority: KeyNavigation.BeforeItem
-        KeyNavigation.tab: area.tabChangesFocus ? area.KeyNavigation.tab : null
-        KeyNavigation.backtab: area.tabChangesFocus ? area.KeyNavigation.backtab : null
-
-        // keep textcursor within scroll view
-        onCursorPositionChanged: {
-            if (cursorRectangle.y >= flickableItem.contentY + viewport.height - 1.5*cursorRectangle.height - documentMargins)
-                flickableItem.contentY = cursorRectangle.y - viewport.height + 1.5*cursorRectangle.height + documentMargins
-            else if (cursorRectangle.y < flickableItem.contentY)
-                flickableItem.contentY = cursorRectangle.y
-
-            if (cursorRectangle.x >= flickableItem.contentX + viewport.width - documentMargins) {
-                flickableItem.contentX = cursorRectangle.x - viewport.width + documentMargins
-            } else if (cursorRectangle.x < flickableItem.contentX)
-                flickableItem.contentX = cursorRectangle.x
-        }
-        onLinkActivated: area.linkActivated(link)
+            Connections {
+                target: area.viewport
+                onWidthChanged: edit.doLayout()
+                onHeightChanged: edit.doLayout()
+            }
+            onContentWidthChanged: edit.doLayout()
+            onContentHeightChanged: edit.doLayout()
+            onWrapModeChanged: edit.doLayout()
+
+            renderType: Text.NativeRendering
+
+            color: palette.text
+            selectionColor: palette.highlight
+            selectedTextColor: palette.highlightedText
+            wrapMode: TextEdit.WordWrap
+            x: __documentMargin
+            y: __documentMargin
+
+            selectByMouse: true
+            readOnly: false
+
+            KeyNavigation.priority: KeyNavigation.BeforeItem
+            KeyNavigation.tab: area.tabChangesFocus ? area.KeyNavigation.tab : null
+            KeyNavigation.backtab: area.tabChangesFocus ? area.KeyNavigation.backtab : null
+
+            // keep textcursor within scroll view
+            onCursorPositionChanged: {
+                if (cursorRectangle.y >= flickableItem.contentY + viewport.height - 1.5*cursorRectangle.height - __documentMargin)
+                    flickableItem.contentY = cursorRectangle.y - viewport.height + 1.5*cursorRectangle.height + __documentMargin
+                else if (cursorRectangle.y < flickableItem.contentY)
+                    flickableItem.contentY = cursorRectangle.y
+
+                if (cursorRectangle.x >= flickableItem.contentX + viewport.width - __documentMargin) {
+                    flickableItem.contentX = cursorRectangle.x - viewport.width + __documentMargin
+                } else if (cursorRectangle.x < flickableItem.contentX)
+                    flickableItem.contentX = cursorRectangle.x
+            }
+            onLinkActivated: area.linkActivated(link)
 
-        MouseArea {
-            parent: area.viewport
-            anchors.fill: parent
-            cursorShape: Qt.IBeamCursor
-            acceptedButtons: Qt.NoButton
+            MouseArea {
+                parent: area.viewport
+                anchors.fill: parent
+                cursorShape: Qt.IBeamCursor
+                acceptedButtons: Qt.NoButton
+            }
         }
     }
 
-- 
GitLab