1/* 2 SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org> 3 SPDX-FileCopyrightText: 2011 Nokia Corporation and /or its subsidiary(-ies) <qt-info@nokia.com> 4 5 This file is part of the Qt Components project. 6 7 SPDX-License-Identifier: BSD-3-Clause 8*/ 9 10import QtQuick 2.2 11import "private/TabGroup.js" as Engine 12 13import org.kde.plasma.core 2.0 as PlasmaCore 14import "." 2.0 as PlasmaComponents 15 16/** 17 * Provides a set of pages for a tab-based interface. 18 * 19 * A tabbed interface is made up of tab buttons plus content for each button. A 20 * TabGroup component has, as its children, each page of content in the 21 * interface. These pages can be any QML items but are typically Page 22 * components for a single page of content or PageStack components when a 23 * hierarchical navigation system is required for the tab content. 24 * 25 * If you use Page components for your tab content, the status property of each 26 * page is updated appropriately when the current tab is changed: the current 27 * page has status PageStatus.Active and other pages have the status 28 * PageStatus.Inactive. During page transitions, PageStatus.Activating (for the 29 * page that is becoming the current page) and PageStatus.Deactivating (for the 30 * page that was the current page) statuses are also set. 31 * 32 * @inherit QtQuick.FocusScope 33 */ 34FocusScope { 35 id: root 36 37 /** 38 * The tab that is currently active and visible to the user. 39 * 40 * The currentTab property is initialized to null and is automatically set 41 * to point to the first tab when content is added. You can set the 42 * currentTab at any time to activate a particular tab. 43 */ 44 property Item currentTab 45 46 property list<Item> privateContents 47 // Qt defect: cannot have list as default property 48 default property alias privateContentsDefault: root.privateContents 49 50 onChildrenChanged: { 51 // [0] is containerHost 52 if (children.length > 1) 53 Engine.addTab(children[1]) 54 } 55 56 onPrivateContentsChanged: { 57 Engine.ensureContainers() 58 } 59 60 Component.onCompleted: { 61 // Set first tabs as current if currentTab is not set by application 62 if (currentTab == null && containerHost.children[0] && containerHost.children[0].children[0]) 63 currentTab = containerHost.children[0].children[0] 64 priv.complete = true; 65 } 66 67 Item { 68 id: containerHost 69 objectName: "containerHost" 70 anchors.fill: parent 71 } 72 73 Component { 74 id: tabContainerComponent 75 Item { 76 id: tabContainerItem 77 78 onChildrenChanged: { 79 if (children.length == 0) 80 Engine.removeContainer(tabContainerItem) 81 82 else if (children.length == 1) { 83 children[0].width = width 84 children[0].height = height 85 // tab content created. set the first tab as current (if not set before, and if 86 // child is added after TabGroup has completed) 87 if (priv.complete && root.currentTab == null) 88 root.currentTab = children[0] 89 } 90 } 91 92 onWidthChanged: { 93 if (children.length > 0) 94 children[0].width = width 95 } 96 97 onHeightChanged: { 98 if (children.length > 0) 99 children[0].height = height 100 } 101 102 Component.onDestruction: { 103 if (typeof(root) != "undefined" && !root.currentTab) { 104 // selected one deleted. try to activate the neighbour 105 var removedIndex = -1 106 for (var i = 0; i < containerHost.children.length; i++) { 107 if (containerHost.children[i] == tabContainerItem) { 108 removedIndex = i 109 break 110 } 111 } 112 var newIndex = -1 113 if (removedIndex != -1) { 114 if (removedIndex != containerHost.children.length - 1) 115 newIndex = removedIndex + 1 116 else if (removedIndex != 0) 117 newIndex = removedIndex - 1 118 } 119 120 if (newIndex != -1) 121 root.currentTab = containerHost.children[newIndex].children[0] 122 else 123 root.currentTab = null 124 } 125 } 126 127 function incomingDone() { 128 state = "" 129 if (priv.incomingPage) { 130 if (priv.incomingPage instanceof PlasmaComponents.Page) { 131 priv.incomingPage.status = PlasmaComponents.PageStatus.Active 132 } 133 priv.incomingPage = null 134 } 135 } 136 137 function outgoingDone() { 138 if (priv.outgoingPage) { 139 if (priv.outgoingPage instanceof PlasmaComponents.Page) { 140 priv.outgoingPage.status = PlasmaComponents.PageStatus.Active 141 } 142 priv.outgoingPage.visible = false 143 priv.outgoingPage = null 144 } 145 state = "HiddenLeft" 146 } 147 148 width: parent ? parent.width : 0 149 height: parent ? parent.height : 0 150 state: "HiddenLeft" 151 152 states: [ 153 State { name: ""; PropertyChanges { target: tabContainerItem; opacity: 1.0; x: 0 } }, 154 State { name: "Incoming"; PropertyChanges { target: tabContainerItem; opacity: 1.0; x: 0 } }, 155 156 State { 157 name: "OutgoingLeft" 158 PropertyChanges { 159 target: tabContainerItem 160 opacity: 0.0 161 x: LayoutMirroring.enabled ? root.width : -root.width 162 } 163 }, 164 State { 165 name: "OutgoingRight" 166 PropertyChanges { 167 target: tabContainerItem 168 opacity: 0.0 169 x: LayoutMirroring.enabled ? -root.width : root.width 170 } 171 }, 172 173 State { 174 name: "HiddenLeft" 175 PropertyChanges { 176 target: tabContainerItem 177 opacity: 0.0 178 x: LayoutMirroring.enabled ? -root.width : root.width 179 } 180 }, 181 182 State { 183 name: "HiddenRight" 184 PropertyChanges { 185 target: tabContainerItem 186 opacity: 0.0 187 x: LayoutMirroring.enabled ? root.width : -root.width 188 } 189 } 190 ] 191 192 transitions: [ 193 Transition { 194 to: "Incoming" 195 enabled: root.visible 196 SequentialAnimation { 197 ScriptAction { script: root.clip = true } 198 ParallelAnimation { 199 OpacityAnimator { 200 easing.type: Easing.InQuad 201 duration: PlasmaCore.Units.longDuration 202 } 203 XAnimator { 204 easing.type: Easing.InQuad 205 duration: PlasmaCore.Units.longDuration 206 } 207 } 208 ScriptAction { script: {incomingDone(); root.clip = false} } 209 } 210 }, 211 Transition { 212 to: "OutgoingLeft,OutgoingRight" 213 enabled: root.visible 214 SequentialAnimation { 215 ParallelAnimation { 216 OpacityAnimator { 217 easing.type: Easing.InQuad 218 duration: PlasmaCore.Units.longDuration 219 } 220 XAnimator { 221 easing.type: Easing.InQuad 222 duration: PlasmaCore.Units.longDuration 223 } 224 } 225 ScriptAction { script: outgoingDone() } 226 } 227 } 228 ] 229 } 230 } 231 232 QtObject { 233 id: priv 234 property bool reparenting: false 235 property bool complete: false 236 property Item currentTabContainer: root.currentTab ? root.currentTab.parent : null 237 property int currentIndex: 0 238 property Item incomingPage 239 property Item outgoingPage 240 property bool animate: true 241 242 onCurrentTabContainerChanged: { 243 var newCurrentIndex = 0 244 for (var i = 0; i < containerHost.children.length; i++) { 245 if (containerHost.children[i] == currentTabContainer) { 246 newCurrentIndex = i 247 break 248 } 249 } 250 251 currentTabContainer.visible = true 252 incomingPage = currentTabContainer.children[0] 253 incomingPage.visible = true 254 if (incomingPage instanceof PlasmaComponents.Page) { 255 incomingPage.status = PlasmaComponents.PageStatus.Activating 256 } 257 if (currentIndex < newCurrentIndex) { 258 currentTabContainer.state = "HiddenLeft" 259 } else { 260 currentTabContainer.state = "HiddenRight" 261 } 262 if (animate) { 263 currentTabContainer.state = "Incoming" 264 } else { 265 currentTabContainer.incomingDone() 266 } 267 268 if (newCurrentIndex == currentIndex) { 269 return 270 } 271 272 var oldPage = containerHost.children[currentIndex] 273 outgoingPage = oldPage.children[0] 274 if (animate) { 275 if (currentIndex < newCurrentIndex) { 276 oldPage.state = "OutgoingLeft" 277 } else { 278 oldPage.state = "OutgoingRight" 279 } 280 } else { 281 oldPage.outgoingDone() 282 } 283 currentIndex = newCurrentIndex 284 } 285 } 286} 287