Warning, /education/gcompris/src/core/GCComboBox.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - GCComboBox.qml 0002 * 0003 * SPDX-FileCopyrightText: 2015 Johnny Jazeix <jazeix@gmail.com> 0004 * 0005 * Authors: 0006 * Johnny Jazeix <jazeix@gmail.com> 0007 * 0008 * SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 import QtQuick 2.12 0011 import QtQuick.Controls 2.12 0012 import GCompris 1.0 0013 0014 /** 0015 * A QML component unifying comboboxes in GCompris. 0016 * @ingroup components 0017 * 0018 * GCComboBox contains a combobox and a label. 0019 * When the combobox isn't active, it is displayed as a button containing the current value 0020 * and the combobox label (its description). 0021 * Once the button is clicked, the list of all available choices is displayed. 0022 * Also, above the list is the combobox label. 0023 * 0024 * Navigation can be done with keys and mouse/gestures. 0025 * As Qt comboboxes, you can either have a js Array or a Qml model as model. 0026 0027 * GCComboBox should now be used wherever you'd use a QtQuick combobox. It has 0028 * been decided to implement comboboxes ourselves in GCompris because of 0029 * some integration problems on some OSes (native dialogs unavailable). 0030 */ 0031 Item { 0032 id: gccombobox 0033 focus: visible 0034 0035 width: parent.width 0036 height: comboColumn.height 0037 0038 /** 0039 * type:Item 0040 * Where the list containing all choices will be displayed. 0041 * Should be the dialogActivityConfig item if used on config. 0042 */ 0043 property Item background 0044 0045 /** 0046 * type:int 0047 * Current index of the combobox. 0048 */ 0049 property int currentIndex: -1 0050 0051 /** 0052 * type:string 0053 * Current text displayed in the combobox when inactive. 0054 */ 0055 property string currentText 0056 0057 0058 /** 0059 * type:alias 0060 * Model for the list (user has to specify one). 0061 */ 0062 property alias model: gridview.model 0063 0064 /** 0065 * type:string 0066 * Text besides the combobox, used to describe what the combobox is for. 0067 */ 0068 property string label 0069 0070 /** 0071 * type:bool 0072 * Internal value. 0073 * If model is an js Array, we access data using modelData and [] else qml Model, we need to use model and get(). 0074 */ 0075 readonly property bool isModelArray: model.constructor === Array 0076 0077 // start and stop trigs the animation 0078 signal start 0079 signal stop 0080 0081 // emitted at stop animation end 0082 signal close 0083 0084 onCurrentIndexChanged: { 0085 currentText = isModelArray ? model[currentIndex].text : (model && model.get(currentIndex) ? model.get(currentIndex).text : "") 0086 } 0087 0088 /** 0089 * type:Column 0090 * Combobox display when inactive: the label and the button with current choice. 0091 */ 0092 Column { 0093 id: comboColumn 0094 width: parent.width 0095 spacing: 5 * ApplicationInfo.ratio 0096 Item { 0097 id: labelArea 0098 width: labelText.width 0099 height: labelText.height 0100 GCText { 0101 id: labelText 0102 text: label 0103 anchors.verticalCenter: parent.verticalCenter 0104 width: comboColumn.width 0105 fontSize: mediumSize 0106 wrapMode: Text.WordWrap 0107 } 0108 } 0109 Rectangle { 0110 id: button 0111 visible: true 0112 border.width: 2 0113 border.color: "#373737" 0114 width: currentTextBox.contentWidth + radius * 2 0115 height: 50 * ApplicationInfo.ratio 0116 radius: 10 0117 gradient: Gradient { 0118 GradientStop { position: 0 ; color: mouseArea.pressed ? "#C03ACAFF" : "#23373737" } 0119 GradientStop { position: 1 ; color: mouseArea.pressed ? "#803ACAFF" : "#13373737" } 0120 } 0121 // Current value of combobox 0122 GCText { 0123 id: currentTextBox 0124 anchors.centerIn: parent 0125 text: currentText 0126 fontSize: mediumSize 0127 fontSizeMode: Text.Fit 0128 horizontalAlignment: Text.AlignHCenter 0129 verticalAlignment: Text.AlignVCenter 0130 width: comboColumn.width - 20 0131 height: 50 * ApplicationInfo.ratio 0132 color: "#373737" 0133 } 0134 MouseArea { 0135 id: mouseArea 0136 anchors.fill: parent 0137 onReleased: { 0138 popup.visible = true 0139 } 0140 } 0141 } 0142 } 0143 0144 /** 0145 * type:Item 0146 * Combobox display when active: header with the description and the gridview containing all the available choices. 0147 */ 0148 Rectangle { 0149 id: popup 0150 visible: false 0151 width: if(parent) parent.width 0152 height: if(parent) parent.height 0153 color: "#696da3" 0154 0155 parent: background 0156 z: 100 0157 focus: visible 0158 0159 // Forward event to activity if key pressed is not one of the handled key 0160 // (ctrl+F should still resize the window for example) 0161 Keys.onPressed: { 0162 if(event.key !== Qt.Key_Back) { 0163 background.currentActivity.Keys.onPressed(event) 0164 } 0165 } 0166 Keys.onReleased: { 0167 if(event.key === Qt.Key_Back) { 0168 // Keep the old value 0169 discardChange(); 0170 hidePopUpAndRestoreFocus(); 0171 event.accepted = true 0172 } 0173 } 0174 0175 Keys.onRightPressed: gridview.moveCurrentIndexRight(); 0176 Keys.onLeftPressed: gridview.moveCurrentIndexLeft(); 0177 Keys.onDownPressed: gridview.moveCurrentIndexDown(); 0178 Keys.onUpPressed: gridview.moveCurrentIndexUp(); 0179 0180 Keys.onEscapePressed: { 0181 // Keep the old value 0182 discardChange(); 0183 hidePopUpAndRestoreFocus(); 0184 } 0185 Keys.onEnterPressed: { 0186 acceptChange(); 0187 hidePopUpAndRestoreFocus(); 0188 } 0189 Keys.onReturnPressed: { 0190 acceptChange(); 0191 hidePopUpAndRestoreFocus(); 0192 } 0193 0194 Keys.onSpacePressed: { 0195 acceptChange(); 0196 hidePopUpAndRestoreFocus(); 0197 } 0198 0199 // Don't accept the list value, restore previous value 0200 function discardChange() { 0201 if(isModelArray) { 0202 for(var i = 0 ; i < model.count ; ++ i) { 0203 if(model[currentIndex].text === currentText) { 0204 currentIndex = i; 0205 break; 0206 } 0207 } 0208 } 0209 else { 0210 for(var i = 0 ; i < model.length ; ++ i) { 0211 if(model.get(currentIndex).text === currentText) { 0212 currentIndex = i; 0213 break; 0214 } 0215 } 0216 } 0217 gridview.currentIndex = currentIndex; 0218 } 0219 0220 // Accept the change. Updates the currentIndex and text of the button 0221 function acceptChange() { 0222 currentIndex = gridview.currentIndex; 0223 currentText = isModelArray ? model[currentIndex].text : (model && model.get(currentIndex) ? model.get(currentIndex).text : "") 0224 } 0225 0226 function hidePopUpAndRestoreFocus() { 0227 popup.visible = false; 0228 // Restore focus on previous activity for keyboard input 0229 parent.forceActiveFocus(); 0230 } 0231 0232 Rectangle { 0233 id : headerDescription 0234 z: 10 0235 color: "#e6e6e6" 0236 radius: 10 * ApplicationInfo.ratio 0237 width: popup.width - 30 0238 height: textDescription.height * 1.2 0239 anchors.top: popup.top 0240 anchors.horizontalCenter: popup.horizontalCenter 0241 anchors.topMargin: 10 * ApplicationInfo.ratio 0242 0243 GCText { 0244 id: textDescription 0245 text: label 0246 width: headerDescription.width - 120 * ApplicationInfo.ratio //minus twice the discard button size 0247 height: 50 * ApplicationInfo.ratio 0248 anchors.centerIn: headerDescription 0249 horizontalAlignment: Text.AlignHCenter 0250 verticalAlignment: Text.AlignVCenter 0251 color: "#191919" 0252 fontSizeMode: Text.Fit 0253 minimumPointSize: 7 0254 fontSize: largeSize 0255 font.weight: Font.DemiBold 0256 wrapMode: Text.WordWrap 0257 } 0258 0259 GCButtonCancel { 0260 id: discardIcon 0261 anchors.verticalCenter: headerDescription.verticalCenter 0262 anchors.margins: 2 * ApplicationInfo.ratio 0263 MouseArea { 0264 anchors.fill: parent 0265 onClicked: { 0266 popup.acceptChange(); 0267 popup.hidePopUpAndRestoreFocus(); 0268 } 0269 } 0270 } 0271 } 0272 0273 GridView { 0274 id: gridview 0275 z: 4 0276 readonly property int elementHeight: 40 * ApplicationInfo.ratio 0277 0278 // each element has a 300 width size minimum. If the screen is larger than it, 0279 // we do a grid with cases with 300px for width at minimum. 0280 // If you have a better idea/formula to have a different column number, don't hesitate, change it :). 0281 readonly property int numberOfColumns: Math.max(1, Math.floor(width / (300 * ApplicationInfo.ratio))) 0282 contentHeight: isModelArray ? elementHeight*model.count/numberOfColumns : elementHeight*model.length/numberOfColumns 0283 width: headerDescription.width 0284 height: popup.height - headerDescription.height - 20 * ApplicationInfo.ratio 0285 currentIndex: gccombobox.currentIndex 0286 flickableDirection: Flickable.VerticalFlick 0287 clip: true 0288 cellWidth: width / numberOfColumns 0289 cellHeight: elementHeight 0290 anchors.top: headerDescription.bottom 0291 anchors.topMargin: 5 * ApplicationInfo.ratio 0292 anchors.horizontalCenter: popup.horizontalCenter 0293 0294 delegate: Component { 0295 Item { 0296 id: gridItem 0297 width: gridview.cellWidth 0298 height: gridview.elementHeight 0299 property bool itemSelected : GridView.isCurrentItem 0300 Rectangle { 0301 width: gridview.cellWidth - radius 0302 height: gridview.elementHeight - radius 0303 color: gridItem.itemSelected ? "#e6e6e6" : "#bdbed0" 0304 border.width: gridItem.itemSelected ? 3 : 1 0305 border.color: "white" 0306 radius: 5 * ApplicationInfo.ratio 0307 anchors.centerIn: parent 0308 } 0309 Image { 0310 id: isSelectedIcon 0311 visible: parent.GridView.isCurrentItem 0312 source: "qrc:/gcompris/src/core/resource/apply.svg" 0313 anchors.left: parent.left 0314 anchors.verticalCenter: parent.verticalCenter 0315 anchors.leftMargin: 10 0316 sourceSize.width: (gridview.elementHeight * 0.8) 0317 } 0318 GCText { 0319 id: textValue 0320 text: isModelArray ? modelData.text : model.text 0321 anchors.centerIn: parent 0322 height: parent.height 0323 width: parent.width - isSelectedIcon.width * 2 - 20 0324 fontSizeMode: Text.Fit 0325 minimumPointSize: 7 0326 fontSize: mediumSize 0327 verticalAlignment: Text.AlignVCenter 0328 horizontalAlignment: Text.AlignHCenter 0329 } 0330 MouseArea { 0331 anchors.fill: parent 0332 onClicked: { 0333 currentIndex = index 0334 popup.acceptChange(); 0335 popup.hidePopUpAndRestoreFocus(); 0336 } 0337 } 0338 } 0339 } 0340 } 0341 0342 } 0343 }