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 }