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 }