diff --git a/src/app/app_module.cpp b/src/app/app_module.cpp index 0d448a72..af0d8554 100644 --- a/src/app/app_module.cpp +++ b/src/app/app_module.cpp @@ -166,15 +166,10 @@ Settings::Variant AppModule::toVariant(const Property& prop) const QByteArray blob; QDataStream stream(&blob, QIODevice::WriteOnly); AppModule::writeRecentFiles(stream, filesProp.value()); - Variant varBlob(blob.toStdString()); - varBlob.setByteArray(true); - return varBlob; + return Variant(QtCoreUtils::toStdByteArray(blob)); } else if (isType(prop)) { - const QByteArray blob = AppUiState::toBlob(constRef(prop)); - Variant varBlob(blob.toStdString()); - varBlob.setByteArray(true); - return varBlob; + return Variant(AppUiState::toBlob(constRef(prop))); } else { return PropertyValueConversion::toVariant(prop); @@ -184,7 +179,7 @@ Settings::Variant AppModule::toVariant(const Property& prop) const bool AppModule::fromVariant(Property* prop, const Settings::Variant& variant) const { if (isType(prop)) { - const QByteArray blob = QtCoreUtils::QByteArray_frowRawData(variant.toConstRefString()); + const QByteArray blob = QtCoreUtils::QByteArray_fromRawData(variant.toConstRefByteArray()); QDataStream stream(blob); RecentFiles recentFiles; AppModule::readRecentFiles(stream, &recentFiles); @@ -193,8 +188,7 @@ bool AppModule::fromVariant(Property* prop, const Settings::Variant& variant) co } else if (isType(prop)) { bool ok = false; - auto blob = QtCoreUtils::QByteArray_frowRawData(variant.toConstRefString()); - auto uiState = AppUiState::fromBlob(blob, &ok); + auto uiState = AppUiState::fromBlob(variant.toConstRefByteArray(), &ok); ptr(prop)->setValue(uiState); return ok; } diff --git a/src/app/app_ui_state.cpp b/src/app/app_ui_state.cpp index e6cae1b6..d80438ad 100644 --- a/src/app/app_ui_state.cpp +++ b/src/app/app_ui_state.cpp @@ -8,21 +8,20 @@ namespace Mayo { template<> const char PropertyAppUiState::TypeName[] = "Mayo::PropertyAppUiState"; -QByteArray AppUiState::toBlob(const AppUiState& state) +std::vector AppUiState::toBlob(const AppUiState& state) { QByteArray blob; QDataStream stream(&blob, QIODevice::WriteOnly); - stream << QtCoreUtils::QByteArray_frowRawData(std::string_view{PropertyAppUiState::TypeName}); + stream << QtCoreUtils::QByteArray_fromRawData(std::string_view{PropertyAppUiState::TypeName}); constexpr uint32_t version = 2; stream << version; - stream << state.mainWindowGeometry; + stream << QtCoreUtils::QByteArray_fromRawData(state.mainWindowGeometry); stream << state.pageDocuments_isLeftSideBarVisible; stream << state.pageDocuments_widgetLeftSideBarWidthFactor; - return blob; - + return QtCoreUtils::toStdByteArray(blob); } -AppUiState AppUiState::fromBlob(const QByteArray& blob, bool* ok) +AppUiState AppUiState::fromBlob(Span blob, bool* ok) { auto fnSetOk = [=](bool v) { if (ok) @@ -31,14 +30,17 @@ AppUiState AppUiState::fromBlob(const QByteArray& blob, bool* ok) fnSetOk(false); AppUiState state; - QDataStream stream(blob); + + QDataStream stream(QtCoreUtils::QByteArray_fromRawData(blob)); QByteArray identifier; stream >> identifier; if (identifier == PropertyAppUiState::TypeName) { uint32_t version = 0; stream >> version; if (version >= 1) { - stream >> state.mainWindowGeometry; + QByteArray blobMainWindowGeom; + stream >> blobMainWindowGeom; + state.mainWindowGeometry = QtCoreUtils::toStdByteArray(blobMainWindowGeom); stream >> state.pageDocuments_isLeftSideBarVisible; fnSetOk(true); } diff --git a/src/app/app_ui_state.h b/src/app/app_ui_state.h index 67b6f3eb..bdf7a47f 100644 --- a/src/app/app_ui_state.h +++ b/src/app/app_ui_state.h @@ -7,19 +7,21 @@ #pragma once #include "../base/property_builtins.h" -#include +#include "../base/span.h" + +#include namespace Mayo { // Stores the UI state of the main application widgets struct AppUiState { - QByteArray mainWindowGeometry; // Provided by QWidget::saveGeometry() + std::vector mainWindowGeometry; // Provided by QWidget::saveGeometry() bool pageDocuments_isLeftSideBarVisible = true; double pageDocuments_widgetLeftSideBarWidthFactor = 0.25; // Serialization functions - static QByteArray toBlob(const AppUiState& state); - static AppUiState fromBlob(const QByteArray& blob, bool* ok = nullptr); + static std::vector toBlob(const AppUiState& state); + static AppUiState fromBlob(Span blob, bool* ok = nullptr); }; using PropertyAppUiState = GenericProperty; diff --git a/src/app/mainwindow.cpp b/src/app/mainwindow.cpp index fdf7f5db..1bc057b7 100644 --- a/src/app/mainwindow.cpp +++ b/src/app/mainwindow.cpp @@ -11,6 +11,7 @@ #include "../base/global.h" #include "../gui/gui_application.h" #include "../gui/gui_document.h" +#include "../qtcommon/qtcore_utils.h" #include "app_context.h" #include "app_module.h" #include "commands_file.h" @@ -74,8 +75,8 @@ MainWindow::~MainWindow() void MainWindow::showEvent(QShowEvent* event) { const auto& uiState = AppModule::get()->properties()->appUiState.value(); - if (!uiState.mainWindowGeometry.isEmpty()) - this->restoreGeometry(uiState.mainWindowGeometry); + if (!uiState.mainWindowGeometry.empty()) + this->restoreGeometry(QtCoreUtils::QByteArray_fromRawData(uiState.mainWindowGeometry)); WidgetMainControl* pageDocs = this->widgetPageDocuments(); if (pageDocs) { @@ -97,7 +98,7 @@ void MainWindow::showEvent(QShowEvent* event) void MainWindow::closeEvent(QCloseEvent* event) { AppUiState uiState = AppModule::get()->properties()->appUiState; - uiState.mainWindowGeometry = this->saveGeometry(); + uiState.mainWindowGeometry = QtCoreUtils::toStdByteArray(this->saveGeometry()); WidgetMainControl* pageDocs = this->widgetPageDocuments(); if (pageDocs) { uiState.pageDocuments_isLeftSideBarVisible = pageDocs->widgetLeftSideBar()->isVisible(); diff --git a/src/app/widget_model_tree_builder_xde.cpp b/src/app/widget_model_tree_builder_xde.cpp index 8dfa9c72..95a2309d 100644 --- a/src/app/widget_model_tree_builder_xde.cpp +++ b/src/app/widget_model_tree_builder_xde.cpp @@ -41,9 +41,9 @@ class WidgetModelTreeBuilder_Xde::Module : public PropertyGroup { // UTF8 rightwards arrow : \xe2\x86\x92 static const char templateBoth[] = "%instance \xe2\x86\x92 %product"; switch (format) { - case NameFormat::Instance: return QtCoreUtils::QByteArray_frowRawData(templateInstance); - case NameFormat::Product: return QtCoreUtils::QByteArray_frowRawData(templateProduct); - case NameFormat::Both: return QtCoreUtils::QByteArray_frowRawData(templateBoth); + case NameFormat::Instance: return QtCoreUtils::QByteArray_fromRawData(templateInstance); + case NameFormat::Product: return QtCoreUtils::QByteArray_fromRawData(templateProduct); + case NameFormat::Both: return QtCoreUtils::QByteArray_fromRawData(templateBoth); } return QByteArray(); } @@ -214,7 +214,7 @@ QTreeWidgetItem* WidgetModelTreeBuilder_Xde::buildXdeTree( QByteArray WidgetModelTreeBuilder_Xde::instanceNameFormat() const { - return QtCoreUtils::QByteArray_frowRawData(Module::get()->instanceNameFormat.valueName()); + return QtCoreUtils::QByteArray_fromRawData(Module::get()->instanceNameFormat.valueName()); } void WidgetModelTreeBuilder_Xde::setInstanceNameFormat(const QByteArray& format) diff --git a/src/base/property_value_conversion.cpp b/src/base/property_value_conversion.cpp index 0a3131e7..f2798da8 100644 --- a/src/base/property_value_conversion.cpp +++ b/src/base/property_value_conversion.cpp @@ -16,6 +16,7 @@ #include "unit_system.h" #include +#include #if __cpp_lib_to_chars # include #endif @@ -346,6 +347,8 @@ std::string PropertyValueConversion::Variant::toString(bool* ok) const return std::to_string(std::get(*this)); else if (std::holds_alternative(*this)) return std::to_string(std::get(*this)); + else if (std::holds_alternative>(*this)) + return std::to_string(std::get(*this)); else return this->toConstRefString(); } @@ -366,19 +369,42 @@ const std::string& PropertyValueConversion::Variant::toConstRefString(bool* ok) return CppUtils::nullString(); } -bool PropertyValueConversion::Variant::isConvertibleToConstRefString() const +std::vector PropertyValueConversion::Variant::toByteArray(bool* ok) const { - return std::holds_alternative(*this) || std::holds_alternative(*this); + assignBoolPtr(ok, true); + if (std::holds_alternative(*this)) { + const std::string& str = std::get(*this); + std::vector bytes; + bytes.resize(str.size()); + std::copy(str.cbegin(), str.cend(), bytes.begin()); + return bytes; + } + else if (std::holds_alternative>(*this)) { + return std::get>(*this); + } + + assignBoolPtr(ok, false); + return {}; } -bool PropertyValueConversion::Variant::isByteArray() const +Span PropertyValueConversion::Variant::toConstRefByteArray(bool* ok) const { - return m_isByteArray && std::holds_alternative(*this); + assignBoolPtr(ok, true); + if (std::holds_alternative>(*this)) + return std::get>(*this); + + assignBoolPtr(ok, false); + return {}; +} + +bool PropertyValueConversion::Variant::isConvertibleToConstRefString() const +{ + return std::holds_alternative(*this) || std::holds_alternative(*this); } -void PropertyValueConversion::Variant::setByteArray(bool on) +bool PropertyValueConversion::Variant::isByteArray() const { - m_isByteArray = on; + return std::holds_alternative>(*this); } } // namespace Mayo diff --git a/src/base/property_value_conversion.h b/src/base/property_value_conversion.h index b2a419e1..6c74b1d4 100644 --- a/src/base/property_value_conversion.h +++ b/src/base/property_value_conversion.h @@ -7,6 +7,8 @@ #pragma once #include "property.h" +#include "span.h" + #include #include #include @@ -16,10 +18,13 @@ namespace Mayo { // Mechanism to convert value of a Property object to/from a basic variant type class PropertyValueConversion { public: + using BaseVariantType = std::variant< + std::monostate, bool, int, double, std::string, std::vector + >; // Variant type to be used when (de)serializing values - class Variant : public std::variant { + class Variant : public BaseVariantType { public: - using BaseType = std::variant; + using BaseType = BaseVariantType; Variant() = default; Variant(bool v) : BaseType(v) {} Variant(int v) : BaseType(v) {} @@ -27,21 +32,21 @@ class PropertyValueConversion { Variant(double v) : BaseType(v) {} Variant(const char* str) : BaseType(std::string(str)) {} Variant(const std::string& str) : BaseType(str) {} + Variant(Span bytes) : BaseType(std::vector(bytes.begin(), bytes.end())) {} bool isValid() const; bool toBool(bool* ok = nullptr) const; int toInt(bool* ok = nullptr) const; double toDouble(bool* ok = nullptr) const; + std::string toString(bool* ok = nullptr) const; const std::string& toConstRefString(bool* ok = nullptr) const; - bool isConvertibleToConstRefString() const; + std::vector toByteArray(bool* ok = nullptr) const; + Span toConstRefByteArray(bool* ok = nullptr) const; + bool isConvertibleToConstRefString() const; bool isByteArray() const; - void setByteArray(bool on); - - private: - bool m_isByteArray = false; }; int doubleToStringPrecision() const { return m_doubleToStringPrecision; } diff --git a/src/qtbackend/qsettings_storage.cpp b/src/qtbackend/qsettings_storage.cpp index 69c27c4a..3ea8bc02 100644 --- a/src/qtbackend/qsettings_storage.cpp +++ b/src/qtbackend/qsettings_storage.cpp @@ -25,8 +25,10 @@ Settings::Variant QSettingsStorage::value(std::string_view key) const { const QVariant value = m_storage.value(to_QString(key)); switch (value.type()) { - case QVariant::ByteArray: - return value.toByteArray().toStdString(); + case QVariant::ByteArray: { + Span bytes = QtCoreUtils::toStdByteArray(value.toByteArray()); + return Settings::Variant(bytes); + } case QVariant::String: { const QString strval = value.toString(); if (strval == "true") @@ -80,11 +82,10 @@ void QSettingsStorage::setValue(std::string_view key, const Settings::Variant& v qvalue = std::get(value); } else if (std::holds_alternative(value)) { - const std::string& str = value.toConstRefString(); - if (value.isByteArray()) - qvalue = QByteArray::fromStdString(str); // Don't use QtCoreUtils::QByteArray_frowRawData(str) - else - qvalue = to_QString(str); + qvalue = to_QString(value.toConstRefString()); + } + else if (std::holds_alternative>(value)) { + qvalue = QtCoreUtils::toQByteArray(value.toConstRefByteArray()); } if (!qvalue.isNull()) diff --git a/src/qtcommon/qtcore_utils.cpp b/src/qtcommon/qtcore_utils.cpp new file mode 100644 index 00000000..13935832 --- /dev/null +++ b/src/qtcommon/qtcore_utils.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** Copyright (c) 2024, Fougue Ltd. +** All rights reserved. +** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt +****************************************************************************/ + +#include "qtcore_utils.h" + +#include + +namespace Mayo::QtCoreUtils { + +std::vector toStdByteArray(const QByteArray& bytes) +{ + std::vector stdBytes; + stdBytes.resize(bytes.size()); + std::copy(bytes.cbegin(), bytes.cend(), stdBytes.begin()); + return stdBytes; +} + +Qt::CheckState toQtCheckState(Mayo::CheckState state) +{ + switch (state) { + case CheckState::Off: return Qt::Unchecked; + case CheckState::Partially: return Qt::PartiallyChecked; + case CheckState::On: return Qt::Checked; + } + + return Qt::Unchecked; +} + +Mayo::CheckState toCheckState(Qt::CheckState state) +{ + switch (state) { + case Qt::Unchecked: return CheckState::Off; + case Qt::PartiallyChecked: return CheckState::Partially; + case Qt::Checked: return CheckState::On; + } + + return CheckState::Off; +} + +} // namespace Mayo::QtCoreUtils diff --git a/src/qtcommon/qtcore_utils.h b/src/qtcommon/qtcore_utils.h index 524f92ec..2b1f8850 100644 --- a/src/qtcommon/qtcore_utils.h +++ b/src/qtcommon/qtcore_utils.h @@ -1,5 +1,5 @@ /**************************************************************************** -** Copyright (c) 2021, Fougue Ltd. +** Copyright (c) 2021, Fougue Ltd. ** All rights reserved. ** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt ****************************************************************************/ @@ -7,56 +7,59 @@ #pragma once #include "../base/global.h" +#include "../base/span.h" + #include #include - -namespace Mayo { +#include // Provides a collection of tools for the QtCore module -namespace QtCoreUtils { +namespace Mayo::QtCoreUtils { // Convenience function over QByteArray::fromRawData() taking a QByteArray object -inline QByteArray QByteArray_frowRawData(const QByteArray& bytes) +inline QByteArray QByteArray_fromRawData(const QByteArray& bytes) { return QByteArray::fromRawData(bytes.data(), bytes.size()); } // Convenience function over QByteArray::fromRawData() taking a std::string_view object -inline QByteArray QByteArray_frowRawData(std::string_view str) +inline QByteArray QByteArray_fromRawData(std::string_view str) { return QByteArray::fromRawData(str.data(), int(str.size())); } // Convenience function over QByteArray::fromRawData() taking a C array of characters template -QByteArray QByteArray_frowRawData(const char (&str)[N]) +QByteArray QByteArray_fromRawData(const char (&str)[N]) { return QByteArray::fromRawData(str, N); } -// Converts Mayo::CheckState -> Qt::CheckState -inline Qt::CheckState toQtCheckState(Mayo::CheckState state) +// Convenience function over QByteArray::fromRawData() taking a span of bytes +template +QByteArray QByteArray_fromRawData(Span bytes) { - switch (state) { - case CheckState::Off: return Qt::Unchecked; - case CheckState::Partially: return Qt::PartiallyChecked; - case CheckState::On: return Qt::Checked; - } - - return Qt::Unchecked; + static_assert(sizeof(ByteType) == 1, "size of ByteType must be one byte"); + return QtCoreUtils::QByteArray_fromRawData( + std::string_view{ reinterpret_cast(bytes.data()), bytes.size() } + ); } -// Converts Qt::CheckState -> Mayo::CheckState -inline Mayo::CheckState toCheckState(Qt::CheckState state) +// Converts a span of bytes to QByteArray object +template +QByteArray toQByteArray(Span bytes) { - switch (state) { - case Qt::Unchecked: return CheckState::Off; - case Qt::PartiallyChecked: return CheckState::Partially; - case Qt::Checked: return CheckState::On; - } - - return CheckState::Off; + static_assert(sizeof(ByteType) == 1, "size of ByteType must be one byte"); + return QByteArray{ reinterpret_cast(bytes.data()), int(bytes.size()) }; } -} // namespace QtCoreUtils -} // namespace Mayo +// Converts a QByteArray object to std::vector +std::vector toStdByteArray(const QByteArray& bytes); + +// Converts Mayo::CheckState -> Qt::CheckState +Qt::CheckState toQtCheckState(Mayo::CheckState state); + +// Converts Qt::CheckState -> Mayo::CheckState +Mayo::CheckState toCheckState(Qt::CheckState state); + +} // namespace Mayo::QtCoreUtils diff --git a/tests/test_app.cpp b/tests/test_app.cpp index f919ba12..7171f3be 100644 --- a/tests/test_app.cpp +++ b/tests/test_app.cpp @@ -21,6 +21,7 @@ #include "../src/base/document.h" #include "../src/qtcommon/filepath_conv.h" #include "../src/qtcommon/qstring_conv.h" +#include "../src/qtcommon/qtcore_utils.h" #include #include @@ -267,9 +268,9 @@ void TestApp::AppUiState_test() { QWidget widget; AppUiState uiState; - uiState.mainWindowGeometry = widget.saveGeometry(); + uiState.mainWindowGeometry = QtCoreUtils::toStdByteArray(widget.saveGeometry()); uiState.pageDocuments_isLeftSideBarVisible = true; - QByteArray blobSave = AppUiState::toBlob(uiState); + std::vector blobSave = AppUiState::toBlob(uiState); bool ok = false; const AppUiState uiState_read = AppUiState::fromBlob(blobSave, &ok); diff --git a/tests/test_base.cpp b/tests/test_base.cpp index a84cfc7e..bf7f4a5b 100644 --- a/tests/test_base.cpp +++ b/tests/test_base.cpp @@ -1054,8 +1054,9 @@ void TestBase::Settings_test() auto settingsStorage = std::make_unique(); settingsStorage->setValue("main/someInt", Settings::Variant{5}); - Settings::Variant bytesVar("abcde_12345"); - bytesVar.setByteArray(true); + const uint8_t bytes[] = { 97, 98, 99, 100, 101, 95, 49, 50, 51, 52, 53 }; + const Settings::Variant bytesVar(Span(bytes, std::size(bytes))); + QVERIFY(std::holds_alternative>(bytesVar)); settingsStorage->setValue("main/someTestData", bytesVar); settings.setStorage(std::move(settingsStorage)); diff --git a/tests/test_measure.cpp b/tests/test_measure.cpp index b7e57c1d..82552742 100644 --- a/tests/test_measure.cpp +++ b/tests/test_measure.cpp @@ -12,6 +12,7 @@ #include "../src/base/unit_system.h" #include "../src/io_occ/io_occ_stl.h" #include "../src/measure/measure_tool_brep.h" +#include "../qtcommon/qstring_conv.h" #include #include @@ -155,7 +156,7 @@ void TestMeasure::BRepMinDistance_TwoConfusedFaces_test() const MeasureDistance minDist = MeasureToolBRep::brepMinDistance(face1, face2); QCOMPARE(minDist.value.value(), 0.); } catch (const IMeasureError& err) { - qDebug() << QString::fromUtf8(err.message().data(), err.message().length()); + qDebug() << to_QString(err.message()); } }