Partie 9: Informations dans l'éditeur

Téléchargements

Code source
Exécutable de l'éditeur de niveaux (Windows 32bits)
Exécutable du jeu - exactement le même que la partie 7 (Windows 32bits)

Avant d'essayer de compiler le code, allez dans l'onglet "Projets" dans le menu de gauche, séléctionnez l'onglet "Run" pour votre kit,
et mettez dans "Working directory" le chemin de "editor\data" pour l'éditeur ou "game\data" pour le jeu.

Envoyer des données au QML

Pour faire du ménage dans le fichier "editor.cpp", j'ai déplacé toutes les fonctions pour dessiner le curseur et les sélections dans un fichier "rectangles.cpp".

Jusqu'à maintenant nous avons seulement envoyé des données depuis le QML vers la partie C++ en appelant des fonctions de "bridge.cpp".
Maintenant, nous allons envoyer des données dans l'autre sens pour afficher des informations sur la map dans l'éditeur.
Pour envoyer une variable au QML, c'est un peu plus compliqué que simplement appeler une fonction.
Dans "bridge.h" vous allez trouver une macro:

		#define qmlMember(_type,_name) \
			public: \
				Q_PROPERTY(_type _name MEMBER m_##_name READ get_##_name WRITE set_##_name NOTIFY _name##Changed); \
				_type get_##_name() const {return m_##_name;} \
				void set_##_name(_type val) {m_##_name = val; emit _name##Changed(val);} \
			private: \
				_type m_##_name;
				
Comme vous pouvez le voir, on doit appeler "Q_PROPERTY" avec les paramètres suivants:
La macro définit tous les paramètres nécessaires sauf le signal parce que ça poserait des problèmes à la compilation.

La position du curseur

Maintenant on va utiliser ça pour une tache simple: on va afficher les coordonnées du curseur en bas de l'écran.
Dans "editor.cpp" j'ai ajouté une fonction getPositionString() qui renvoie la chaine qu'on veut afficher en fonction de l'onglet courant.
Dans "bridge.h" on définit notre variable:

			qmlMember(QString,cursorInfo)
			[...]

		signals:
			[...]
			void    cursorInfoChanged(QString);
				
Dans "MainForm.ui.qml" on l'affiche simplement dans un rectangle:

		Rectangle {
			id: rectangle
			y: 446
			height: cursorInfos.height + 8
			color: "#00000000"
			border.color: "#808080"
			border.width: 1
			[...]

			Label {
				id: cursorInfos
				x: 0
				text: bridge.cursorInfo
				[...]
			}
		}
				
Et dans "rectangles.cpp", on modifie la variable à chaque fois qu'on dessine le curseur:

		void Rectangles::drawCursor(QImage* image, CVec2 mousePos, ETabIndex tabIndex)
		{
			if (mousePos.x != -1)
			{
				if (tabIndex == eTabTiles)
					drawTileRect(image, mousePos, CURSOR_COLOR);
				else if (tabIndex == eTabWalls)
					drawWallRect(image, mousePos, CURSOR_COLOR);
				else if (tabIndex == eTabObjects)
					drawObjectRect(image, mousePos, CURSOR_COLOR);

				QString text = editor.getPositionString(mousePos, tabIndex);
				bridge.set_cursorInfo(text);
			}
			else
			{
				bridge.set_cursorInfo(QString());
			}
		}
				

Les informations de la sélection

En dessous de la TabView on va maintenant afficher une boite avec des informations sur la sélection.
Il y aura 4 contenus différents suivant qu'on a sélectionné 1 case, 1 mur, 1 case d'objet ou un rectangle de cases:

		GroupBox {
			id: selectionBox
			x: 403
			width: tabView1.width
			[...]
			title: qsTr("Sélection")

			ScrollView {
				id: tileSelScroll
				visible: bridge.tileSelScrollVis
				anchors.fill: parent

				Grid {
					id: tSelGrid
					spacing: 4
					rows: 3
					columns: 2
					anchors.fill: parent
					[...]
				}
			}

			ScrollView {
				id: wallSelScroll
				visible: bridge.wallSelScrollVis
				anchors.fill: parent

				Grid {
					id: wSelGrid
					spacing: 4
					rows: 3
					columns: 2
					anchors.fill: parent
					[...]
				}
			}

			ScrollView {
				id: objectSelScroll
				visible: bridge.objectSelScrollVis
				anchors.fill: parent

				Grid {
					id: oSelGrid
					spacing: 4
					rows: 3
					columns: 2
					anchors.fill: parent
					[...]
				}
			}

			ScrollView {
				id: rectSelScroll
				visible: bridge.rectSelScrollVis
				anchors.fill: parent

				Grid {
					id: rSelGrid
					spacing: 4
					rows: 3
					columns: 2
					anchors.fill: parent
					[...]
				}
			}
		}
				
Comme vous le voyez, chaque ScrollView peut être affichée ou cachée en modifiant la variable correspondante: tileSelScrollVis, wallSelScrollVis,
objectSelScrollVis ou rectSelScrollVis.
Ces variables sont définies de la même façon que l'on a défini les coordonnées du curseur ci-dessus.

A l'intérieur de chacune de ces ScrollViews il y a une "Grid" pour organiser le conenu en lignes.
La première ligne de chaque contenu sera la même: les coordonnées de la sélection. Par exemple:

		Label {
			id: tPosLabel
			height: 20
			text: qsTr("Position:")
			verticalAlignment: Text.AlignVCenter
			renderType: Text.QtRendering
		}

		Label {
			id: tPosValue
			height: 20
			text: bridge.selPosition
			verticalAlignment: Text.AlignVCenter
		}
				
Cette position est définie par la même fonction getPositionString() qu'on a utilisée auparavant.

On n'ajoutera rien au contenu pour l'onglet "objects" pour le moment. On verra comment on les gère plus tard.
Pour une sélection "rectangulaire", la deuxième ligne sera simplement la taille du rectangle.
Dans les contenus pour un case ou un mur, La 2ième ligne contiendra une "ComboBox" qui nous permettra de changer le type de l'élément sélectionné.

Les ComboBox

A l'intérieur de nos "grids", les lignes des ComboBox sont définies comme ça:

		Label {
			id: tTypeLabel
			height: 20
			text: qsTr("Type:")
			verticalAlignment: Text.AlignVCenter
			horizontalAlignment: Text.AlignLeft
		}

		ComboBox {
			id: tTypeValue
			model: tilesList
			currentIndex: bridge.selTileType
		}
				
Le "model" représente toutes les valeurs qui seront affichées dans la ComboBox. C'est une liste de chaines de caractères qui est définie par la fonction
initLists() dans "editor.cpp" et enregistrée pour le QML dans main.cpp:

		// enregistre nos fonctions
		ctxt = engine.rootContext();
		editor.initLists();
		ctxt->setContextProperty("bridge", &bridge);
		ctxt->setContextProperty("tilesList", QVariant::fromValue(bridge.tilesList));
		ctxt->setContextProperty("wallsList", QVariant::fromValue(bridge.wallsList));
				
Le "currentIndex" est la variable qu'on va utiliser pour définir le type de l'élément sélectionné du coté C++.
Maintenant, si j'ai choisi de mettre une comboBox, c'est pour être capable de changer le type de l'élément sélectionne, donc, quand on change le valeur de la
ComboBox, on doit l'envoyer du QML au C++. C'est fait dans "main.qml":

		tTypeValue.onCurrentIndexChanged: bridge.setSelTileType(tTypeValue.currentIndex);
		wTypeValue.onCurrentIndexChanged: bridge.setSelWallType(wTypeValue.currentIndex);
				
Vous pourrez voir comment ces valeurs sont gérées dans le code C++ en cherchant les variables et les fonction correspondantes.