diff options
author | Samuel Rødal <samuel.rodal@nokia.com> | 2011-11-02 13:10:37 +0100 |
---|---|---|
committer | Samuel Rødal <samuel.rodal@nokia.com> | 2011-11-02 13:11:10 +0100 |
commit | 7855aaff6271c7544b68e31d744443bfdeb92e96 (patch) | |
tree | fad5fb5d7853914c7731327783ad0085e02aece6 /components | |
parent | 8b27785d0f33c2f4f33768e5ecf6293480d047f3 (diff) | |
parent | 0959b13bd88fe195d0715820c750c3e5c8ba8fb1 (diff) | |
download | qtquickcontrols-7855aaff6271c7544b68e31d744443bfdeb92e96.tar.gz |
Attempted merge of origin/master
Prepare for borkenness
Reviewed-by: Jens
Reviewed-by: Tor Arne
Conflicts:
components/Button.qml
components/ButtonRow.qml
components/CheckBox.qml
components/ChoiceList.qml
components/ContextMenu.qml
components/Dial.qml
components/Frame.qml
components/GroupBox.qml
components/ProgressBar.qml
components/RadioButton.qml
components/ScrollArea.qml
components/ScrollBar.qml
components/Slider.qml
components/SpinBox.qml
components/Switch.qml
components/Tab.qml
components/TabBar.qml
components/TabFrame.qml
components/TableColumn.qml
components/TableView.qml
components/TextArea.qml
components/TextField.qml
components/ToolBar.qml
components/ToolButton.qml
components/custom/BasicButton.qml
components/custom/Button.qml
components/custom/CheckBox.qml
components/custom/ChoiceList.qml
components/custom/GroupBox.qml
components/custom/ProgressBar.qml
components/custom/Slider.qml
components/custom/SpinBox.qml
components/custom/TextField.qml
components/custom/behaviors/ButtonBehavior.qml
components/custom/behaviors/ModalPopupBehavior.qml
components/custom/private/ChoiceListPopup.qml
examples/Gallery.qml
src/qstyleitem.cpp
src/qtmenuitem.h
src/qtoplevelwindow.cpp
src/qwheelarea.h
src/qwindowitem.cpp
src/styleitem/qwheelarea.cpp
src/styleitem/styleitem.pro
Diffstat (limited to 'components')
48 files changed, 1503 insertions, 1465 deletions
diff --git a/components/ApplicationWindow.qml b/components/ApplicationWindow.qml new file mode 100644 index 00000000..bb19a91e --- /dev/null +++ b/components/ApplicationWindow.qml @@ -0,0 +1,41 @@ +import QtQuick 1.1 + +Window { + width: 320 + height: 240 + + property alias toolBar: toolBarArea.data + property alias statusBar: statusBarArea.data + default property alias data: contentArea.data + + SystemPalette {id: syspal} + + + Rectangle { + anchors.fill: parent + color: syspal.button + } + + + Column { + id: toolBarArea + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + } + + Item { + id: contentArea + anchors.top: toolBarArea.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: statusBarArea.top + } + + Column { + id: statusBarArea + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + } +} diff --git a/components/Button.qml b/components/Button.qml index 7dbbb158..9f456d31 100644 --- a/components/Button.qml +++ b/components/Button.qml @@ -3,15 +3,21 @@ import "custom" as Components import QtDesktop 0.1 Components.Button { - id:button + id: button - width: Math.max(80, sizehint.width) - height: Math.max(22, sizehint.height) + implicitWidth: Math.max(72, backgroundItem.implicitWidth) + implicitHeight: Math.max(22, backgroundItem.implicitHeight) - property variant sizehint: backgroundItem.sizeFromContents(80, 6) - property bool defaultbutton - property string hint + property alias containsMouse: tooltip.containsMouse + property bool defaultbutton: false + property string styleHint + TooltipArea { + // Note this will eat hover events + id: tooltip + anchors.fill: parent + text: button.tooltip + } background: StyleItem { id: styleitem @@ -21,18 +27,11 @@ Components.Button { raised: !(pressed || checked) hover: containsMouse text: iconSource === "" ? "" : button.text - focus: button.focus - hint: button.hint + hasFocus: button.focus + hint: button.styleHint // If no icon, let the style do the drawing - activeControl: focus ? "default" : "" - Connections{ - target: button - onToolTipTriggered: styleitem.showTip() - } - function showTip(){ - showToolTip(tooltip); - } + activeControl: defaultbutton ? "default" : "f" } label: Item { @@ -57,6 +56,6 @@ Components.Button { } } } - Keys.onSpacePressed:clicked() + Keys.onSpacePressed:animateClick() } diff --git a/components/ButtonColumn.qml b/components/ButtonColumn.qml new file mode 100644 index 00000000..0ba25f91 --- /dev/null +++ b/components/ButtonColumn.qml @@ -0,0 +1,5 @@ +import QtQuick 1.1 +import "custom" as Components + +Components.ButtonColumn { +} diff --git a/components/CheckBox.qml b/components/CheckBox.qml index 11caa92a..9eed75e8 100644 --- a/components/CheckBox.qml +++ b/components/CheckBox.qml @@ -4,24 +4,34 @@ import QtDesktop 0.1 // jb : Size should not depend on background, we should make it consistent -Components.CheckBox{ - id:checkbox +Components.CheckBox { + id: checkbox property string text - property string hint - width: Math.max(110, backgroundItem.textWidth(text) + 40) - height: 20 + property string styleHint + + implicitWidth: Math.max(120, backgroundItem.implicitWidth) + implicitHeight: backgroundItem.implicitHeight background: StyleItem { - id:styleitem - elementType:"checkbox" - sunken:pressed - on:checked || pressed - hover:containsMouse - text:checkbox.text - enabled:checkbox.enabled - focus:checkbox.focus - hint:checkbox.hint + elementType: "checkbox" + sunken: pressed + on: checked || pressed + hover: containsMouse + enabled: checkbox.enabled + hasFocus: checkbox.activeFocus + hint: checkbox.styleHint + contentHeight: textitem.implicitHeight + contentWidth: textitem.implicitWidth + indicatorWidth + property int indicatorWidth: pixelMetric("indicatorwidth") + 2 + Text { + id: textitem + text: checkbox.text + anchors.left: parent.left + anchors.leftMargin: parent.indicatorWidth + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + elide: Text.ElideRight + } } - Keys.onSpacePressed:checked = !checked } diff --git a/components/ChoiceList.qml b/components/ChoiceList.qml deleted file mode 100644 index aea5b199..00000000 --- a/components/ChoiceList.qml +++ /dev/null @@ -1,64 +0,0 @@ -import QtQuick 2.0 -import "custom" as Components -import "plugin" -import "shadereffects" - -Components.ChoiceList { - - id: choicelist - - property int buttonHeight: backgroundItem.sizeFromContents(100, 18).height - property int buttonWidth: backgroundItem.sizeFromContents(100, 18).width - - property string hint - - height: buttonHeight - width: buttonWidth - topMargin: 4 - bottomMargin: 4 - - background: StyleItem { - anchors.fill: parent - elementType: "combobox" - sunken: pressed - raised: !pressed - hover: containsMouse - enabled: choicelist.enabled - text: currentItemText - focus: choicelist.focus - hint: choicelist.hint - } - - listItem: Item { - id:item - - height: 22 - anchors.left: parent.left - width: choicelist.width - StyleItem { - anchors.fill: parent - elementType: "comboboxitem" - text: itemText - selected: highlighted - - } - } - popupFrame: StyleItem { - property string popupLocation: backgroundItem.styleHint("comboboxpopup") ? "center" : "below" - anchors.leftMargin: backgroundItem.pixelMetric("menuhmargin") + fw - anchors.rightMargin: backgroundItem.pixelMetric("menuhmargin") + fw - anchors.topMargin: backgroundItem.pixelMetric("menuvmargin") + fw - anchors.bottomMargin: backgroundItem.pixelMetric("menuvmargin") + fw - - property string behavior: backgroundItem.styleHint("comboboxpopup") ? "MacOS" : "Windows" - property int fw: backgroundItem.pixelMetric("menupanelwidth"); - QStyleItem { - id: menu - anchors.fill: parent - elementType: "menu" - } - DropShadow { - itemSource: menu - } - } -} diff --git a/components/ComboBox.qml b/components/ComboBox.qml index 61337f10..81fadf1d 100644 --- a/components/ComboBox.qml +++ b/components/ComboBox.qml @@ -1,4 +1,4 @@ -import QtQuick 1.0 +import QtQuick 1.1 import "custom" as Custom import QtDesktop 0.1 @@ -63,7 +63,7 @@ Custom.BasicButton { property alias hoveredIndex: popup.hoveredIndex property alias selectedText: popup.selectedText property alias hoveredText: popup.hoveredText - property string hint + property string styleHint background: StyleItem { anchors.fill: parent @@ -73,20 +73,20 @@ Custom.BasicButton { hover: comboBox.containsMouse enabled: comboBox.enabled text: comboBox.selectedText - focus: comboBox.focus + hasFocus: comboBox.focus + contentHeight: 18 } -// ToDo: adjust margins so that selected popup label -// centers directly above button label when -// popup.centerOnSelectedText === true -// property int leftMargin: 0 -// property int topMargin: 0 -// property int rightMargin: 0 -// property int bottomMargin: 0 +// ToDo: adjust margins so that selected popup label +// centers directly above button label when +// popup.centerOnSelectedText === true - width: backgroundItem.sizeFromContents(100, 18).height + + width: implicitWidth + height: implicitHeight + implicitWidth: Math.max(80, backgroundItem.implicitWidth) + implicitHeight: backgroundItem.implicitHeight onWidthChanged: popup.setMinimumWidth(width) - height: backgroundItem.sizeFromContents(100, 18).height checkable: false onPressedChanged: if (pressed) popup.visible = true diff --git a/components/Dial.qml b/components/Dial.qml index 2972e1f7..61a03347 100644 --- a/components/Dial.qml +++ b/components/Dial.qml @@ -1,22 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project on Qt Labs. +** +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions contained +** in the Technology Preview License Agreement accompanying this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +****************************************************************************/ + import QtQuick 2.0 + import "custom" as Components import QtDesktop 0.1 // jens: ContainsMouse breaks drag functionality -StyleItem { +Item { id: dial - width:100 - height:100 + width: 100 + height: 100 property alias maximumValue: range.maximumValue property alias minimumValue: range.minimumValue property alias containsMouse: mouseArea.containsMouse property alias value: range.value + property alias stepSize: range.stepSize property bool wrapping: false - property bool tickmarks: true // not implemented + property bool tickmarksEnabled: false + property bool activeFocusOnPress: false RangeModel { id: range @@ -40,7 +69,7 @@ StyleItem { } onPressed: { value = valueFromPoint(mouseX, mouseY) - dial.focus = true + if (activeFocusOnPress) dial.focus = true } onReleased:inDrag = false; @@ -76,7 +105,27 @@ StyleItem { return maximumValue - bound(v/100) } } - + StyleItem { + anchors.fill: parent + elementType: "dial" + hasFocus: dial.focus + sunken: mouseArea.pressed + maximum: range.maximumValue * 100 + minimum: range.minimumValue * 100 + value: visualPos * 100 + enabled: dial.enabled + step: range.stepSize * 100 + activeControl: tickmarksEnabled ? "tick" : "" + property double visualPos : range.value + + Behavior on visualPos { + enabled: !mouseArea.inDrag + NumberAnimation { + duration: 300 + easing.type: Easing.OutSine + } + } + } WheelArea { id: wheelarea anchors.fill: parent @@ -94,20 +143,4 @@ StyleItem { value += horizontalDelta/4*step } } - - elementType:"dial" - sunken: mouseArea.pressed - maximum: range.maximumValue*90 - minimum: range.minimumValue*90 - focus:dial.focus - value: visualPos*90 - enabled: dial.enabled - property double visualPos : range.value - Behavior on visualPos { - enabled: !mouseArea.inDrag - NumberAnimation { - duration: 300 - easing.type: Easing.OutSine - } - } } diff --git a/components/Dialog.qml b/components/Dialog.qml new file mode 100644 index 00000000..22fd263f --- /dev/null +++ b/components/Dialog.qml @@ -0,0 +1,123 @@ +import QtQuick 1.1 + +Window { + id: dialog + + width: 400 + height: 200 + + signal closed + signal accepted + signal rejected + signal buttonClicked + + property QtObject clickedButton: null + + property int noRole: 0 + property int acceptRole: 1 + property int rejectRole: 2 + property int helpRole: 3 + + property int ok: 0x00000400 + property int cancel: 0x00400000 + property int close: 0x00200000 + property int help: 0x02000000 + + property int buttons: ok | cancel + + modal: false + + default property alias data: content.data + + Item { + id: content + anchors.topMargin:16 + anchors.margins: 16 + anchors.top: parent.top + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: buttonrow.top + } + + // Dialogs should center on parent + onVisibleChanged: center() + + Row { + property bool mac: (style.style == "mac") + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.margins: 16 + anchors.topMargin:0 + anchors.bottomMargin: style.isMac ? 12 : 8 + spacing: 6 + + Button { + id: helpbutton + property int role: helpRole + visible: buttons & help + text: "Help" + focus: false + Component.onCompleted: if (style.isMac) width = 22 + background: style.isMac ? machelpdelegate : cancelbutton.background + onClicked: { + clickedButton = helpbutton + buttonClicked() + } + Component { + id: machelpdelegate + StyleItem { + anchors.fill: parent + elementType: "machelpbutton" + width: 22 + height: 22 + sunken: helpbutton.pressed + anchors.centerIn: parent + } + } + } + } + Row { + id: buttonrow + spacing: 6 + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 16 + anchors.topMargin: 0 + anchors.bottomMargin: 8 + layoutDirection: style.isMac ? Qt.LeftToRight : Qt.RightToLeft + + Button { + id: cancelbutton + visible: buttons & cancel + property int role: rejectRole + text: "Cancel" + onClicked: { + visible: dialog.visible = false + clickedButton = cancelbutton + rejected() + closed() + buttonClicked(role) + } + } + Button { + id: okbutton + property int role: acceptRole + visible: buttons & ok + text: "OK" + defaultbutton: true + onClicked: { + visible: dialog.visible = false + clickedButton = okbutton + accepted() + closed() + buttonClicked() + } + } + } + StyleItem { + id: style + visible: false + property bool isMac: (style.style == "mac") + + } +} diff --git a/components/Frame.qml b/components/Frame.qml index ee66bd63..bc7de579 100644 --- a/components/Frame.qml +++ b/components/Frame.qml @@ -3,11 +3,12 @@ import "custom" as Components import QtDesktop 0.1 Item { - default property alias children: content.children - width: Math.max(100, content.childrenRect.width + 2 * content.frameWidth) - height: Math.max(100, content.childrenRect.height + 2 * content.frameWidth) + default property alias data: content.data + implicitWidth: adjustToContentSize ? content.childrenRect.width + 2 * content.frameWidth : 30 + implicitHeight: adjustToContentSize ? content.childrenRect.height + 2 * content.frameWidth : 30 property alias raised: style.raised property alias sunken: style.sunken + property bool adjustToContentSize: false StyleItem { id: style anchors.fill: parent diff --git a/components/GroupBox.qml b/components/GroupBox.qml index 8cea42d3..6837ab1f 100644 --- a/components/GroupBox.qml +++ b/components/GroupBox.qml @@ -4,9 +4,8 @@ import QtDesktop 0.1 Components.GroupBox { id: groupbox - width: Math.max(200, contentWidth + sizeHint.width) - height: contentHeight + sizeHint.height + 4 - property variant sizeHint: backgroundItem.sizeFromContents(0, 24) + implicitWidth: Math.max(200, contentWidth + backgroundItem.implicitWidth) + implicitHeight: contentHeight + backgroundItem.implicitHeight + 4 property bool flat: false background : StyleItem { id: styleitem @@ -15,8 +14,9 @@ Components.GroupBox { text: groupbox.title hover: checkbox.containsMouse on: checkbox.checked - focus: checkbox.activeFocus + hasFocus: checkbox.activeFocus activeControl: checkable ? "checkbox" : "" sunken: !flat + contentHeight: (title.length > 0 || checkable) ? 24 : 4 } } diff --git a/components/Label.qml b/components/Label.qml new file mode 100644 index 00000000..fe1e3769 --- /dev/null +++ b/components/Label.qml @@ -0,0 +1,8 @@ +import QtQuick 1.1 + +Text { + id: label + font.pixelSize: 11 + color: pal.text + SystemPalette {id:pal} +} diff --git a/components/MenuItem.qml b/components/MenuItem.qml index 0c8bbc24..15abc4dc 100644 --- a/components/MenuItem.qml +++ b/components/MenuItem.qml @@ -1,7 +1,8 @@ -import QtQuick 1.0 +import QtQuick 1.1 Item { property string text + property string iconName signal hovered signal selected } diff --git a/components/ProgressBar.qml b/components/ProgressBar.qml index 0ee764a8..822c13f9 100644 --- a/components/ProgressBar.qml +++ b/components/ProgressBar.qml @@ -5,12 +5,12 @@ import QtDesktop 0.1 Components.ProgressBar { id:progressbar - property variant sizehint: backgroundItem.sizeFromContents(23, 23) property int orientation: Qt.Horizontal - property string hint + property string styleHint + + implicitWidth: orientation === Qt.Horizontal ? 200 : backgroundItem.implicitHeight + implicitHeight: orientation === Qt.Horizontal ? backgroundItem.implicitHeight : 200 - height: orientation === Qt.Horizontal ? sizehint.height : 200 - width: orientation === Qt.Horizontal ? 200 : sizehint.height SystemPalette {id: syspal} @@ -25,7 +25,9 @@ Components.ProgressBar { maximum: indeterminate ? 0 : progressbar.maximumValue * factor enabled: progressbar.enabled horizontal: progressbar.orientation == Qt.Horizontal - hint: progressbar.hint + hint: progressbar.styleHint + contentWidth: 23 + contentHeight: 23 } } diff --git a/components/RadioButton.qml b/components/RadioButton.qml index 7909e61d..56a2d227 100644 --- a/components/RadioButton.qml +++ b/components/RadioButton.qml @@ -5,22 +5,34 @@ import QtDesktop 0.1 // jb : Size should not depend on background, we should make it consistent Components.CheckBox { - id:radiobutton + id: radiobutton property string text - property string hint - width:110 - height:20 + property string styleHint + + implicitWidth: Math.max(120, backgroundItem.implicitWidth) + implicitHeight: backgroundItem.implicitHeight background: StyleItem { - elementType:"radiobutton" - sunken:pressed - on:checked || pressed - hover:containsMouse - text:radiobutton.text - enabled:radiobutton.enabled - focus:radiobutton.focus - hint:radiobutton.hint + elementType: "radiobutton" + sunken: pressed + on: checked || pressed + hover: containsMouse + enabled: radiobutton.enabled + hasFocus: radiobutton.activeFocus + hint: radiobutton.styleHint + contentHeight: textitem.implicitHeight + contentWidth: textitem.implicitWidth + indicatorWidth + property int indicatorWidth: pixelMetric("indicatorwidth") + 2 + Text { + id: textitem + text: radiobutton.text + anchors.left: parent.left + anchors.leftMargin: parent.indicatorWidth + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + elide: Text.ElideRight + } } - Keys.onSpacePressed:clicked() + Keys.onSpacePressed: {clicked(); checked = !checked; } } diff --git a/components/ScrollArea.qml b/components/ScrollArea.qml index 3e61018d..53421729 100644 --- a/components/ScrollArea.qml +++ b/components/ScrollArea.qml @@ -1,46 +1,32 @@ import QtQuick 2.0 import "custom" as Components +import "private" as Private import QtDesktop 0.1 FocusScope { - id: scrollarea + id: root width: 100 height: 100 - property int frameWidth: frame ? styleitem.pixelMetric("defaultframewidth") : 0; - property int contentHeight : content.childrenRect.height - property int contentWidth: content.childrenRect.width - property alias color: colorRect.color + // Cosmetic propeties property bool frame: true - property bool highlightOnFocus: false property bool frameAroundContents: styleitem.styleHint("framearoundcontents") - property alias verticalValue: vscrollbar.value - property alias horizontalValue: hscrollbar.value - - property alias horizontalScrollBar: hscrollbar - property alias verticalScrollBar: vscrollbar - - property int viewportHeight: height - (hscrollbar.visible ? hscrollbar.height : 0) - 2 * frameWidth - property int viewportWidth: width - (vscrollbar.visible ? vscrollbar.width : 0) - 2 * frameWidth - property bool blockUpdates: false + property bool highlightOnFocus: false + property alias color: colorRect.color // background color + property int frameWidth: frame ? styleitem.frameWidth : 0 - default property alias data: content.data + // Item properties + property alias horizontalScrollBar: scroller.horizontalScrollBar + property alias verticalScrollBar: scroller.verticalScrollBar - property int contentY + // Viewport properties property int contentX - - onContentYChanged: { - blockUpdates = true - vscrollbar.value = contentY - wheelarea.verticalValue = contentY - blockUpdates = false - } - onContentXChanged: { - blockUpdates = true - hscrollbar.value = contentX - wheelarea.horizontalValue = contentX - blockUpdates = false - } + property int contentY + property int contentHeight : content.childrenRect.height + property int contentWidth: content.childrenRect.width + property int viewportHeight: height - (horizontalScrollBar.visible ? verticalScrollBar.height : 0) - 2 * frameWidth + property int viewportWidth: width - (verticalScrollBar.visible ? verticalScrollBar.width : 0) - 2 * frameWidth + default property alias data: content.data Rectangle { id: colorRect @@ -52,109 +38,58 @@ FocusScope { StyleItem { id: styleitem elementType: "frame" - onElementTypeChanged: scrollarea.frameWidth = styleitem.pixelMetric("defaultframewidth"); sunken: true visible: frame anchors.fill: parent - anchors.rightMargin: frame ? (frameAroundContents ? (vscrollbar.visible ? vscrollbar.width + 2 * frameMargins : 0) : -frameWidth) : 0 - anchors.bottomMargin: frame ? (frameAroundContents ? (hscrollbar.visible ? hscrollbar.height + 2 * frameMargins : 0) : -frameWidth) : 0 - anchors.topMargin: frame ? (frameAroundContents ? 0 : -frameWidth) : 0 + anchors.rightMargin: frame ? (frameAroundContents ? (verticalScrollBar.visible ? verticalScrollBar.width + 2 * frameMargins : 0) : 0) : 0 + anchors.bottomMargin: frame ? (frameAroundContents ? (horizontalScrollBar.visible ? horizontalScrollBar.height + 2 * frameMargins : 0) : 0) : 0 + anchors.topMargin: frame ? (frameAroundContents ? 0 : 0) : 0 + property int frameWidth property int scrollbarspacing: styleitem.pixelMetric("scrollbarspacing"); property int frameMargins : frame ? scrollbarspacing : 0 - property int frameoffset: style === "mac" ? -1 : 0 + Component.onCompleted: frameWidth = styleitem.pixelMetric("defaultframewidth"); + } + + onContentYChanged: { + scroller.blockUpdates = true + verticalScrollBar.value = contentY + scroller.verticalValue = contentY + scroller.blockUpdates = false + } + + onContentXChanged: { + scroller.blockUpdates = true + horizontalScrollBar.value = contentX + scroller.horizontalValue = contentX + scroller.blockUpdates = false } Item { - id: flickable + id: clipper anchors.fill: styleitem anchors.margins: frameWidth clip: true - Item { id: content - x: -scrollarea.contentX - y: -scrollarea.contentY + x: -root.contentX + y: -root.contentY } } - WheelArea { - id: wheelarea - anchors.fill: parent - horizontalMinimumValue: hscrollbar.minimumValue - horizontalMaximumValue: hscrollbar.maximumValue - verticalMinimumValue: vscrollbar.minimumValue - verticalMaximumValue: vscrollbar.maximumValue - - onVerticalValueChanged: { - if (!blockUpdates) - contentY = verticalValue - } - onHorizontalValueChanged: { - if (!blockUpdates) - contentX = horizontalValue - } - } - - ScrollBar { - id: hscrollbar - orientation: Qt.Horizontal - property int availableWidth : scrollarea.width - (vscrollbar.visible ? vscrollbar.width : 0) - visible: contentWidth > availableWidth - maximumValue: contentWidth > availableWidth ? scrollarea.contentWidth - availableWidth: 0 - minimumValue: 0 - anchors.bottom: parent.bottom - anchors.bottomMargin: styleitem.frameoffset - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: (frame ? frameWidth : 0) - anchors.rightMargin: { vscrollbar.visible ? scrollbarExtent : (frame ? 1 : 0) } - property int scrollbarExtent : styleitem.pixelMetric("scrollbarExtent"); - onValueChanged: { - if (!blockUpdates) - contentX = value - } - } - - ScrollBar { - id: vscrollbar - orientation: Qt.Vertical - property int availableHeight : scrollarea.height - (hscrollbar.visible ? (hscrollbar.height) : 0) - visible: contentHeight > availableHeight - maximumValue: contentHeight > availableHeight ? scrollarea.contentHeight - availableHeight : 0 - minimumValue: 0 - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.topMargin: styleitem.style == "mac" ? 1 : 0 - anchors.rightMargin: styleitem.frameoffset - anchors.bottomMargin: hscrollbar.visible ? hscrollbar.height : styleitem.frameoffset - onValueChanged: { - if (!blockUpdates) - contentY = value - } - } - - Rectangle { - // This is the filled corner between scrollbars - id: cornerFill - anchors.left: vscrollbar.left - anchors.right: vscrollbar.right - anchors.top: hscrollbar.top - anchors.bottom: hscrollbar.bottom - visible: hscrollbar.visible && vscrollbar.visible - SystemPalette { id: syspal } - color: syspal.window + Private.ScrollAreaHelper { + id: scroller + anchors.fill: parent } StyleItem { z: 2 anchors.fill: parent - anchors.topMargin: -4 + anchors.topMargin: -3 anchors.leftMargin: -3 anchors.rightMargin: -5 - anchors.bottomMargin: -6 + anchors.bottomMargin: -5 visible: highlightOnFocus && parent.activeFocus && styleitem.styleHint("focuswidget") elementType: "focusframe" diff --git a/components/ScrollBar.qml b/components/ScrollBar.qml index c7de2270..1fa0aa17 100644 --- a/components/ScrollBar.qml +++ b/components/ScrollBar.qml @@ -13,8 +13,8 @@ Item { property alias value: slider.value property bool scrollToClickposition: styleitem.styleHint("scrollToClickPosition") - width: orientation == Qt.Horizontal ? 200 : internal.scrollbarExtent - height: orientation == Qt.Horizontal ? internal.scrollbarExtent : 200 + implicitWidth: orientation == Qt.Horizontal ? 200 : internal.scrollbarExtent + implicitHeight: orientation == Qt.Horizontal ? internal.scrollbarExtent : 200 onValueChanged: internal.updateHandle() @@ -163,4 +163,5 @@ Item { positionAtMaximum: internal.grooveSize } } + } diff --git a/components/Slider.qml b/components/Slider.qml index 65f971cc..0994f7c2 100644 --- a/components/Slider.qml +++ b/components/Slider.qml @@ -7,17 +7,22 @@ import QtDesktop 0.1 Components.Slider{ id: slider - property bool tickmarksEnabled: true + property bool tickmarksEnabled: false property string tickPosition: "Below" // "Above", "Below", "BothSides" - StyleItem { id:buttonitem; elementType: "slider" } + StyleItem { + id:buttonitem + elementType: "slider" + contentWidth:23 + contentHeight:23 + } - property variant sizehint: buttonitem.sizeFromContents(23, 23) property int orientation: Qt.Horizontal - height: orientation === Qt.Horizontal ? sizehint.height : 200 - width: orientation === Qt.Horizontal ? 200 : sizehint.height - property string hint; + implicitWidth: orientation === Qt.Horizontal ? 200 : buttonitem.implicitHeight + implicitHeight: orientation === Qt.Horizontal ? buttonitem.implicitHeight : 200 + + property string styleHint; groove: StyleItem { anchors.fill:parent @@ -29,8 +34,8 @@ Components.Slider{ value: slider.value*100 horizontal: slider.orientation == Qt.Horizontal enabled: slider.enabled - focus: slider.focus - hint: slider.hint + hasFocus: slider.focus + hint: slider.styleHint activeControl: tickmarksEnabled ? tickPosition.toLowerCase() : "" } diff --git a/components/SpinBox.qml b/components/SpinBox.qml index 9b476fde..4e6e84b7 100644 --- a/components/SpinBox.qml +++ b/components/SpinBox.qml @@ -8,21 +8,26 @@ Components.SpinBox { property variant __upRect; property variant __downRect; property int __margin: (height -16)/2 - property string hint + property string styleHint // Align height with button - topMargin:__margin - bottomMargin:__margin + topMargin: __margin + bottomMargin: __margin leftMargin:6 rightMargin:6 - StyleItem { id:edititem ; elementType:"edit" ; visible:false } - property int buttonHeight: edititem.sizeFromContents(70, 20).height - property int buttonWidth: edititem.sizeFromContents(70, 20).width + StyleItem { + id:edititem + elementType: "edit" + visible: false + contentWidth: 70 + contentHeight: 20 + } + + implicitWidth: edititem.implicitWidth + implicitHeight: edititem.implicitHeight - height: buttonHeight - width: buttonWidth clip:false background: Item { @@ -70,13 +75,13 @@ Components.SpinBox { elementType: "spinbox" sunken: (downEnabled && downPressed) | (upEnabled && upPressed) hover: containsMouse - focus: spinbox.focus + hasFocus: spinbox.focus enabled: spinbox.enabled value: (upPressed ? 1 : 0) | (downPressed == 1 ? 1<<1 : 0) | (upEnabled ? (1<<2) : 0) | (downEnabled == 1 ? (1<<3) : 0) - hint: spinbox.hint + hint: spinbox.styleHint } } diff --git a/components/SplitterColumn.qml b/components/SplitterColumn.qml new file mode 100644 index 00000000..de6dd25d --- /dev/null +++ b/components/SplitterColumn.qml @@ -0,0 +1,123 @@ +import QtQuick 1.1 +import "custom" as Components + +/* +* +* SplitterColumn +* +* SplitterColumn is a component that provides a way to layout items horisontally with +* a draggable splitter added in-between each item. +* +* Add items to the SplitterColumn by inserting them as child items. The splitter handle +* is outsourced as a delegate (handleBackground). To enable the user to drag the handle, +* it will need to contain a mouse area that communicates with the SplitterColumn by binding +* 'drag.target: handle'. The 'handle' property points to the handle item that embedds +* the delegate. To change handle positions, either change 'y; (or 'height') of 'handle', or +* change the height of the child items inside the SplitterColumn. If you set the visibility +* of a child item to false, the corresponding handle will also be hidden, and the +* SplitterColumn will perform a layout update to fill up available space. +* +* There will always be one (and only one) item in the SplitterColumn that is 'expanding'. +* The expanding item is the child that will get all the remaining space in the SplitterColumn +* (down to its own mimimumHeight/Height) when all other items have been layed out. +* This means that that 'height', 'percentageHeight' and 'maximumHeight' will be ignored for this item. +* By default, the last visible child item of the SplitterColumn will be 'expanding'. +* +* A handle can belong to the item on the left side, or the right side, of the handle. Which one depends +* on the expaning item. If the expanding item is to the right of the handle, the +* handle will belong to the item on the left. If it is to the left, it will belong to the item on the +* right. This will again control which item that gets resized when the user drags a handle, and which +* handle that gets hidden when an item is told to hide. +* +* NB: Since SplitterColumn might modify geometry properties like 'height' and 'y; of child items +* to e.g. ensure they stay within minimumHeight/maximumHeight, explicit expression bindings +* to such properties can easily be broken up by the SplitterColumn, and is not recommended. +* +* The SplitterColumn contains the following 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 handle - convenience property that points to the item where the handle background is +* instanciated as a child. Identical to splitterColumn.handles[handleIndex]. The handle +* background iteself can be accessed through handle.item. +* Modify 'handle[d.offset]' to move the handle (or change 'height' of SplitterColumn child items). +* Item splitterItem - convenience property that points to the child item that the handle controls. +* Also refer to information about the expanding item above. +* Item splitterColumn - points to the SplitterColumn that the handle is in. +* List<Item> items - contains the list of child items in the SplitterColumn. Currently read-only. +* List<Item> handles - contains the list of splitter handles in the SplitterColumn. Note that this list will +* be populated after all child items has completed, so accessing it from Component.onCompleted +* inside a SplitterColumn child item will not work. To get to the handle background, access the +* 'background' property of the handle, e.g. handles[0].background. Read-only. +* real preferredHeight/Height - contains the accumulated with of all child items and handles, except +* the expanding item. If the expanding item has a minimum height, the minimum height will +* be included. +* +* The following attached properties can be used for each child item of SplitterColumn: +* +* real Splitter.minimumSize - ensures that the item cannot be resized below the +* given pixelvalue. A value of -1 will disable it. +* real Splitter.maximumSize - ensures that the item cannot be resized above the +* given pixelvalue. A value of -1 will disable it. +* real percentageHeight - This value specifies a percentage (0 - 100) of the height of the +* SplitterColumn height. If the height of the SplitterColumn change, the height of the item will +* change as well. 'percentageHeight' have precedence over 'height', which means that +* SplitterColumn will ignore any assignments to 'height'. A value of -1 disables it. +* bool Splitter.expanding - See explanation of 'expanding' above. If set to true, the current item +* will be the expanding item in the SplitterColumn. If set to false, the SplitterColumn will +* autmatically choose the last visible child of the SplitterColumn as expanding instead. +* int Splitter.itemIndex - will be assigned a read-only value with the item index. Can be used to e.g. look-up +* the handles sourrounding the item (parent.handles[itemIndex]) +* +* Example: +* +* To create a SplitterColumn with three items, and let +* the center item be the one that should be expanding, one +* could do the following: +* +* SplitterColumn { +* anchors.fill: parent +* +* Rectangle { +* Splitter.maximumSize: 400 +* color: "gray" +* height: 200 +* } +* Rectangle { +* Splitter.minimumSize: 50 +* Splitter.expanding: true +* color: "darkgray" +* } +* Rectangle { +* color: "gray" +* height: 200 +* } +* } +*/ + +Components.Splitter { + orientation: Qt.Vertical + handleBackground: StyleItem { + id: styleitem + elementType: "splitter" + height: handleWidth != -1 ? handleWidth : pixelMetric("splitterwidth") + horizontal: false + property alias pressed: mouseArea.pressed + property bool dragged: mouseArea.drag.active + + MouseArea { + id: mouseArea + anchors.fill: parent + anchors.topMargin: (parent.height <= 1) ? -2 : 0 + anchors.bottomMargin: (parent.height <= 1) ? -2 : 0 + drag.axis: Qt.XandYAxis // Why doesn't X-axis work? + drag.target: handle + CursorArea { + anchors.fill: parent + cursor: CursorArea.SplitVCursor + } + } + } +} diff --git a/components/SplitterRow.qml b/components/SplitterRow.qml index c6012e4c..eb0fd17f 100644 --- a/components/SplitterRow.qml +++ b/components/SplitterRow.qml @@ -2,26 +2,124 @@ import QtQuick 1.1 import "custom" as Components import QtDesktop 0.1 -Components.SplitterRow { +/* +* +* 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). To enable the user to drag the handle, +* it will need to contain a mouse area that communicates with the SplitterRow by binding +* 'drag.target: handle'. The 'handle' property points to the handle item that embedds +* the delegate. To change handle positions, either change 'x' (or 'width') of 'handle', or +* change the width of the child items inside the SplitterRow. If you set the visibility +* of a child item to false, the corresponding handle will also be hidden, and the +* SplitterRow will perform a layout update to fill up available space. +* +* There will always be one (and only one) item in the SplitterRow that is 'expanding'. +* The expanding item is the child that will get all the remaining space in the SplitterRow +* (down to its own mimimumWidth/Height) when all other items have been layed out. +* This means that that 'width', 'percentageWidth' and 'maximumWidth' will be ignored for this item. +* By default, the last visible child item of the SplitterRow will be 'expanding'. +* +* A handle can belong to the item on the left side, or the right side, of the handle. Which one depends +* on the expaning item. If the expanding item is to the right of the handle, the +* handle will belong to the item on the left. If it is to the left, it will belong to the item on the +* right. This will again control which item that gets resized when the user drags a handle, and which +* handle that gets hidden when an item is told to hide. +* +* NB: Since SplitterRow might modify geometry properties like 'width' and 'x' of child items +* to e.g. ensure they stay within minimumWidth/maximumWidth, explicit expression bindings +* to such properties can easily be broken up by the SplitterRow, and is not recommended. +* +* The SplitterRow contains the following 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 handle - convenience property that points to the item where the handle background is +* instanciated as a child. Identical to splitterRow.handles[handleIndex]. The handle +* background iteself can be accessed through handle.item. +* Modify 'handle[d.offset]' to move the handle (or change 'width' of SplitterRow child items). +* Item splitterItem - convenience property that points to the child item that the handle controls. +* Also refer to information about the expanding item above. +* Item splitterRow - points to the SplitterRow that the handle is in. +* List<Item> items - contains the list of child items in the SplitterRow. Currently read-only. +* List<Item> handles - contains the list of splitter handles in the SplitterRow. Note that this list will +* be populated after all child items has completed, so accessing it from Component.onCompleted +* inside a SplitterRow child item will not work. To get to the handle background, access the +* 'background' property of the handle, e.g. handles[0].background. Read-only. +* real preferredWidth/Height - contains the accumulated with of all child items and handles, except +* the expanding item. If the expanding item has a minimum width, the minimum width will +* be included. +* +* The following attached properties can optionally be used for each child item of SplitterRow: +* +* real Splitter.minimumSize - ensures that the item cannot be resized below the +* given pixelvalue. A value of -1 will disable it. +* real Splitter.maximumSixe - ensures that the item cannot be resized above the +* given value. A value of -1 will disable it. +* real Splitter.percentageSize - This value specifies a percentage (0 - 100) 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 to 'width'. A value of -1 disables it. +* bool Splitter.expanding - See explanation of 'expanding' above. If set to true, the current item +* will be the expanding item in the SplitterRow. If set to false, the SplitterRow will +* autmatically choose the last visible child of the SplitterRow as expanding instead. +* int Splitter.itemIndex - will be assigned a read-only value with the item index. Can be used to e.g. look-up +* the handles sourrounding the item (parent.handles[itemIndex]) +* +* 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 +* +* Rectangle { +* Splitter.maximumWidth: 400 +* color: "gray" +* width: 200 +* } +* Rectangle { +* Splitter.minimumWidth: 50 +* Splitter.expanding: true +* color: "darkgray" +* } +* Rectangle { +* color: "gray" +* width: 200 +* } +* } +*/ + + +Components.Splitter { + orientation: Qt.Horizontal handleBackground: StyleItem { - id: styleitem - elementType: "splitter" - width: pixelMetric("splitterwidth") - property alias pressed: mouseArea.pressed - property bool dragged: mouseArea.drag.active + id: styleitem + elementType: "splitter" + width: handleWidth != -1 ? handleWidth : pixelMetric("splitterwidth") + property alias pressed: mouseArea.pressed + property bool dragged: mouseArea.drag.active - MouseArea { - id: mouseArea - anchors.fill: parent - anchors.leftMargin: (parent.width <= 1) ? -2 : 0 - anchors.rightMargin: (parent.width <= 1) ? -2 : 0 - drag.axis: Qt.YAxis - drag.target: handle + MouseArea { + id: mouseArea + anchors.fill: parent + anchors.leftMargin: (parent.width <= 1) ? -2 : 0 + anchors.rightMargin: (parent.width <= 1) ? -2 : 0 + drag.axis: Qt.YAxis + drag.target: handle - StyleItem { - anchors.fill: parent - cursor: "splithcursor" - } + CursorArea { + anchors.fill: parent + cursor: CursorArea.SplitHCursor } + } } } diff --git a/components/StatusBar.qml b/components/StatusBar.qml new file mode 100644 index 00000000..92bea256 --- /dev/null +++ b/components/StatusBar.qml @@ -0,0 +1,12 @@ +import QtQuick 1.1 +import "." +import "custom" as Components + +Item { + width: parent ? parent.width : 200 + height: 24 + StyleItem { + anchors.fill: parent + elementType: "statusbar" + } +} diff --git a/components/Switch.qml b/components/Switch.qml deleted file mode 100644 index 9031e8ce..00000000 --- a/components/Switch.qml +++ /dev/null @@ -1,21 +0,0 @@ -import QtQuick 2.0 -import "custom" as Components - -Components.Switch { - id:widget - minimumWidth:100 - minimumHeight:30 - - groove:StyleItem { - elementType:"edit" - sunken: true - } - - handle: StyleItem { - elementType:"button" - width:widget.width/2 - height:widget.height-4 - hover:containsMouse - } -} - diff --git a/components/TabBar.qml b/components/TabBar.qml index 69df6dee..ad70fd9c 100644 --- a/components/TabBar.qml +++ b/components/TabBar.qml @@ -48,7 +48,6 @@ Item { Row { id: tabrow - focus: true property int paintMargins: 1 states: State { @@ -69,34 +68,35 @@ Item { focus:true property int tabindex: index property bool selected : tabFrame.current == index - z: selected ? 1 : -index + z: selected ? 1 : -1 + width: Math.min(implicitWidth, tabbar.width/tabs.length) function updateRect() { - var rect = style.sizeFromContents(textitem.width + tabHSpace + 2, Math.max(style.fontHeight + tabVSpace + 6, 0)) - width = rect.width - height = rect.height + implicitWidth = style.implicitWidth + height = style.implicitHeight } - // Component.onCompleted: print("taboverlap" + tabOverlap + " tabbaseoverlap " + tabBaseOverlap + " overlap " +__overlap + " hspace " + tabHSpace) StyleItem { id: style elementType: "tab" selected: tab.selected info: tabbar.position - text: tabFrame.tabs[index].title + text: tabFrame.tabs[index].title hover: mousearea.containsMouse - focus: tabbar.focus && selected + hasFocus: tabbar.focus && selected property bool first: index === 0 paintMargins: tabrow.paintMargins - activeControl: tabFrame.count == 1 ? "only" : index === 0 ? "beginning" : - index == tabFrame.count-1 ? "end" : "middle" + activeControl: tabFrame.count === 1 ? "only" : index === 0 ? "beginning" : + index === tabFrame.count-1 ? "end" : "middle" anchors.fill: parent anchors.margins: -paintMargins + contentWidth: textitem.width + tabHSpace + 2 + contentHeight: Math.max(style.fontHeight + tabVSpace + 6, 0) Text { id: textitem // Used for size hint visible: false onWidthChanged: updateRect() onHeightChanged: updateRect() - text: tabFrame.tabs[index].title + text: tabFrame.tabs[index].title } } MouseArea { diff --git a/components/TabFrame.qml b/components/TabFrame.qml index 43794937..725bec91 100644 --- a/components/TabFrame.qml +++ b/components/TabFrame.qml @@ -6,23 +6,16 @@ Item { id: tabWidget width: 100 height: 100 - focus: true - property TabBar tabbar property int current: 0 property int count: stack.children.length - property bool frame:true + property bool frame: true property bool tabsVisible: true property string position: "North" default property alias tabs : stack.children + property Item tabBar: tabbarItem onCurrentChanged: __setOpacities() Component.onCompleted: __setOpacities() - onTabbarChanged: { - tabbar.tabFrame = tabWidget - tabbar.anchors.top = tabWidget.top - tabbar.anchors.left = tabWidget.left - tabbar.anchors.right = tabWidget.right - } property int __baseOverlap : frameitem.pixelMetric("tabbaseoverlap")// add paintmargins; function __setOpacities() { @@ -31,10 +24,18 @@ Item { } } - function addTab(component) { - var tab = component.createObject(null); + Component { + id: tabcomp + Tab {} + } + + function addTab(component, title) { + var tab = tabcomp.createObject(this); + component.createObject(tab) tab.parent = stack - current = count-1 + tab.title = title + __setOpacities() + return tab } function removeTab(id) { @@ -49,9 +50,9 @@ Item { z: style == "oxygen" ? 1 : 0 elementType: "tabframe" info: position - value: tabbar && tabsVisible && tabbar.tab(current) ? tabbar.tab(current).x : 0 - minimum: tabbar && tabsVisible && tabbar.tab(current) ? tabbar.tab(current).width : 0 - maximum: tabbar && tabsVisible ? tabbar.tabWidth : width + value: tabbarItem && tabsVisible && tabbarItem.tab(current) ? tabbarItem.tab(current).x : 0 + minimum: tabbarItem && tabsVisible && tabbarItem.tab(current) ? tabbarItem.tab(current).width : 0 + maximum: tabbarItem && tabsVisible ? tabbarItem.tabWidth : width anchors.fill: parent property int frameWidth: pixelMetric("defaultframewidth") @@ -64,27 +65,34 @@ Item { anchors.bottomMargin: anchors.margins + (frameitem.style =="mac" ? 6 : 0) } - anchors.topMargin: tabbar && tabsVisible && position == "North" ? tabbar.height - __baseOverlap : 0 + anchors.topMargin: tabbarItem && tabsVisible && position == "North" ? Math.max(0, tabbarItem.height - __baseOverlap) : 0 states: [ State { name: "South" - when: position == "South" && tabbar!= undefined + when: position == "South" && tabbarItem!= undefined PropertyChanges { target: frameitem anchors.topMargin: 0 - anchors.bottomMargin: tabbar ? tabbar.height - __baseOverlap: 0 + anchors.bottomMargin: tabbarItem ? tabbarItem.height - __baseOverlap: 0 } PropertyChanges { - target: tabbar + target: tabbarItem anchors.topMargin: -__baseOverlap } AnchorChanges { - target: tabbar + target: tabbarItem anchors.top: frameitem.bottom anchors.bottom: undefined } } ] } + TabBar { + id: tabbarItem + tabFrame: tabWidget + anchors.top: tabWidget.top + anchors.left: tabWidget.left + anchors.right: tabWidget.right + } } diff --git a/components/TableColumn.qml b/components/TableColumn.qml index 5142a962..42fb12e4 100644 --- a/components/TableColumn.qml +++ b/components/TableColumn.qml @@ -1,9 +1,12 @@ import QtQuick 2.0 QtObject { - property string caption - property string property + property string title + property string role property int width: 160 + property int x property bool visible: true property int elideMode: Text.ElideRight + property int textAlignment: Text.AlignLeft + property Component delegate } diff --git a/components/TableView.qml b/components/TableView.qml index b813e2ab..257f370f 100644 --- a/components/TableView.qml +++ b/components/TableView.qml @@ -1,5 +1,6 @@ import QtQuick 2.0 import QtDesktop 0.1 +import "private" as Private /* * @@ -41,8 +42,8 @@ import QtDesktop 0.1 * by setting the default header property : * * TableView { -* TableColumn{ property: "column1" ; caption: "Column 1" ; width:100} -* TableColumn{ property: "column2" ; caption: "Column 2" ; width:200} +* TableColumn{ role: "column1" ; title: "Column 1" ; width:100} +* TableColumn{ role: "column2" ; title: "Column 2" ; width:200} * model: datamodel * } * @@ -62,45 +63,65 @@ import QtDesktop 0.1 FocusScope{ id: root + property variant model - property int frameWidth: frame ? styleitem.pixelMetric("defaultframewidth") : 0; - property alias contentHeight : tree.contentHeight - property alias contentWidth: tree.contentWidth - property bool frame: true - property bool highlightOnFocus: false - property bool frameAroundContents: styleitem.styleHint("framearoundcontents") - property int sortColumn // Index of currently selected sort column - property bool sortIndicatorVisible: false // enables or disables sort indicator - property string sortIndicatorDirection: "down" // "up" or "down" depending on current state + // Framewidth seems to be 1 regardless of style + property int frameWidth: frame ? frameitem.frameWidth : 0; + width: 200 + height: 200 + // Cosmetic properties + property bool frame: true + property bool frameAroundContents: styleitem.styleHint("framearoundcontents") + property bool highlightOnFocus: false property bool alternateRowColor: true - property alias contentX: tree.contentX - property alias contentY: tree.contentY - - property alias currentIndex: tree.currentIndex // Should this be currentRowIndex? - - property int headerHeight: headerrow.height + property bool headerVisible: true + // Styling properties property Component itemDelegate: standardDelegate property Component rowDelegate: rowDelegate property Component headerDelegate: headerDelegate - property alias cacheBuffer: tree.cacheBuffer + property color backgroundColor: "white" - property bool headerVisible: true + // Sort properties + property int sortColumn // Index of currently selected sort column + property bool sortIndicatorVisible: false // enables or disables sort indicator + property string sortIndicatorDirection: "down" // "up" or "down" depending on current state + // Item properties default property alias header: tree.header + property alias horizontalScrollBar: scroller.horizontalScrollBar + property alias verticalScrollBar: scroller.verticalScrollBar + // Viewport properties + property alias contentX: tree.contentX + property alias contentY: tree.contentY + property alias contentHeight : tree.contentHeight + property alias contentWidth: tree.contentWidth + property alias viewportWidth: scroller.availableWidth + property alias viewportHeight: scroller.availableHeight + property alias count: tree.count + + property alias cacheBuffer: tree.cacheBuffer + property alias currentIndex: tree.currentIndex // Should this be currentRowIndex? + + // Signals signal activated Component { id: standardDelegate Item { + height: Math.max(16, styleitem.implicitHeight) property int implicitWidth: sizehint.paintedWidth + 4 Text { + id: label width: parent.width - anchors.margins: 4 + anchors.margins: 6 + font.pointSize: itemstyle.fontPointSize anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: itemTextAlignment anchors.verticalCenter: parent.verticalCenter elide: itemElideMode text: itemValue ? itemValue : "" @@ -108,12 +129,19 @@ FocusScope{ } Text { id: sizehint + font: label.font text: itemValue ? itemValue : "" - visible:false + visible: false } } } + StyleItem { + id: itemstyle + elementType: "item" + visible:false + } + Component { id: nativeDelegate // This gives more native styling, but might be less performant @@ -133,6 +161,7 @@ FocusScope{ sunken: itemPressed text: itemValue hover: itemContainsMouse + info: itemPosition } } @@ -148,23 +177,25 @@ FocusScope{ Rectangle { id: colorRect - color: "white" + color: backgroundColor anchors.fill: frameitem anchors.margins: frameWidth - anchors.rightMargin: (!frameAroundContents && vscrollbar.visible ? vscrollbar.width : 0) + frameWidth - anchors.bottomMargin: (!frameAroundContents && hscrollbar.visible ? hscrollbar.height : 0) +frameWidth + anchors.rightMargin: (!frameAroundContents && verticalScrollBar.visible ? verticalScrollBar.width : 0) + frameWidth + anchors.bottomMargin: (!frameAroundContents && horizontalScrollBar.visible ? horizontalScrollBar.height : 0) +frameWidth } StyleItem { id: frameitem elementType: "frame" - onElementTypeChanged: scrollarea.frameWidth = styleitem.pixelMetric("defaultframewidth"); + Component.onCompleted: frameWidth = styleitem.pixelMetric("defaultframewidth"); sunken: true visible: frame anchors.fill: parent - anchors.rightMargin: frame ? (frameAroundContents ? (vscrollbar.visible ? vscrollbar.width + 2 * frameMargins : 0) : -frameWidth) : 0 - anchors.bottomMargin: frame ? (frameAroundContents ? (hscrollbar.visible ? hscrollbar.height + 2 * frameMargins : 0) : -frameWidth) : 0 - anchors.topMargin: frame ? (frameAroundContents ? 0 : -frameWidth) : 0 + anchors.rightMargin: frame ? (frameAroundContents ? (verticalScrollBar.visible ? verticalScrollBar.width + 2 * frameMargins : 0) : 0) : 0 + anchors.bottomMargin: frame ? (frameAroundContents ? (horizontalScrollBar.visible ? horizontalScrollBar.height + 2 * frameMargins : 0) : 0) : 0 + anchors.topMargin: frame ? frameAroundContents : 0 + anchors.leftMargin: frame ? frameAroundContents : 0 + property int frameWidth property int scrollbarspacing: styleitem.pixelMetric("scrollbarspacing"); property int frameMargins : frame ? scrollbarspacing : 0 } @@ -182,27 +213,28 @@ FocusScope{ } // Handle vertical scrolling whem dragging mouse outside boundraries - - Timer { running: mousearea.autoincrement; repeat: true; interval: 20 ; onTriggered: incrementCurrentIndex()} - Timer { running: mousearea.autodecrement; repeat: true; interval: 20 ; onTriggered: decrementCurrentIndex()} + Timer { running: mousearea.autoincrement && verticalScrollBar.visible; repeat: true; interval: 20 ; onTriggered: incrementCurrentIndex()} + Timer { running: mousearea.autodecrement && verticalScrollBar.visible; repeat: true; interval: 20 ; onTriggered: decrementCurrentIndex()} onPositionChanged: { if (mouseY > tree.height && pressed) { - if (autoincrement)return - autodecrement = false - autoincrement = true + if (autoincrement) return; + autodecrement = false; + autoincrement = true; } else if (mouseY < 0 && pressed) { - if (autodecrement)return - autoincrement = false - autodecrement = true + if (autodecrement) return; + autoincrement = false; + autodecrement = true; } else { - autoincrement = false - autodecrement = false + autoincrement = false; + autodecrement = false; } - var y = Math.min(contentY + tree.height - 5, Math.max(mouseY + contentY, contentY)) - var newIndex = tree.indexAt(0, y) - tree.currentIndex = tree.indexAt(0, y) + var y = Math.min(contentY + tree.height - 5, Math.max(mouseY + contentY, contentY)); + var newIndex = tree.indexAt(0, y); + if (newIndex >= 0) + tree.currentIndex = tree.indexAt(0, y); } + onPressed: { tree.forceActiveFocus() var x = Math.min(contentWidth - 5, Math.max(mouseX + contentX, 0)) @@ -216,56 +248,82 @@ FocusScope{ } function decrementCurrentIndex() { - tree.blockUpdates = true; + scroller.blockUpdates = true; tree.decrementCurrentIndex(); - wheelarea.verticalValue = contentY/wheelarea.scale; - tree.blockUpdates = false; + scroller.verticalValue = contentY; + scroller.blockUpdates = false; } function incrementCurrentIndex() { - tree.blockUpdates = true; + scroller.blockUpdates = true; tree.incrementCurrentIndex(); - wheelarea.verticalValue = contentY/wheelarea.scale; - tree.blockUpdates = false; + scroller.verticalValue = contentY; + scroller.blockUpdates = false; } ListView { id: tree + property list<TableColumn> header - property bool blockUpdates: false highlightFollowsCurrentItem: true model: root.model - interactive: false + anchors.top: tableColumn.bottom - anchors.topMargin: -frameWidth anchors.left: frameitem.left anchors.right: frameitem.right anchors.bottom: frameitem.bottom anchors.margins: frameWidth - - anchors.rightMargin: (!frameAroundContents && vscrollbar.visible ? vscrollbar.width: 0) + frameWidth - anchors.bottomMargin: (!frameAroundContents && hscrollbar.visible ? hscrollbar.height : 0) + frameWidth + anchors.topMargin: -frameWidth + anchors.rightMargin: (!frameAroundContents && verticalScrollBar.visible ? verticalScrollBar.width: 0) + frameWidth + anchors.bottomMargin: (!frameAroundContents && horizontalScrollBar.visible ? horizontalScrollBar.height : 0) + frameWidth focus: true clip: true + // Fills extra rows with alternate color + Column { + id: rowfiller + property variant rowHeight: Math.max(1, contentHeight / count) + property int rowCount: height/rowHeight + y: contentHeight + width: parent.width + visible: contentHeight > 0 && alternateRowColor && !verticalScrollBar.visible + height: parent.height - contentHeight + Repeater { + model: visible ? rowfiller.rowCount : 0 + StyleItem { + id: rowfill + elementType: "itemrow" + width: rowfiller.width + height: rowfiller.rowHeight + activeControl: (index + count) % 2 === 1 ? "alternate" : "" + } + } + } + Keys.onUpPressed: root.decrementCurrentIndex() Keys.onDownPressed: root.incrementCurrentIndex() Keys.onPressed: { if (event.key == Qt.Key_PageUp) { - vscrollbar.value = vscrollbar.value - tree.height + verticalScrollBar.value = verticalScrollBar.value - tree.height } else if (event.key == Qt.Key_PageDown) - vscrollbar.value = vscrollbar.value + tree.height - } + verticalScrollBar.value = verticalScrollBar.value + tree.height + } onContentYChanged: { - // positionViewAtIndex(currentIndex, ListView.Visible) - // highlight follows item - blockUpdates = true - vscrollbar.value = tree.contentY - blockUpdates = false + scroller.blockUpdates = true + scroller.verticalValue = tree.contentY + verticalScrollBar.value = tree.contentY + scroller.blockUpdates = false + } + + onContentXChanged: { + scroller.blockUpdates = true + scroller.horizontalValue = tree.contentX + horizontalScrollBar.value = tree.contentX + scroller.blockUpdates = false } delegate: Item { @@ -275,6 +333,7 @@ FocusScope{ anchors.margins: frameWidth property int rowIndex: model.index property bool itemAlternateBackground: alternateRowColor && rowIndex % 2 == 1 + property variant itemModelData: hasOwnProperty("modelData") ? modelData : null Loader { id: rowstyle // row delegate @@ -298,16 +357,21 @@ FocusScope{ Loader { id: itemDelegateLoader visible: header[index].visible - sourceComponent: itemDelegate + sourceComponent: header[index].delegate ? header[index].delegate : itemDelegate property variant model: tree.model - property variant itemProperty: header[index].property + property variant role: header[index].role + property variant modelData: itemModelData width: header[index].width - height: item ? item.height : Math.max(16, styleitem.sizeFromContents(16, 16).height) + height: item !== undefined ? item.height : Math.max(16, styleitem.implicitHeight) function getValue() { - if (hasOwnProperty(header[index].property)) - return this[header[index].property] + if (header[index].role.length && hasOwnProperty(header[index].role)) + return this[header[index].role] + else if (modelData && modelData.hasOwnProperty(header[index].role)) + return modelData[header[index].role] + else if (modelData) + return modelData return "" } property variant itemValue: getValue() @@ -316,37 +380,45 @@ FocusScope{ property int rowIndex: rowitem.rowIndex property int columnIndex: index property int itemElideMode: header[index].elideMode + property int itemTextAlignment: header[index].textAlignment } } onWidthChanged: tree.contentWidth = width } } } + + Text{ id:text } Item { id: tableColumn - clip: true + anchors.top: frameitem.top anchors.left: frameitem.left anchors.right: frameitem.right anchors.margins: frameWidth + + clip: true visible: headerVisible - Behavior on height { NumberAnimation{duration:80}} - height: headerVisible ? styleitem.sizeFromContents(text.font.pixelSize, styleitem.fontHeight).height : frameWidth + height: headerVisible ? styleitem.implicitHeight : frameWidth + + Behavior on height { NumberAnimation{ duration: 80 } } Row { id: headerrow - anchors.top: parent.top height:parent.height x: -tree.contentX Repeater { id: repeater - model: header.length + property int targetIndex: -1 property int dragIndex: -1 + + model: header.length + delegate: Item { z:-index width: header[index].width @@ -356,10 +428,13 @@ FocusScope{ Loader { sourceComponent: root.headerDelegate anchors.fill: parent - property string itemValue: header[index].caption + property string itemValue: header[index].title property string itemSort: (sortIndicatorVisible && index == sortColumn) ? (sortIndicatorDirection == "up" ? "up" : "down") : ""; property bool itemPressed: headerClickArea.pressed property bool itemContainsMouse: headerClickArea.containsMouse + property string itemPosition: header.length === 1 ? "only" : + index===header.length-1 ? "end" : + index===0 ? "beginning" : "" } Rectangle{ id: targetmark @@ -368,6 +443,7 @@ FocusScope{ opacity: (index == repeater.targetIndex && repeater.targetIndex != repeater.dragIndex) ? 0.5 : 0 Behavior on opacity { NumberAnimation{duration:160}} color: palette.highlight + SystemPalette{id:palette} } MouseArea{ @@ -420,14 +496,16 @@ FocusScope{ Loader { id: draghandle - parent: tableColumn - sourceComponent: root.headerDelegate - width: header[index].width - height: parent.height - property string itemValue: header[index].caption + property string itemValue: header[index].title property string itemSort: (sortIndicatorVisible && index == sortColumn) ? (sortIndicatorDirection == "up" ? "up" : "down") : ""; property bool itemPressed: headerClickArea.pressed property bool itemContainsMouse: headerClickArea.containsMouse + property string itemPosition + + parent: tableColumn + width: header[index].width + height: parent.height + sourceComponent: root.headerDelegate visible: headerClickArea.pressed opacity: 0.5 } @@ -460,9 +538,9 @@ FocusScope{ header[index].width = minWidth } onPressedChanged: if(pressed)offset=mouseX - StyleItem { + CursorArea { anchors.fill: parent - cursor: "splithcursor" + cursor: CursorArea.SplitHCursor } } } @@ -470,95 +548,28 @@ FocusScope{ } Loader { id: loader - z:-1 - sourceComponent: root.headerDelegate - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: headerrow.bottom - anchors.rightMargin: -2 - width: root.width - headerrow.width property string itemValue property string itemSort property bool itemPressed property bool itemContainsMouse - } - } - - WheelArea { - id: wheelarea - anchors.fill: parent - property int scale: 5 - horizontalMinimumValue: hscrollbar.minimumValue/scale - horizontalMaximumValue: hscrollbar.maximumValue/scale - verticalMinimumValue: vscrollbar.minimumValue/scale - verticalMaximumValue: vscrollbar.maximumValue/scale - - verticalValue: contentY/scale - horizontalValue: contentX/scale - - onVerticalValueChanged: { - if(!tree.blockUpdates) { - contentY = verticalValue * scale - vscrollbar.value = contentY - } - } - - onHorizontalValueChanged: { - if(!tree.blockUpdates) { - contentX = horizontalValue * scale - hscrollbar.value = contentX - } - } - } - - ScrollBar { - id: hscrollbar - orientation: Qt.Horizontal - property int availableWidth: root.width - vscrollbar.width - visible: contentWidth > availableWidth - maximumValue: contentWidth > availableWidth ? tree.contentWidth - availableWidth : 0 - minimumValue: 0 - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: frameWidth - anchors.bottomMargin: styleitem.frameoffset - anchors.rightMargin: vscrollbar.visible ? scrollbarExtent : (frame ? 1 : 0) - onValueChanged: { - if (!tree.blockUpdates) - contentX = value - } - property int scrollbarExtent : styleitem.pixelMetric("scrollbarExtent"); - } + property string itemPosition - ScrollBar { - id: vscrollbar - z:-1 - orientation: Qt.Vertical - // We cannot bind directly to tree.height due to binding loops so we have to redo the calculation here - property int availableHeight : root.height - (hscrollbar.visible ? hscrollbar.height : 0) - tableColumn.height - visible: contentHeight > availableHeight - maximumValue: contentHeight > availableHeight ? tree.contentHeight - availableHeight : 0 - minimumValue: 0 - anchors.rightMargin: styleitem.frameoffset - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.topMargin: styleitem.style == "mac" ? tableColumn.height : 0 - onValueChanged: { - if(!tree.blockUpdates) - time.start() + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: headerrow.bottom + anchors.rightMargin: -2 + sourceComponent: root.headerDelegate + width: root.width - headerrow.width + 2 + visible: root.header.length + z:-1 } - anchors.bottomMargin: hscrollbar.visible ? hscrollbar.height : styleitem.frameoffset - - Keys.onUpPressed: if (tree.currentIndex > 0) tree.currentIndex = tree.currentIndex - 1 - Keys.onDownPressed: if (tree.currentIndex< tree.count - 1) tree.currentIndex = tree.currentIndex + 1 } - Timer{ - id:time - interval: 0 - onTriggered:contentY = vscrollbar.value + Private.ScrollAreaHelper { + id: scroller + anchors.fill: parent + anchors.topMargin: styleitem.style == "mac" ? tableColumn.height + frameWidth: 0 + scrollSpeed: 2 } StyleItem { @@ -573,14 +584,15 @@ FocusScope{ id: styleitem elementType: "header" visible:false - property int frameoffset: style === "mac" ? -1 : 0 + contentWidth: 16 + contentHeight: fontHeight } + StyleItem { id: rowstyleitem - elementType: "item" - visible:false property color textColor: styleHint("textColor") property color highlightedTextColor: styleHint("highlightedTextColor") + elementType: "item" + visible: false } - SystemPalette{id:palette} } diff --git a/components/TextArea.qml b/components/TextArea.qml index f406264a..66c2ea14 100644 --- a/components/TextArea.qml +++ b/components/TextArea.qml @@ -14,25 +14,32 @@ ScrollArea { property alias readOnly: edit.readOnly property bool tabChangesFocus: false property alias font: edit.font + property alias activeFocusOnPress: edit.activeFocusOnPress highlightOnFocus: true property int documentMargins: 4 frame: true + function append (string) { + text += "\n" + string + verticalScrollBar.value = verticalScrollBar.maximumValue + } + Item { anchors.left: parent.left anchors.top: parent.top - height: edit.paintedHeight + area.height - viewportHeight + height: edit.paintedHeight + area.height - viewportHeight + 2 * documentMargins + 4 anchors.margins: documentMargins TextEdit { id: edit wrapMode: TextEdit.WordWrap; - width: 200 + width: area.width + height: area.height selectByMouse: true readOnly: false - focus: true color: syspal.text + SystemPalette { id: syspal colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled diff --git a/components/TextField.qml b/components/TextField.qml index 4350c2c8..e6950422 100644 --- a/components/TextField.qml +++ b/components/TextField.qml @@ -12,16 +12,22 @@ Components.TextField { leftMargin: 6 rightMargin: 6 - height: backgroundItem.sizeFromContents(200, 25).height - width: 200 + implicitWidth: backgroundItem.implicitWidth + implicitHeight: backgroundItem.implicitHeight + clip: false + property string styleHint + background: StyleItem { anchors.fill: parent elementType: "edit" sunken: true - focus: textfield.activeFocus + hasFocus: textfield.activeFocus hover: containsMouse + hint: textfield.styleHint + contentWidth: 200 + contentHeight: 25 } Item{ @@ -36,6 +42,7 @@ Components.TextField { anchors.bottomMargin:-4 anchors.fill: parent visible: textfield.activeFocus + hint: textfield.styleHint elementType: "focusframe" } } diff --git a/components/ToolBar.qml b/components/ToolBar.qml index fbec7973..f2ebc9d7 100644 --- a/components/ToolBar.qml +++ b/components/ToolBar.qml @@ -1,11 +1,11 @@ import QtQuick 2.0 +import "." import "custom" as Components import QtDesktop 0.1 -StyleItem{ +StyleItem { id: toolbar - width: 200 - height: sizeFromContents(32, 32).height + width: parent ? parent.width : 200 + height: implicitHeight elementType: "toolbar" } - diff --git a/components/ToolButton.qml b/components/ToolButton.qml index e128511a..5d597a07 100644 --- a/components/ToolButton.qml +++ b/components/ToolButton.qml @@ -5,30 +5,55 @@ import QtDesktop 0.1 Components.Button { id:button - height: 40; //styleitem.sizeFromContents(32, 32).height - width: 40; //styleitem.sizeFromContents(32, 32).width + property alias containsMouse: tooltip.containsMouse + property string iconName + property string styleHint + property int iconSize: (backgroundItem && backgroundItem.style === "mac" && button.styleHint.indexOf("segmented") !== -1) ? 16 : 24 - StyleItem {elementType: "toolbutton"; id:styleitem } + implicitWidth: backgroundItem.implicitWidth + implicitHeight: backgroundItem.implicitHeight - background: StyleItem { + TooltipArea { + // Note this will eat hover events + id: tooltip anchors.fill: parent + text: button.tooltip + } + + background: StyleItem { id: styleitem + anchors.fill: parent elementType: "toolbutton" on: pressed | checked sunken: pressed raised: containsMouse hover: containsMouse - + info: __position + hint: button.styleHint + contentWidth: Math.max(textitem.paintedWidth, 32) + contentHeight: 32 Text { + id: textitem text: button.text anchors.centerIn: parent visible: button.iconSource == "" } - + } + Image { + id: themeIcon + anchors.centerIn: parent + opacity: enabled ? 1 : 0.5 + smooth: true + sourceSize.width: iconSize + property string iconPath: "image://desktoptheme/" + button.iconName + source: backgroundItem && backgroundItem.hasThemeIcon(iconName) ? iconPath : "" + fillMode: Image.PreserveAspectFit Image { - source: button.iconSource + // Use fallback icon anchors.centerIn: parent - opacity: enabled ? 1 : 0.5 + sourceSize: parent.sourceSize + visible: (themeIcon.status != Image.Ready) + source: visible ? button.iconSource : "" } } } diff --git a/components/components.pro b/components/components.pro index 37a518f3..a761830d 100644 --- a/components/components.pro +++ b/components/components.pro @@ -1,4 +1,4 @@ -TEMPLATE = subdirs # XXX: Avoid call the linker + TEMPLATE = subdirs # XXX: Avoid call the linker TARGETPATH = QtDesktop symbian { @@ -9,15 +9,18 @@ symbian { QML_FILES = \ qmldir \ + Label.qml \ Button.qml \ ComboBox.qml \ Dial.qml \ + Dialog.qml \ ProgressBar.qml \ ScrollBar.qml \ Switch.qml \ TableView.qml \ ToolBar.qml \ ButtonRow.qml \ + ButtonColumn.qml \ Frame.qml \ MenuItem.qml \ Slider.qml \ @@ -31,14 +34,17 @@ QML_FILES = \ SpinBox.qml \ TabFrame.qml \ TextArea.qml \ - ChoiceList.qml \ ScrollArea.qml \ SplitterRow.qml \ + SplitterColumn.qml \ + StatusBar.qml \ TableColumn.qml \ - TextField.qml + TextField.qml \ + ApplicationWindow.qml QML_DIRS = \ custom \ + private \ images qmlfiles.files = $$QML_FILES diff --git a/components/custom/BasicButton.qml b/components/custom/BasicButton.qml index 2a840121..572e8130 100644 --- a/components/custom/BasicButton.qml +++ b/components/custom/BasicButton.qml @@ -9,12 +9,12 @@ Item { property alias containsMouse: behavior.containsMouse property alias checkable: behavior.checkable // button toggles between checked and !checked property alias checked: behavior.checked + property bool activeFocusOnPress: false property Component background: null property Item backgroundItem: backgroundLoader.item property color textColor: syspal.text; - property bool activeFocusOnPress: false property string tooltip signal toolTipTriggered @@ -22,8 +22,20 @@ Item { // implementation property string __position: "only" - width: backgroundLoader.item.width - height: backgroundLoader.item.height + implicitWidth: backgroundLoader.item.width + implicitHeight: backgroundLoader.item.height + + function animateClick() { + behavior.pressed = true + behavior.clicked() + animateClickTimer.start() + } + + Timer { + id: animateClickTimer + interval: 250 + onTriggered: behavior.pressed = false + } Loader { id: backgroundLoader diff --git a/components/custom/ButtonColumn.qml b/components/custom/ButtonColumn.qml index 45a02696..4253db6e 100644 --- a/components/custom/ButtonColumn.qml +++ b/components/custom/ButtonColumn.qml @@ -28,6 +28,12 @@ Column { property bool exclusive: true /* + * Property: styleHint + * [string] Used to indicate special OS specific button types + */ + property string styleHint + + /* * Property: checkedButton * [string] Contains the last checked Button. */ diff --git a/components/custom/ButtonGroup.js b/components/custom/ButtonGroup.js index 19d05fee..5b0d9adb 100644 --- a/components/custom/ButtonGroup.js +++ b/components/custom/ButtonGroup.js @@ -33,6 +33,10 @@ function build() { nonVisibleButtons = []; for (var i = 0, item; (item = self.children[i]); i++) { + + if (item.hasOwnProperty("styleHint")) + item.styleHint = styleHint; + if (!hasChecked(item)) continue; diff --git a/components/custom/ButtonRow.qml b/components/custom/ButtonRow.qml index c5bc9a33..13d1380f 100644 --- a/components/custom/ButtonRow.qml +++ b/components/custom/ButtonRow.qml @@ -20,14 +20,19 @@ Row { /* * Property: exclusive - * [bool=true] Specifies the grouping behavior. If enabled, the checked property on buttons contained + * [bool=false] Specifies the grouping behavior. If enabled, the checked property on buttons contained * in the group will be exclusive. * * Note that a button in an exclusive group will allways be checkable */ - property bool exclusive: true + property bool exclusive: false /* + * Property: styleHint + * [string] Used to indicate special OS specific button types + */ + property string styleHint + /* * Property: checkedButton * [string] Contains the last checked Button. */ diff --git a/components/custom/CheckBox.qml b/components/custom/CheckBox.qml index 5a1a8696..295b41d0 100644 --- a/components/custom/CheckBox.qml +++ b/components/custom/CheckBox.qml @@ -1,14 +1,14 @@ import QtQuick 2.0 import "./behaviors" -Item { +FocusScope { id: checkBox signal clicked property alias pressed: behavior.pressed property alias checked: behavior.checked property alias containsMouse: behavior.containsMouse - property bool activeFocusOnPress: false + property Component background: null property Item backgroundItem: backgroundLoader.item @@ -23,10 +23,14 @@ Item { ButtonBehavior { id: behavior + focus: true anchors.fill: parent checkable: true - onClicked: {if (activeFocusOnPress)checkBox.focus = true; checkBox.clicked()} + onClicked: { + if (checkBox.activeFocusOnPress) + checkBox.forceActiveFocus(); + checkBox.clicked(); + } } - - SystemPalette { id: syspal } + Keys.onSpacePressed: {clicked(); checked = !checked; } } diff --git a/components/custom/ChoiceList.qml b/components/custom/ChoiceList.qml deleted file mode 100644 index 7b4f9ceb..00000000 --- a/components/custom/ChoiceList.qml +++ /dev/null @@ -1,50 +0,0 @@ -import QtQuick 2.0 -import "./private" as Private // for ChoiceListPopup - - -Item { - id: choiceList - - property alias model: popup.model - property alias currentIndex: popup.currentIndex - property alias currentText: popup.currentText - property alias popupOpen: popup.popupOpen - property alias containsMouse: popup.containsMouse - property alias pressed: popup.buttonPressed - - property Component background: null - property Item backgroundItem: backgroundLoader.item - property Component listItem: null - property Component popupFrame: null - - property int leftMargin: 0 - property int topMargin: 0 - property int rightMargin: 0 - property int bottomMargin: 0 - - property string popupBehavior - width: 0 - height: 0 - - property bool activeFocusOnPress: true - - Loader { - id: backgroundLoader - property alias styledItem: choiceList - sourceComponent: background - anchors.fill: parent - property string currentItemText: model.get(currentIndex).text - } - - Private.ChoiceListPopup { - // NB: This ChoiceListPopup is also the mouse area - // for the component (to enable drag'n'release) - id: popup - listItem: choiceList.listItem - popupFrame: choiceList.popupFrame - } - - Keys.onSpacePressed: { choiceList.popupOpen = !choiceList.popupOpen } - Keys.onUpPressed: { if (currentIndex < model.count - 1) currentIndex++ } - Keys.onDownPressed: {if (currentIndex > 0) currentIndex-- } -} diff --git a/components/custom/GroupBox.qml b/components/custom/GroupBox.qml index 1803f90a..1c25fa8d 100644 --- a/components/custom/GroupBox.qml +++ b/components/custom/GroupBox.qml @@ -1,10 +1,10 @@ import QtQuick 2.0 -FocusScope { +Item { id: groupbox - width: Math.max(200, contentWidth + loader.leftMargin + loader.rightMargin) - height: contentHeight + loader.topMargin + loader.bottomMargin + implicitWidth: adjustToContentSize ? Math.max(200, contentWidth + loader.leftMargin + loader.rightMargin) : 100 + implicitHeight: adjustToContentSize ? contentHeight + loader.topMargin + loader.bottomMargin : 100 default property alias data: content.data @@ -17,38 +17,38 @@ FocusScope { property Component background: null property Item backgroundItem: loader.item - property CheckBox checkbox: check + property Item checkbox: check property alias checked: check.checked - + property bool adjustToContentSize: false // Resizes groupbox to fit contents. + // Note when using this, you cannot anchor children Loader { id: loader anchors.fill: parent - property int topMargin: 22 + property int topMargin: title.length > 0 || checkable ? 22 : 4 property int bottomMargin: 4 property int leftMargin: 4 property int rightMargin: 4 - property alias styledItem: groupbox sourceComponent: background - - Item { - id:content - z: 1 - opacity: contentOpacity - anchors.topMargin: loader.topMargin - anchors.leftMargin: 8 - anchors.top:parent.top - anchors.left:parent.left - enabled: (!checkable || checkbox.checked) - } - - CheckBox { - id: check - checked: true - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: loader.topMargin - } + } + CheckBox { + id: check + checked: true + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: loader.topMargin + } + Item { + id:content + z: 1 + focus: true + opacity: contentOpacity + anchors.topMargin: loader.topMargin + anchors.leftMargin: 8 + anchors.rightMargin: 8 + anchors.bottomMargin: 8 + anchors.fill: parent + enabled: (!checkable || checkbox.checked) } } diff --git a/components/custom/ProgressBar.qml b/components/custom/ProgressBar.qml index 3e85e139..6b42470e 100644 --- a/components/custom/ProgressBar.qml +++ b/components/custom/ProgressBar.qml @@ -17,9 +17,6 @@ Item { property int minimumWidth: 0 property int minimumHeight: 0 - width: minimumWidth - height: minimumHeight - property Component background: null property Item backgroundItem: groove.item diff --git a/components/custom/SpinBox.qml b/components/custom/SpinBox.qml index 654c8dc7..657f0f3a 100644 --- a/components/custom/SpinBox.qml +++ b/components/custom/SpinBox.qml @@ -16,12 +16,6 @@ FocusScope { property int rightMargin: 0 property int bottomMargin: 0 - width: Math.max(minimumWidth, - input.width + leftMargin + rightMargin) - - height: Math.max(minimumHeight, - input.height + topMargin + bottomMargin) - property real value: 0.0 property real maximumValue: 99 property real minimumValue: 0 diff --git a/components/custom/Splitter.qml b/components/custom/Splitter.qml new file mode 100644 index 00000000..417a6b31 --- /dev/null +++ b/components/custom/Splitter.qml @@ -0,0 +1,405 @@ +import QtQuick 1.1 +import QtDesktop 0.1 + +Splitter { + id: root + default property alias items: splitterItems.children + property alias handles: splitterHandles.children + property Component handleBackground: Rectangle { width:3; color: "black" } + property int handleWidth: -1 + property real preferredSize: 0 + property int orientation: Qt.Horizontal + + clip: true + Component.onCompleted: d.init(); + onWidthChanged: d.updateLayout(); + onHeightChanged: d.updateLayout(); + + QtObject { + id: d + + property bool horizontal: orientation == Qt.Horizontal + property string size: horizontal ? "width" : "height" + property string minimum: horizontal ? "minimumWidth" : "minimumHeight" + property string maximum: horizontal ? "maximumWidth" : "maximumHeight" + + property string offset: horizontal ? "x" : "y" + property int expandingIndex: -1 + property bool updateLayoutGuard: true + property bool itemWidthGuard: false + property bool itemExpandingGuard: true + + function init() + { + for (var i=0; i<items.length; ++i) { + var item = items[i]; + + item.Splitter.itemIndex = i + // Assign one, and only one, item to be expanding: + if (item.Splitter.expanding === true) { + if (d.expandingIndex === -1 && item.visible === true) + d.expandingIndex = i + else + item.Splitter.expanding = false + } + + // Anchor each item to fill out all space vertically: + if (d.horizontal) { + item.anchors.top = splitterItems.top + item.anchors.bottom = splitterItems.bottom + } else { + item.anchors.left = splitterItems.left + item.anchors.right = splitterItems.right + + } + + // Listen for changes to width and expanding: + propertyChangeListener.createObject(item, {"itemIndex":i}); + if (i < items.length-1) { + // Create a handle for the item, unless its the last: + var handle = handleBackgroundLoader.createObject(splitterHandles, {"handleIndex":i}); + + if (d.horizontal) { + handle.anchors.top = splitterHandles.top + handle.anchors.bottom = splitterHandles.bottom + } else { + handle.anchors.left = splitterHandles.left + handle.anchors.right = splitterHandles.right + } + } + } + + if (d.expandingIndex === -1) { + // INVARIANT: No item was set as expanding. + // We then choose the last visible item instead: + d.expandingIndex = items.length - 1 + for (i=items.length-1; i>=0; --i) { + var item = items[i] + if (item.visible === true) { + d.expandingIndex = i + item = items[i] + break + } + } + item.Splitter.expanding = true + } + + d.itemExpandingGuard = false + d.updateLayoutGuard = false + d.updateLayout() + } + + function accumulatedSize(firstIndex, lastIndex, includeExpandingMinimum) + { + // Go through items and handles, and + // calculate their acummulated width. + var w = 0 + for (var i=firstIndex; i<lastIndex; ++i) { + var item = items[i] + if (item.visible) { + if (i !== d.expandingIndex) + w += item[d.size]; + else if (includeExpandingMinimum && item.Splitter[minimum] != -1) + w += item[minimum] + } + + var handle = handles[i] + if (handle && items[i + ((d.expandingIndex > i) ? 0 : 1)].visible) + w += handle[d.size] + } + return w + } + + function updateLayout() + { + // This function will reposition both handles and + // items according to the _width of the each item_ + if (items.length === 0) + return; + if (d.updateLayoutGuard === true) + return + d.updateLayoutGuard = true + + // Use a temporary variable to store values to avoid breaking + // property bindings when the value does not actually change: + var newValue + + // Ensure all items within min/max: + for (var i=0; i<items.length; ++i) { + if (i !== d.expandingIndex) { + item = items[i]; + // If the item is using percentage width, convert + // that number to real width now: + if (item.Splitter.percentageSize !== -1) { + newValue = item.Splitter.percentageSize * (root[d.size] / 100) + if (newValue !== item[d.size]) + item[d.size] = newValue + } + // Ensure item width is not more than maximumSize: + if (item.Splitter[maximum] !== -1) { + newValue = Math.min(item[d.size], item.Splitter[maximum]) + if (newValue !== item[d.size]) + item[d.size] = newValue + } + // Ensure item width is not more less minimumWidth: + if (item.Splitter[minimum] !== -1) { + newValue = Math.max(item[d.size], item.Splitter[minimum]) + if (newValue !== item[d.size]) + item[d.size] = newValue + } + } + } + + // Special case: set width of expanding item to available space: + newValue = root[d.size] - d.accumulatedSize(0, items.length, false); + var expandingItem = items[d.expandingIndex] + var expandingMinimum = 0 + if (expandingItem.Splitter[minimum] !== -1) + expandingMinimum = expandingItem.Splitter[minimum] + newValue = Math.max(newValue, expandingMinimum) + if (expandingItem[d.size] !== 0 && expandingItem.Splitter.percentageSize !== -1) + expandingItem.Splitter.percentageSize = newValue * (100 / root[d.size]) + if (expandingItem[d.size] !== newValue) + expandingItem[d.size] = newValue + + // Then, position items and handles according to their width: + var item, lastVisibleItem + var handle, lastVisibleHandle + var newpreferredSize = expandingMinimum - expandingItem[d.size] + + for (i=0; i<items.length; ++i) { + // Position item to the right of the previous visible handle: + item = items[i]; + if (item.visible) { + if (lastVisibleHandle) { + newValue = lastVisibleHandle[d.offset] + lastVisibleHandle[d.size] + if (newValue !== item[d.offset]) + item[d.offset] = newValue + } else { + newValue = 0 + if (newValue !== item[d.offset]) + item[d.offset] = newValue + } + newpreferredSize += item[d.size] + lastVisibleItem = item + } + + // Position handle to the right of the previous visible item. We use an alterative way of + // checking handle visibility because that property might not have updated correctly yet: + handle = handles[i] + if (handle && items[i + ((d.expandingIndex > i) ? 0 : 1)].visible) { + newValue = lastVisibleItem[d.offset] + Math.max(0, lastVisibleItem[d.size]) + if (newValue !== handle[d.offset]) + handle[d.offset] = newValue + newpreferredSize += handle[d.size] + lastVisibleHandle = handle + } + } + + root.preferredSize = newpreferredSize + d.updateLayoutGuard = false + } + } + + Component { + id: handleBackgroundLoader + Loader { + id: myHandle + property int handleIndex: 0 + property Item handle: myHandle + property Item splitterItem: items[handleIndex + ((d.expandingIndex > handleIndex) ? 0 : 1)] + + // 'splitterRow' should be an alias, but that fails to resolve runtime: + property Item splitterRow: root + property Item background: item + + visible: splitterItem.visible + sourceComponent: handleBackground + onWidthChanged: d.updateLayout() + + onXChanged: { + // Moving the handle means resizing an item. Which one, + // left or right, depends on where the expanding item is. + // 'updateLayout' will override in case new width violates max/min. + // And 'updateLayout will be triggered when an item changes width. + if (d.updateLayoutGuard) + return + + var leftHandle, leftItem, rightItem, rightHandle + var leftEdge, rightEdge, newWidth, leftStopX, rightStopX + var i + + if (d.expandingIndex > handleIndex) { + // Resize item to the left. + // Ensure that the handle is not crossing other handles. So + // find the first visible handle to the left to determine the left edge: + leftEdge = 0 + for (i=handleIndex-1; i>=0; --i) { + leftHandle = handles[i] + if (leftHandle.visible) { + leftEdge = leftHandle[d.offset] + leftHandle[d.size] + break; + } + } + + // Ensure: leftStopX >= myHandle[d.offset] >= rightStopX + var min = d.accumulatedSize(handleIndex+1, items.length, true) + rightStopX = root[d.size] - min - myHandle[d.size] + leftStopX = Math.max(leftEdge, myHandle[d.offset]) + myHandle[d.offset] = Math.min(rightStopX, Math.max(leftStopX, myHandle[d.offset])) + + newWidth = myHandle[d.offset] - leftEdge + leftItem = items[handleIndex] + if (root[d.size] != 0 && leftItem.Splitter.percentageSize !== -1) + leftItem.Splitter.percentageSize = newWidth * (100 / root[d.size]) + // The next line will trigger 'updateLayout' inside 'propertyChangeListener': + leftItem[d.size] = newWidth + } else { + // Resize item to the right. + // Ensure that the handle is not crossing other handles. So + // find the first visible handle to the right to determine the right edge: + rightEdge = root[d.size] + for (i=handleIndex+1; i<handles.length; ++i) { + rightHandle = handles[i] + if (rightHandle.visible) { + rightEdge = rightHandle[d.offset] + break; + } + } + + // Ensure: leftStopX <= myHandle[d.offset] <= rightStopX + var min = d.accumulatedSize(0, handleIndex+1, true) + leftStopX = min - myHandle[d.size] + rightStopX = Math.min((rightEdge - myHandle[d.size]), myHandle[d.offset]) + myHandle[d.offset] = Math.max(leftStopX, Math.min(myHandle[d.offset], rightStopX)) + + newWidth = rightEdge - (myHandle[d.offset] + myHandle[d.size]) + rightItem = items[handleIndex+1] + if (root[d.size] !== 0 && rightItem[d.percentageSize] !== -1) + rightItem.Splitter.percentageSize = newWidth * (100 / root[d.size]) + // The next line will trigger 'updateLayout' inside 'propertyChangeListener': + rightItem[d.size] = 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 listen for changes to their width, expanding etc. + id: propertyChangeListener + Item { + id: target + width: parent[d.size] + property bool expanding: parent.Splitter.expanding + property real percentageSize: parent.Splitter.percentageSize + property real minimumWidth: parent.Splitter[d.minimum] + property real maximumSize: parent.Splitter[d.maximum] + property int itemIndex: parent.Splitter.itemIndex + + onPercentageSizeChanged: d.updateLayout(); + onMinimumWidthChanged: d.updateLayout(); + onMaximumSizeChanged: d.updateLayout(); + onExpandingChanged: updateExpandingIndex() + + function updateExpandingIndex() + { + // The following code is needed to avoid a binding + // loop, since we might change 'expanding' again to a different item: + if (d.itemExpandingGuard === true) + return + d.itemExpandingGuard = true + // break binding: + expanding = false + + // 'expanding' follows radio button behavior: + // First, find the new expanding item: + var newIndex = items.length-1 + for (var i=0; i<items.length; ++i) { + var item = items[i] + if (i !== d.expandingIndex && item.Splitter.expanding === true && item.visible === true) { + newIndex = i + break + } + } + item = items[newIndex] + if (item.visible === false) { + // So now we ended up with the last item in the splitter to be + // expanding, but it turns out to not be visible. So we need to + // traverse backwards again to find one that is visible... + for (i=items.length-2; i>=0; --i) { + var item = items[i] + if (item.visible === true) { + newIndex = i + item = items[newIndex] + break + } + } + } + + // Tell the found item that it is expanding: + if (item.Splitter.expanding !== true) + item.Splitter.expanding = true + // ...and the old one that it is not: + if (newIndex !== d.expandingIndex) { + item = items[d.expandingIndex] + if (item.Splitter.expanding !== false) + item.Splitter.expanding = false + } + // update index: + d.expandingIndex = newIndex + d.updateLayout(); + // recreate binding: + expanding = function() { return parent.Splitter.expanding } + d.itemExpandingGuard = false + } + + function handleSizeChanged() { + // We need to update the layout. + // The following code is needed to avoid a binding + // loop, since we might change 'width' again to a different value: + if (d.itemWidthGuard === true) + return + d.itemWidthGuard = true + // Break binding: + this[d.size] = 0 + + d.updateLayout() + + // Restablish binding: + width = function() { return parent[d.size]; } + d.itemWidthGuard = false + } + + onWidthChanged: handleSizeChanged() + onHeightChanged: handleSizeChanged() + onVisibleChanged: { + // Hiding the expanding item forces us to + // select a new one (and therefore not recommended): + if (d.expandingIndex === itemIndex) { + updateExpandingIndex() + } else { + if (visible) { + // Try to keep all items within the SplitterRow. When an item + // has been hidden, the expanding item might no longer be large enough + // to give away space to the new items width. So we need to resize: + var overflow = d.accumulatedSize(0, items.length, true) - root[d.size]; + if (overflow > 0) + parent[d.size] -= overflow + } + d.updateLayout() + } + } + } + } +} diff --git a/components/custom/SplitterRow.qml b/components/custom/SplitterRow.qml deleted file mode 100644 index af8de528..00000000 --- a/components/custom/SplitterRow.qml +++ /dev/null @@ -1,494 +0,0 @@ -import QtQuick 1.1 -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). To enable the user to drag the handle, -* it will need to contain a mouse area that communicates with the SplitterRow by binding -* 'drag.target: handle'. The 'handle' property points to the handle item that embedds -* the delegate. To change handle positions, either change 'x' (or 'width') of 'handle', or -* change the width of the child items inside the SplitterRow. If you set the visibility -* of a child item to false, the corresponding handle will also be hidden, and the -* SplitterRow will perform a layout update to fill up available space. -* -* There will always be one (and only one) item in the SplitterRow that is 'expanding'. -* The expanding item is the child that will get all the remaining space in the SplitterRow -* (down to its own mimimumSize) when all other items have been layed out. -* This means that that 'width', 'percentageWidth' and 'maximumWidth' will be ignored for this item. -* By default, the last visible child item of the SplitterRow will be 'expanding'. -* -* A handle can belong to the item on the left side, or the right side, of the handle. Which one depends -* on the expaning item. If the expanding item is to the right of the handle, the -* handle will belong to the item on the left. If it is to the left, it will belong to the item on the -* right. This will again control which item that gets resized when the user drags a handle, and which -* handle that gets hidden when an item is told to hide. -* -* NB: Since SplitterRow might modify geometry properties like 'width' and 'x' of child items -* to e.g. ensure they stay within minimumWidth/maximumWidth, explicit expression bindings -* to such properties can easily be broken up by the SplitterRow, and is not recommended. -* -* The SplitterRow contains the following 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 handle - convenience property that points to the item where the handle background is -* instanciated as a child. Identical to splitterRow.handles[handleIndex]. The handle -* background iteself can be accessed through handle.item. -* Modify 'handle.x' to move the handle (or change 'width' of SplitterRow child items). -* Item splitterItem - convenience property that points to the child item that the handle controls. -* Also refer to information about the expanding item above. -* Item splitterRow - points to the SplitterRow that the handle is in. -* List<Item> items - contains the list of child items in the SplitterRow. Currently read-only. -* List<Item> handles - contains the list of splitter handles in the SplitterRow. Note that this list will -* be populated after all child items has completed, so accessing it from Component.onCompleted -* inside a SplitterRow child item will not work. To get to the handle background, access the -* 'background' property of the handle, e.g. handles[0].background. Read-only. -* real preferredWidth - contains the accumulated with of all child items and handles, except -* the expanding item. If the expanding item has a minimum width, the minimum width will -* be included. -* -* The following properties can optionally be added for each child item of SplitterRow: -* -* real minimumWidth - ensures that the item cannot be resized below the -* given value. A value of -1 will disable it. -* real maximumWidth - ensures that the item cannot be resized above the -* given value. A value of -1 will disable it. -* real percentageWidth - This value specifies a percentage (0 - 100) 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 to 'width'. A value of -1 disables it. -* bool expanding - See explanation of 'expanding' above. If set to true, the current item -* will be the expanding item in the SplitterRow. If set to false, the SplitterRow will -* autmatically choose the last visible child of the SplitterRow as expanding instead. -* int itemIndex - will be assigned a read-only value with the item index. Can be used to e.g. look-up -* the handles sourrounding the item (parent.handles[itemIndex]) -* -* 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: handle -* } -* } -* -* Rectangle { -* property real maximumWidth: 400 -* color: "gray" -* width: 200 -* } -* Rectangle { -* property real minimumWidth: 50 -* 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" } - property real preferredWidth: 0 - clip: true - - Component.onCompleted: d.init(); - onWidthChanged: d.updateLayout(); - - QtObject { - id: d - property int expandingIndex: -1 - property bool updateLayoutGuard: true - property bool itemWidthGuard: false - property bool itemExpandingGuard: true - - function init() - { - for (var i=0; i<items.length; ++i) { - var item = items[i]; - // If the item has an 'itemIndex' defined, assign it a value: - if (item.itemIndex != undefined) - item.itemIndex = i - - // Assign one, and only one, item to be expanding: - if (item.expanding != undefined && item.expanding === true) { - if (d.expandingIndex === -1 && item.visible === true) - d.expandingIndex = i - else - item.expanding = false - } - - // Anchor each item to fill out all space vertically: - item.anchors.top = splitterItems.top - item.anchors.bottom = splitterItems.bottom - - // Listen for changes to width and expanding: - propertyChangeListener.createObject(item, {"itemIndex":i}); - if (i < items.length-1) { - // Create a handle for the item, unless its the last: - var handle = handleBackgroundLoader.createObject(splitterHandles, {"handleIndex":i}); - - handle.anchors.top = splitterHandles.top - handle.anchors.bottom = splitterHandles.bottom - } - } - - if (d.expandingIndex === -1) { - // INVARIANT: No item was set as expanding. - // We then choose the last visible item instead: - d.expandingIndex = items.length - 1 - for (i=items.length-1; i>=0; --i) { - var item = items[i] - if (item.visible === true) { - d.expandingIndex = i - item = items[i] - break - } - } - if (item.expanding != undefined) - item.expanding = true - } - - d.itemExpandingGuard = false - d.updateLayoutGuard = false - d.updateLayout() - } - - function accumulatedWidth(firstIndex, lastIndex, includeExpandingMinimum) - { - // Go through items and handles, and - // calculate their acummulated width. - var w = 0 - for (var i=firstIndex; i<lastIndex; ++i) { - var item = items[i] - if (item.visible) { - if (i !== d.expandingIndex) - w += item.width; - else if (includeExpandingMinimum && item.minimumWidth != undefined && item.minimumWidth != -1) - w += item.minimumWidth - } - - var handle = handles[i] - if (handle && items[i + ((d.expandingIndex > i) ? 0 : 1)].visible) - w += handle.width - } - return w - } - - function updateLayout() - { - // This function will reposition both handles and - // items according to the _width of the each item_ - if (items.length === 0) - return; - if (d.updateLayoutGuard === true) - return - d.updateLayoutGuard = true - - // Use a temporary variable to store values to avoid breaking - // property bindings when the value does not actually change: - var newValue - - // Ensure all items within min/max: - for (var i=0; i<items.length; ++i) { - if (i !== d.expandingIndex) { - item = items[i]; - // If the item is using percentage width, convert - // that number to real width now: - if (item.percentageWidth != undefined && item.percentageWidth !== -1) { - newValue = item.percentageWidth * (root.width / 100) - if (newValue !== item.width) - item.width = newValue - } - // Ensure item width is not more than maximumWidth: - if (item.maximumWidth != undefined && item.maximumWidth != -1) { - newValue = Math.min(item.width, item.maximumWidth) - if (newValue !== item.width) - item.width = newValue - } - // Ensure item width is not more less minimumWidth: - if (item.minimumWidth != undefined && item.minimumWidth != -1) { - newValue = Math.max(item.width, item.minimumWidth) - if (newValue !== item.width) - item.width = newValue - } - } - } - - // Special case: set width of expanding item to available space: - newValue = root.width - d.accumulatedWidth(0, items.length, false); - var expandingItem = items[d.expandingIndex] - var expandingMinimum = 0 - if (expandingItem.minimumWidth != undefined && expandingItem.minimumWidth != -1) - expandingMinimum = expandingItem.minimumWidth - newValue = Math.max(newValue, expandingMinimum) - if (expandingItem.width != 0 && expandingItem.percentageWidth != undefined && expandingItem.percentageWidth !== -1) - expandingItem.percentageWidth = newValue * (100 / root.width) - if (expandingItem.width !== newValue) - expandingItem.width = newValue - - // Then, position items and handles according to their width: - var item, lastVisibleItem - var handle, lastVisibleHandle - var newPreferredWidth = expandingMinimum - expandingItem.width - - for (i=0; i<items.length; ++i) { - // Position item to the right of the previous visible handle: - item = items[i]; - if (item.visible) { - if (lastVisibleHandle) { - newValue = lastVisibleHandle.x + lastVisibleHandle.width - if (newValue !== item.x) - item.x = newValue - } else { - newValue = 0 - if (newValue !== item.x) - item.x = newValue - } - newPreferredWidth += item.width - lastVisibleItem = item - } - - // Position handle to the right of the previous visible item. We use an alterative way of - // checking handle visibility because that property might not have updated correctly yet: - handle = handles[i] - if (handle && items[i + ((d.expandingIndex > i) ? 0 : 1)].visible) { - newValue = lastVisibleItem.x + Math.max(0, lastVisibleItem.width) - if (newValue !== handle.x) - handle.x = newValue - newPreferredWidth += handle.width - lastVisibleHandle = handle - } - } - - root.preferredWidth = newPreferredWidth - d.updateLayoutGuard = false - } - } - - Component { - id: handleBackgroundLoader - Loader { - id: myHandle - property int handleIndex: 0 - property Item handle: myHandle - property Item splitterItem: items[handleIndex + ((d.expandingIndex > handleIndex) ? 0 : 1)] - - // 'splitterRow' should be an alias, but that fails to resolve runtime: - property Item splitterRow: root - property Item background: item - - visible: splitterItem.visible - sourceComponent: handleBackground - onWidthChanged: d.updateLayout() - - onXChanged: { - // Moving the handle means resizing an item. Which one, - // left or right, depends on where the expanding item is. - // 'updateLayout' will override in case new width violates max/min. - // And 'updateLayout will be triggered when an item changes width. - if (d.updateLayoutGuard) - return - - var leftHandle, leftItem, rightItem, rightHandle - var leftEdge, rightEdge, newWidth, leftStopX, rightStopX - var i - - if (d.expandingIndex > handleIndex) { - // Resize item to the left. - // Ensure that the handle is not crossing other handles. So - // find the first visible handle to the left to determine the left edge: - leftEdge = 0 - for (i=handleIndex-1; i>=0; --i) { - leftHandle = handles[i] - if (leftHandle.visible) { - leftEdge = leftHandle.x + leftHandle.width - break; - } - } - - // Ensure: leftStopX >= myHandle.x >= rightStopX - var min = d.accumulatedWidth(handleIndex+1, items.length, true) - rightStopX = root.width - min - myHandle.width - leftStopX = Math.max(leftEdge, myHandle.x) - myHandle.x = Math.min(rightStopX, Math.max(leftStopX, myHandle.x)) - - newWidth = myHandle.x - leftEdge - leftItem = items[handleIndex] - 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. - // Ensure that the handle is not crossing other handles. So - // find the first visible handle to the right to determine the right edge: - rightEdge = root.width - for (i=handleIndex+1; i<handles.length; ++i) { - rightHandle = handles[i] - if (rightHandle.visible) { - rightEdge = rightHandle.x - break; - } - } - - // Ensure: leftStopX <= myHandle.x <= rightStopX - var min = d.accumulatedWidth(0, handleIndex+1, true) - leftStopX = min - myHandle.width - rightStopX = Math.min((rightEdge - myHandle.width), myHandle.x) - myHandle.x = Math.max(leftStopX, Math.min(myHandle.x, rightStopX)) - - newWidth = rightEdge - (myHandle.x + myHandle.width) - rightItem = items[handleIndex+1] - 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 listen 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 - property int itemIndex: 0 - - onPercentageWidthChanged: d.updateLayout(); - onMinimumWidthChanged: d.updateLayout(); - onMaximumWidthChanged: d.updateLayout(); - onExpandingChanged: updateExpandingIndex() - - function updateExpandingIndex() - { - // The following code is needed to avoid a binding - // loop, since we might change 'expanding' again to a different item: - if (d.itemExpandingGuard === true) - return - d.itemExpandingGuard = true - // break binding: - expanding = false - - // 'expanding' follows radio button behavior: - // First, find the new expanding item: - var newIndex = items.length-1 - for (var i=0; i<items.length; ++i) { - var item = items[i] - if (i !== d.expandingIndex && item.expanding != undefined && item.expanding === true && item.visible === true) { - newIndex = i - break - } - } - item = items[newIndex] - if (item.visible === false) { - // So now we ended up with the last item in the splitter to be - // expanding, but it turns out to not be visible. So we need to - // traverse backwards again to find one that is visible... - for (i=items.length-2; i>=0; --i) { - var item = items[i] - if (item.visible === true) { - newIndex = i - item = items[newIndex] - break - } - } - } - - // Tell the found item that it is expanding: - if (item.expanding != undefined && item.expanding !== true) - item.expanding = true - // ...and the old one that it is not: - if (newIndex !== d.expandingIndex) { - item = items[d.expandingIndex] - if (item.expanding != undefined && item.expanding !== false) - item.expanding = false - } - // update index: - d.expandingIndex = newIndex - d.updateLayout(); - // recreate binding: - expanding = function() { return (parent.expanding != undefined) ? parent.expanding : false; } - d.itemExpandingGuard = false - } - - onWidthChanged: { - // We need to update the layout. - // The following code is needed to avoid a binding - // loop, since we might change 'width' again to a different value: - if (d.itemWidthGuard === true) - return - d.itemWidthGuard = true - // Break binding: - width = 0 - - d.updateLayout() - - // Restablish binding: - width = function() { return parent.width; } - d.itemWidthGuard = false - } - - onVisibleChanged: { - // Hiding the expanding item forces us to - // select a new one (and therefore not recommended): - if (d.expandingIndex === itemIndex) { - updateExpandingIndex() - } else { - if (visible) { - // Try to keep all items within the SplitterRow. When an item - // has been hidden, the expanding item might no longer be large enough - // to give away space to the new items width. So we need to resize: - var overflow = d.accumulatedWidth(0, items.length, true) - root.width; - if (overflow > 0) - parent.width -= overflow - } - d.updateLayout() - } - } - } - } -} diff --git a/components/custom/TextField.qml b/components/custom/TextField.qml index 8d9d3ac1..39792de0 100644 --- a/components/custom/TextField.qml +++ b/components/custom/TextField.qml @@ -24,6 +24,7 @@ FocusScope { property alias echoMode: textInput.echoMode property alias cursorPosition: textInput.cursorPosition property alias inputMethodHints: textInput.inputMethodHints + property alias activeFocusOnPress: textInput.activeFocusOnPress property color textColor: syspal.text property color backgroundColor: syspal.base @@ -75,12 +76,6 @@ FocusScope { return textInput.positionToRectangle(p); } - width: Math.max(minimumWidth, - textInput.width + leftMargin + rightMargin) - - height: Math.max(minimumHeight, - textInput.height + topMargin + bottomMargin) - // Implementation clip: true @@ -106,7 +101,6 @@ FocusScope { TextInput { // see QTBUG-14936 id: textInput selectByMouse:true - focus: true anchors.leftMargin: leftMargin anchors.topMargin: topMargin @@ -118,7 +112,7 @@ FocusScope { anchors.verticalCenter: parent.verticalCenter color: syspal.text - echoMode: passwordMode ? _hints.passwordEchoMode : TextInput.Normal + echoMode: passwordMode ? TextInput.Password : TextInput.Normal } diff --git a/components/custom/components.pro b/components/custom/components.pro index 62e0651d..e063d91d 100644 --- a/components/custom/components.pro +++ b/components/custom/components.pro @@ -17,8 +17,7 @@ QML_FILES = \ ButtonGroup.js \ Button.qml \ CheckBox.qml \ - ChoiceList.qml \ - SplitterRow.qml \ + Splitter.qml \ ProgressBar.qml \ RadioButton.qml \ ScrollDecorator.qml \ diff --git a/components/custom/private/ChoiceListPopup.qml b/components/custom/private/ChoiceListPopup.qml deleted file mode 100644 index 7d6e29a0..00000000 --- a/components/custom/private/ChoiceListPopup.qml +++ /dev/null @@ -1,322 +0,0 @@ -import QtQuick 2.0 - -MouseArea { - id: popup - - // There is no global z-ordering that can stack this popup in front, so we - // need to reparent it to the root item to fake it upon showing the popup. - // In that case, the popup will also fill the whole window to allow the user to - // close the popup by clicking anywhere in the window. Letting the popup act as the mouse - // area for the button that 'owns' it is also nessesary to support drag'n'release behavior. - - // The 'popupframe' delegate will be told to show or hide by assigning - // opacity to 1 or 0, respectively. - - anchors.fill: parent - hoverEnabled: true - - // Set 'popupOpen' to show/hide the popup. The 'state' property is more - // internal, and contains additional states used to protect the popup from - // e.g. receiving mouse clicks while its about to hide etc. - property bool popupOpen: false - - property bool desktopBehavior: true - property int previousCurrentIndex: -1 - property alias model: listView.model - property alias currentIndex: listView.currentIndex - property string currentText: model && currentIndex >= 0 ? model.get(currentIndex).text : "" - - // buttonPressed will be true when the mouse press starts - // while the popup is closed. At that point, this component can be - // seen as a button, and not yet a popup menu: - property bool buttonPressed: false - - property Component listItem - property Component listHighlight - property Component popupFrame - - property Item originalParent: parent - - onPopupOpenChanged: { - if (popupFrameLoader.item === null) - return; - if (popupOpen) { - var oldMouseX = mouseX - - // Reparent to root, so the popup stacks in front: - originalParent = parent; - var p = parent; - while (p.parent != undefined) - p = p.parent - parent = p; - - previousCurrentIndex = currentIndex; - positionPopup(); - popupFrameLoader.item.opacity = 1; - if (oldMouseX === mouseX){ - // Work around bug: mouseX and mouseY does not immidiatly - // update after reparenting and resizing the mouse area: - var pos = originalParent.mapToItem(parent, mouseX, mouseY) - highlightItemAt(pos.x, pos.y); - } else { - highlightItemAt(mouseX, mouseY); - } - listView.forceActiveFocus(); - state = "popupOpen" - } else { - popupFrameLoader.item.opacity = 0; - popup.hideHighlight(); - if (popupFrameLoader.item.opacity !== 0) - state = "popupClosed" - } - } - - Component.onCompleted: { - // In case 'popupOpen' was set to 'true' before - // 'popupFrameLoader' was finished, we open the popup now instead: - if (popup.popupOpen){ - popup.popupOpen = false - popup.popupOpen = true - } - } - - function highlightItemAt(posX, posY) - { - var mappedPos = mapToItem(listView.contentItem, posX, posY); - var indexAt = listView.indexAt(mappedPos.x, mappedPos.y); - if (indexAt == listView.highlightedIndex) - return; - if (indexAt >= 0) { - listView.highlightedIndex = indexAt; - } else { - if(posY > listView.y+listView.height && listView.highlightedIndex+1 < listView.count ) { - listView.highlightedIndex++; - } else if(posY < listView.y && listView.highlightedIndex > 0) { - listView.highlightedIndex--; - } else if(posX < popupFrameLoader.x || posX > popupFrameLoader.x+popupFrameLoader.width) { - popup.hideHighlight(); - } - } - } - - function hideHighlight() { - listView.highlightedIndex = -1; - listView.highlightedItem = null; // will trigger positionHighlight() what will hide the highlight - } - - function positionPopup() { - // Set initial values to top left corner of original parent: - var globalPos = mapFromItem(originalParent, 0, 0); - var newX = globalPos.x; - var newY = globalPos.y - var newW = originalParent.width; - var newH = listView.contentHeight - - switch (popupFrameLoader.item.popupLocation) { - case "center": - // Show centered over original parent with respect to selected item: - var itemHeight = Math.max(listView.contentHeight/listView.count, 0); - var currentItemY = Math.max(currentIndex*itemHeight, 0); - currentItemY += Math.floor(itemHeight/2 - choiceList.height/2); // correct for choiceLists that are higher than items in the list - newY -= currentItemY; - break; - case "below": - case "": - // Show below original parent: - newX -= popupFrameLoader.anchors.leftMargin; - newY += originalParent.height - popupFrameLoader.anchors.topMargin; - break; - } - - // Ensure the popup is inside the window: - if (newX < popupFrameLoader.anchors.leftMargin) - newX = popupFrameLoader.anchors.leftMargin; - else if (newX + newW > popup.width - popupFrameLoader.anchors.rightMargin) - newX = popup.width - popupFrameLoader.anchors.rightMargin - newW; - - if (newY < popupFrameLoader.anchors.topMargin) - newY = popupFrameLoader.anchors.topMargin; - else if (newY + newH > popup.height - popupFrameLoader.anchors.bottomMargin) - newY = popup.height - popupFrameLoader.anchors.bottomMargin - newH; - - // Todo: handle case when the list itself is larger than the window... - - listView.x = newX - listView.y = newY - listView.width = newW - listView.height = newH - } - - Loader { - id: popupFrameLoader - property alias styledItem: popup.originalParent - anchors.fill: listView - anchors.leftMargin: -item.anchors.leftMargin - anchors.rightMargin: -item.anchors.rightMargin - anchors.topMargin: -item.anchors.topMargin - anchors.bottomMargin: -item.anchors.bottomMargin - sourceComponent: popupFrame - onItemChanged: item.opacity = 0 - } - - ListView { - id: listView - focus: true - opacity: popupFrameLoader.item.opacity - boundsBehavior: desktopBehavior ? ListView.StopAtBounds : ListView.DragOverBounds - keyNavigationWraps: !desktopBehavior - highlightFollowsCurrentItem: false // explicitly handled below - - interactive: !desktopBehavior // disable flicking. also disables key handling - onCurrentItemChanged: { - if(desktopBehavior) { - positionViewAtIndex(currentIndex, ListView.Contain); - } - } - - property int highlightedIndex: -1 - onHighlightedIndexChanged: positionViewAtIndex(highlightedIndex, ListView.Contain) - - property variant highlightedItem: null - onHighlightedItemChanged: { - if(desktopBehavior) { - positionHighlight(); - } - } - - function positionHighlight() { - if(!Qt.isQtObject(highlightItem)) - return; - - if(!Qt.isQtObject(highlightedItem)) { - highlightItem.opacity = 0; // hide when no item is highlighted - } else { - highlightItem.x = highlightedItem.x; - highlightItem.y = highlightedItem.y; - highlightItem.width = highlightedItem.width; - highlightItem.height = highlightedItem.height; - highlightItem.opacity = 1; // show once positioned - } - } - - delegate: Item { - id: itemDelegate - width: delegateLoader.item.width - height: delegateLoader.item.height - property int theIndex: index // for some reason the loader can't bind directly to 'index' - - Loader { - id: delegateLoader - property variant model: listView.model - property alias index: itemDelegate.theIndex - property Item styledItem: choiceList - property bool highlighted: theIndex == listView.highlightedIndex - property string itemText: popup.model.get(theIndex).text - sourceComponent: listItem - } - - states: State { - name: "highlighted" - when: index == listView.highlightedIndex - StateChangeScript { - script: { - if(Qt.isQtObject(listView.highlightedItem)) { - listView.highlightedItem.yChanged.disconnect(listView.positionHighlight); - } - listView.highlightedItem = itemDelegate; - listView.highlightedItem.yChanged.connect(listView.positionHighlight); - } - } - - } - } - - function firstVisibleItem() { return indexAt(contentX+10,contentY+10); } - function lastVisibleItem() { return indexAt(contentX+width-10,contentY+height-10); } - function itemsPerPage() { return lastVisibleItem() - firstVisibleItem(); } - - Keys.onPressed: { - // with the ListView !interactive (non-flicking) we have to handle arrow keys - if (event.key == Qt.Key_Up) { - if(!highlightedItem) highlightedIndex = lastVisibleItem(); - else if(highlightedIndex > 0) highlightedIndex--; - } else if (event.key == Qt.Key_Down) { - if(!highlightedItem) highlightedIndex = firstVisibleItem(); - else if(highlightedIndex+1 < model.count) highlightedIndex++; - } else if (event.key == Qt.Key_PageUp) { - if(!highlightedItem) highlightedIndex = lastVisibleItem(); - else highlightedIndex = Math.max(highlightedIndex-itemsPerPage(), 0); - } else if (event.key == Qt.Key_PageDown) { - if(!highlightedItem) highlightedIndex = firstVisibleItem(); - else highlightedIndex = Math.min(highlightedIndex+itemsPerPage(), model.count-1); - } else if (event.key == Qt.Key_Home) { - highlightedIndex = 0; - } else if (event.key == Qt.Key_End) { - highlightedIndex = model.count-1; - } else if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { - if(highlightedIndex != -1) { - listView.currentIndex = highlightedIndex; - } else { - listView.currentIndex = popup.previousCurrentIndex; - } - - popup.popupOpen = false; - } else if (event.key == Qt.Key_Escape) { - listView.currentIndex = popup.previousCurrentIndex; - popup.popupOpen = false; - } - event.accepted = true; // consume all keys while popout has focus - } - - highlight: popup.listHighlight - } - - Timer { - // This is the time-out value for when we consider the - // user doing a press'n'release, and not just a click to - // open the popup: - id: pressedTimer - interval: 400 // Todo: fetch value from style object - } - - onPressed: { - if (state == "popupClosed") { - // Show the popup: - pressedTimer.running = true - popup.popupOpen = true - popup.buttonPressed = true - } - } - - onReleased: { - if (state == "popupOpen" && pressedTimer.running === false) { - // Either we have a 'new' click on the popup, or the user has - // done a drag'n'release. In either case, the user has done a selection: - var mappedPos = mapToItem(listView.contentItem, mouseX, mouseY); - var indexAt = listView.indexAt(mappedPos.x, mappedPos.y); - if(indexAt != -1) - listView.currentIndex = indexAt; - popup.popupOpen = false - } - popup.buttonPressed = false - } - - onPositionChanged: { - if (state == "popupOpen") - popup.highlightItemAt(mouseX, mouseY) - } - - states: [ - State { - name: "popupClosed" - when: popupFrameLoader.item.opacity === 0; - StateChangeScript { - script: parent = originalParent - } - } - ] -} - - - - diff --git a/components/custom/qmldir b/components/custom/qmldir index 8da1fd80..716e44fa 100644 --- a/components/custom/qmldir +++ b/components/custom/qmldir @@ -6,9 +6,9 @@ Button 1.0 Button.qml ButtonColumn 1.0 ButtonColumn.qml ButtonRow 1.0 ButtonRow.qml CheckBox 1.0 CheckBox.qml -ChoiceList 1.0 ChoiceList.qml ProgressBar 1.0 ProgressBar.qml Slider 1.0 Slider.qml SpinBox 1.0 SpinBox.qml TextField 1.0 TextField.qml GroupBox 1.0 GroupBox.qml +Splitter 1.0 Splitter.qml diff --git a/components/private/ScrollAreaHelper.qml b/components/private/ScrollAreaHelper.qml new file mode 100644 index 00000000..7a58d5df --- /dev/null +++ b/components/private/ScrollAreaHelper.qml @@ -0,0 +1,83 @@ +import QtQuick 1.1 +import "../" + +WheelArea { + id: wheelarea + + property alias horizontalScrollBar: hscrollbar + property alias verticalScrollBar: vscrollbar + property int macOffset: styleitem.style == "mac" ? 1 : 0 + property bool blockUpdates: false + property int availableHeight : root.height - (hscrollbar.visible ? hscrollbar.height : 0) + property int availableWidth: root.width - vscrollbar.width + + anchors.fill: parent + anchors.margins: frameWidth + horizontalMinimumValue: hscrollbar.minimumValue + horizontalMaximumValue: hscrollbar.maximumValue + verticalMinimumValue: vscrollbar.minimumValue + verticalMaximumValue: vscrollbar.maximumValue + + onVerticalValueChanged: { + if (!blockUpdates) + verticalScrollBar.value = verticalValue + } + + onHorizontalValueChanged: { + if (!blockUpdates) + horizontalScrollBar.value = horizontalValue + } + + StyleItem { + // This is the filled corner between scrollbars + id: cornerFill + elementType: "scrollareacorner" + width: vscrollbar.width + anchors.right: parent.right + height: hscrollbar.height + anchors.bottom: parent.bottom + visible: hscrollbar.visible && vscrollbar.visible + } + + ScrollBar { + id: hscrollbar + orientation: Qt.Horizontal + visible: contentWidth > availableWidth + maximumValue: contentWidth > availableWidth ? root.contentWidth - availableWidth : 0 + minimumValue: 0 + anchors.bottom: parent.bottom + anchors.leftMargin: parent.macOffset + anchors.bottomMargin: -parent.macOffset + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: vscrollbar.visible ? vscrollbar.width -parent.macOffset: 0 + onValueChanged: { + if (!blockUpdates) { + contentX = value + horizontalValue = value + } + } + } + + ScrollBar { + id: vscrollbar + orientation: Qt.Vertical + // We cannot bind directly to tree.height due to binding loops so we have to redo the calculation here + visible: contentHeight > availableHeight + maximumValue: contentHeight > availableHeight ? root.contentHeight - availableHeight : 0 + minimumValue: 0 + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.topMargin: parent.macOffset + anchors.rightMargin: -parent.macOffset + anchors.bottomMargin: hscrollbar.visible ? hscrollbar.height - parent.macOffset : 0 + + onValueChanged: { + if (!blockUpdates) { + contentY = value + verticalValue = value + } + } + } +} diff --git a/components/qmldir b/components/qmldir index ae9e9eff..000c0218 100644 --- a/components/qmldir +++ b/components/qmldir @@ -2,6 +2,7 @@ Slider 0.1 Slider.qml SpinBox 0.1 SpinBox.qml GroupBox 0.1 GroupBox.qml Button 0.1 Button.qml +Label 0.1 Label.qml ToolBar 0.1 ToolBar.qml TabFrame 0.1 TabFrame.qml TabBar 0.1 TabBar.qml @@ -9,7 +10,6 @@ Tab 0.1 Tab.qml Frame 0.1 Frame.qml ScrollArea 0.1 ScrollArea.qml ScrollBar 0.1 ScrollBar.qml -ChoiceList 0.1 ChoiceList.qml ComboBox 0.1 ComboBox.qml ToolButton 0.1 ToolButton.qml TextArea 0.1 TextArea.qml @@ -18,6 +18,7 @@ ProgressBar 0.1 ProgressBar.qml ButtonRow 0.1 ButtonRow.qml ButtonColumn 0.1 ButtonColumn.qml SplitterRow 0.1 SplitterRow.qml +SplitterColumn 0.1 SplitterColumn.qml Dial 0.1 Dial.qml TableView 0.1 TableView.qml CheckBox 0.1 CheckBox.qml @@ -26,3 +27,6 @@ plugin styleplugin plugin TableColumn 0.1 TableColumn.qml ContextMenu 0.1 ContextMenu.qml MenuItem 0.1 MenuItem.qml +Dialog 0.1 Dialog.qml +StatusBar 0.1 StatusBar.qml +ApplicationWindow 0.1 ApplicationWindow.qml |