diff --git a/src/activities/graph-coloring/ActivityConfig.qml b/src/activities/graph-coloring/ActivityConfig.qml new file mode 100644 index 000000000..f95b8ec2b --- /dev/null +++ b/src/activities/graph-coloring/ActivityConfig.qml @@ -0,0 +1,65 @@ +/* GCompris - ActivityConfig.qml + * + * Copyright (C) 2020 Johnny Jazeix + * + * Authors: + * Johnny Jazeix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +import QtQuick 2.6 + +import "../../core" + +Item { + id: activityConfiguration + property Item background + property alias modeBox: modeBox + width: if(background) background.width + property var availableModes: [ + { "text": qsTr("Colors"), "value": "color" }, + { "text": qsTr("Shapes"), "value": "symbol" } + ] + Flow { + id: flow + spacing: 5 + width: parent.width + GCComboBox { + id: modeBox + model: availableModes + background: activityConfiguration.background + label: qsTr("Select your mode") + } + } + + property var dataToSave + + function setDefaultValues() { + if(dataToSave["mode"] === undefined) { + dataToSave["mode"] = "color"; + modeBox.currentIndex = 0 + } + for(var i = 0 ; i < availableModes.length ; i ++) { + if(availableModes[i].value === dataToSave["mode"]) { + modeBox.currentIndex = i; + break; + } + } + } + + function saveValues() { + var newMode = availableModes[modeBox.currentIndex].value; + dataToSave = {"mode": newMode}; + } +} diff --git a/src/activities/graph-coloring/GraphColoring.qml b/src/activities/graph-coloring/GraphColoring.qml index 2ec9f7e38..ec98d9ddd 100644 --- a/src/activities/graph-coloring/GraphColoring.qml +++ b/src/activities/graph-coloring/GraphColoring.qml @@ -1,395 +1,355 @@ /* GCompris - graph-coloring.qml * * Copyright (C) 2015 Akshat Tandon * * Authors: * * Akshat Tandon (Qt Quick version) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import "../../core" import "graph-coloring.js" as Activity import GCompris 1.0 ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background anchors.fill: parent source: "qrc:/gcompris/src/activities/tic_tac_toe/resource/background.svg" sourceSize.width: parent.width fillMode: Image.PreserveAspectCrop focus: true signal start signal stop Component.onCompleted: { - dialogActivityConfig.getInitialConfiguration() + dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } MouseArea { anchors.fill: parent onClicked: showChooser(false); } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property alias colorsRepeater: colorsRepeater property alias nodesRepeater: nodesRepeater property alias edgesRepeater: edgesRepeater property alias chooserGrid: chooserGrid + property string mode } onStart: { Activity.start(items) } onStop: { Activity.stop() } Column { id: colorsColumn anchors.left: parent.left anchors.leftMargin: 5 * ApplicationInfo.ratio anchors.top: parent.top anchors.topMargin: 5 * ApplicationInfo.ratio spacing: 3 * ApplicationInfo.ratio add: Transition { NumberAnimation { properties: "y"; duration: 1000; easing.type: Easing.OutBounce } } Repeater { id: colorsRepeater model: ListModel {} delegate: Node { width: 40 * ApplicationInfo.ratio height: 40 * ApplicationInfo.ratio border.width: 2 border.color: "white" searchItemIndex: itemIndex } } } Item { id: graphRect anchors.left: parent.left anchors.leftMargin: 100 * ApplicationInfo.ratio anchors.bottom: parent.bottom anchors.bottomMargin: 50 * ApplicationInfo.ratio anchors.top: parent.top anchors.topMargin: 50 * ApplicationInfo.ratio height: background.height - 100 * ApplicationInfo.ratio width: background.width - 150 * ApplicationInfo.ratio property int diameter: graphRect.width/11 property int minDiameter: 40 * ApplicationInfo.ratio property int maxDiameter: 80 * ApplicationInfo.ratio property int optDiameter: diameter < minDiameter ? minDiameter : ( diameter > maxDiameter ? maxDiameter : diameter) Repeater { id: edgesRepeater model: ListModel {} delegate: Rectangle { id: line opacity: 1 antialiasing: true color: highlight == true ? "red" : "black" transformOrigin: Item.TopLeft x: xp * graphRect.width y: yp * graphRect.height property var x2: xpp * graphRect.width property var y2: ypp * graphRect.height width: Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y- y2, 2)) height: highlight == true ? 7 * ApplicationInfo.ratio : 3 * ApplicationInfo.ratio rotation: (Math.atan((y2 - y)/(x2-x)) * 180 / Math.PI) + (((y2-y) < 0 && (x2-x) < 0) * 180) + (((y2-y) >= 0 && (x2-x) < 0) * 180) Behavior on color { ColorAnimation { duration: 2000 easing.type: Easing.OutExpo } } Behavior on height { NumberAnimation { duration: 2000 easing.type: Easing.OutExpo } } } } Repeater{ id: nodesRepeater model: ListModel {} delegate: Node{ id: currentNode x: posX * graphRect.width - width/2 y: posY * graphRect.height - height/2 width: graphRect.optDiameter height: width radius: width/2 border.color: highlight ? "red" : "black" border.width: highlight ? 7 : 4 symbolRotation: highlight searchItemIndex: colIndex Behavior on border.color { ColorAnimation { duration: 2000 easing.type: Easing.OutExpo } } Behavior on border.width { NumberAnimation { duration: 2000 easing.type: Easing.OutExpo } } MouseArea { id: mouseArea anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton enabled: true z: 3 hoverEnabled: ApplicationInfo.isMobile ? false : true onClicked:{ var obj = items.nodesRepeater.model.get(index); showChooser(true, index, parent); } } states: State { name: "scaled"; when: mouseArea.containsMouse PropertyChanges { target: currentNode scale: 1.1 } } transitions: Transition { NumberAnimation { properties: "scale"; easing.type: Easing.OutCubic } } } } } function showChooser(visible, guessIndex, item) { if (!visible) { chooserTimer.stop(); chooser.scale = 0; return; } var modelObj = items.nodesRepeater.model.get(guessIndex); var absolute = graphRect.mapToItem(background, item.x, item.y); chooserGrid.colIndex = modelObj.colIndex; chooserGrid.guessIndex = guessIndex; var targetX = absolute.x + item.width; var targetY = absolute.y - item.height/2; if (targetX < 0) { targetX = 0; } if (targetX + chooser.width > background.width) { targetX = background.width - chooser.width - 10; } if (targetY < 0) { targetY = 0; } if (targetY + chooser.height > background.height) { targetY = background.height - chooser.height - 10; } chooser.x = targetX; chooser.y = targetY; chooser.scale = 1; chooser.visible = true; chooserTimer.restart(); //console.log(" item.x = " + item.x + " item.y" + item.y+" absolute.x" + absolute.x +" absolute.y" + absolute.y) } Rectangle { id: chooser width: chooserGrid.width + 5 height: chooserGrid.height + 5 color: "darkgray" border.width: 0 border.color: "white" opacity: 1 scale: 0 visible: false z: 10 GridView { id: chooserGrid cellWidth: graphRect.optDiameter - 2 cellHeight: cellWidth width: Math.ceil(count / 2) * cellWidth height: 2 * cellHeight anchors.centerIn: parent z: 11 clip: false interactive: false verticalLayoutDirection: GridView.TopToBottom layoutDirection: Qt.LeftToRight flow: GridView.FlowLeftToRight property int gridCount : count property int colIndex: 0 property int guessIndex: 0 Timer { id: chooserTimer interval: 5000 onTriggered: showChooser(false); } model: new Array() delegate: Node { id: chooserItem width: graphRect.optDiameter - 5 height: width border.width: index == chooserGrid.colIndex ? 3 : 1 border.color: index == chooserGrid.colIndex ? "white" : "darkgray" searchItemIndex: modelData highlightSymbol: index == chooserGrid.colIndex radius: 5 MouseArea { id: chooserMouseArea anchors.fill: parent acceptedButtons: Qt.LeftButton z: 11 hoverEnabled: ApplicationInfo.isMobile ? false : true onClicked: { chooserGrid.colIndex = chooserItem.searchItemIndex; var obj = items.nodesRepeater.model; obj.setProperty(chooserGrid.guessIndex, "colIndex", chooserGrid.colIndex); showChooser(false); Activity.checkAdjacent() Activity.checkGuess() } } } } } DialogHelp { id: dialogHelp onClose: home() } - DialogActivityConfig { + DialogChooseLevel { id: dialogActivityConfig - currentActivity: activity - content: Component { - Item { - property alias modeBox: modeBox - - property var availableModes: [ - { "text": qsTr("Colors"), "value": "color" }, - { "text": qsTr("Shapes"), "value": "symbol" } - ] - - Flow { - id: flow - spacing: 5 - width: dialogActivityConfig.width - GCComboBox { - id: modeBox - model: availableModes - background: dialogActivityConfig - label: qsTr("Select your mode") - } - } - } - } - onClose: home() - onLoadData: { - if(dataToSave && dataToSave["mode"]) { - Activity.mode = dataToSave["mode"]; - } - } + currentActivity: activity.activityInfo - onSaveData: { - var newMode = dialogActivityConfig.configItem.availableModes[dialogActivityConfig.configItem.modeBox.currentIndex].value; - if (newMode !== Activity.mode) { - chooserGrid.model = new Array(); - Activity.mode = newMode; - dataToSave = {"mode": Activity.mode}; - Activity.initLevel(); - } + onClose: { + home() } - - function setDefaultValues() { - for(var i = 0 ; i < dialogActivityConfig.configItem.availableModes.length ; i ++) { - if(dialogActivityConfig.configItem.availableModes[i].value === Activity.mode) { - dialogActivityConfig.configItem.modeBox.currentIndex = i; - break; - } + onLoadData: { + if(activityData && activityData["mode"]) { + items.mode = activityData["mode"]; } } } Bar { id: bar - content: BarEnumContent { value: config | help | home | level } + content: BarEnumContent { value: activityConfig | help | home | level } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() - onConfigClicked: { - dialogActivityConfig.active = true - // Set default values - dialogActivityConfig.setDefaultValues(); + onActivityConfigClicked: { displayDialog(dialogActivityConfig) } } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/graph-coloring/Node.qml b/src/activities/graph-coloring/Node.qml index 6a27a96fc..046d649ab 100644 --- a/src/activities/graph-coloring/Node.qml +++ b/src/activities/graph-coloring/Node.qml @@ -1,96 +1,93 @@ /* GCompris - SearchItem.qml * * Copyright (C) Holger Kaelberer * * Authors: * Holger Kaelberer * Akshat Tandon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import GCompris 1.0 -//import QtGraphicalEffects 1.0 import "graph-coloring.js" as Activity - - Item { id: root property int searchItemIndex: -1 property alias border: color.border property alias radius: color.radius property bool highlightSymbol: false property bool symbolRotation: false Image { id: symbol - visible: Activity.mode === "symbol" + visible: items.mode === "symbol" fillMode: Image.PreserveAspectFit source: searchItemIndex == -1 ? Activity.url + "shapes/" + "circle_node.svg" : Activity.symbols[root.searchItemIndex] anchors.left: parent.left anchors.top: parent.top anchors.margins: 3 width: parent.width - 6 height: parent.height - 6 SequentialAnimation { id: anim running: root.symbolRotation loops: Animation.Infinite NumberAnimation { target: symbol property: "rotation" from: -10; to: 10 duration: 500 easing.type: Easing.InOutQuad } NumberAnimation { target: symbol property: "rotation" from: 10; to: -10 duration: 500 easing.type: Easing.InOutQuad } } NumberAnimation { id: rotationStop running: !root.symbolRotation target: symbol property: "rotation" to: 0 duration: 500 easing.type: Easing.InOutQuad } } Rectangle { id: symbolHighlighter - visible: (Activity.mode === "symbol") && root.highlightSymbol + visible: (items.mode === "symbol") && root.highlightSymbol anchors.fill: parent width: parent.width height: parent.height border.width: 3 border.color: "white" color: "transparent" } Rectangle { id: color - visible: Activity.mode === "color" + visible: items.mode === "color" color: root.searchItemIndex == -1 ? "white" : Activity.colors[root.searchItemIndex] anchors.fill: parent width: parent.width height: parent.height radius: width / 2 } } diff --git a/src/activities/graph-coloring/graph-coloring.js b/src/activities/graph-coloring/graph-coloring.js index 06dc3846f..6682371d5 100644 --- a/src/activities/graph-coloring/graph-coloring.js +++ b/src/activities/graph-coloring/graph-coloring.js @@ -1,355 +1,354 @@ /* GCompris - graph-coloring.js * * Copyright (C) Akshat Tandon * * Authors: * * Akshat Tandon (Qt Quick version) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ .pragma library .import QtQuick 2.6 as Quick var currentLevel = 0 var items var url = "qrc:/gcompris/src/activities/graph-coloring/resource/" var coloringLeft var colors = [ "#FF0000FF", // dark blue "#FF00FF00", // light green "#FFFF0000", // red "#FF00FFFF", // light blue "#FFFF00FF", // magenta "#FFFFFF00", // yellow "#FF8e7016", // brown "#FF04611a", // dark green "#FFa0174b" // dark magenta ]; var symbols = [ url + "shapes/" + "star.svg", url + "shapes/" + "triangle.svg", url + "shapes/" + "heart.svg", url + "shapes/" + "cloud.svg", url + "shapes/" + "diamond.svg", url + "shapes/" + "star_simple.svg", url + "shapes/" + "cross.svg", url + "shapes/" + "ring.svg", url + "shapes/" + "circle.svg", ]; var graphs = [ { minColor:3, edgeList:[ [0, 1], [0, 4], [1, 4], [1, 2], [1, 3], [2, 3] ], nodePositions : [ [0, 0], [0.5, 0.4], [1, 0], [1, 0.7], [0, 0.7] ] }, { minColor: 3, edgeList:[ [0, 1], [0, 3], [1, 2], [1, 3], [2, 3] ], nodePositions : [ [0, 0.4], [0.5, 0], [1, 0.4], [0.5, 0.8] ] }, { minColor: 4, edgeList:[ [0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3] ], nodePositions : [ [0.628, 0.401], [0.083, 0.401], [0.900, 0.030], [0.900, 0.773] ] }, { minColor: 3, edgeList:[ [0,1], [1,2], [2,3], [3,4], [4,0], [5,7], [7,9], [9,6], [6,8], [8,5], [0,5], [1,6], [2,7], [3,8], [4,9] ], nodePositions : [ [0.5,0], [0.90,0.35], [0.80,0.80], [0.20, 0.80], [0.10, 0.35], [0.5,0.20], [0.75,0.45], [0.65, 0.65], [0.35, 0.65], [0.25, 0.45] ] }, { minColor: 5, edgeList: [ [5, 1], [5, 0], [0, 3], [0, 1], [0, 2], [2, 4], [2, 1], [3, 4], [3, 2], [4, 1], [5, 4], [5, 3] ], nodePositions : [ [0.75, 0.00], [0.75, 0.80], [1.00, 0.40], [0.25, 0.00], [0.25, 0.80], [0.00, 0.40] ] }, { minColor: 3, edgeList: [ [5, 4], [2, 0], [0, 1], [1, 5], [4, 3], [3, 2], [0, 11], [1, 6], [7, 5], [3, 9], [8, 4], [2, 10], [11, 9], [7, 9], [11, 7], [6, 8], [10, 8], [6, 10] ], nodePositions : [ [0.26, 0.00], [0.74, 0.00], [0.00, 0.40], [0.26, 0.80], [0.74, 0.80], [1.00, 0.40], [0.62, 0.26], [0.74, 0.40], [0.62, 0.64], [0.38, 0.64], [0.26, 0.40], [0.38, 0.26] ] }, { minColor: 4, edgeList: [ [0, 8], [0, 4], [3, 6], [10, 3], [2, 11], [7, 2], [9, 1], [5, 1], [0, 1], [1, 2], [4, 6], [8, 9], [10, 11], [0, 3], [3, 2], [8, 11], [10, 9], [4, 7], [5, 7], [6, 5], [6, 9], [10, 5], [4, 11], [8, 7] ], nodePositions : [ [0.00, 0.00], [1.00, 0.00], [1.00, 0.80], [0.00, 0.80], [0.32, 0.32], [0.74, 0.32], [0.32, 0.53], [0.74, 0.53], [0.42, 0.22], [0.63, 0.22], [0.42, 0.64], [0.63, 0.64] ] } ] var levels = [ {extraColor:1, graph:graphs[0]}, {extraColor:0, graph:graphs[0]}, {extraColor:1, graph:graphs[1]}, {extraColor:0, graph:graphs[1]}, {extraColor:1, graph:graphs[2]}, {extraColor:0, graph:graphs[2]}, {extraColor:1, graph:graphs[3]}, {extraColor:0, graph:graphs[3]}, {extraColor:1, graph:graphs[4]}, {extraColor:0, graph:graphs[4]}, {extraColor:1, graph:graphs[5]}, {extraColor:0, graph:graphs[5]}, {extraColor:1, graph:graphs[6]}, {extraColor:0, graph:graphs[6]} ]; var numberOfLevel = levels.length -var mode = "symbol"; function start(items_) { items = items_ currentLevel = 0 initLevel() } function stop() { } function initLevel() { coloringLeft = true items.bar.level = currentLevel + 1 var currentIndeces = new Array(); var levelData = levels[currentLevel].graph items.colorsRepeater.model.clear(); items.nodesRepeater.model.clear(); items.edgesRepeater.model.clear(); var numColors = levelData.minColor + levels[currentLevel].extraColor; for (var i = 0; i < numColors; ++i) { currentIndeces[i] = i; items.colorsRepeater.model.append({"itemIndex": i}); } items.chooserGrid.model = currentIndeces for (var i = 0; i < levelData.nodePositions.length; ++i){ items.nodesRepeater.model.append({ "posX":levelData.nodePositions[i][0], "posY":levelData.nodePositions[i][1], "colIndex": -1, "highlight": false }); } for (var i = 0; i < levelData.edgeList.length; ++i){ var node1 = levelData.edgeList[i][0] var node2 = levelData.edgeList[i][1] items.edgesRepeater.model.append({ "xp": levelData.nodePositions[node1][0], "yp": levelData.nodePositions[node1][1], "xpp": levelData.nodePositions[node2][0], "ypp": levelData.nodePositions[node2][1], "highlight": false }); } } function checkGuess() { var flag = false; var levelData = levels[currentLevel].graph //Check whether all the nodes have been colored or not for (var i = 0; i < levelData.nodePositions.length; i++){ var node1 = items.nodesRepeater.model.get(i) if (node1.colIndex == -1){ flag = true; break; } } //Check whether the adjacent nodes do not have the same color for (var i = 0; i < levelData.edgeList.length; i++){ var node1 = items.nodesRepeater.model.get(levelData.edgeList[i][0]) var node2 = items.nodesRepeater.model.get(levelData.edgeList[i][1]) //console.log("node1 " + levelData.edgeList[i][0] + " node2 "+ levelData.edgeList[i][1]+" node1 color "+ node1.colIndex+ " node2 color " + node2.colIndex); if (node1.colIndex == node2.colIndex) { //console.log("node1 " + levelData.edgeList[i][0] + " node2 "+ levelData.edgeList[i][1]+" node1 color "+ node1.colIndex+ " node2 color " + node2.colIndex); flag = true; break; } } //console.log("flag is " + flag); if (flag == false) { items.bonus.good("lion"); } } function checkAdjacent() { var levelData = levels[currentLevel].graph var flagNodes = new Array(levelData.nodePositions.length) for (var i = 0; i < levelData.nodePositions.length; i++){ flagNodes[i] = false } for (var i = 0; i < levelData.edgeList.length; i++){ var node1 = items.nodesRepeater.model.get(levelData.edgeList[i][0]) var node1Num = levelData.edgeList[i][0] var node2 = items.nodesRepeater.model.get(levelData.edgeList[i][1]) var node2Num = levelData.edgeList[i][1] if (node1.colIndex == node2.colIndex && node2.colIndex != -1) { items.nodesRepeater.model.setProperty(node1Num, "highlight", true) items.nodesRepeater.model.setProperty(node2Num, "highlight", true) items.edgesRepeater.model.setProperty(i, "highlight", true) flagNodes[node1Num] = true flagNodes[node2Num] = true } else { if(!flagNodes[node1Num]) { items.nodesRepeater.model.setProperty(node1Num, "highlight", false) } if(!flagNodes[node2Num]) { items.nodesRepeater.model.setProperty(node2Num, "highlight", false) } items.edgesRepeater.model.setProperty(i, "highlight", false) } } } function nextLevel() { if(numberOfLevel <= ++currentLevel ) { currentLevel = 0 } initLevel(); } function previousLevel() { if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); }