diff --git a/examples/quick/controls/gallery/content/Styles.qml b/examples/quick/controls/gallery/content/Styles.qml
index f6892b7a026b9e8f9445f58e540203f1c50eda75..73850ce5aeec44a654ca98e926f0e4ee1d98dab4 100644
--- a/examples/quick/controls/gallery/content/Styles.qml
+++ b/examples/quick/controls/gallery/content/Styles.qml
@@ -242,6 +242,7 @@ Item {
         tabOverlap: 16
         tabsLeftPadding: 12
         frameOverlap: 4
+        tabsMovable: true
 
         frame: Rectangle {
             gradient: Gradient{
diff --git a/src/private/TabBar.qml b/src/private/TabBar.qml
index e9214c7407aa722e018068339280776a378dc608..ebaef02a358b4a4db829d42e21ebc05e8aa97ca7 100644
--- a/src/private/TabBar.qml
+++ b/src/private/TabBar.qml
@@ -69,6 +69,8 @@ FocusScope {
     property var style
     property var styleItem: tabView.__styleItem ? tabView.__styleItem : null
 
+    property bool tabsMovable: styleItem ? styleItem.tabsMovable : false
+
     property int tabsAlignment: styleItem ? styleItem.tabsAlignment : Qt.AlignLeft
 
     property int tabOverlap: styleItem ? styleItem.tabOverlap : 0
@@ -84,11 +86,25 @@ FocusScope {
         return null;
     }
 
-    Row {
+    ListView {
         id: tabrow
         objectName: "tabrow"
         Accessible.role: Accessible.PageTabList
         spacing: -tabOverlap
+        orientation: Qt.Horizontal
+        interactive: false
+        focus: true
+
+        width: contentItem ? contentItem.width : 0
+        height: currentItem ? currentItem.height : 0
+
+        displaced: Transition {
+            NumberAnimation {
+                property: "x"
+                duration: 125
+                easing.type: Easing.OutQuad
+            }
+        }
 
         states: [
             State {
@@ -110,60 +126,113 @@ FocusScope {
             }
         ]
 
+        model: tabView.__tabs
 
-        Repeater {
-            id: repeater
-            objectName: "repeater"
+        delegate: MouseArea {
+            id: tabitem
+            objectName: "mousearea"
+            hoverEnabled: true
             focus: true
-            model: tabView.__tabs
-
-            delegate: Item {
-                id: tabitem
-                focus: true
-
-                property int tabindex: index
-                property bool selected : tabView.currentIndex === index
-                property bool hover: mousearea.containsMouse
-                property string title: modelData.title
-                property bool nextSelected: tabView.currentIndex === index + 1
-                property bool previousSelected: tabView.currentIndex === index - 1
-
-                z: selected ? 1 : -index
-                implicitWidth: Math.min(tabloader.implicitWidth, tabbar.width/repeater.count) + 1
-                implicitHeight: tabloader.implicitHeight
-
-                Loader {
-                    id: tabloader
-
-                    sourceComponent: loader.item ? loader.item.tab : null
-                    anchors.fill: parent
-
-                    property Item control: tabView
-                    property int index: tabindex
-
-                    property QtObject tab: QtObject {
-                        readonly property alias index: tabitem.tabindex
-                        readonly property alias selected: tabitem.selected
-                        readonly property alias title: tabitem.title
-                        readonly property alias nextSelected: tabitem.nextSelected
-                        readonly property alias previsousSelected: tabitem.previousSelected
-                        readonly property alias hovered: tabitem.hover
-                        readonly property bool activeFocus: tabbar.activeFocus
-                    }
+
+            drag.target: tabsMovable ? tabloader : null
+            drag.axis: Drag.XAxis
+            drag.minimumX: drag.active ? 0 : -Number.MAX_VALUE
+            drag.maximumX: tabrow.width - tabitem.width
+
+            property int tabindex: index
+            property bool selected : tabView.currentIndex === index
+            property bool hover: containsMouse
+            property string title: modelData.title
+            property bool nextSelected: tabView.currentIndex === index + 1
+            property bool previousSelected: tabView.currentIndex === index - 1
+
+            z: selected ? 1 : -index
+            implicitWidth: Math.min(tabloader.implicitWidth, tabbar.width/tabrow.count) + 1
+            implicitHeight: tabloader.implicitHeight
+
+            onPressed: {
+                tabView.currentIndex = index;
+                tabbar.nextItemInFocusChain(true).forceActiveFocus();
+            }
+
+            Loader {
+                id: tabloader
+
+                property Item control: tabView
+                property int index: tabindex
+
+                property QtObject tab: QtObject {
+                    readonly property alias index: tabitem.tabindex
+                    readonly property alias selected: tabitem.selected
+                    readonly property alias title: tabitem.title
+                    readonly property alias nextSelected: tabitem.nextSelected
+                    readonly property alias previsousSelected: tabitem.previousSelected
+                    readonly property alias hovered: tabitem.hover
+                    readonly property bool activeFocus: tabbar.activeFocus
                 }
 
-                MouseArea {
-                    id: mousearea
-                    objectName: "mousearea"
-                    anchors.fill: parent
-                    hoverEnabled: true
-                    onPressed: {
-                        tabView.currentIndex = index;
-                        tabbar.nextItemInFocusChain(true).forceActiveFocus();
+                sourceComponent: loader.item ? loader.item.tab : null
+
+                Drag.keys: "application/x-tabbartab"
+                Drag.active: tabitem.drag.active
+                Drag.source: tabitem
+
+                property real __prevX: 0
+                property real __dragX: 0
+                onXChanged: {
+                    if (Drag.active) {
+                        // keep track for the snap back animation
+                        __dragX = tabitem.mapFromItem(tabrow, tabloader.x, 0).x
+
+                        // when moving to the left, the hot spot is the left edge and vice versa
+                        Drag.hotSpot.x = x < __prevX ? 0 : width
+                        __prevX = x
                     }
                 }
-                Accessible.role: Accessible.PageTab
-                Accessible.name: modelData.title
+
+                width: tabitem.width
+                state: Drag.active ? "drag" : ""
+
+                transitions: [
+                    Transition {
+                        to: "drag"
+                        PropertyAction { target: tabloader; property: "parent"; value: tabrow }
+                    },
+                    Transition {
+                        from: "drag"
+                        SequentialAnimation {
+                            PropertyAction { target: tabloader; property: "parent"; value: tabitem }
+                            NumberAnimation {
+                                target: tabloader
+                                duration: 50
+                                easing.type: Easing.OutQuad
+                                property: "x"
+                                from: tabloader.__dragX
+                                to: 0
+                            }
+                        }
+                    }
+                ]
+            }
+
+            Accessible.role: Accessible.PageTab
+            Accessible.name: modelData.title
+        }
+    }
+
+    DropArea {
+        anchors.fill: tabrow
+        keys: "application/x-tabbartab"
+        onPositionChanged: {
+            var source = drag.source
+            var target = tabrow.itemAt(drag.x, drag.y)
+            if (source && target && source !== target) {
+                source = source.drag.target
+                target = target.drag.target
+                var center = target.parent.x + target.width / 2
+                if ((source.index > target.index && source.x < center)
+                        || (source.index < target.index && source.x + source.width > center))
+                    tabView.moveTab(source.index, target.index)
             }
         }
     }
diff --git a/src/styles/Desktop/TabViewStyle.qml b/src/styles/Desktop/TabViewStyle.qml
index 830732bbe54780976342955486a163d1300f0299..bd3adb94fd9a5acac87201a549e8c7bc91526ea7 100644
--- a/src/styles/Desktop/TabViewStyle.qml
+++ b/src/styles/Desktop/TabViewStyle.qml
@@ -44,6 +44,7 @@ import QtQuick.Controls.Styles 1.0
 Style {
     id: root
 
+    property bool tabsMovable: false
     property int tabsLeftPadding: 0
     property int tabsRightPadding: 0
     property int tabsAlignment: __barstyle.styleHint("tabbaralignment") === "center" ? Qt.AlignHCenter : Qt.AlignLeft;
diff --git a/src/styles/TabViewStyle.qml b/src/styles/TabViewStyle.qml
index b08848917806fe621e06d1c4209e7ad6ba18945c..b9666db2344c9465f885a116d9c14f06a49b6d4b 100644
--- a/src/styles/TabViewStyle.qml
+++ b/src/styles/TabViewStyle.qml
@@ -83,6 +83,10 @@ Style {
     /*! The \l ScrollView attached to this style. */
     readonly property TabView control: __control
 
+    /*! This property holds whether the user can move the tabs.
+        Tabs are not movable by default. */
+    property bool tabsMovable: false
+
     /*! This property holds the horizontal alignment of
         the tab buttons. Supported values are:
         \list
diff --git a/tests/auto/controls/data/tst_tabview.qml b/tests/auto/controls/data/tst_tabview.qml
index 43c42c99e888112cd55355bc70e127c2ac8c7abc..64e2fc44594137362ad5e32f792239ab93a36b14 100644
--- a/tests/auto/controls/data/tst_tabview.qml
+++ b/tests/auto/controls/data/tst_tabview.qml
@@ -274,6 +274,7 @@ TestCase {
         compare(tabView.count, 2)
         verify(tabView.tab1.status === Loader.Ready)
         verify(tabView.tab2.status === Loader.Ready)
+        waitForRendering(tabView)
 
         var column1 = getColumnItem(tabView.tab1, "column1")
         verify(column1 !== null)
@@ -294,7 +295,7 @@ TestCase {
         var mouseareas = populateMouseAreaItems(tabrowItem)
         verify(mouseareas.length, 2)
 
-        var tab1 = mouseareas[0].parent
+        var tab1 = mouseareas[0]
         verify(tab1 !== null)
         //printGeometry(tab1)
 
@@ -302,7 +303,7 @@ TestCase {
         mouseClick(tab1, tab1.width/2, tab1.height/2)
         verify(child1.activeFocus)
 
-        var tab2 = mouseareas[1].parent
+        var tab2 = mouseareas[1]
         verify(tab2 !== null)
         //printGeometry(tab2)