Launcher: fix a bug when using MP server 0 or 1
Due to how the popup menu was refresh, using MP servers at index 0 or 1 didn’t work correctly. Change some logic and add a helper to make this work reliably, without a second Repeater and dummy properties.
This commit is contained in:
parent
df9c368099
commit
66d635dc9c
8 changed files with 212 additions and 67 deletions
|
@ -164,6 +164,8 @@ if (HAVE_QT)
|
||||||
FlightPlanController.hxx
|
FlightPlanController.hxx
|
||||||
RouteDiagram.cxx
|
RouteDiagram.cxx
|
||||||
RouteDiagram.hxx
|
RouteDiagram.hxx
|
||||||
|
ModelDataExtractor.cxx
|
||||||
|
ModelDataExtractor.hxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include "UnitsModel.hxx"
|
#include "UnitsModel.hxx"
|
||||||
#include "NavaidSearchModel.hxx"
|
#include "NavaidSearchModel.hxx"
|
||||||
#include "FlightPlanController.hxx"
|
#include "FlightPlanController.hxx"
|
||||||
|
#include "ModelDataExtractor.hxx"
|
||||||
|
|
||||||
using namespace simgear::pkg;
|
using namespace simgear::pkg;
|
||||||
|
|
||||||
|
@ -164,6 +165,8 @@ void LauncherController::initQML()
|
||||||
qmlRegisterType<RouteDiagram>("FlightGear", 1, 0, "RouteDiagram");
|
qmlRegisterType<RouteDiagram>("FlightGear", 1, 0, "RouteDiagram");
|
||||||
qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup");
|
qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup");
|
||||||
|
|
||||||
|
qmlRegisterType<ModelDataExtractor>("FlightGear", 1, 0, "ModelDataExtractor");
|
||||||
|
|
||||||
qmlRegisterSingletonType(QUrl("qrc:/qml/OverlayShared.qml"), "FlightGear", 1, 0, "OverlayShared");
|
qmlRegisterSingletonType(QUrl("qrc:/qml/OverlayShared.qml"), "FlightGear", 1, 0, "OverlayShared");
|
||||||
|
|
||||||
QNetworkDiskCache* diskCache = new QNetworkDiskCache(this);
|
QNetworkDiskCache* diskCache = new QNetworkDiskCache(this);
|
||||||
|
|
|
@ -140,6 +140,7 @@ void MPServersModel::onRefreshMPServersFailed(simgear::HTTP::Request*)
|
||||||
m_servers.clear();
|
m_servers.clear();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
emit validChanged();
|
emit validChanged();
|
||||||
|
emit currentIndexChanged(m_currentIndex);
|
||||||
restoreMPServerSelection();
|
restoreMPServerSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,19 +151,21 @@ void MPServersModel::restoreMPServerSelection()
|
||||||
settings.beginGroup("mpSettings");
|
settings.beginGroup("mpSettings");
|
||||||
QString host = settings.value("mp-server").toString();
|
QString host = settings.value("mp-server").toString();
|
||||||
if (host == "__custom__") {
|
if (host == "__custom__") {
|
||||||
emit restoreIndex(m_servers.size());
|
setCurrentIndex(m_servers.size());
|
||||||
} else {
|
} else {
|
||||||
// restore a built-in server
|
// restore a built-in server
|
||||||
auto it = std::find_if(m_servers.begin(), m_servers.end(), [host](const ServerInfo& info)
|
auto it = std::find_if(m_servers.begin(), m_servers.end(), [host](const ServerInfo& info)
|
||||||
{ return (info.host == host); });
|
{ return (info.host == host); });
|
||||||
|
|
||||||
if (it != m_servers.end()) {
|
if (it != m_servers.end()) {
|
||||||
emit restoreIndex(std::distance(m_servers.begin(), it));
|
setCurrentIndex(std::distance(m_servers.begin(), it));
|
||||||
} else {
|
} else {
|
||||||
emit restoreDefault();
|
setCurrentIndex(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// force a refresh on the QML side, since the model may have changed
|
||||||
|
emit currentIndexChanged(m_currentIndex);
|
||||||
m_doRestoreMPServer = false;
|
m_doRestoreMPServer = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,33 +175,25 @@ void MPServersModel::requestRestore()
|
||||||
m_doRestoreMPServer = true;
|
m_doRestoreMPServer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MPServersModel::serverForIndex(int index) const
|
QString MPServersModel::currentServer() const
|
||||||
{
|
{
|
||||||
if (!valid()) {
|
if (!valid()) {
|
||||||
return (index == 1) ? "__custom__" : "__noservers__";
|
return (m_currentIndex == 1) ? "__custom__" : "__noservers__";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((index < 0) || (index > m_servers.size())) {
|
if (m_currentIndex == m_servers.size()) {
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == m_servers.size()) {
|
|
||||||
return "__custom__";
|
return "__custom__";
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_servers.at(index).host;
|
return m_servers.at(m_currentIndex).host;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MPServersModel::portForIndex(int index) const
|
int MPServersModel::currentPort() const
|
||||||
{
|
{
|
||||||
if (!valid())
|
if (!valid())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((index < 0) || (index >= m_servers.size())) {
|
return m_servers.at(m_currentIndex).port;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_servers.at(index).port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MPServersModel::valid() const
|
bool MPServersModel::valid() const
|
||||||
|
@ -206,6 +201,15 @@ bool MPServersModel::valid() const
|
||||||
return !m_servers.empty();
|
return !m_servers.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MPServersModel::setCurrentIndex(int currentIndex)
|
||||||
|
{
|
||||||
|
if (m_currentIndex == currentIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_currentIndex = currentIndex;
|
||||||
|
emit currentIndexChanged(m_currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
MPServersModel::ServerInfo::ServerInfo(QString n, QString l, QString h, int p)
|
MPServersModel::ServerInfo::ServerInfo(QString n, QString l, QString h, int p)
|
||||||
{
|
{
|
||||||
name = n;
|
name = n;
|
||||||
|
|
|
@ -11,9 +11,14 @@ class MPServersModel : public QAbstractListModel
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
|
Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QString currentServer READ currentServer NOTIFY currentIndexChanged)
|
||||||
|
Q_PROPERTY(int currentPort READ currentPort NOTIFY currentIndexChanged)
|
||||||
public:
|
public:
|
||||||
MPServersModel(QObject* parent = nullptr);
|
MPServersModel(QObject* parent = nullptr);
|
||||||
~MPServersModel();
|
~MPServersModel() override;
|
||||||
|
|
||||||
int rowCount(const QModelIndex& index) const override;
|
int rowCount(const QModelIndex& index) const override;
|
||||||
|
|
||||||
|
@ -30,15 +35,22 @@ public:
|
||||||
|
|
||||||
void requestRestore();
|
void requestRestore();
|
||||||
|
|
||||||
Q_INVOKABLE QString serverForIndex(int index) const;
|
QString currentServer() const;
|
||||||
Q_INVOKABLE int portForIndex(int index) const;
|
int currentPort() const;
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
|
|
||||||
|
int currentIndex() const
|
||||||
|
{
|
||||||
|
return m_currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setCurrentIndex(int currentIndex);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void restoreIndex(int index);
|
|
||||||
void restoreDefault();
|
|
||||||
void validChanged();
|
void validChanged();
|
||||||
|
void currentIndexChanged(int currentIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -54,6 +66,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ServerInfo> m_servers;
|
std::vector<ServerInfo> m_servers;
|
||||||
|
int m_currentIndex = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MPSERVERSMODEL_H
|
#endif // MPSERVERSMODEL_H
|
||||||
|
|
88
src/GUI/ModelDataExtractor.cxx
Normal file
88
src/GUI/ModelDataExtractor.cxx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#include "ModelDataExtractor.hxx"
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
ModelDataExtractor::ModelDataExtractor(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ModelDataExtractor::data() const
|
||||||
|
{
|
||||||
|
if (m_model) {
|
||||||
|
QModelIndex m = m_model->index(m_index, 0);
|
||||||
|
if (!m.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
int role = Qt::DisplayRole;
|
||||||
|
if (!m_role.isEmpty()) {
|
||||||
|
const auto& names = m_model->roleNames();
|
||||||
|
role = names.key(m_role.toUtf8(), Qt::DisplayRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_model->data(m, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_value.isArray()) {
|
||||||
|
return m_value.property(m_index).toVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelDataExtractor::setModel(QJSValue model)
|
||||||
|
{
|
||||||
|
if (m_value.equals(model))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_model) {
|
||||||
|
// disconnect from everything
|
||||||
|
disconnect(m_model, nullptr, this, nullptr);
|
||||||
|
m_model = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_value = model;
|
||||||
|
if (m_value.isQObject()) {
|
||||||
|
m_model = qobject_cast<QAbstractItemModel*>(m_value.toQObject());
|
||||||
|
if (m_model) {
|
||||||
|
connect(m_model, &QAbstractItemModel::modelReset,
|
||||||
|
this, &ModelDataExtractor::dataChanged);
|
||||||
|
connect(m_model, &QAbstractItemModel::dataChanged,
|
||||||
|
this, &ModelDataExtractor::onDataChanged);
|
||||||
|
|
||||||
|
// ToDo: handle rows added / removed
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// might be null, or an array
|
||||||
|
}
|
||||||
|
|
||||||
|
emit modelChanged();
|
||||||
|
emit dataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelDataExtractor::setIndex(int index)
|
||||||
|
{
|
||||||
|
if (m_index == index)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_index = index;
|
||||||
|
emit indexChanged(m_index);
|
||||||
|
emit dataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelDataExtractor::setRole(QString role)
|
||||||
|
{
|
||||||
|
if (m_role == role)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_role = role;
|
||||||
|
emit roleChanged(m_role);
|
||||||
|
emit dataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelDataExtractor::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||||
|
{
|
||||||
|
if ((topLeft.row() >= m_index) && (bottomRight.row() <= m_index)) {
|
||||||
|
emit dataChanged();
|
||||||
|
}
|
||||||
|
}
|
61
src/GUI/ModelDataExtractor.hxx
Normal file
61
src/GUI/ModelDataExtractor.hxx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#ifndef MODELDATAEXTRACTOR_HXX
|
||||||
|
#define MODELDATAEXTRACTOR_HXX
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJSValue>
|
||||||
|
|
||||||
|
class QAbstractItemModel;
|
||||||
|
|
||||||
|
class ModelDataExtractor : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ModelDataExtractor(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
Q_PROPERTY(QJSValue model READ model WRITE setModel NOTIFY modelChanged)
|
||||||
|
Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged)
|
||||||
|
Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QVariant data READ data NOTIFY dataChanged)
|
||||||
|
|
||||||
|
QJSValue model() const
|
||||||
|
{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index() const
|
||||||
|
{
|
||||||
|
return m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString role() const
|
||||||
|
{
|
||||||
|
return m_role;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant data() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void modelChanged();
|
||||||
|
void indexChanged(int index);
|
||||||
|
void roleChanged(QString role);
|
||||||
|
|
||||||
|
void dataChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setModel(QJSValue model);
|
||||||
|
void setIndex(int index);
|
||||||
|
void setRole(QString role);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAbstractItemModel* m_model = nullptr;
|
||||||
|
QJSValue m_value;
|
||||||
|
int m_index = 0;
|
||||||
|
QString m_role;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MODELDATAEXTRACTOR_HXX
|
|
@ -7,11 +7,10 @@ Item {
|
||||||
id: root
|
id: root
|
||||||
property alias label: label.text
|
property alias label: label.text
|
||||||
|
|
||||||
property var model: undefined
|
property var model: nil
|
||||||
property string displayRole: "display"
|
property string displayRole: "display"
|
||||||
property bool enabled: true
|
property bool enabled: true
|
||||||
property int currentIndex: 0
|
property int currentIndex: 0
|
||||||
property bool __dummy: false
|
|
||||||
property string headerText: ""
|
property string headerText: ""
|
||||||
|
|
||||||
implicitHeight: Math.max(label.implicitHeight, currentChoiceFrame.height)
|
implicitHeight: Math.max(label.implicitHeight, currentChoiceFrame.height)
|
||||||
|
@ -22,32 +21,11 @@ Item {
|
||||||
root.currentIndex = index;
|
root.currentIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
ModelDataExtractor {
|
||||||
Repeater {
|
id: currentItemText
|
||||||
id: internalModel
|
model: root.model
|
||||||
model: root.model
|
role: root.displayRole
|
||||||
|
index: root.currentIndex
|
||||||
Item {
|
|
||||||
id: internalModelItem
|
|
||||||
|
|
||||||
// Taken from TableViewItemDelegateLoader.qml to follow QML role conventions
|
|
||||||
readonly property var text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel
|
|
||||||
: modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject
|
|
||||||
: modelData != undefined ? modelData : "" // Models without role
|
|
||||||
readonly property bool selected: root.currentIndex === model.index
|
|
||||||
readonly property QtObject modelObj: model
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
// hack to force updating of currentText after internalModel
|
|
||||||
// has been populated
|
|
||||||
__dummy = !__dummy
|
|
||||||
}
|
|
||||||
|
|
||||||
onModelChanged: {
|
|
||||||
__dummy = !__dummy // force update of currentText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function haveHeader()
|
function haveHeader()
|
||||||
|
@ -60,10 +38,7 @@ Item {
|
||||||
if ((currentIndex == -1) && haveHeader())
|
if ((currentIndex == -1) && haveHeader())
|
||||||
return headerText;
|
return headerText;
|
||||||
|
|
||||||
var foo = __dummy; // fake propery dependency to update this
|
return currentItemText.data
|
||||||
var item = internalModel.itemAt(currentIndex);
|
|
||||||
if (!item) return "";
|
|
||||||
return item.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
|
|
@ -179,9 +179,8 @@ Item {
|
||||||
description: qsTr("Select a server close to you for better responsiveness and reduced lag when flying online.")
|
description: qsTr("Select a server close to you for better responsiveness and reduced lag when flying online.")
|
||||||
choices: _launcher.mpServersModel
|
choices: _launcher.mpServersModel
|
||||||
|
|
||||||
readonly property bool currentIsCustom: (_launcher.mpServersModel.serverForIndex(selectedIndex) === "__custom__")
|
readonly property bool currentIsCustom: (_launcher.mpServersModel.currentServer === "__custom__")
|
||||||
readonly property bool currentIsNoServers: (_launcher.mpServersModel.serverForIndex(selectedIndex) === "__noservers__")
|
readonly property bool currentIsNoServers: (_launcher.mpServersModel.currentServer === "__noservers__")
|
||||||
|
|
||||||
property string __savedServer;
|
property string __savedServer;
|
||||||
|
|
||||||
keywords: ["server", "hostname"]
|
keywords: ["server", "hostname"]
|
||||||
|
@ -195,7 +194,7 @@ Item {
|
||||||
{
|
{
|
||||||
// these values match the code in MPServersModel.cpp, sorry for that
|
// these values match the code in MPServersModel.cpp, sorry for that
|
||||||
// nastyness
|
// nastyness
|
||||||
_config.setValueForKey("mpSettings", "mp-server", _launcher.mpServersModel.serverForIndex(selectedIndex) );
|
_config.setValueForKey("mpSettings", "mp-server", _launcher.mpServersModel.currentServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreState()
|
function restoreState()
|
||||||
|
@ -203,11 +202,13 @@ Item {
|
||||||
// no-op, this is triggered by MPServersModel::restoreMPServerSelection
|
// no-op, this is triggered by MPServersModel::restoreMPServerSelection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// can't use a Binding here, since we need a bidrectional link
|
||||||
|
onSelectedIndexChanged: _launcher.mpServersModel.currentIndex = selectedIndex
|
||||||
|
|
||||||
Connections
|
Connections
|
||||||
{
|
{
|
||||||
target: _launcher.mpServersModel
|
target: _launcher.mpServersModel
|
||||||
onRestoreIndex: { mpServer.selectedIndex = index }
|
onCurrentIndexChanged: mpServer.selectedIndex = _launcher.mpServersModel.currentIndex
|
||||||
onRestoreDefault: { mpServer.selectedIndex = 0; }
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -229,26 +230,24 @@ Item {
|
||||||
console.warn("MP enabled but no valid server selected, skipping");
|
console.warn("MP enabled but no valid server selected, skipping");
|
||||||
} else if (mpServer.currentIsCustom) {
|
} else if (mpServer.currentIsCustom) {
|
||||||
var pieces = mpCustomServer.value.split(':')
|
var pieces = mpCustomServer.value.split(':')
|
||||||
var port = defaultMPPort;
|
var customPort = defaultMPPort;
|
||||||
if (pieces.length > 1) {
|
if (pieces.length > 1) {
|
||||||
port = pieces[1];
|
customPort = pieces[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pieces[0].length > 0) {
|
if (pieces[0].length > 0) {
|
||||||
_config.setProperty("/sim/multiplay/txhost", pieces[0]);
|
_config.setProperty("/sim/multiplay/txhost", pieces[0]);
|
||||||
_config.setProperty("/sim/multiplay/txport", port);
|
_config.setProperty("/sim/multiplay/txport", customPort);
|
||||||
} else {
|
} else {
|
||||||
console.warn("Custom MP server selected but no hostname defined");
|
console.warn("Custom MP server selected but no hostname defined");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var sel = mpServer.selectedIndex
|
var host = _launcher.mpServersModel.currentServer;
|
||||||
var host = _launcher.mpServersModel.serverForIndex(sel);
|
|
||||||
console.info("MP host is " + host)
|
|
||||||
if (host.length > 0) {
|
if (host.length > 0) {
|
||||||
_config.setProperty("/sim/multiplay/txhost", host);
|
_config.setProperty("/sim/multiplay/txhost", host);
|
||||||
}
|
}
|
||||||
|
|
||||||
var port = _launcher.mpServersModel.portForIndex(sel);
|
var port = _launcher.mpServersModel.currentPort;
|
||||||
if (port === 0) {
|
if (port === 0) {
|
||||||
console.info("Using default MP port");
|
console.info("Using default MP port");
|
||||||
port = defaultMPPort;
|
port = defaultMPPort;
|
||||||
|
|
Loading…
Add table
Reference in a new issue