vehicle/decoder: decoders are now directly linked to vehicles, they are no longer managed seperatly. This makes it easier to understand for users.
Dieser Commit ist enthalten in:
Ursprung
35d25c8c07
Commit
5567ba39b8
@ -71,6 +71,7 @@ file(GLOB SOURCES
|
||||
"src/utils/*.cpp"
|
||||
"src/widget/*.hpp"
|
||||
"src/widget/*.cpp"
|
||||
"src/widget/decoder/*.cpp"
|
||||
"src/widget/list/*.hpp"
|
||||
"src/widget/list/*.cpp"
|
||||
"src/widget/object/*.hpp"
|
||||
|
||||
@ -53,8 +53,6 @@ QWidget* createWidgetIfCustom(const ObjectPtr& object, QWidget* parent)
|
||||
{
|
||||
return new InterfaceListWidget(object, parent);
|
||||
}
|
||||
else if(classId == "decoder_list")
|
||||
return new ObjectListWidget(object, parent); // todo remove
|
||||
else if(classId == "controller_list")
|
||||
return new ObjectListWidget(object, parent); // todo remove
|
||||
else if(classId == "rail_vehicle_list")
|
||||
|
||||
199
client/src/widget/decoder/decoderfunctionsmodel.cpp
Normale Datei
199
client/src/widget/decoder/decoderfunctionsmodel.cpp
Normale Datei
@ -0,0 +1,199 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 Reinder Feenstra
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "decoderfunctionsmodel.hpp"
|
||||
#include <traintastic/locale/locale.hpp>
|
||||
#include "../../network/connection.hpp"
|
||||
#include "../../network/error.hpp"
|
||||
#include "../../network/abstractproperty.hpp"
|
||||
#include "../../network/object.hpp"
|
||||
#include "../../network/objectvectorproperty.hpp"
|
||||
#include "../../utils/enum.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
const std::array<QString, 4> columnProperty{{
|
||||
QStringLiteral("number"),
|
||||
QStringLiteral("function"),
|
||||
QStringLiteral("name"),
|
||||
QStringLiteral("type"),
|
||||
}};
|
||||
|
||||
}
|
||||
|
||||
DecoderFunctionsModel::DecoderFunctionsModel(ObjectPtr object, QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_object{std::move(object)}
|
||||
, m_items{m_object->getObjectVectorProperty("items")}
|
||||
, m_requestId{Connection::invalidRequestId}
|
||||
{
|
||||
connect(m_items, &ObjectVectorProperty::valueChanged, this, &DecoderFunctionsModel::itemsChanged);
|
||||
itemsChanged();
|
||||
}
|
||||
|
||||
DecoderFunctionsModel::~DecoderFunctionsModel()
|
||||
{
|
||||
cancelRequest();
|
||||
}
|
||||
|
||||
const ObjectPtr& DecoderFunctionsModel::getObject(int row) const
|
||||
{
|
||||
assert(row >= 0 && row < static_cast<int>(m_functions.size()));
|
||||
return m_functions[row];
|
||||
}
|
||||
|
||||
Qt::ItemFlags DecoderFunctionsModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
if(auto* p = m_functions[index.row()]->getProperty(columnProperty[index.column()]); p && p->getAttributeBool(AttributeName::Enabled, true))
|
||||
{
|
||||
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
|
||||
}
|
||||
return QAbstractTableModel::flags(index);
|
||||
}
|
||||
|
||||
int DecoderFunctionsModel::rowCount(const QModelIndex& /*parent*/) const
|
||||
{
|
||||
return static_cast<int>(m_functions.size());
|
||||
}
|
||||
|
||||
int DecoderFunctionsModel::columnCount(const QModelIndex& /*parent*/) const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
QVariant DecoderFunctionsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
|
||||
{
|
||||
switch(section)
|
||||
{
|
||||
case columnNumber:
|
||||
return QStringLiteral("#");
|
||||
|
||||
case columnFunction:
|
||||
return Locale::tr("decoder_function:function");
|
||||
|
||||
case columnName:
|
||||
return Locale::tr("object:name");
|
||||
|
||||
case columnType:
|
||||
return Locale::tr("decoder_function:type");
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariant DecoderFunctionsModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if(role == Qt::DisplayRole || role == Qt::EditRole)
|
||||
{
|
||||
const auto& function = *m_functions[index.row()];
|
||||
|
||||
if(role == Qt::EditRole)
|
||||
{
|
||||
return function.getPropertyValue(columnProperty[index.column()]);
|
||||
}
|
||||
|
||||
switch(index.column())
|
||||
{
|
||||
case columnNumber:
|
||||
return QString("F%1").arg(function.getPropertyValueInt(QStringLiteral("number"), -1));
|
||||
|
||||
case columnFunction:
|
||||
if(auto* property = function.getProperty(QStringLiteral("function"))) [[likely]]
|
||||
{
|
||||
return translateEnum(*property);
|
||||
}
|
||||
break;
|
||||
|
||||
case columnName:
|
||||
return function.getPropertyValueString(QStringLiteral("name"));
|
||||
|
||||
case columnType:
|
||||
if(auto* property = function.getProperty(QStringLiteral("type"))) [[likely]]
|
||||
{
|
||||
return translateEnum(*property);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool DecoderFunctionsModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if(role == Qt::EditRole)
|
||||
{
|
||||
auto& function = *m_functions[index.row()];
|
||||
switch(index.column())
|
||||
{
|
||||
case columnNumber:
|
||||
function.setPropertyValue(columnProperty[index.column()], value.toInt());
|
||||
break;
|
||||
|
||||
default:
|
||||
function.setPropertyValue(columnProperty[index.column()], value);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DecoderFunctionsModel::cancelRequest()
|
||||
{
|
||||
if(m_requestId != Connection::invalidRequestId)
|
||||
{
|
||||
m_items->object().connection()->cancelRequest(m_requestId);
|
||||
m_requestId = Connection::invalidRequestId;
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderFunctionsModel::itemsChanged()
|
||||
{
|
||||
if(!m_items) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cancelRequest();
|
||||
|
||||
if(!m_items->empty())
|
||||
{
|
||||
m_requestId = m_items->getObjects(
|
||||
[this](const std::vector<ObjectPtr>& objects, std::optional<const Error> error)
|
||||
{
|
||||
m_requestId = Connection::invalidRequestId;
|
||||
if(!error)
|
||||
{
|
||||
beginResetModel();
|
||||
m_functions = objects;
|
||||
endResetModel();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
beginResetModel();
|
||||
m_functions.clear();
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
62
client/src/widget/decoder/decoderfunctionsmodel.hpp
Normale Datei
62
client/src/widget/decoder/decoderfunctionsmodel.hpp
Normale Datei
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 Reinder Feenstra
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_CLIENT_WIDGET_DECODER_DECODERFUNCTIONSMODEL_HPP
|
||||
#define TRAINTASTIC_CLIENT_WIDGET_DECODER_DECODERFUNCTIONSMODEL_HPP
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include "../../network/objectptr.hpp"
|
||||
|
||||
class ObjectVectorProperty;
|
||||
|
||||
class DecoderFunctionsModel : public QAbstractTableModel
|
||||
{
|
||||
private:
|
||||
ObjectPtr m_object;
|
||||
ObjectVectorProperty* m_items;
|
||||
std::vector<ObjectPtr> m_functions;
|
||||
int m_requestId;
|
||||
|
||||
void cancelRequest();
|
||||
void itemsChanged();
|
||||
|
||||
public:
|
||||
static constexpr int columnNumber = 0;
|
||||
static constexpr int columnFunction = 1;
|
||||
static constexpr int columnName = 2;
|
||||
static constexpr int columnType = 3;
|
||||
|
||||
explicit DecoderFunctionsModel(ObjectPtr object, QObject* parent = nullptr);
|
||||
~DecoderFunctionsModel() override;
|
||||
|
||||
const ObjectPtr& getObject(int row) const;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
223
client/src/widget/decoder/decoderfunctionswidget.cpp
Normale Datei
223
client/src/widget/decoder/decoderfunctionswidget.cpp
Normale Datei
@ -0,0 +1,223 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 Reinder Feenstra
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "decoderfunctionswidget.hpp"
|
||||
#include <QVBoxLayout>
|
||||
#include <QToolBar>
|
||||
#include <QTableView>
|
||||
#include <QHeaderView>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QComboBox>
|
||||
#include "../../network/callmethod.hpp"
|
||||
#include "../../network/connection.hpp"
|
||||
#include "../../network/error.hpp"
|
||||
#include "../../network/method.hpp"
|
||||
#include "../../network/object.hpp"
|
||||
#include "../../network/objectproperty.hpp"
|
||||
#include "../../misc/methodaction.hpp"
|
||||
#include "../../theme/theme.hpp"
|
||||
#include "decoderfunctionsmodel.hpp"
|
||||
#include <traintastic/enum/decoderfunctionfunction.hpp>
|
||||
#include <traintastic/enum/decoderfunctiontype.hpp>
|
||||
#include <traintastic/locale/locale.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
class ComboBoxDelegate : public QStyledItemDelegate
|
||||
{
|
||||
private:
|
||||
const std::span<const T> m_values;
|
||||
|
||||
public:
|
||||
ComboBoxDelegate(std::span<const T> values, QObject *parent = nullptr)
|
||||
: QStyledItemDelegate(parent)
|
||||
, m_values{values}
|
||||
{
|
||||
}
|
||||
|
||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const override
|
||||
{
|
||||
QComboBox* editor = new QComboBox(parent);
|
||||
for(auto value : m_values)
|
||||
{
|
||||
editor->addItem(Locale::tr(QString("%1:%2").arg(EnumName<T>::value).arg(EnumValues<T>::value.at(value))), static_cast<qint64>(value));
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
void setEditorData(QWidget* editor, const QModelIndex& index) const override
|
||||
{
|
||||
QComboBox* comboBox = static_cast<QComboBox*>(editor);
|
||||
const int n = comboBox->findData(index.data(Qt::EditRole));
|
||||
if(n >= 0)
|
||||
{
|
||||
comboBox->setCurrentIndex(n);
|
||||
}
|
||||
}
|
||||
|
||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override
|
||||
{
|
||||
QComboBox* comboBox = static_cast<QComboBox*>(editor);
|
||||
model->setData(index, comboBox->currentData(), Qt::EditRole);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
DecoderFunctionsWidget::DecoderFunctionsWidget(ObjectProperty& decoderProperty, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_decoderProperty{decoderProperty}
|
||||
, m_toolbar{new QToolBar(this)}
|
||||
, m_table{new QTableView(this)}
|
||||
, m_requestId{Connection::invalidRequestId}
|
||||
{
|
||||
setWindowTitle(Locale::tr("decoder:functions"));
|
||||
|
||||
setLayout(new QVBoxLayout());
|
||||
layout()->addWidget(m_toolbar);
|
||||
layout()->addWidget(m_table);
|
||||
|
||||
connect(&m_decoderProperty, &ObjectProperty::valueChanged, this, &DecoderFunctionsWidget::decoderChanged);
|
||||
decoderChanged();
|
||||
}
|
||||
|
||||
DecoderFunctionsWidget::~DecoderFunctionsWidget()
|
||||
{
|
||||
cancelRequest();
|
||||
}
|
||||
|
||||
const ObjectPtr& DecoderFunctionsWidget::getSelectedFunction() const
|
||||
{
|
||||
static const ObjectPtr null;
|
||||
if(m_table->selectionModel()->hasSelection())
|
||||
{
|
||||
const int row = m_table->selectionModel()->selection().first().indexes().front().row();
|
||||
return static_cast<DecoderFunctionsModel*>(m_table->model())->getObject(row);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void DecoderFunctionsWidget::cancelRequest()
|
||||
{
|
||||
if(m_requestId != Connection::invalidRequestId)
|
||||
{
|
||||
m_decoderProperty.object().connection()->cancelRequest(m_requestId);
|
||||
m_requestId = Connection::invalidRequestId;
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderFunctionsWidget::decoderChanged()
|
||||
{
|
||||
m_table->setModel(nullptr);
|
||||
|
||||
cancelRequest();
|
||||
|
||||
if(m_decoderProperty.hasObject())
|
||||
{
|
||||
const auto id = m_decoderProperty.objectId() + ".functions";
|
||||
m_requestId = m_decoderProperty.object().connection()->getObject(id,
|
||||
[this](const ObjectPtr& object, std::optional<const Error> /*error*/)
|
||||
{
|
||||
m_requestId = Connection::invalidRequestId;
|
||||
if(object)
|
||||
{
|
||||
if(auto* create = object->getMethod(QStringLiteral("create")))
|
||||
{
|
||||
m_toolbar->addAction(new MethodAction(Theme::getIcon("add"), *create));
|
||||
}
|
||||
if(auto* delete_ = object->getMethod(QStringLiteral("delete")))
|
||||
{
|
||||
m_delete = new MethodAction(Theme::getIcon("delete"), *delete_,
|
||||
[this, delete_]()
|
||||
{
|
||||
if(const auto& function = getSelectedFunction())
|
||||
{
|
||||
callMethod(*delete_, nullptr, function);
|
||||
}
|
||||
});
|
||||
m_toolbar->addAction(m_delete);
|
||||
}
|
||||
if(!m_toolbar->actions().isEmpty())
|
||||
{
|
||||
m_toolbar->addSeparator();
|
||||
}
|
||||
if(auto* moveUp = object->getMethod(QStringLiteral("move_up")))
|
||||
{
|
||||
m_moveUp = new MethodAction(Theme::getIcon("up"), *moveUp,
|
||||
[this, moveUp]()
|
||||
{
|
||||
if(const auto& function = getSelectedFunction())
|
||||
{
|
||||
callMethod(*moveUp, nullptr, function);
|
||||
}
|
||||
});
|
||||
m_toolbar->addAction(m_moveUp);
|
||||
}
|
||||
if(auto* moveDown = object->getMethod(QStringLiteral("move_down")))
|
||||
{
|
||||
m_moveDown = new MethodAction(Theme::getIcon("down"), *moveDown,
|
||||
[this, moveDown]()
|
||||
{
|
||||
if(const auto& function = getSelectedFunction())
|
||||
{
|
||||
callMethod(*moveDown, nullptr, function);
|
||||
}
|
||||
});
|
||||
m_toolbar->addAction(m_moveDown);
|
||||
}
|
||||
|
||||
const int acw = m_table->fontMetrics().averageCharWidth();
|
||||
m_table->setModel(new DecoderFunctionsModel(object, this));
|
||||
|
||||
m_table->setColumnWidth(DecoderFunctionsModel::columnNumber, 4 * acw);
|
||||
m_table->setColumnWidth(DecoderFunctionsModel::columnFunction, 15 * acw);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(DecoderFunctionsModel::columnName, QHeaderView::Stretch);
|
||||
m_table->setColumnWidth(DecoderFunctionsModel::columnType, 15 * acw);
|
||||
|
||||
m_table->setItemDelegateForColumn(DecoderFunctionsModel::columnFunction, new ComboBoxDelegate<DecoderFunctionFunction>(decoderFunctionFunctionValues, m_table));
|
||||
m_table->setItemDelegateForColumn(DecoderFunctionsModel::columnType, new ComboBoxDelegate<DecoderFunctionType>(decoderFunctionTypeValues, m_table));
|
||||
|
||||
m_table->setSelectionMode(QTableView::SingleSelection);
|
||||
connect(m_table->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
[this](const QItemSelection& selected, const QItemSelection& /*deselected*/)
|
||||
{
|
||||
const bool emptySelection = selected.empty();
|
||||
const bool firstRowSelected = !emptySelection && selected.front().indexes().front().row() == 0;
|
||||
const bool lastRowSelected = !emptySelection && selected.front().indexes().front().row() == m_table->model()->rowCount() - 1;
|
||||
|
||||
if(m_delete)
|
||||
{
|
||||
m_delete->setForceDisabled(emptySelection);
|
||||
}
|
||||
if(m_moveUp)
|
||||
{
|
||||
m_moveUp->setForceDisabled(emptySelection || firstRowSelected);
|
||||
}
|
||||
if(m_moveDown)
|
||||
{
|
||||
m_moveDown->setForceDisabled(emptySelection || lastRowSelected);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
55
client/src/widget/decoder/decoderfunctionswidget.hpp
Normale Datei
55
client/src/widget/decoder/decoderfunctionswidget.hpp
Normale Datei
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 Reinder Feenstra
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_CLIENT_WIDGET_DECODER_DECODERFUNCTIONSWIDGET_HPP
|
||||
#define TRAINTASTIC_CLIENT_WIDGET_DECODER_DECODERFUNCTIONSWIDGET_HPP
|
||||
|
||||
#include <QWidget>
|
||||
#include "../../network/objectptr.hpp"
|
||||
|
||||
class QToolBar;
|
||||
class QTableView;
|
||||
class ObjectProperty;
|
||||
class MethodAction;
|
||||
|
||||
class DecoderFunctionsWidget : public QWidget
|
||||
{
|
||||
private:
|
||||
ObjectProperty& m_decoderProperty;
|
||||
ObjectPtr m_decoder;
|
||||
QToolBar* m_toolbar;
|
||||
MethodAction* m_delete = nullptr;
|
||||
MethodAction* m_moveUp = nullptr;
|
||||
MethodAction* m_moveDown = nullptr;
|
||||
QTableView* m_table;
|
||||
int m_requestId;
|
||||
|
||||
const ObjectPtr& getSelectedFunction() const;
|
||||
|
||||
void cancelRequest();
|
||||
void decoderChanged();
|
||||
|
||||
public:
|
||||
explicit DecoderFunctionsWidget(ObjectProperty& decoderProperty, QWidget* parent = nullptr);
|
||||
~DecoderFunctionsWidget() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
130
client/src/widget/decoder/decoderwidget.cpp
Normale Datei
130
client/src/widget/decoder/decoderwidget.cpp
Normale Datei
@ -0,0 +1,130 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 Reinder Feenstra
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "decoderwidget.hpp"
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QFormLayout>
|
||||
#include "../../network/connection.hpp"
|
||||
#include "../../network/error.hpp"
|
||||
#include "../../network/method.hpp"
|
||||
#include "../../network/object.hpp"
|
||||
#include "../../network/objectproperty.hpp"
|
||||
#include "../../network/property.hpp"
|
||||
#include "../object/objecteditwidget.hpp"
|
||||
#include "../interfaceitemnamelabel.hpp"
|
||||
#include "../objectpropertycombobox.hpp"
|
||||
#include "../createwidget.hpp"
|
||||
|
||||
DecoderWidget::DecoderWidget(ObjectProperty& decoderProperty, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_decoderProperty{decoderProperty}
|
||||
, m_createDecoder{m_decoderProperty.object().getMethod("create_decoder")}
|
||||
, m_requestId{Connection::invalidRequestId}
|
||||
{
|
||||
setWindowTitle(m_decoderProperty.displayName());
|
||||
|
||||
connect(&m_decoderProperty, &ObjectProperty::valueChanged, this, &DecoderWidget::decoderChanged);
|
||||
|
||||
decoderChanged();
|
||||
}
|
||||
|
||||
DecoderWidget::~DecoderWidget()
|
||||
{
|
||||
cancelRequest();
|
||||
}
|
||||
|
||||
void DecoderWidget::cancelRequest()
|
||||
{
|
||||
if(m_requestId != Connection::invalidRequestId)
|
||||
{
|
||||
m_decoderProperty.object().connection()->cancelRequest(m_requestId);
|
||||
m_requestId = Connection::invalidRequestId;
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderWidget::decoderChanged()
|
||||
{
|
||||
delete layout();
|
||||
delete m_createDecoderButton;
|
||||
|
||||
m_createDecoderButton = nullptr;
|
||||
m_object.reset();
|
||||
|
||||
cancelRequest();
|
||||
|
||||
if(m_decoderProperty.hasObject())
|
||||
{
|
||||
m_requestId = m_decoderProperty.getObject(
|
||||
[this](const ObjectPtr& object, std::optional<const Error> /*error*/)
|
||||
{
|
||||
m_requestId = Connection::invalidRequestId;
|
||||
if(object)
|
||||
{
|
||||
m_object = object;
|
||||
|
||||
auto* form = new QFormLayout();
|
||||
if(auto* interface = dynamic_cast<ObjectProperty*>(m_object->getProperty("interface")))
|
||||
{
|
||||
form->addRow(new InterfaceItemNameLabel(*interface, this), new ObjectPropertyComboBox(*interface, this));
|
||||
}
|
||||
if(auto* protocol = dynamic_cast<Property*>(m_object->getProperty("protocol")))
|
||||
{
|
||||
form->addRow(new InterfaceItemNameLabel(*protocol, this), createWidget(*protocol, this));
|
||||
}
|
||||
if(auto* address = dynamic_cast<Property*>(m_object->getProperty("address")))
|
||||
{
|
||||
form->addRow(new InterfaceItemNameLabel(*address, this), createWidget(*address, this));
|
||||
}
|
||||
if(auto* speedSteps = dynamic_cast<Property*>(m_object->getProperty("speed_steps")))
|
||||
{
|
||||
form->addRow(new InterfaceItemNameLabel(*speedSteps, this), createWidget(*speedSteps, this));
|
||||
}
|
||||
setLayout(form);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_createDecoderButton = new QPushButton("Add decoder", this);
|
||||
m_createDecoderButton->setEnabled(m_createDecoder);
|
||||
connect(m_createDecoderButton, &QPushButton::clicked,
|
||||
[this]()
|
||||
{
|
||||
if(m_createDecoder) [[likely]]
|
||||
{
|
||||
m_createDecoder->call();
|
||||
}
|
||||
});
|
||||
|
||||
auto* h = new QHBoxLayout();
|
||||
h->addStretch();
|
||||
h->addWidget(m_createDecoderButton);
|
||||
h->addStretch();
|
||||
|
||||
auto* v = new QVBoxLayout();
|
||||
v->addStretch();
|
||||
v->addLayout(h);
|
||||
v->addStretch();
|
||||
setLayout(v);
|
||||
}
|
||||
}
|
||||
50
client/src/widget/decoder/decoderwidget.hpp
Normale Datei
50
client/src/widget/decoder/decoderwidget.hpp
Normale Datei
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 Reinder Feenstra
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_CLIENT_WIDGET_DECODER_DECODERWIDGET_HPP
|
||||
#define TRAINTASTIC_CLIENT_WIDGET_DECODER_DECODERWIDGET_HPP
|
||||
|
||||
#include <QWidget>
|
||||
#include "../../network/objectptr.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class ObjectProperty;
|
||||
class Method;
|
||||
class ObjectEditWidget;
|
||||
|
||||
class DecoderWidget : public QWidget
|
||||
{
|
||||
private:
|
||||
ObjectProperty& m_decoderProperty;
|
||||
Method* m_createDecoder;
|
||||
QPushButton* m_createDecoderButton = nullptr;
|
||||
ObjectPtr m_object;
|
||||
int m_requestId;
|
||||
|
||||
void cancelRequest();
|
||||
void decoderChanged();
|
||||
|
||||
public:
|
||||
explicit DecoderWidget(ObjectProperty& decoderProperty, QWidget* parent = nullptr);
|
||||
~DecoderWidget() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020,2023-2024 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -43,6 +43,8 @@
|
||||
#include "../unitpropertycombobox.hpp"
|
||||
#include "../unitpropertyedit.hpp"
|
||||
#include "../createwidget.hpp"
|
||||
#include "../decoder/decoderwidget.hpp"
|
||||
#include "../decoder/decoderfunctionswidget.hpp"
|
||||
#include "../../theme/theme.hpp"
|
||||
#include <traintastic/enum/direction.hpp>
|
||||
#include <traintastic/locale/locale.hpp>
|
||||
@ -94,7 +96,13 @@ void ObjectEditWidget::buildForm()
|
||||
if(baseProperty->type() == ValueType::Object)
|
||||
{
|
||||
ObjectProperty* property = static_cast<ObjectProperty*>(baseProperty);
|
||||
if(contains(baseProperty->flags(), PropertyFlags::SubObject))
|
||||
if(property->name() == "decoder")
|
||||
{
|
||||
tabs.append(new DecoderWidget(*property, this));
|
||||
tabs.append(new DecoderFunctionsWidget(*property, this));
|
||||
continue;
|
||||
}
|
||||
else if(contains(baseProperty->flags(), PropertyFlags::SubObject))
|
||||
{
|
||||
tabs.append(new ObjectEditWidget(*property, this));
|
||||
continue;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2024 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -484,7 +484,7 @@ void ObjectListWidget::tableSelectionChanged(bool hasSelection)
|
||||
|
||||
bool ObjectListWidget::hasEdit() const
|
||||
{
|
||||
if(object()->classId() == "list.output")
|
||||
if(object()->classId() == "list.decoder" || object()->classId() == "list.output")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2023,2025 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -39,11 +39,15 @@
|
||||
|
||||
CREATE_IMPL(Decoder)
|
||||
|
||||
std::shared_ptr<Decoder> Decoder::create(World& world)
|
||||
{
|
||||
return create(world, world.getUniqueId(defaultId));
|
||||
}
|
||||
|
||||
const std::shared_ptr<Decoder> Decoder::null;
|
||||
|
||||
Decoder::Decoder(World& world, std::string_view _id) :
|
||||
IdObject(world, _id),
|
||||
name{this, "name", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
|
||||
interface{this, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](const std::shared_ptr<DecoderController>& value)
|
||||
{
|
||||
@ -110,15 +114,10 @@ Decoder::Decoder(World& world, std::string_view _id) :
|
||||
changed(DecoderChangeFlags::Throttle);
|
||||
updateEditable();
|
||||
}},
|
||||
functions{this, "functions", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
|
||||
notes{this, "notes", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
functions{this, "functions", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
{
|
||||
functions.setValueInternal(std::make_shared<DecoderFunctions>(*this, functions.name()));
|
||||
|
||||
Attributes::addDisplayName(name, DisplayName::Object::name);
|
||||
Attributes::addEnabled(name, false);
|
||||
m_interfaceItems.add(name);
|
||||
|
||||
Attributes::addDisplayName(interface, DisplayName::Hardware::interface);
|
||||
Attributes::addEnabled(interface, false);
|
||||
Attributes::addObjectList(interface, m_world.decoderControllers);
|
||||
@ -162,8 +161,6 @@ Decoder::Decoder(World& world, std::string_view _id) :
|
||||
Attributes::addObjectEditor(throttle, false);
|
||||
m_interfaceItems.add(throttle);
|
||||
m_interfaceItems.add(functions);
|
||||
Attributes::addDisplayName(notes, DisplayName::Object::notes);
|
||||
m_interfaceItems.add(notes);
|
||||
|
||||
updateEditable();
|
||||
updateMute();
|
||||
@ -361,6 +358,11 @@ void Decoder::destroying()
|
||||
m_driver->release();
|
||||
assert(!m_driver);
|
||||
}
|
||||
if(vehicle)
|
||||
{
|
||||
assert(vehicle->decoder.value().get() == this);
|
||||
vehicle->decoder.setValueInternal(nullptr);
|
||||
}
|
||||
if(interface.value())
|
||||
interface = nullptr;
|
||||
m_world.decoders->removeObject(shared_ptr<Decoder>());
|
||||
@ -473,7 +475,6 @@ void Decoder::updateEditable()
|
||||
void Decoder::updateEditable(bool editable)
|
||||
{
|
||||
const bool stopped = editable && almostZero(throttle.value());
|
||||
Attributes::setEnabled(name, editable);
|
||||
Attributes::setEnabled(interface, stopped);
|
||||
Attributes::setEnabled(protocol, stopped && protocol.getSpanAttribute<DecoderProtocol>(AttributeName::Values).length() > 1);
|
||||
Attributes::setEnabled(address, stopped);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2023,2025 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -77,7 +77,9 @@ class Decoder : public IdObject
|
||||
|
||||
public:
|
||||
CLASS_ID("decoder")
|
||||
DEFAULT_ID("decoder")
|
||||
CREATE_DEF(Decoder)
|
||||
static std::shared_ptr<Decoder> create(World& world);
|
||||
|
||||
static constexpr uint8_t speedStepsAuto = 0;
|
||||
static constexpr float throttleMin = 0;
|
||||
@ -101,7 +103,6 @@ class Decoder : public IdObject
|
||||
|
||||
static const std::shared_ptr<Decoder> null;
|
||||
|
||||
Property<std::string> name;
|
||||
ObjectProperty<DecoderController> interface;
|
||||
Property<DecoderProtocol> protocol;
|
||||
Property<uint16_t> address;
|
||||
@ -113,7 +114,6 @@ class Decoder : public IdObject
|
||||
ObjectProperty<RailVehicle> vehicle;
|
||||
Property<float> throttle;
|
||||
ObjectProperty<DecoderFunctions> functions;
|
||||
Property<std::string> notes;
|
||||
|
||||
boost::signals2::signal<void (Decoder&, DecoderChangeFlags, uint32_t)> decoderChanged;
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
#include "../../core/object.hpp"
|
||||
#include "../../core/property.hpp"
|
||||
#include "../../enum/decoderfunctiontype.hpp"
|
||||
#include "../../enum/decoderfunctionfunction.hpp"
|
||||
#include <traintastic/enum/decoderfunctionfunction.hpp>
|
||||
|
||||
class Decoder;
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -31,30 +31,8 @@
|
||||
|
||||
DecoderList::DecoderList(Object& _parent, std::string_view parentPropertyName, DecoderListColumn _columns) :
|
||||
ObjectList<Decoder>(_parent, parentPropertyName),
|
||||
columns{_columns},
|
||||
create{*this, "create",
|
||||
[this]()
|
||||
{
|
||||
auto& world = getWorld(parent());
|
||||
auto decoder = Decoder::create(world, world.getUniqueId("decoder"));
|
||||
if(const auto controller = std::dynamic_pointer_cast<DecoderController>(parent().shared_from_this()))
|
||||
{
|
||||
// todo: select free address?
|
||||
decoder->interface = controller;
|
||||
}
|
||||
return decoder;
|
||||
}}
|
||||
, delete_{*this, "delete", std::bind(&DecoderList::deleteMethodHandler, this, std::placeholders::_1)}
|
||||
columns{_columns}
|
||||
{
|
||||
const bool editable = contains(getWorld(parent()).state.value(), WorldState::Edit);
|
||||
|
||||
Attributes::addDisplayName(create, DisplayName::List::create);
|
||||
Attributes::addEnabled(create, editable);
|
||||
m_interfaceItems.add(create);
|
||||
|
||||
Attributes::addDisplayName(delete_, DisplayName::List::remove);
|
||||
Attributes::addEnabled(delete_, editable);
|
||||
m_interfaceItems.add(delete_);
|
||||
}
|
||||
|
||||
TableModelPtr DecoderList::getModel()
|
||||
@ -88,16 +66,6 @@ std::shared_ptr<Decoder> DecoderList::getDecoder(DecoderProtocol protocol, uint1
|
||||
return {};
|
||||
}
|
||||
|
||||
void DecoderList::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
ObjectList<Decoder>::worldEvent(state, event);
|
||||
|
||||
const bool editable = contains(state, WorldState::Edit);
|
||||
|
||||
Attributes::setEnabled(create, editable);
|
||||
Attributes::setEnabled(delete_, editable);
|
||||
}
|
||||
|
||||
bool DecoderList::isListedProperty(std::string_view name)
|
||||
{
|
||||
return DecoderListTableModel::isListedProperty(name);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -29,18 +29,14 @@
|
||||
|
||||
class DecoderList : public ObjectList<Decoder>
|
||||
{
|
||||
CLASS_ID("decoder_list")
|
||||
CLASS_ID("list.decoder")
|
||||
|
||||
protected:
|
||||
void worldEvent(WorldState state, WorldEvent event) final;
|
||||
bool isListedProperty(std::string_view name) final;
|
||||
|
||||
public:
|
||||
const DecoderListColumn columns;
|
||||
|
||||
Method<std::shared_ptr<Decoder>()> create;
|
||||
Method<void(const std::shared_ptr<Decoder>&)> delete_;
|
||||
|
||||
DecoderList(Object& _parent, std::string_view parentPropertyName, DecoderListColumn _columns);
|
||||
|
||||
TableModelPtr getModel() final;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -23,6 +23,7 @@
|
||||
#include "decoderlisttablemodel.hpp"
|
||||
#include "decoderlist.hpp"
|
||||
#include "../../../core/objectproperty.tpp"
|
||||
#include "../../../vehicle/rail/railvehicle.hpp"
|
||||
#include "../../../utils/displayname.hpp"
|
||||
|
||||
static std::string_view displayName(DecoderListColumn column)
|
||||
@ -88,7 +89,11 @@ std::string DecoderListTableModel::getText(uint32_t column, uint32_t row) const
|
||||
return decoder.id;
|
||||
|
||||
case DecoderListColumn::Name:
|
||||
return decoder.name;
|
||||
if(decoder.vehicle) [[likely]]
|
||||
{
|
||||
return decoder.vehicle->name;
|
||||
}
|
||||
return {};
|
||||
|
||||
case DecoderListColumn::Interface:
|
||||
if(const auto& interface = std::dynamic_pointer_cast<Object>(decoder.interface.value()))
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2023 Reinder Feenstra
|
||||
* Copyright (C) 2023-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -29,6 +29,10 @@
|
||||
#include "../../../core/attributes.hpp"
|
||||
#include "../../../core/method.tpp"
|
||||
#include "../../../core/objectproperty.tpp"
|
||||
#include "../../../vehicle/rail/railvehiclelist.hpp"
|
||||
#include "../../../vehicle/rail/railvehicle.hpp"
|
||||
#include "../../../vehicle/rail/locomotive.hpp"
|
||||
#include "../../../train/train.hpp"
|
||||
#include "../../../world/getworld.hpp"
|
||||
#include "../../../world/world.hpp"
|
||||
|
||||
@ -145,7 +149,7 @@ void MarklinCANLocomotiveList::import(const MarklinCAN::LocomotiveList::Locomoti
|
||||
auto it = std::find_if(decoders.begin(), decoders.end(),
|
||||
[&locomotive](const auto& item)
|
||||
{
|
||||
return item->name.value() == locomotive.name;
|
||||
return item->vehicle && item->vehicle->name.value() == locomotive.name;
|
||||
});
|
||||
|
||||
if(it != decoders.end())
|
||||
@ -167,11 +171,15 @@ void MarklinCANLocomotiveList::import(const MarklinCAN::LocomotiveList::Locomoti
|
||||
|
||||
if(!decoder) // not found, create a new one
|
||||
{
|
||||
decoder = decoders.create();
|
||||
auto vehicle = interface().world().railVehicles->create(Locomotive::classId);
|
||||
decoder = vehicle->decoder.value();
|
||||
}
|
||||
|
||||
// update it:
|
||||
decoder->name = locomotive.name;
|
||||
if(decoder->vehicle)
|
||||
{
|
||||
decoder->vehicle->name = locomotive.name;
|
||||
}
|
||||
decoder->protocol = locomotive.protocol;
|
||||
if(decoder->protocol == DecoderProtocol::MFX)
|
||||
{
|
||||
|
||||
@ -24,34 +24,21 @@
|
||||
#include "railvehiclelist.hpp"
|
||||
#include "railvehiclelisttablemodel.hpp"
|
||||
#include "../../hardware/decoder/decoder.hpp"
|
||||
#include "../../hardware/decoder/list/decoderlist.hpp"
|
||||
#include "../../world/world.hpp"
|
||||
#include "../../core/attributes.hpp"
|
||||
#include "../../core/objectproperty.tpp"
|
||||
#include "../../core/objectvectorproperty.tpp"
|
||||
#include "../../core/method.tpp"
|
||||
#include "../../core/controllerlist.hpp"
|
||||
#include "../../utils/displayname.hpp"
|
||||
#include "../../train/train.hpp"
|
||||
#include "../../train/trainvehiclelist.hpp"
|
||||
#include "../../hardware/decoder/decodercontroller.hpp"
|
||||
|
||||
RailVehicle::RailVehicle(World& world, std::string_view _id) :
|
||||
Vehicle(world, _id),
|
||||
decoder{this, "decoder", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
|
||||
[this](const std::shared_ptr<Decoder>& value)
|
||||
{
|
||||
if(decoder)
|
||||
{
|
||||
decoder->vehicle.setValueInternal(nullptr);
|
||||
}
|
||||
if(value)
|
||||
{
|
||||
if(value->vehicle)
|
||||
{
|
||||
value->vehicle->decoder = nullptr;
|
||||
}
|
||||
value->vehicle.setValueInternal(shared_ptr<RailVehicle>());
|
||||
}
|
||||
return true;
|
||||
}},
|
||||
decoder{this, "decoder", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store},
|
||||
lob{*this, "lob", 0, LengthUnit::MilliMeter, PropertyFlags::ReadWrite | PropertyFlags::Store},
|
||||
speedMax{*this, "speed_max", 0, SpeedUnit::KiloMeterPerHour, PropertyFlags::ReadWrite | PropertyFlags::Store},
|
||||
weight{*this, "weight", 0, WeightUnit::Ton, PropertyFlags::ReadWrite | PropertyFlags::Store, [this](double /*value*/, WeightUnit /*unit*/){ updateTotalWeight(); }},
|
||||
@ -60,6 +47,28 @@ RailVehicle::RailVehicle(World& world, std::string_view _id) :
|
||||
, noSmoke{this, "no_smoke", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, activeTrain{this, "active_train", nullptr, PropertyFlags::ReadOnly | PropertyFlags::ScriptReadOnly | PropertyFlags::StoreState}
|
||||
, trains{*this, "trains", {}, PropertyFlags::ReadOnly | PropertyFlags::ScriptReadOnly | PropertyFlags::NoStore}
|
||||
, createDecoder{*this, "create_decoder", MethodFlags::NoScript,
|
||||
[this]()
|
||||
{
|
||||
if(!decoder)
|
||||
{
|
||||
decoder.setValueInternal(Decoder::create(m_world));
|
||||
decoder->vehicle.setValueInternal(shared_ptr<RailVehicle>());
|
||||
if(m_world.decoderControllers->length == 1)
|
||||
{
|
||||
decoder->interface = std::dynamic_pointer_cast<DecoderController>(m_world.decoderControllers->getObject(0));
|
||||
}
|
||||
}
|
||||
}}
|
||||
, deleteDecoder{*this, "delete_decoder", MethodFlags::NoScript,
|
||||
[this]()
|
||||
{
|
||||
if(decoder)
|
||||
{
|
||||
decoder->destroy();
|
||||
assert(!decoder);
|
||||
}
|
||||
}}
|
||||
{
|
||||
const bool editable = contains(m_world.state.value(), WorldState::Edit);
|
||||
|
||||
@ -96,6 +105,12 @@ RailVehicle::RailVehicle(World& world, std::string_view _id) :
|
||||
|
||||
Attributes::addObjectEditor(trains, false);
|
||||
m_interfaceItems.insertBefore(trains, notes);
|
||||
|
||||
Attributes::addObjectEditor(createDecoder, false);
|
||||
m_interfaceItems.add(createDecoder);
|
||||
|
||||
Attributes::addObjectEditor(deleteDecoder, false);
|
||||
m_interfaceItems.add(deleteDecoder);
|
||||
}
|
||||
|
||||
void RailVehicle::setActiveTrain(const std::shared_ptr<Train>& train)
|
||||
@ -149,7 +164,10 @@ void RailVehicle::destroying()
|
||||
{
|
||||
auto self = shared_ptr<RailVehicle>();
|
||||
if(decoder)
|
||||
decoder = nullptr;
|
||||
{
|
||||
decoder->destroy();
|
||||
assert(!decoder);
|
||||
}
|
||||
for(const auto& train : trains)
|
||||
{
|
||||
train->vehicles->remove(self);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021,2023-2024 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -29,6 +29,7 @@
|
||||
#include "../../core/lengthproperty.hpp"
|
||||
#include "../../core/speedproperty.hpp"
|
||||
#include "../../core/weightproperty.hpp"
|
||||
#include "../../core/method.hpp"
|
||||
|
||||
class Decoder;
|
||||
class Train;
|
||||
@ -58,6 +59,9 @@ class RailVehicle : public Vehicle
|
||||
ObjectProperty<Train> activeTrain;
|
||||
ObjectVectorProperty<Train> trains;
|
||||
|
||||
Method<void()> createDecoder;
|
||||
Method<void()> deleteDecoder;
|
||||
|
||||
void setActiveTrain(const std::shared_ptr<Train>& train);
|
||||
|
||||
void updateMute();
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021,2023-2024 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -23,6 +23,7 @@
|
||||
#include "railvehiclelist.hpp"
|
||||
#include "railvehiclelisttablemodel.hpp"
|
||||
#include "railvehicles.hpp"
|
||||
#include "poweredrailvehicle.hpp"
|
||||
#include "../../world/getworld.hpp"
|
||||
#include "../../core/attributes.hpp"
|
||||
#include "../../core/objectproperty.tpp"
|
||||
@ -38,7 +39,12 @@ RailVehicleList::RailVehicleList(Object& _parent, std::string_view parentPropert
|
||||
[this](std::string_view railVehicleClassId)
|
||||
{
|
||||
auto& world = getWorld(parent());
|
||||
return RailVehicles::create(world, railVehicleClassId, world.getUniqueId("vehicle"));
|
||||
auto vehicle = RailVehicles::create(world, railVehicleClassId, world.getUniqueId("vehicle"));
|
||||
if(dynamic_cast<PoweredRailVehicle*>(vehicle.get()))
|
||||
{
|
||||
vehicle->createDecoder();
|
||||
}
|
||||
return vehicle;
|
||||
}}
|
||||
, delete_{*this, "delete",
|
||||
[this](const std::shared_ptr<RailVehicle>& railVehicle)
|
||||
|
||||
@ -454,7 +454,6 @@ World::World(Private /*unused*/) :
|
||||
World::~World()
|
||||
{
|
||||
deleteAll(*interfaces);
|
||||
deleteAll(*decoders);
|
||||
deleteAll(*inputs);
|
||||
deleteAll(*identifications);
|
||||
deleteAll(*boards);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic test suite.
|
||||
*
|
||||
* Copyright (C) 2023-2024 Reinder Feenstra
|
||||
* Copyright (C) 2023-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -31,6 +31,9 @@
|
||||
#include "../src/hardware/output/list/outputlist.hpp"
|
||||
#include "../src/hardware/identification/identification.hpp"
|
||||
#include "../src/hardware/identification/list/identificationlist.hpp"
|
||||
#include "../src/vehicle/rail/locomotive.hpp"
|
||||
#include "../src/vehicle/rail/railvehiclelist.hpp"
|
||||
#include "../src/train/train.hpp"
|
||||
#include "interfaces.hpp"
|
||||
|
||||
TEMPLATE_TEST_CASE("Assign decoder to another interface", "[interface]", INTERFACES_DECODER)
|
||||
@ -53,7 +56,19 @@ TEMPLATE_TEST_CASE("Assign decoder to another interface", "[interface]", INTERFA
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
REQUIRE(interfaceWeak2.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = interfaceWeak1.lock()->decoders->create();
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::dynamic_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(locomotiveWeak.lock()->decoder);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->decoder->interface);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 2);
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 1);
|
||||
REQUIRE(interfaceWeak1.lock()->decoders->length == 0);
|
||||
REQUIRE(interfaceWeak2.lock()->decoders->length == 0);
|
||||
|
||||
decoderWeak.lock()->interface = interfaceWeak1.lock();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(decoderWeak.lock()->interface.value() == std::dynamic_pointer_cast<DecoderController>(interfaceWeak1.lock()));
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 2);
|
||||
@ -73,6 +88,7 @@ TEMPLATE_TEST_CASE("Assign decoder to another interface", "[interface]", INTERFA
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(interfaceWeak1.expired());
|
||||
REQUIRE(interfaceWeak2.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(decoderWeak.expired());
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic test suite.
|
||||
*
|
||||
* Copyright (C) 2021-2024 Reinder Feenstra
|
||||
* Copyright (C) 2021-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -122,48 +122,90 @@ TEMPLATE_TEST_CASE("Create world and interface => destroy interface", "[object-c
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world and decoder => destroy world", "[object-create-destroy]")
|
||||
TEST_CASE("Create world, locomotive and decoder => destroy world", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = world->decoders->create();
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::static_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(locomotiveWeak.lock()->getClassId() == Locomotive::classId);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(decoderWeak.lock()->getClassId() == Decoder::classId);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(decoderWeak.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world and decoder => destroy decoder", "[object-create-destroy]")
|
||||
TEST_CASE("Create world, locomotive and decoder => destroy locomotive", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = world->decoders->create();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 1);
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::static_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
world->decoders->delete_(decoderWeak.lock());
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(world->decoders->length == 1);
|
||||
|
||||
world->railVehicles->delete_(locomotiveWeak.lock());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(decoderWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
REQUIRE(world->railVehicles->length == 0);
|
||||
REQUIRE(world->decoders->length == 0);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world, decoder and function => destroy world", "[object-create-destroy]")
|
||||
TEST_CASE("Create world, locomotive and decoder => delete decoder", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = world->decoders->create();
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::static_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(world->decoders->length == 1);
|
||||
|
||||
locomotiveWeak.lock()->deleteDecoder();
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(decoderWeak.expired());
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->decoder);
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
REQUIRE(world->decoders->length == 0);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world, locomotive, decoder and function => destroy world", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::static_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 1);
|
||||
|
||||
@ -181,17 +223,22 @@ TEST_CASE("Create world, decoder and function => destroy world", "[object-create
|
||||
REQUIRE(functionWeak.expired());
|
||||
REQUIRE(functionsWeak.expired());
|
||||
REQUIRE(decoderWeak.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world, decoder and function => destroy function", "[object-create-destroy]")
|
||||
TEST_CASE("Create world, locomotive, decoder and function => destroy function", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = world->decoders->create();
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::static_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 1);
|
||||
|
||||
@ -210,10 +257,11 @@ TEST_CASE("Create world, decoder and function => destroy function", "[object-cre
|
||||
world.reset();
|
||||
REQUIRE(functionsWeak.expired());
|
||||
REQUIRE(decoderWeak.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE("Create world, interface and decoder => destroy interface", "[object-create-destroy]", INTERFACES_DECODER)
|
||||
TEMPLATE_TEST_CASE("Create world, interface, locomotive and decoder => destroy interface", "[object-create-destroy]", INTERFACES_DECODER)
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
@ -227,7 +275,11 @@ TEMPLATE_TEST_CASE("Create world, interface and decoder => destroy interface", "
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
REQUIRE(interfaceWeak.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = interfaceWeak.lock()->decoders->create();
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::static_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(decoderWeak.lock()->interface.value() == std::dynamic_pointer_cast<DecoderController>(interfaceWeak.lock()));
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 1);
|
||||
@ -243,10 +295,11 @@ TEMPLATE_TEST_CASE("Create world, interface and decoder => destroy interface", "
|
||||
|
||||
world.reset();
|
||||
REQUIRE(decoderWeak.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE("Create world, interface and decoder => destroy decoder", "[object-create-destroy]", INTERFACES_DECODER)
|
||||
TEMPLATE_TEST_CASE("Create world, interface, locomotive and decoder => destroy locomotive", "[object-create-destroy]", INTERFACES_DECODER)
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
@ -260,14 +313,58 @@ TEMPLATE_TEST_CASE("Create world, interface and decoder => destroy decoder", "[o
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
REQUIRE(interfaceWeak.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = interfaceWeak.lock()->decoders->create();
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::static_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(decoderWeak.lock()->interface.value() == std::dynamic_pointer_cast<DecoderController>(interfaceWeak.lock()));
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 1);
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 1);
|
||||
REQUIRE(interfaceWeak.lock()->decoders->length == 1);
|
||||
|
||||
world->decoders->delete_(decoderWeak.lock());
|
||||
world->railVehicles->delete_(locomotiveWeak.lock());
|
||||
REQUIRE_FALSE(interfaceWeak.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(decoderWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 1);
|
||||
REQUIRE(worldWeak.lock()->railVehicles->length == 0);
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
REQUIRE(interfaceWeak.lock()->decoders->length == 0);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(interfaceWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE("Create world, interface, locomotive and decoder => delete decoder", "[object-create-destroy]", INTERFACES_DECODER)
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 0);
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<TestType> interfaceWeak = std::dynamic_pointer_cast<TestType>(world->interfaces->create(TestType::classId));
|
||||
REQUIRE_FALSE(interfaceWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 1);
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 0);
|
||||
REQUIRE(interfaceWeak.lock()->decoders->length == 0);
|
||||
|
||||
std::weak_ptr<Locomotive> locomotiveWeak = std::static_pointer_cast<Locomotive>(world->railVehicles->create(Locomotive::classId));
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
std::weak_ptr<Decoder> decoderWeak = locomotiveWeak.lock()->decoder.value();
|
||||
REQUIRE_FALSE(decoderWeak.expired());
|
||||
REQUIRE(decoderWeak.lock()->interface.value() == std::dynamic_pointer_cast<DecoderController>(interfaceWeak.lock()));
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 1);
|
||||
REQUIRE(worldWeak.lock()->decoders->length == 1);
|
||||
REQUIRE(interfaceWeak.lock()->decoders->length == 1);
|
||||
|
||||
locomotiveWeak.lock()->deleteDecoder();
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->decoder.value());
|
||||
REQUIRE_FALSE(interfaceWeak.expired());
|
||||
REQUIRE(decoderWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->interfaces->length == 1);
|
||||
@ -275,6 +372,7 @@ TEMPLATE_TEST_CASE("Create world, interface and decoder => destroy decoder", "[o
|
||||
REQUIRE(interfaceWeak.lock()->decoders->length == 0);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(interfaceWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
@ -394,7 +492,7 @@ TEMPLATE_TEST_CASE("Create world and rail vehicle => destroy world", "[object-cr
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE("Create world and rail vehicle => destroy interface", "[object-create-destroy]", RAIL_VEHICLES)
|
||||
TEMPLATE_TEST_CASE("Create world and rail vehicle => destroy rail vehicle", "[object-create-destroy]", RAIL_VEHICLES)
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
|
||||
@ -44,11 +44,14 @@ TEST_CASE("Train: Save/Load", "[train][train-saveload]")
|
||||
{
|
||||
worldUUID = world->uuid;
|
||||
|
||||
auto decoder = world->decoders->create();
|
||||
REQUIRE(world->decoders->length == 0);
|
||||
REQUIRE(world->railVehicles->length == 0);
|
||||
|
||||
auto locomotive = world->railVehicles->create(Locomotive::classId);
|
||||
locomotive->decoder = decoder;
|
||||
REQUIRE(decoder->vehicle.value() == locomotive);
|
||||
REQUIRE(world->decoders->length == 1);
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
REQUIRE(locomotive->decoder);
|
||||
REQUIRE(locomotive->decoder->vehicle.value() == locomotive);
|
||||
|
||||
auto train = world->trains->create();
|
||||
train->vehicles->add(locomotive);
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren