Warning, /education/khangman/src/qml/GamePage.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2012 Laszlo Papp <lpapp@kde.org>
0002 // SPDX-FileCopyrightText: 2014 Rahul Chowdhury <rahul.chowdhury@kdemail.net>
0003 // SPDX-License-Identifier: LGPL-2.1-or-later
0004 
0005 import QtQuick
0006 import QtQuick.Controls
0007 import QtQuick.Layouts
0008 import QtQuick.Window
0009 import QtMultimedia
0010 import QtQml
0011 import Qt5Compat.GraphicalEffects
0012 
0013 import org.kde.kirigami as Kirigami
0014 import org.kde.kirigamiaddons.formcard as FormCard
0015 import org.kde.kirigamiaddons.delegates as Delegates
0016 import org.kde.newstuff as NewStuff
0017 import org.kde.khangman
0018 
0019 Kirigami.Page {
0020     id: gamePage
0021 
0022     focus: true
0023 
0024     property variant alphabet: KHangMan.alphabet
0025     property color currentWordLetterRectangleColor: Qt.rgba(0, 0, 0, 0)
0026     property int countDownTimerValue: KHangMan.resolveTime
0027     property int gallowsSeriesCounter: 0
0028     property bool initialized: false
0029     property alias isPlaying: secondTimer.running
0030     property string missedLetters: ""
0031 
0032     background: Image {
0033         id: backgroundImage
0034         smooth: true
0035         anchors.fill: parent
0036         source: KHangMan.backgroundUrl
0037     }
0038 
0039     function nextWord(): void {
0040         KHangMan.nextWord();
0041 
0042         countDownTimerValue = KHangMan.resolveTime;
0043 
0044         // Re enable all alphabet buttons
0045         for (var i = 0; i < alphabetLetterRepeater.count; ++i) {
0046             alphabetLetterRepeater.itemAt(i).enabled = true;
0047             alphabetLetterRepeater.itemAt(i).buttonColor = "black";
0048         }
0049 
0050         // Reset variables for the new word.
0051         gallowsSeriesCounter = 0;
0052         successImage.visible = false;
0053         missedLetters = "";
0054 
0055         hintLabel.visible = false;
0056 
0057         if (KHangMan.soundEnabled) {
0058             nextWordSoundEffect.play();
0059         }
0060     }
0061 
0062     function startTimer(): void {
0063         secondTimer.repeat = true;
0064         secondTimer.running = true;
0065         secondTimer.start();
0066     }
0067 
0068     function disableLetterButton(letter: string): void {
0069         for (var i = 0; i < alphabetLetterRepeater.count; ++i) {
0070             if (alphabetLetterRepeater.itemAt(i).upperCase == letter) {
0071                 alphabetLetterRepeater.itemAt(i).enabled = false;
0072                 break;
0073             }
0074         }
0075     }
0076 
0077     function guessLetter(letter: string): void {
0078         letter = letter.toUpperCase()
0079         if (KHangMan.soundEnabled) {
0080             khangmanAlphabetButtonPressSoundEffect.play();
0081         }
0082 
0083         disableLetterButton(letter);
0084         changeButtonColor(letter);
0085         if (KHangMan.containsChar(letter)) {
0086             KHangMan.replaceLetters(letter);
0087 
0088             if (KHangMan.isResolved()) {
0089                 // the current puzzle is solved
0090                 KHangMan.winCount++;
0091                 successImage.visible = true;
0092                 khangmanResultTimer.start();
0093 
0094                 if (KHangMan.soundEnabled) {
0095                     ewDialogAppearSoundEffect.play();
0096                 }
0097             }
0098         } else {
0099             // Only add to missedLetters if it's not already there
0100             if (missedLetters.indexOf(letter) == -1) {
0101                 if (gallowsSeriesCounter++ == 9) {
0102                     // wrong solution given for current puzzle
0103                     KHangMan.lossCount++;
0104                     if (KHangMan.soundEnabled) {
0105                         wrongSoundEffect.play();
0106                     }
0107 
0108                     khangmanResultTimer.start();
0109                 }
0110 
0111                 missedLetters += letter
0112             }
0113         }
0114     }
0115 
0116     function changeButtonColor(letter: string): void {
0117         for (var i = 0; i < alphabetLetterRepeater.count; ++i) {
0118             if (alphabetLetterRepeater.itemAt(i).upperCase == letter) {
0119                 alphabetLetterRepeater.itemAt(i).buttonColor = KHangMan.containsChar(letter) ? "green" : "red";
0120             }
0121         }
0122     }
0123 
0124     SelectionDialog {
0125         id: categorySelectionDialog;
0126 
0127         title: i18n("Choose the word category");
0128         model: KHangMan.categories
0129         Component.onCompleted: {
0130             currentIndex = KHangMan.currentCategory;
0131         }
0132 
0133         onCurrentIndexChanged: {
0134             if (KHangMan.soundEnabled) {
0135                 if (!initialized) {
0136                     initialized = true;
0137                 } else {
0138                     nextWordSoundEffect.play();
0139                 }
0140             }
0141 
0142             KHangMan.setCurrentCategory(currentIndex);
0143             KHangMan.readFile();
0144             nextWord();
0145         }
0146 
0147         delegate: Delegates.RoundedItemDelegate {
0148             required property int index
0149             required property string modelData
0150 
0151             text: modelData
0152             onClicked: {
0153                 categorySelectionDialog.currentIndex = index
0154                 categorySelectionDialog.close();
0155             }
0156         }
0157     }
0158 
0159     SelectionDialog {
0160         id: languageSelectionDialog;
0161 
0162         title: i18n("Select a language");
0163         model: KHangMan.languages
0164         Component.onCompleted: currentIndex = KHangMan.currentLanguage
0165 
0166         delegate: Delegates.RoundedItemDelegate {
0167             required property int index
0168             required property string modelData
0169 
0170             text: modelData
0171 
0172             onClicked: {
0173                 languageSelectionDialog.currentIndex = index;
0174 
0175                 KHangMan.setCurrentLanguage(index);
0176                 KHangMan.readFile();
0177                 nextWord();
0178                 languageSelectionDialog.close();
0179             }
0180         }
0181     }
0182 
0183     SelectionDialog {
0184         id: themeSelectionDialog;
0185         title: i18n("Select a theme");
0186         model: KHangMan.themes
0187         Component.onCompleted: currentIndex = KHangMan.currentTheme
0188 
0189         delegate: Delegates.RoundedItemDelegate {
0190             required property int index
0191             required property string modelData
0192 
0193             text: modelData
0194 
0195             onClicked: {
0196                 themeSelectionDialog.currentIndex = index;
0197 
0198                 KHangMan.setCurrentTheme(index);
0199                 themeSelectionDialog.close();
0200             }
0201         }
0202     }
0203 
0204     Timer {
0205         id: secondTimer;
0206         interval: 1000;
0207         repeat: true;
0208         running: false;
0209         triggeredOnStart: false;
0210 
0211         onTriggered: {
0212             if (KHangMan.resolveTime != 0 && --countDownTimerValue == 0) {
0213                 stop();
0214                 khangmanResultTimer.start();
0215                 if (KHangMan.soundEnabled) {
0216                     wrongSoundEffect.play();
0217                 }
0218             }
0219         }
0220     }
0221 
0222     Timer {
0223         id: khangmanResultTimer;
0224         interval: 1000;
0225         repeat: false;
0226         running: false;
0227         triggeredOnStart: false;
0228 
0229         onTriggered: {
0230             nextWord();
0231             startTimer();
0232         }
0233     }
0234 
0235     actions: [
0236         Kirigami.Action {
0237             id: playPauseButton
0238             icon.name: gamePage.isPlaying ? "media-playback-pause" : "media-playback-start"
0239 
0240             text: gamePage.isPlaying ? i18n("Pause") : i18n("Play")
0241 
0242             onTriggered: {
0243                 if (gamePage.isPlaying ) { // game is currently going on, so pause it
0244                     secondTimer.repeat = false
0245                     secondTimer.running = false
0246                     hintLabel.visible = false
0247                     secondTimer.stop();
0248                 } else {  // the game is paused or not yet started, so resume or start it
0249                     // if the game is not yet started, play nextWordSoundeffect
0250                     // denotes the game is not yet started, should return false if game is paused instead
0251                     if (KHangMan.soundEnabled) {
0252                         nextWordSoundEffect.play()
0253                     }
0254                     startTimer()
0255                 }
0256             }
0257         },
0258 
0259         Kirigami.Action {
0260             id: themeSelectionButton
0261             text: themeSelectionDialog.model[themeSelectionDialog.currentIndex]
0262 
0263             onTriggered: {
0264                 themeSelectionDialog.open()
0265             }
0266         },
0267 
0268         Kirigami.Action {
0269             id: settingsButton
0270             icon.name: "settings-configure-symbolic"
0271             text: i18nc("@action:button", "Configure")
0272 
0273             property bool wasPlaying: false
0274 
0275             onTriggered: {
0276                 // if game is currently going on then pause it
0277                 settingsButton.wasPlaying = isPlaying
0278                 if( gamePage.isPlaying ) {
0279                     secondTimer.repeat = false
0280                     secondTimer.running = false
0281                     hintLabel.visible = false
0282                     secondTimer.stop();
0283                 }
0284 
0285                 const item = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent("org.kde.khangman", "SettingsPage"), {}, {title: i18n("Configure"), width: Kirigami.Units.gridUnit * 24});
0286 
0287                 item.okClicked.connect(() => {
0288                     // close the settings dialog
0289                     if (wasPlaying) {
0290                         // game is going on, so load a new word and start with the saved settings
0291                         nextWord()
0292                         startTimer()
0293                     }
0294                 });
0295 
0296                 item.cancelClicked.connect(() => {
0297                     if (wasPlaying) {
0298                         // game was in progress, so resume the timer countdown
0299                         startTimer()
0300                     }
0301                 });
0302             }
0303         },
0304 
0305         NewStuff.Action {
0306             id: ghnsButton
0307 
0308             configFile: "khangman.knsrc"
0309             icon.name: "get-hot-new-stuff"
0310             text: i18n("Get new language files")
0311             visible: NewStuff.Settings.allowedByKiosk
0312 
0313             onEntryEvent: (entry, event) => {
0314                 if (event === NewStuff.Entry.StatusChangedEvent) {
0315                     KHangMan.slotDownloadNewStuff(entry)
0316                 }
0317             }
0318         },
0319 
0320         Kirigami.Action {
0321             id: showHandbookButton
0322             icon.name: "help-browser"
0323             text: i18n("View the KHangMan Handbook")
0324             displayHint: Kirigami.DisplayHint.AlwaysHide
0325 
0326             onTriggered: {
0327                 KHangMan.showHandbook()
0328             }
0329         },
0330 
0331         Kirigami.Action {
0332             id: aboutKhangmanButton
0333             icon.name: "khangman"
0334             text: i18n("About KHangMan")
0335             displayHint: Kirigami.DisplayHint.AlwaysHide
0336 
0337             onTriggered: {
0338                 applicationWindow().pageStack.pushDialogLayer("qrc:/qt/qml/org/kde/khangman/qml/Settings/AboutPage.qml", {} , {
0339                     width: Kirigami.Units.gridUnit * 24,
0340                     title: i18n("About KHangMan")
0341                 })
0342             }
0343         },
0344 
0345         Kirigami.Action {
0346             id: aboutKDEButton
0347             text: i18n("About KDE")
0348             icon.name: "help-about-symbolic"
0349             displayHint: Kirigami.DisplayHint.AlwaysHide
0350 
0351             onTriggered: {
0352                 applicationWindow().pageStack.pushDialogLayer("qrc:/qt/qml/org/kde/khangman/qml/Settings/AboutKDEPage.qml", {}, {
0353                     width: Kirigami.Units.gridUnit * 24,
0354                     title: i18n("About KDE")
0355                 })
0356             }
0357         }
0358     ]
0359 
0360     // display the remaining number of wrong guesses
0361     Row {
0362         id: misses
0363         spacing: 5
0364         visible: isPlaying
0365 
0366         anchors {
0367             top: parent.top
0368             topMargin: 5
0369             right: parent.right
0370             rightMargin: 5
0371         }
0372 
0373         Label {
0374             id: missesLabel
0375             text: i18n("Remaining guesses: ")
0376             font.pixelSize: 40
0377             font.bold: true
0378             color: KHangMan.letterColor
0379         }
0380 
0381         Text {
0382             id: remainingGuessesCount
0383             text: i18n(10 - gallowsSeriesCounter)
0384             color: gallowsSeriesCounter >= 7 ? "red" : "black"
0385             font.pixelSize: 40
0386             font.bold: true
0387         }
0388     }
0389 
0390     Label {
0391         id: scoreLabel
0392         visible: isPlaying
0393 
0394         anchors {
0395             top: misses.top
0396             left: parent.left
0397             leftMargin: 5
0398         }
0399 
0400         text: i18n("Score: ")
0401         font.pixelSize: 40
0402         font.bold: true
0403         color: KHangMan.letterColor
0404     }
0405 
0406     Label {
0407         id: netScoreLabel
0408         visible: isPlaying
0409 
0410         anchors {
0411             left: scoreLabel.right
0412             top: scoreLabel.top
0413         }
0414 
0415         text: KHangMan.netScore
0416         color: KHangMan.netScore < 0 ? "red" : "black"
0417         font.pixelSize: 40
0418         font.bold: true
0419     }
0420 
0421     Row {
0422         id: winCountRow
0423         spacing: 15
0424         visible: isPlaying
0425 
0426         anchors {
0427             top: scoreLabel.bottom
0428             left: parent.left
0429             leftMargin: 5
0430         }
0431 
0432         Label {
0433             id: winLabel
0434             text: i18n("Wins: ")
0435             font.pixelSize: 40
0436             font.bold: true
0437             color: KHangMan.letterColor
0438         }
0439 
0440         Label {
0441             id: winCountLabel
0442             text: KHangMan.winCount
0443             font.pixelSize: 40
0444             font.bold: true
0445             color: KHangMan.letterColor
0446         }
0447     }
0448 
0449     Row {
0450         id: lossCountRow
0451         spacing: 15
0452         visible: isPlaying
0453 
0454         anchors {
0455             top: winCountRow.bottom
0456             left: parent.left
0457             leftMargin: 5
0458         }
0459 
0460         Label {
0461             id: lossLabel
0462             text: i18n("Losses: ")
0463             font.pixelSize: 40
0464             font.bold: true
0465             color: KHangMan.letterColor
0466         }
0467 
0468         Label {
0469             id: lossCountLabel
0470             text: KHangMan.lossCount
0471             font.pixelSize: 40
0472             font.bold: true
0473             color: KHangMan.letterColor
0474         }
0475     }
0476 
0477     Kirigami.Icon {
0478         id: successImage
0479 
0480         source: "data-success"
0481         visible: false
0482 
0483         width: Kirigami.Units.iconSizes.huge
0484         height: Kirigami.Units.iconSizes.huge
0485 
0486         anchors {
0487             horizontalCenter: parent.horizontalCenter;
0488             verticalCenter: parent.verticalCenter;
0489             verticalCenterOffset: -parent.height/4;
0490         }
0491     }
0492 
0493     Image {
0494         id: gallowsSeriesImage;
0495         source: gallowsSeriesCounter == 0 ? "" : "qrc:/qml/gallows/gallows" + gallowsSeriesCounter + ".png"
0496         visible: (isPlaying && gallowsSeriesCounter > 0)
0497 
0498         anchors {
0499             horizontalCenter: parent.horizontalCenter;
0500             verticalCenter: parent.verticalCenter;
0501             verticalCenterOffset: -parent.height/4;
0502         }
0503     }
0504 
0505     Grid {
0506         id: currentWordGrid;
0507         visible: gamePage.isPlaying
0508         anchors {
0509             centerIn: parent;
0510         }
0511 
0512         spacing: 5;
0513         columns: 13;
0514         Repeater {
0515             id: currentWordLetterRepeater;
0516             model: KHangMan.currentWord;
0517             LetterElement {
0518                 id: currentWordLetterId;
0519                 letterText: modelData;
0520             }
0521         }
0522     }
0523 
0524     Grid {
0525         id: alphabetGrid;
0526         visible: gamePage.isPlaying
0527         anchors {
0528             horizontalCenter: parent.horizontalCenter;
0529             bottom: parent.bottom;
0530             bottomMargin: 10;
0531         }
0532 
0533         spacing: gamePage.width/35;
0534         columns: 13;
0535         Repeater {
0536             id: alphabetLetterRepeater;
0537             model: alphabet;
0538             Button {
0539                 id: alphabetButton;
0540                 property string letter: modelData
0541                 property string upperCase: modelData.toUpperCase()
0542                 property string buttonColor: "black"
0543 
0544                 background: Rectangle {
0545                     id: alphabetLetterIdStyleRectangle
0546                     implicitWidth: gamePage.width / 22
0547                     implicitHeight: gamePage.width / 22
0548                     color: buttonColor
0549                     radius: 8
0550                     layer.enabled: true
0551                     layer.effect: DropShadow {
0552                         radius: 4
0553                         horizontalOffset: 3
0554                         verticalOffset: 3
0555                         spread: 0
0556                         samples: radius * 2
0557                         source: alphabetLetterIdStyleRectangle
0558                         color: Qt.rgba(0, 0, 0, 0.5)
0559                         transparentBorder: true
0560                     }
0561                 }
0562 
0563                 contentItem: Text {
0564                     id: buttonLabel
0565                     anchors.centerIn: parent
0566                     text: letter
0567                     font.family : "Arial"
0568                     font.pixelSize: gamePage.width / 40
0569                     font.capitalization : Font.AllUppercase
0570                     font.weight : Font.Bold
0571                     horizontalAlignment : Text.AlignHCenter
0572                     verticalAlignment : Text.AlignVCenter
0573                     color: parent.enabled ? "white" : "grey"
0574                 }
0575 
0576                 onClicked: {
0577                     guessLetter(modelData);
0578                 }
0579             }
0580         }
0581     }
0582 
0583     Label {
0584         id: hintLabel
0585         text: KHangMan.currentHint
0586         font.family: "serif-sans"
0587         color: "green"
0588         font.italic: true
0589         font.pixelSize: gamePage.width / 60
0590         anchors.top: currentWordGrid.bottom
0591         anchors.bottom: alphabetGrid.top
0592         anchors.horizontalCenter: parent.horizontalCenter
0593         visible: false
0594     }
0595 
0596     footer: ToolBar {
0597         id: mainPageTools
0598         visible: isPlaying
0599 
0600         RowLayout {
0601             anchors.fill: parent
0602 
0603             ToolButton {
0604                 id: helpHintButton
0605                 icon.source: "Images/help-hint.png"
0606                 ToolTip.text: i18n("Display the hint.")
0607                 ToolTip.visible: hovered
0608                 ToolTip.delay: Kirigami.Units.toolTipDelay
0609                 enabled: hintLabel.text != ""
0610 
0611                 onClicked: {
0612                     // make the button toggle between display and hide the hint
0613                     hintLabel.visible = hintLabel.visible ? false : true
0614                 }
0615             }
0616 
0617             ToolButton {
0618                 id: categorySelectionButton
0619                 Layout.fillWidth: true
0620                 text: categorySelectionDialog.model[categorySelectionDialog.currentIndex];
0621                 ToolTip.text: i18n("Change the category.")
0622                 ToolTip.visible: hovered
0623                 ToolTip.delay: Kirigami.Units.toolTipDelay
0624 
0625                 onClicked: {
0626                     categorySelectionDialog.open();
0627                 }
0628             }
0629 
0630             ToolButton {
0631                 id: languageSelectionButton
0632                 Layout.fillWidth: true
0633                 text: languageSelectionDialog.model[languageSelectionDialog.currentIndex]
0634                 ToolTip.text: i18n("Change the language.")
0635                 ToolTip.visible: hovered
0636                 ToolTip.delay: Kirigami.Units.toolTipDelay
0637 
0638                 onClicked: {
0639                     languageSelectionDialog.open()
0640                 }
0641             }
0642 
0643             ToolButton {
0644                 id: revealWordButton
0645                 Layout.fillWidth: true
0646                 text: i18n("Reveal Word")
0647                 ToolTip.text: i18n("Reveal the current word.")
0648                 ToolTip.visible: hovered
0649                 ToolTip.delay: Kirigami.Units.toolTipDelay
0650 
0651                 onClicked: {
0652                     KHangMan.revealCurrentWord();
0653                     KHangMan.lossCount++;
0654                     if (KHangMan.soundEnabled) {
0655                         wrongSoundEffect.play();
0656                     }
0657 
0658                     khangmanResultTimer.start();
0659                 }
0660             }
0661 
0662             Text {
0663                 id: timerText
0664                 visible: KHangMan.resolveTime == 0 ? false : true
0665                 text: Math.floor(countDownTimerValue / 60) + ":" + Math.floor(countDownTimerValue % 60 / 10)
0666                       + Math.floor(countDownTimerValue % 60 % 10)
0667             }
0668 
0669             ToolButton {
0670                 id: nextWordButton
0671                 Layout.fillWidth: true
0672                 icon.name: "go-next"
0673                 ToolTip.text: i18n("Load the next word and start a new game.")
0674                 ToolTip.visible: hovered
0675                 ToolTip.delay: Kirigami.Units.toolTipDelay
0676 
0677                 onClicked: {
0678                     if (KHangMan.soundEnabled) {
0679                         nextWordSoundEffect.play();
0680                     }
0681 
0682                     nextWord();
0683 
0684                     secondTimer.repeat = true;
0685                     secondTimer.restart();
0686                 }
0687             }
0688         }
0689     }
0690 
0691     Keys.onPressed: (event) => {
0692         if (event.text.length > 0) {
0693             guessLetter(event.text);
0694         }
0695     }
0696 }