/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: http://www.qt-project.org/ ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** **************************************************************************/ import QtQuick 1.0 import "private" /* * * SplitterRow * * SplitterRow is a component that provides a way to layout items horisontally with * a draggable splitter added in-between each item. * * Add items to the SplitterRow by inserting them as child items. The splitter handle * is outsourced as a delegate (handleBackground). For this delegate to work properly, * it will need to contain a mouse area that communicates with the SplitterRow by binding * 'onMouseXChanged: handleDragged(handleIndex)', and 'drag.target: dragTarget'. * * The SplitterRow contains the followin API: * * Component handleBackground - delegate that will be instanciated between each * child item. Inside the delegate, the following properties are available: * int handleIndex - specifies the index of the splitter handle. The handle * between the first and the second item will get index 0, the next handle index 1 etc. * Item handleDragTarget - convenience property that tells which drag target any * inner mouse areas that controls the handle should bind to. * function handleDragged(handleIndex) - function that should be called whenever * the handle is dragged to a new position * * The following properties can optionally be added for each direct child item of SplitterRow: * * real minimumWidth - if present, ensures that the item cannot be resized below the * given value. A value of -1 will disable it. * real maximumWidth - if present, ensures that the item cannot be resized above the * given value. A value of -1 will disable it. * real percentageWidth - if present, should be a value between 0-100. This value specifies * a percentage of the width of the SplitterRow width. If the width of the SplitterRow * change, the width of the item will change as well. 'percentageWidth' have precedence * over 'width', which means that SplitterRow will ignore any assignments done to 'width'. * A value of -1 disables it. * bool expanding - if present, the item will consume all extra space in the SplitterRow, down to * minimumWidth. This means that that 'width', 'percentageWidth' and 'maximumWidth' will be ignored. * There will always be one (and only one) item in the SplitterRow that has this behaviour, and by * default, it will be the last child item of the SplitterRow. Also note that which item that gets * resized upon dragging a handle depends on whether the expanding item is located towards the left * or the right of the handle. * * Example: * * To create a SplitterRow with three items, and let * the center item be the one that should be expanding, one * could do the following: * * SplitterRow { * anchors.fill: parent * * handleBackground: Rectangle { * width: 1 * color: "black" * * MouseArea { * anchors.fill: parent * anchors.leftMargin: -2 * anchors.rightMargin: -2 * drag.axis: Qt.YAxis * drag.target: handleDragTarget * onMouseXChanged: handleDragged(handleIndex) * } * } * * Rectangle { * color: "gray" * width: 200 * } * Rectangle { * property real minimumWidth: 50 * property real maximumWidth: 400 * property bool expanding: true * color: "darkgray" * } * Rectangle { * color: "gray" * width: 200 * } * } */ Item { id: root default property alias items: splitterItems.children property alias handles: splitterHandles.children property Component handleBackground: Rectangle { width:3; color: "black" } clip: true Component.onCompleted: d.init(); onWidthChanged: d.updateLayout(); QtObject { id: d property int expandingIndex: items.length-1 property bool updateOptimizationBlock: true property bool bindingRecursionGuard: false function init() { for (var i=0; i handleIndex) { // Resize item to the left. // Ensure that the handle is not crossing other handles: leftHandle = handles[handleIndex-1] leftItem = items[handleIndex] leftEdge = leftHandle ? (leftHandle.x + leftHandle.width) : 0 handle.x = Math.max(leftEdge, handle.x) newWidth = handle.x - leftEdge if (root.width != 0 && leftItem.percentageWidth != undefined && leftItem.percentageWidth !== -1) leftItem.percentageWidth = newWidth * (100 / root.width) // The next line will trigger 'updateLayout' inside 'propertyChangeListener': leftItem.width = newWidth } else { // Resize item to the right: // Since the first item in the splitter always will have x=0, we need // to ensure that the user cannot drag the handle more left than what // we got space for: var min = d.accumulatedWidth(0, handleIndex+1, true) // Ensure that the handle is not crossing other handles: rightItem = items[handleIndex+1] rightHandle = handles[handleIndex+1] rightEdge = (rightHandle ? rightHandle.x : root.width) handle.x = Math.max(min, Math.max(Math.min((rightEdge - handle.width), handle.x))) newWidth = rightEdge - (handle.x + handle.width) if (root.width != 0 && rightItem.percentageWidth != undefined && rightItem.percentageWidth !== -1) rightItem.percentageWidth = newWidth * (100 / root.width) // The next line will trigger 'updateLayout' inside 'propertyChangeListener': rightItem.width = newWidth } } } } Item { id: splitterItems anchors.fill: parent } Item { id: splitterHandles anchors.fill: parent } Component { // This dummy item becomes a child of all // items it the splitter, just to provide a way // to listed for changes to their width, expanding etc. id: propertyChangeListener Item { id: target width: parent.width property bool expanding: (parent.expanding != undefined) ? parent.expanding : false property real percentageWidth: (parent.percentageWidth != undefined) ? parent.percentageWidth : -1 property real minimumWidth: (parent.minimumWidth != undefined) ? parent.minimumWidth : -1 property real maximumWidth: (parent.maximumWidth != undefined) ? parent.maximumWidth : -1 onPercentageWidthChanged: d.updateLayout(); onMinimumWidthChanged: d.updateLayout(); onMaximumWidthChanged: d.updateLayout(); onExpandingChanged: { // Find out which item that has the expanding flag: for (var i=0; i