From 315de47a12255484472c7c7a66afa04200411be4 Mon Sep 17 00:00:00 2001 From: Beat Hangartner Date: Tue, 6 Oct 2026 09:50:08 +0200 Subject: [PATCH] Keep the width of free texts in the project database The independent_text table had the text and rotation of a free text but not its width, and a width change did not update the row at all -- not even its width and height, which follow the wrapped text. Add a text_width column (NULL for the automatic width) and update the row on textWidthChanged. Test: tst_scriptfreetextwidth reads the table with qet.query() (fails before). Co-Authored-By: Claude Opus 5.5 --- sources/dataBase/projectdatabase.cpp | 11 ++- tests/qttest/CMakeLists.txt | 11 +++ tests/qttest/tst_scriptfreetextwidth.cpp | 100 +++++++++++++++++++++++ 3 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 tests/qttest/tst_scriptfreetextwidth.cpp diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 653bcf172..e93d53061 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -1248,6 +1248,8 @@ void projectDataBase::addDrawingItem(QGraphicsItem *item) } else if (auto text = qobject_cast(object)) { connect(text, &IndependentTextItem::uuidChanged, this, &projectDataBase::drawingItemChanged, unique); + //Also changes the height of the text, kept in the row + connect(text, &IndependentTextItem::textWidthChanged, this, &projectDataBase::drawingItemChanged, unique); //Sent by the document, not the item: drawingItemChanged() walks //back up to the item. It is the one signal that catches every way //the text changes -- typing, undo, a script's setTextContent(). @@ -1424,6 +1426,9 @@ bool projectDataBase::writeDrawingItem(QObject *object) query = &m_insert_independent_text_query; query->bindValue(QStringLiteral(":text"), text->toPlainText()); query->bindValue(QStringLiteral(":rotation"), text->rotation()); + //NULL for the automatic width + query->bindValue(QStringLiteral(":text_width"), text->textWidth() > 0 + ? QVariant(text->textWidth()) : QVariant()); } else if (auto image = qobject_cast(object)) { @@ -1667,7 +1672,7 @@ bool projectDataBase::createDataBase() "type VARCHAR(20), color VARCHAR(20), fill VARCHAR(20), " "FOREIGN KEY (diagram_uuid) REFERENCES diagram (uuid))", QStringLiteral("CREATE TABLE independent_text (") + drawing_columns + - "text TEXT, rotation REAL, " + "text TEXT, rotation REAL, text_width REAL, " "FOREIGN KEY (diagram_uuid) REFERENCES diagram (uuid))", QStringLiteral("CREATE TABLE image (") + drawing_columns + "pixel_width INTEGER, pixel_height INTEGER, " @@ -2123,8 +2128,8 @@ void projectDataBase::prepareQuery() ", :type, :color, :fill)"); m_insert_independent_text_query = QSqlQuery(m_data_base); m_insert_independent_text_query.prepare("INSERT OR REPLACE INTO independent_text (" + drawing_columns + - ", text, rotation) VALUES (" + drawing_values + - ", :text, :rotation)"); + ", text, rotation, text_width) VALUES (" + drawing_values + + ", :text, :rotation, :text_width)"); m_insert_image_query = QSqlQuery(m_data_base); m_insert_image_query.prepare("INSERT OR REPLACE INTO image (" + drawing_columns + ", pixel_width, pixel_height) VALUES (" + drawing_values + diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 33134d0da..0c49470f8 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -543,6 +543,17 @@ if(QET_HAS_SCRIPTING) target_compile_definitions(tst_scriptlayoutreads PRIVATE "QET_TEST_BINARY_PATH=\"$\"") + # The project database keeps the width of a free text (independent_text), + # read with qet.query() on fixtures/free_text_width.qet. + add_executable( + tst_scriptfreetextwidth + tst_scriptfreetextwidth.cpp) + add_test(NAME tst_scriptfreetextwidth COMMAND tst_scriptfreetextwidth) + add_dependencies(tst_scriptfreetextwidth qelectrotech) + target_link_libraries(tst_scriptfreetextwidth PRIVATE Qt::Test) + target_compile_definitions(tst_scriptfreetextwidth PRIVATE + "QET_TEST_BINARY_PATH=\"$\"") + # QET_SETTINGS_DIR moves the settings into an INI file there (#1178): a # script places a symbol only the folder's settings file can resolve. add_executable( diff --git a/tests/qttest/tst_scriptfreetextwidth.cpp b/tests/qttest/tst_scriptfreetextwidth.cpp new file mode 100644 index 000000000..7d9b2b396 --- /dev/null +++ b/tests/qttest/tst_scriptfreetextwidth.cpp @@ -0,0 +1,100 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech 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. + + QElectroTech 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 QElectroTech. If not, see . +*/ +#include +#include +#include +#include +#include +#include + +/** + The project database keeps the width of a free text (text_width in the + independent_text table, NULL for the automatic width), read here with + qet.query() from a script run on fixtures/free_text_width.qet. +*/ +class tst_scriptfreetextwidth : public QObject +{ + Q_OBJECT + + QTemporaryDir m_dir; + + /// The rows of the independent_text table, by the first word of the text. + QHash rows() + { + const QString path = m_dir.filePath(QStringLiteral("probe.js")); + const QString home = m_dir.filePath(QStringLiteral("home")); + QDir().mkpath(home); + QFile f(path); + if (!f.open(QIODevice::WriteOnly)) return {}; + f.write("qet.log('PROBE ' + JSON.stringify(qet.query(" + "'SELECT text, text_width, width, height FROM independent_text')));\n"); + f.close(); + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen")); + env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1")); + env.insert(QStringLiteral("HOME"), home); + env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config")); + env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data")); + QProcess proc; + proc.setProcessEnvironment(env); + proc.start(QStringLiteral(QET_TEST_BINARY_PATH), + {QStringLiteral("--run"), path, QFINDTESTDATA("fixtures/free_text_width.qet")}); + if (!proc.waitForFinished(60000)) return {}; + + const QString out = QString::fromUtf8(proc.readAllStandardOutput() + proc.readAllStandardError()); + const QString mark = QStringLiteral("PROBE "); + QHash result; + for (const QString &line : out.split(QLatin1Char('\n'))) { + const int i = line.indexOf(mark); + if (i < 0) continue; + const QJsonArray array = QJsonDocument::fromJson(line.mid(i + mark.size()).toUtf8()).array(); + for (const QJsonValue &row : array) + result.insert(row.toObject().value(QStringLiteral("text")).toString().section(QLatin1Char(' '), 0, 0), + row.toObject()); + } + return result; + } + +private slots: + void initTestCase() + { + QVERIFY(m_dir.isValid()); + QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH))); + QVERIFY(!QFINDTESTDATA("fixtures/free_text_width.qet").isEmpty()); + } + + void widthIsInTheDatabase() + { + const QHash r = rows(); + QCOMPARE(r.size(), 3); + + QCOMPARE(r.value(QStringLiteral("FreeAlpha")).value(QStringLiteral("text_width")).toDouble(), 70.0); + QCOMPARE(r.value(QStringLiteral("CentAlpha")).value(QStringLiteral("text_width")).toDouble(), 120.0); + //The automatic width is NULL + const QJsonValue open = r.value(QStringLiteral("OpenAlpha")).value(QStringLiteral("text_width")); + QVERIFY2(open.isNull() || open.toString().isEmpty(), qPrintable(open.toVariant().toString())); + + //The box of the row is the wrapped one: taller than one line + QVERIFY(r.value(QStringLiteral("FreeAlpha")).value(QStringLiteral("height")).toDouble() + > 2 * r.value(QStringLiteral("OpenAlpha")).value(QStringLiteral("height")).toDouble()); + } +}; + +QTEST_APPLESS_MAIN(tst_scriptfreetextwidth) +#include "tst_scriptfreetextwidth.moc"