diff --git a/src/core/ClientNetworkMessages.cpp b/src/core/ClientNetworkMessages.cpp index 534f0268c..9071a9ee5 100644 --- a/src/core/ClientNetworkMessages.cpp +++ b/src/core/ClientNetworkMessages.cpp @@ -1,337 +1,337 @@ /* GCompris - ClientNetworkMessages.cpp * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * This file was originally created from Digia example code under BSD license * and heavily modified since then. * * 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 . */ #include #include #include #include #include "Messages.h" #include "DataStreamConverter.h" #include "ApplicationSettings.h" #include "ClientNetworkMessages.h" ClientNetworkMessages* ClientNetworkMessages::_instance = 0; ClientNetworkMessages::ClientNetworkMessages(): QObject(), tcpSocket(new QTcpSocket(this)), udpSocket(new QUdpSocket(this)), networkSession(Q_NULLPTR), _connected(false) { if(!udpSocket->bind(5678, QUdpSocket::ShareAddress)) qDebug("could not bind"); else qDebug("success"); connect(udpSocket, &QUdpSocket::readyRead, this, &ClientNetworkMessages::udpRead); connect(tcpSocket, &QTcpSocket::connected, this, &ClientNetworkMessages::connected); connect(tcpSocket, &QTcpSocket::disconnected, this, &ClientNetworkMessages::serverDisconnected); connect(tcpSocket, &QAbstractSocket::readyRead, this, &ClientNetworkMessages::readFromSocket); QNetworkConfigurationManager manager; if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) { // Get saved network configuration QString id; { QSettings settings(QSettings::UserScope, QLatin1String("QtProject")); settings.beginGroup(QLatin1String("QtNetwork")); id = settings.value(QLatin1String("DefaultNetworkConfiguration")).toString(); settings.endGroup(); } // If the saved network configuration is not currently discovered use the system default QNetworkConfiguration config = manager.configurationFromIdentifier(id); if ((config.state() & QNetworkConfiguration::Discovered) != QNetworkConfiguration::Discovered) { config = manager.defaultConfiguration(); } networkSession = new QNetworkSession(config, this); connect(networkSession, &QNetworkSession::opened, this, &ClientNetworkMessages::sessionOpened); networkSession->open(); } } ClientNetworkMessages::~ClientNetworkMessages() { _instance = 0; } // It is not recommended to create a singleton of Qml Singleton registered // object but we could not found a better way to let us access ClientNetworkMessages // on the C++ side. All our test shows that it works. // Using the singleton after the QmlEngine has been destroyed is forbidden! ClientNetworkMessages* ClientNetworkMessages::getInstance() { if (!_instance) _instance = new ClientNetworkMessages; return _instance; } QObject *ClientNetworkMessages::systeminfoProvider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return getInstance(); } void ClientNetworkMessages::init() { qmlRegisterSingletonType("GCompris", 1, 0, "ClientNetworkMessages", systeminfoProvider); } void ClientNetworkMessages::connectToServer(const QString& serverName) { QString ip = _host; int port = _port; //if we are already connected to some server, disconnect from it first and then make a connection with new server if(_connected) { // and newServer != currentServer disconnectFromServer(); } if(serverMap.count(serverName) != 0) { ip = serverMap.value(serverName).toString(); port = 5678; } qDebug()<< "connect to " << ip << ":" << port; if(tcpSocket->state() != QAbstractSocket::ConnectedState) { tcpSocket->connectToHost(ip, port); } ApplicationSettings::getInstance()->setCurrentServer(serverName); } void ClientNetworkMessages::disconnectFromServer() { tcpSocket->disconnectFromHost(); ApplicationSettings::getInstance()->setCurrentServer(""); } void ClientNetworkMessages::connected() { QTcpSocket* socket = qobject_cast(sender()); _host = serverMap.key(socket->peerAddress()); _connected = true; emit connectionStatus(); emit hostChanged(); // if we have saved data for this server, we send it sendStoredData(); } -void ClientNetworkMessages::sendLoginMessage(const QString &newLogin) +void ClientNetworkMessages::sendLoginMessage(const QString &newLogin, const QString& password) { // store the username in config ApplicationSettings::getInstance()->setUserName(newLogin); // Send Login message QByteArray bytes; QDataStream out(&bytes, QIODevice::WriteOnly); - Login login { newLogin }; + Login login { newLogin, password }; out << MessageIdentifier::LOGIN << login; sendMessage(bytes); } void ClientNetworkMessages::serverDisconnected() { _host = ""; _connected = false; emit connectionStatus(); emit hostChanged(); } void ClientNetworkMessages::udpRead() { // someone is out there whom I can connect with.Let's get it's address and store it in the list; qDebug() << "Receiving data"; // to get the address we need to read the datagram sent by server .Is there a way to get server's address without // reading the datagram ? QByteArray datagram; QHostAddress address; quint16 port; datagram.resize(udpSocket->pendingDatagramSize()); udpSocket->readDatagram(datagram.data(), datagram.size(), &address, &port); // since our server keeps on sending the broadcast message udpread() will be called everytime it receives the broadcast message // add the server's address to list only if it was not added before; QDataStream in(&datagram, QIODevice::ReadOnly); in.setVersion(QDataStream::Qt_4_0); Identifier messageId; in >> messageId; switch(messageId._id) { case MessageIdentifier::REQUEST_CONTROL: { QString serverName; in >> serverName; qDebug() << "control requested by " << serverName; // todo use the real server name and a real message with QDataStream if(!_connected) emit requestConnection(serverName); if(!serversAvailable.contains(serverName)) { serversAvailable.append(serverName); serverMap.insert(serverName, address); emit newServers(); } } break; default: qDebug() << messageId._id << " received but not handled"; } } void ClientNetworkMessages::sessionOpened() { // Save the used configuration QNetworkConfiguration config = networkSession->configuration(); QString id; if (config.type() == QNetworkConfiguration::UserChoice) id = networkSession->sessionProperty(QLatin1String("UserChoiceConfiguration")).toString(); else id = config.identifier(); } void ClientNetworkMessages::sendActivityData(const QString &activity, const QVariantMap &data) { qDebug() << "Activity: " << activity << ", date: " << QDateTime::currentDateTime() << ", data:" << data; const QString &username = ApplicationSettings::getInstance()->userName(); ActivityRawData activityData { activity, username, QDateTime::currentDateTime(), data }; QByteArray bytes; QDataStream out(&bytes, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_0); out << MessageIdentifier::ACTIVITY_DATA << activityData; if(!sendMessage(bytes) && !ApplicationSettings::getInstance()->currentServer().isEmpty()) { // store only if the user did not explicitly disconnect from the server QFile file(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + + "/" + ApplicationSettings::getInstance()->currentServer() + ".dat"); file.open(QIODevice::WriteOnly | QIODevice::Append); QDataStream outFile(&file); outFile.setVersion(QDataStream::Qt_4_0); outFile << MessageIdentifier::ACTIVITY_DATA << activityData; file.close(); } } bool ClientNetworkMessages::sendStoredData() { QFile file(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + + "/" + ApplicationSettings::getInstance()->currentServer() + ".dat"); if(file.exists()) { file.open(QIODevice::ReadOnly); QDataStream in(&file); in.setVersion(QDataStream::Qt_4_0); QByteArray bytes; QDataStream out(&bytes, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_0); while(!in.atEnd()) { Identifier messageId; ActivityRawData act; in >> messageId >> act; out << messageId << act; } if(tcpSocket->state() == QAbstractSocket::ConnectedState) { int size = tcpSocket->write(bytes); qDebug() << "size sent: " << size << "/" << bytes.size() << endl; } file.close(); file.remove(); } } bool ClientNetworkMessages::sendMessage(const QByteArray &message) { int size = 0; if(tcpSocket->state() == QAbstractSocket::ConnectedState) { size = tcpSocket->write(message); } return size != 0; } void ClientNetworkMessages::readFromSocket() { QByteArray data = tcpSocket->readAll(); QDataStream in(&data, QIODevice::ReadOnly); in.setVersion(QDataStream::Qt_4_0); Identifier messageId; in >> messageId; switch(messageId._id) { case MessageIdentifier::DISPLAYED_ACTIVITIES: { DisplayedActivities activities; in >> activities; qDebug() << "--" << activities.activitiesToDisplay; ApplicationSettings::getInstance()->setActivitiesToDisplay(activities.activitiesToDisplay); break; } case MessageIdentifier::ACTIVITY_CONFIGURATION: { ActivityConfiguration config; in >> config; qDebug() << "Configuration received for: " << config.activityName << ", data: " << config.data; ApplicationSettings::getInstance()->storeActivityConfiguration(config.activityName, config.data); break; } case MessageIdentifier::LOGINS_LIST: { AvailableLogins logins; in >> logins._logins; in >> logins._passwords; qDebug() << "logins received: " << logins._logins; qDebug() << "passwords : " << logins._passwords; // todo emit loginListReceived(logins._logins, logins._passwords); break; } default: qDebug() << messageId._id << " received but not handled"; } } //void ClientNetworkMessages::displayError(QAbstractSocket::SocketError socketError) //{ // switch (socketError) { // case QAbstractSocket::RemoteHostClosedError: // break; // case QAbstractSocket::HostNotFoundError: // qDebug() << tr("The host was not found. Please check the " // "host name and port settings."); // break; // case QAbstractSocket::ConnectionRefusedError: // qDebug() << tr("The connection was refused by the peer. " // "Make sure the server is running, " // "and check that the host name and port " // "settings are correct."); // break; // default: // qDebug() << tr("The following error occurred: %1.").arg(tcpSocket->errorString()); // } //} diff --git a/src/core/ClientNetworkMessages.h b/src/core/ClientNetworkMessages.h index a235e439a..a54882798 100644 --- a/src/core/ClientNetworkMessages.h +++ b/src/core/ClientNetworkMessages.h @@ -1,112 +1,112 @@ /* GCompris - ClientNetworkMessages.h * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * This file was originally created from Digia example code under BSD license * and heavily modified since then. * * 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 . */ #ifndef CLIENTNETWORKMESSAGES_H #define CLIENTNETWORKMESSAGES_H #include #include class QTcpSocket; class QUdpSocket; class QNetworkSession; class ClientNetworkMessages : public QObject { Q_OBJECT Q_PROPERTY(QStringList serversAvailable MEMBER serversAvailable NOTIFY newServers) Q_PROPERTY(bool connected MEMBER _connected NOTIFY connectionStatus) Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged) Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged) signals: void newServers(); void hostChanged(); void portChanged(); void connectionStatus(); void loginListReceived(const QStringList& logins, const QStringList& passwords); void requestConnection(const QString& serverName); private: ClientNetworkMessages(); // prohibit external creation, we are a singleton! static ClientNetworkMessages* _instance; // singleton instance QString _host; int _port; bool _connected; public: /** * Registers ClientNetworkMessages singleton in the QML engine. */ static void init(); static QObject *systeminfoProvider(QQmlEngine *engine, QJSEngine *scriptEngine); static ClientNetworkMessages* getInstance(); Q_INVOKABLE void sendActivityData(const QString &activity, const QVariantMap &data); bool sendMessage(const QByteArray &message); virtual ~ClientNetworkMessages(); Q_INVOKABLE void connectToServer(const QString& serverName); Q_INVOKABLE void disconnectFromServer(); - Q_INVOKABLE void sendLoginMessage(const QString &newLogin); + Q_INVOKABLE void sendLoginMessage(const QString &newLogin, const QString& password); QStringList serversAvailable; QString host() const{ return _host; } void setHost(const QString &newHost) { _host = newHost; emit hostChanged(); } int port() const{ return _port; } void setPort(const int &newPort) { _port = newPort; emit portChanged(); } private slots: void readFromSocket(); // void displayError(QAbstractSocket::SocketError socketError); void sessionOpened(); void udpRead(); void connected(); void serverDisconnected(); private: bool sendStoredData(); QTcpSocket *tcpSocket; QUdpSocket* udpSocket; QNetworkSession *networkSession; QMap serverMap; }; #endif diff --git a/src/core/DataStreamConverter.h b/src/core/DataStreamConverter.h index 3dc75664e..1a0c14578 100644 --- a/src/core/DataStreamConverter.h +++ b/src/core/DataStreamConverter.h @@ -1,104 +1,106 @@ /* GCompris - DataStreamConverter.h * * Copyright (C) 2016 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 . */ #ifndef DATASTREAM_CONVERTER_H #define DATASTREAM_CONVERTER_H #include "Messages.h" #include inline QDataStream& operator<<(QDataStream &dataStream, const Identifier &id) { dataStream << int(id._id); return dataStream; }; inline QDataStream& operator>>(QDataStream &dataStream, Identifier &id) { int identifier; dataStream >> identifier; id._id = (MessageIdentifier)identifier; return dataStream; }; inline QDataStream& operator<<(QDataStream &dataStream, const Login &login) { dataStream << login._name; + dataStream << login._password; return dataStream; }; inline QDataStream& operator>>(QDataStream &dataStream, Login &login) { dataStream >> login._name; + dataStream >> login._password; return dataStream; }; inline QDataStream& operator<<(QDataStream &dataStream, const AvailableLogins &logins) { dataStream << logins._logins; dataStream << logins._passwords; return dataStream; }; inline QDataStream& operator>>(QDataStream &dataStream, AvailableLogins &logins) { dataStream >> logins._logins; dataStream >> logins._passwords; return dataStream; }; inline QDataStream& operator<<(QDataStream &dataStream, const DisplayedActivities &act) { dataStream << act.activitiesToDisplay; return dataStream; }; inline QDataStream& operator>>(QDataStream &dataStream, DisplayedActivities &act) { dataStream >> act.activitiesToDisplay; return dataStream; }; inline QDataStream& operator<<(QDataStream &dataStream, const ActivityRawData &act) { dataStream << act.activityName << act.username << act.date << act.data; return dataStream; }; inline QDataStream& operator>>(QDataStream &dataStream, ActivityRawData &act) { dataStream >> act.activityName >> act.username >> act.date >> act.data; return dataStream; }; inline QDataStream& operator<<(QDataStream &dataStream, const ActivityConfiguration &act) { dataStream << act.activityName << act.data; return dataStream; }; inline QDataStream& operator>>(QDataStream &dataStream, ActivityConfiguration &act) { dataStream >> act.activityName >> act.data; return dataStream; }; #endif diff --git a/src/core/Messages.h b/src/core/Messages.h index 918efecc5..def951d75 100644 --- a/src/core/Messages.h +++ b/src/core/Messages.h @@ -1,74 +1,75 @@ /* GCompris - Messages.h * * Copyright (C) 2016 Emmanuel Charruau , Johnny Jazeix * * Authors: * Emmanuel Charruau * 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 . */ #ifndef MESSAGES_H #define MESSAGES_H #include #include #include #include #include enum MessageIdentifier : int { LOGIN = 0, REQUEST_CONTROL, LOGINS_LIST, REQUEST_USERNAME, DISPLAYED_ACTIVITIES, ACTIVITY_DATA, ACTIVITY_CONFIGURATION }; struct Identifier { MessageIdentifier _id; }; struct Login { QString _name; + QString _password; }; struct AvailableLogins { QStringList _logins; QStringList _passwords; }; struct DisplayedActivities { QStringList activitiesToDisplay; }; struct ActivityRawData { QString activityName; QString username; QDateTime date; QVariantMap data; }; struct ActivityConfiguration { QString activityName; QVariantMap data; }; struct ConfigurationData { QList activities; }; #endif diff --git a/src/core/main.qml b/src/core/main.qml index 7ae3c620b..445480a75 100644 --- a/src/core/main.qml +++ b/src/core/main.qml @@ -1,412 +1,466 @@ /* GCompris - main.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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.2 import QtQuick.Controls 1.0 import QtQuick.Window 2.1 import QtQml 2.2 import GCompris 1.0 import "qrc:/gcompris/src/core/core.js" as Core /** * GCompris' main QML file defining the top level window. * @ingroup infrastructure * * Handles application start (Component.onCompleted) and shutdown (onClosing) * on the QML layer. * * Contains the central GCAudio objects audio effects and audio voices. * * Contains the top level StackView presenting and animating GCompris' * full screen views. * * @sa BarButton, BarEnumContent * @inherit QtQuick.Window */ Window { id: main // Start in window mode at full screen size width: ApplicationSettings.previousWidth height: ApplicationSettings.previousHeight minimumWidth: 400 * ApplicationInfo.ratio minimumHeight: 400 * ApplicationInfo.ratio title: "GCompris" /// @cond INTERNAL_DOCS property var applicationState: Qt.application.state onApplicationStateChanged: { if (ApplicationInfo.isMobile && applicationState != Qt.ApplicationActive) { audioVoices.stop(); audioEffects.stop(); } } onClosing: Core.quit(main) GCAudio { id: audioVoices muted: !ApplicationSettings.isAudioVoicesEnabled Timer { id: delayedWelcomeTimer interval: 10000 /* Make sure, that playing welcome.ogg if delayed * because of not yet registered voices, will only * happen max 10sec after startup */ repeat: false onTriggered: { DownloadManager.voicesRegistered.disconnect(playWelcome); } function playWelcome() { audioVoices.append(ApplicationInfo.getAudioFilePath("voices-$CA/$LOCALE/misc/welcome.$CA")); } } Component.onCompleted: { if(ApplicationSettings.isAudioEffectsEnabled) append(ApplicationInfo.getAudioFilePath("qrc:/gcompris/src/core/resource/intro.$CA")) if (DownloadManager.areVoicesRegistered()) delayedWelcomeTimer.playWelcome(); else { DownloadManager.voicesRegistered.connect( delayedWelcomeTimer.playWelcome); delayedWelcomeTimer.start(); } } } GCAudio { id: audioEffects muted: !ApplicationSettings.isAudioEffectsEnabled } function playIntroVoice(name) { name = name.split("/")[0] audioVoices.append(ApplicationInfo.getAudioFilePath("voices-$CA/$LOCALE/intro/" + name + ".$CA")) } function checkWordset() { var wordset = ApplicationSettings.wordset if(wordset == '') // Maybe the wordset has been bundled or copied manually // we have to register it if we find it. wordset = 'data2/words/words.rcc' // check for words.rcc: if (DownloadManager.isDataRegistered("words")) { // words.rcc is already registered -> nothing to do } else if(DownloadManager.haveLocalResource(wordset)) { // words.rcc is there -> register old file first // then try to update in the background if(DownloadManager.updateResource(wordset)) { ApplicationSettings.wordset = wordset } } else if(ApplicationSettings.wordset) { // Only if wordset specified // words.rcc has not been downloaded yet -> ask for download Core.showMessageDialog( main, qsTr("The images for several activities are not yet installed. " + "Do you want to download them now?"), qsTr("Yes"), function() { if (DownloadManager.downloadResource(wordset)) var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); }, qsTr("No"), null, function() { pageView.currentItem.focus = true } ); } } ChangeLog { id: changelog } Component.onCompleted: { console.log("enter main.qml (run #" + ApplicationSettings.exeCount + ", ratio=" + ApplicationInfo.ratio + ", fontRatio=" + ApplicationInfo.fontRatio + ", dpi=" + Math.round(Screen.pixelDensity*25.4) + ", sharedWritablePath=" + ApplicationInfo.getSharedWritablePath() + ")"); if (ApplicationSettings.exeCount === 1 && !ApplicationSettings.isKioskMode && ApplicationInfo.isDownloadAllowed) { // first run var dialog; dialog = Core.showMessageDialog( main, qsTr("Welcome to GCompris!") + '\n' + qsTr("You are running GCompris for the first time.") + '\n' + qsTr("You should verify that your application settings especially your language is set correctly, and that all language specific sound files are installed. You can do this in the Preferences Dialog.") + "\n" + qsTr("Have Fun!") + "\n" + qsTr("Your current language is %1 (%2).") .arg(Qt.locale(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale)).nativeLanguageName) .arg(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale)) + "\n" + qsTr("Do you want to download the corresponding sound files now?"), qsTr("Yes"), function() { if (DownloadManager.downloadResource( DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale))) var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); }, qsTr("No"), null, function() { pageView.currentItem.focus = true checkWordset() } ); } else { // Register voices-resources for current locale, updates/downloads only if // not prohibited by the settings if (!DownloadManager.areVoicesRegistered()) { DownloadManager.updateResource( DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)); } checkWordset() if(changelog.isNewerVersion(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode)) { // display log between ApplicationSettings.lastGCVersionRan and ApplicationInfo.GCVersionCode var dialog; dialog = Core.showMessageDialog( main, qsTr("GCompris has been updated! Here are the new changes:
") + changelog.getLogBetween(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode), "", null, "", null, function() { pageView.currentItem.focus = true } ); // Store new version ApplicationSettings.lastGCVersionRan = ApplicationInfo.GCVersionCode; } } } Loading { id: loading } StackView { id: pageView anchors.fill: parent initialItem: { "item": "qrc:/gcompris/src/activities/" + ActivityInfoTree.rootMenu.name, "properties": { 'audioVoices': audioVoices, 'audioEffects': audioEffects, 'loading': loading } } focus: ApplicationInfo.QTVersion >= "5.4.0" delegate: StackViewDelegate { id: root function getTransition(properties) { audioVoices.clearQueue() if(!properties.exitItem.isDialog && // if coming from menu and !properties.enterItem.isDialog) // going into an activity then playIntroVoice(properties.enterItem.activityInfo.name); // play intro if (!properties.exitItem.isDialog || // if coming from menu or properties.enterItem.alwaysStart) // start signal enforced (for special case like transition from config-dialog to editor) properties.enterItem.start(); if(properties.name === "pushTransition") { if(properties.enterItem.isDialog) { return pushVTransition } else { return pushHTransition } } else { if(properties.exitItem.isDialog) { return popVTransition } else { return popHTransition } } } function transitionFinished(properties) { properties.exitItem.opacity = 1 if(!properties.enterItem.isDialog) { properties.exitItem.stop() } } property Component pushHTransition: StackViewTransition { PropertyAnimation { target: enterItem property: "x" from: target.width to: 0 duration: 500 easing.type: Easing.OutSine } PropertyAnimation { target: exitItem property: "x" from: 0 to: -target.width duration: 500 easing.type: Easing.OutSine } } property Component popHTransition: StackViewTransition { PropertyAnimation { target: enterItem property: "x" from: -target.width to: 0 duration: 500 easing.type: Easing.OutSine } PropertyAnimation { target: exitItem property: "x" from: 0 to: target.width duration: 500 easing.type: Easing.OutSine } } property Component pushVTransition: StackViewTransition { PropertyAnimation { target: enterItem property: "y" from: -target.height to: 0 duration: 500 easing.type: Easing.OutSine } PropertyAnimation { target: exitItem property: "y" from: 0 to: target.height duration: 500 easing.type: Easing.OutSine } } property Component popVTransition: StackViewTransition { PropertyAnimation { target: enterItem property: "y" from: target.height to: 0 duration: 500 easing.type: Easing.OutSine } PropertyAnimation { target: exitItem property: "y" from: 0 to: -target.height duration: 500 easing.type: Easing.OutSine } } property Component replaceTransition: pushHTransition } } Connections { id: connection target: ClientNetworkMessages property string serverName onRequestConnection: { connection.serverName = serverName Core.showMessageDialog(main, qsTr("Do you want to connect to server %1?").arg(connection.serverName), qsTr("Yes"), function() { ClientNetworkMessages.connectToServer(connection.serverName); }, qsTr("No"), null, null); } onLoginListReceived: { chooseLogin.model = logins; + choosePassword.model = passwords; chooseLogin.visible = true chooseLogin.start() } } GCInputDialog { id: chooseLogin visible: false active: visible anchors.fill: parent message: qsTr("Select your login") onClose: chooseLogin.visible = false; button1Text: qsTr("OK") button2Text: qsTr("Cancel") - onButton1Hit: ClientNetworkMessages.sendLoginMessage(chosenLogin) + onButton1Hit: { + + choosePassword.visible = true + choosePassword.start() +// ClientNetworkMessages.sendLoginMessage(chosenLogin) + } focus: true property string chosenLogin property var model - content: ListView { id: view width: chooseLogin.width height: 100 * ApplicationInfo.ratio contentHeight: 60 * ApplicationInfo.ratio * model.count interactive: true clip: true model: chooseLogin.model delegate: GCDialogCheckBox { id: userBox text: modelData checked: false exclusiveGroup: exclusiveGroupItem Component.onCompleted: { if (exclusiveGroup) exclusiveGroup.bindCheckable(userBox) } Component.onDestruction: { if (exclusiveGroup) exclusiveGroup.unbindCheckable(userBox) } } } ExclusiveGroup { id: exclusiveGroupItem onCurrentChanged: { if(current) chooseLogin.chosenLogin = current.text; } } } + GCInputDialog { + id: choosePassword + visible: false + active: visible + anchors.fill: parent + + message: qsTr("Select your password") + onClose: choosePassword.visible = false; + + + button1Text: qsTr("OK") + button2Text: qsTr("Cancel") + onButton1Hit: { + console.debug("selected password: ", chosenPassword) + console.debug("selected user name: ", chooseLogin.chosenLogin) + ClientNetworkMessages.sendLoginMessage(chooseLogin.chosenLogin, chosenPassword) + } + focus: true + + property string chosenPassword + property var model + content: ListView { + id: view + width: choosePassword.width + height: 100 * ApplicationInfo.ratio + contentHeight: 60 * ApplicationInfo.ratio * model.count + interactive: true + clip: true + model: choosePassword.model + delegate: GCDialogCheckBox { + id: userBox + text: modelData + checked: false + exclusiveGroup: exclusiveGroupItem_2 + Component.onCompleted: { + if (exclusiveGroup) + exclusiveGroup.bindCheckable(userBox) + } + Component.onDestruction: { + if (exclusiveGroup) + exclusiveGroup.unbindCheckable(userBox) + } + } + } + ExclusiveGroup { + id: exclusiveGroupItem_2 + onCurrentChanged: { if(current) choosePassword.chosenPassword = current.text; } + } + } /// @endcond }