diff --git a/sources/TerminalStrip/realterminal.cpp b/sources/TerminalStrip/realterminal.cpp index 8b86dd31f..92b367fb3 100644 --- a/sources/TerminalStrip/realterminal.cpp +++ b/sources/TerminalStrip/realterminal.cpp @@ -413,8 +413,10 @@ bool RealTerminal::isShield() const { * typed in by hand when none does -- the Target cell of the terminal * strip editor is read only in the first case, so a target the drawing * cannot work out is still worth naming by hand. A text typed in is - * written the way the fields are shown in the column, - * "Installation / Location / Label", and gives one line per field. + * written one line per field, Installation, Location and Label (BMK), + * the way the cell shows them: only a line break breaks it into fields. + * A "/" written inside a field (a name like "ASP/01") is part of that + * field and never breaks anything. * @return the lines to write in the box under the cable */ QVector RealTerminal::cableTarget() const @@ -430,13 +432,15 @@ QVector RealTerminal::cableTarget() const .value(cableTargetInfoKey()).toString()}; if (!typed_.isEmpty()) { - //Written the way the fields are shown in the column, - //"Installation / Location / Label": the box draws one line - //per field, without the separators, like it does for a - //target worked out from the project + //One line by field, and only a line break breaks the text + //into fields : a "/" written inside a field stays where it is + QString text_{typed_}; + text_.replace(QStringLiteral("\r\n"), QStringLiteral("\n")); + text_.replace(QLatin1Char('\r'), QLatin1Char('\n')); + QVector lines_; for (const QString &field_ - : typed_.split(QStringLiteral(" / "), Qt::SkipEmptyParts)) { + : text_.split(QLatin1Char('\n'), Qt::SkipEmptyParts)) { lines_ << field_; } return lines_; diff --git a/sources/TerminalStrip/ui/modelTerminalData.h b/sources/TerminalStrip/ui/modelTerminalData.h index 0e8778832..ff14f21de 100644 --- a/sources/TerminalStrip/ui/modelTerminalData.h +++ b/sources/TerminalStrip/ui/modelTerminalData.h @@ -27,10 +27,12 @@ struct modelRealTerminalData { ///The Installation, Location and Label (BMK) of the piece the cable - ///lands on, joined in one line : what the strip draws in the box - ///under its cable (RealTerminal::cableTarget), in the same order. - ///A target typed in by hand is one single line, and nothing is - ///shown when the cable lands on nothing worth naming and no text + ///lands on, one line by field : what the strip draws in the box + ///under its cable (RealTerminal::cableTarget) and what the Target + ///cell shows, both read line by line. A "/" written inside a field + ///is part of that field, never a separator. + ///A target typed in by hand is kept as it was written, and nothing + ///is shown when the cable lands on nothing worth naming and no text ///was ever written in. static QString targetText(const QVector &target_) { @@ -41,7 +43,7 @@ struct modelRealTerminalData fields_ << field_; } } - return fields_.join(QStringLiteral(" / ")); + return fields_.join(QLatin1Char('\n')); } static modelRealTerminalData data(const QSharedPointer &real_t) diff --git a/sources/TerminalStrip/ui/terminalstripeditor.cpp b/sources/TerminalStrip/ui/terminalstripeditor.cpp index 9d17884e9..8b714fa0c 100644 --- a/sources/TerminalStrip/ui/terminalstripeditor.cpp +++ b/sources/TerminalStrip/ui/terminalstripeditor.cpp @@ -253,6 +253,14 @@ void TerminalStripEditor::setCurrentStrip(TerminalStrip *strip_) //the width of its own text, never a cut off Installation ui->m_table_widget->horizontalHeader()->setSectionResizeMode( TerminalStripModel::CableTarget, QHeaderView::ResizeToContents); + //That column holds one line by field: its rows must be tall + //enough for as many lines as a cell shows, while a cell of + //one line keeps the height the rows have always had + auto v_header_ = ui->m_table_widget->verticalHeader(); + v_header_->setMinimumSectionSize(qMax( + v_header_->defaultSectionSize(), + ui->m_table_widget->rowHeight(0))); + v_header_->setSectionResizeMode(QHeaderView::ResizeToContents); m_model->buildBridgePixmap(setUpBridgeCellWidth()); connect(ui->m_table_widget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TerminalStripEditor::selectionChanged); } @@ -880,8 +888,15 @@ void TerminalStripEditor::copySelectionToClipboard() for (auto row = top ; row <= bottom ; ++row) { QStringList cells; - for (auto column = left ; column <= right ; ++column) { - cells << texts.value((qint64(row) << 32) | qint64(column)); + for (auto column = left ; column <= right ; ++column) + { + //A cell may hold several lines (the Target cell holds one + //line by field): the text of the clipboard keeps one line by + //row, the fields of that cell are joined back the way a + //spreadsheet reads them + QString cell_{texts.value((qint64(row) << 32) | qint64(column))}; + cell_.replace(QLatin1Char('\n'), QStringLiteral(" / ")); + cells << cell_; } lines << cells.join(QLatin1Char('\t')); } diff --git a/sources/TerminalStrip/ui/terminalstripmodel.cpp b/sources/TerminalStrip/ui/terminalstripmodel.cpp index b15be27a9..9ac943241 100644 --- a/sources/TerminalStrip/ui/terminalstripmodel.cpp +++ b/sources/TerminalStrip/ui/terminalstripmodel.cpp @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include /** * Some const int who describe what a column contain @@ -517,8 +520,9 @@ QVariant TerminalStripModel::headerData(int section, Qt::Orientation orientation orientation == Qt::Horizontal && section == CABLE_TARGET_CELL) { - //Same three fields as the box drawn under the cable - return tr("Installation, Location and Label (BMK) of the piece the cable lands on, past this strip"); + //Same three fields as the box drawn under the cable, one line + //by field: only a line break separates them + return tr("Installation, Location and Label (BMK) of the piece the cable lands on, past this strip, one line per field"); } return QVariant(); @@ -1128,10 +1132,59 @@ QWidget *TerminalStripModelDelegate::createEditor(QWidget *parent, const QStyleO return qcb; } + if (index.column() == CABLE_TARGET_CELL) { + //The Target cell holds one line by field, so it takes a multi + //line editor : Return writes a new line (the cell gives its + //text back when the focus goes somewhere else), Ctrl+Return + //hands it over at once, Escape throws the text away + auto te = new QPlainTextEdit(parent); + te->setObjectName(QStringLiteral("terminal_cable_target")); + te->setFrameStyle(QFrame::NoFrame); + te->setTabChangesFocus(true); + te->setToolTip(tr("One line per field: Installation, Location, Label (BMK).\n" + "Ctrl+Return gives the cell back, Escape throws the text away.")); + te->installEventFilter(const_cast(this)); + + return te; + } return QStyledItemDelegate::createEditor(parent, option, index); } +/** + * @brief TerminalStripModelDelegate::eventFilter + * The filter taken on by the Target cell editor: only the key which is + * handled here is the one giving the text back to the table. The filter + * of the base class is left aside for that cell on purpose, it is written + * for one line editors and would take Return away from the text. + * @param object the editor of the cell + * @param event the event sent to it + * @return true when the event is taken by this delegate + */ +bool TerminalStripModelDelegate::eventFilter(QObject *object, QEvent *event) +{ + if (object && object->objectName() == QLatin1String("terminal_cable_target")) + { + if (event && event->type() == QEvent::KeyPress) + { + const auto key_event = static_cast(event); + if ((key_event->key() == Qt::Key_Return || + key_event->key() == Qt::Key_Enter) && + key_event->modifiers().testFlag(Qt::ControlModifier)) + { + auto editor_ = qobject_cast(object); + emit commitData(editor_); + emit closeEditor(editor_, QAbstractItemDelegate::NoHint); + + return true; + } + } + return false; + } + + return QStyledItemDelegate::eventFilter(object, event); +} + void TerminalStripModelDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.isValid() && @@ -1214,6 +1267,17 @@ void TerminalStripModelDelegate::setEditorData(QWidget *editor, const QModelInde return; } + if (index.isValid() && + editor->objectName() == QLatin1String("terminal_cable_target")) + { + if (auto te = dynamic_cast(editor)) { + //The cell shows one line by field: the editor gets them + //back exactly as they are, nothing joined, nothing split + te->setPlainText(index.data(Qt::EditRole).toString()); + } + return; + } + QStyledItemDelegate::setEditorData(editor, index); } @@ -1254,6 +1318,14 @@ void TerminalStripModelDelegate::setModelData(QWidget *editor, QAbstractItemMode model->setData(index, qcb->currentData(), Qt::EditRole); } } + else if (editor->objectName() == QLatin1String("terminal_cable_target")) + { + if (auto te = dynamic_cast(editor)) { + //The lines are handed over as they were written, so a + //" / " inside a field is part of it and never a separator + model->setData(index, te->toPlainText(), Qt::EditRole); + } + } else { QStyledItemDelegate::setModelData(editor, model, index); } diff --git a/sources/TerminalStrip/ui/terminalstripmodel.h b/sources/TerminalStrip/ui/terminalstripmodel.h index ac0fc9e7f..366638f97 100644 --- a/sources/TerminalStrip/ui/terminalstripmodel.h +++ b/sources/TerminalStrip/ui/terminalstripmodel.h @@ -141,6 +141,7 @@ class TerminalStripModelDelegate : public QStyledItemDelegate QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + bool eventFilter(QObject *object, QEvent *event) override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; diff --git a/tests/qttest/tst_cabletarget.cpp b/tests/qttest/tst_cabletarget.cpp index 36095fd31..d12490dab 100644 --- a/tests/qttest/tst_cabletarget.cpp +++ b/tests/qttest/tst_cabletarget.cpp @@ -51,9 +51,10 @@ Also here: the box at the end of a cable is written at the font of the drawing -- the end zone the settings leave is what gives it the room, so three lines never end up squeezed into a tiny text. A - target typed in by hand is written the way the fields are shown in - the column, "Installation / Location / Label", and the box draws one - line per field, with no slash in it. Two cables of one strip end + target typed in by hand is written one line per field, Installation, + Location, Label, the way the column shows them, and only a line + break breaks it into fields: a "/" written inside a field is part of + it, the box draws one line holding it. Two cables of one strip end close to each other, and their boxes are wider than the room between them: the box standing in the way of the other is set lower, its stub drawn longer, and the strip reserves the room for it too. @@ -529,12 +530,14 @@ void tst_cabletarget::realisticStyleDrawsTheCable() } /** - A target typed in by hand is written the way the fields are shown in - the column, "Installation / Location / Label": the box under the - cable draws one line per field, with no slash in it. The picture of - a strip whose cable ends on three fields is therefore that much - deeper than the picture of the same strip with one single field -- - two lines of the font of the drawing. + A target typed in by hand is written one line per field, and only a + line break breaks it into fields: the box under the cable draws one + line per field, with no separator in it. The picture of a strip + whose cable ends on three fields is therefore that much deeper than + the picture of the same strip with one single field -- two lines of + the font of the drawing. A "/" written inside a field, an + installation named "Betrieb 1/2", keeps its line: the box is no + deeper than with one plain field. */ void tst_cabletarget::typedTargetIsDrawnInItsOwnLines() { @@ -604,9 +607,9 @@ void tst_cabletarget::typedTargetIsDrawnInItsOwnLines() QVERIFY(one_line >= 0); //Three fields: the box is two lines deeper, at the font of the - //drawing -- the slashes are only there to separate the fields in - //the column, the box never draws them - type_in(QStringLiteral("Boiler room / Cabinet 3 / -K3")); + //drawing -- the fields are written one under the other, the cell + //holding them only tells them apart by their line breaks + type_in(QStringLiteral("Boiler room\nCabinet 3\n-K3")); QCOMPARE(a_->cableTarget(), (QVector{QStringLiteral("Boiler room"), QStringLiteral("Cabinet 3"), QStringLiteral("-K3")})); @@ -614,6 +617,16 @@ void tst_cabletarget::typedTargetIsDrawnInItsOwnLines() const QFontMetricsF metrics{pattern_->font()}; QVERIFY2(three_lines - one_line > 1.5 * metrics.height(), "three fields must give a box two lines deeper than one field"); + + //A slash written inside a field is part of it: the text gives + //one single line, the box is back to the depth of one field and + //holds the slash where it was typed + type_in(QStringLiteral("Betrieb 1/2 / Keller")); + QCOMPARE(a_->cableTarget(), + QVector{QStringLiteral("Betrieb 1/2 / Keller")}); + const qreal one_slashed_field{lowest_row(render())}; + QVERIFY2(qAbs(one_slashed_field - one_line) < metrics.height() / 2, + "a slash inside a field must not split it into lines"); } /** @@ -688,7 +701,7 @@ void tst_cabletarget::boxesOfTwoCablesAreSetApart() second_data_.m_informations.addValue(RealTerminal::cableInfoKey(), QStringLiteral("X98")); second_data_.m_informations.addValue(RealTerminal::cableTargetInfoKey(), - QStringLiteral("Plant 9 / Cabinet 9 / -S9")); + QStringLiteral("Plant 9\nCabinet 9\n-S9")); second_->setElementData(second_data_); QCOMPARE(b_->cableTarget(), (QVector{QStringLiteral("Plant 9"), diff --git a/tests/qttest/tst_terminalcableauto.cpp b/tests/qttest/tst_terminalcableauto.cpp index 56c764590..e0dfd7dae 100644 --- a/tests/qttest/tst_terminalcableauto.cpp +++ b/tests/qttest/tst_terminalcableauto.cpp @@ -30,6 +30,12 @@ #include "qetproject.h" #include +#include +#include +#include +#include +#include +#include /** The Cable and Color / wire number columns of the terminal strip editor @@ -45,10 +51,11 @@ cable it is read only -- only the drawing decides it -- and with no cable landing anywhere it is there to be typed in, just like the Cable cell, the text then stored in the element and kept until a - cable works a target out again. A text typed in is written the way - the fields are shown in the column, "Installation / Location / - Label", so the box draws one line per field and not one long line - needing the slashes. + cable works a target out again. A text typed in is written one line + per field, Installation, Location, Label, the way the column shows + them: only a line break breaks it into fields, so a "/" written + inside a field (a name like "ASP/01") is part of that field and the + box draws one line containing it. fixtures/every_feature.qet has the strip X1 whose two terminals are wired to conductors on the folio, and no cable at all. @@ -358,11 +365,11 @@ private slots: QCOMPARE(model.data(model.index(0, TerminalStripModel::Cable)) .toString(), QStringLiteral("15W1")); - //The three fields, in the order the box draws them + //The three fields, one on its own line as the box draws them const QModelIndex target_cell = model.index(0, TerminalStripModel::CableTarget); QCOMPARE(model.data(target_cell).toString(), - QStringLiteral("Plant 7 / Cabinet 7 / -S7")); + QStringLiteral("Plant 7\nCabinet 7\n-S7")); QVERIFY(!(model.flags(target_cell) & Qt::ItemIsEditable)); QCOMPARE(model.headerData(TerminalStripModel::CableTarget, Qt::Horizontal).toString(), @@ -409,7 +416,7 @@ private slots: QVERIFY(model->data(cell).toString().isEmpty()); QVERIFY(model->flags(cell) & Qt::ItemIsEditable); - const QString typed{QStringLiteral("Boiler room / Cabinet 3 / -K3")}; + const QString typed{QStringLiteral("Boiler room\nCabinet 3\n-K3")}; QVERIFY(model->setData(cell, typed, Qt::EditRole)); QCOMPARE(model->data(cell, Qt::EditRole).toString(), typed); QCOMPARE(model->data(cell).toString(), typed); @@ -418,8 +425,8 @@ private slots: editor.apply(); QCOMPARE(element->elementData().m_informations .value(RealTerminal::cableTargetInfoKey()).toString(), typed); - //Three fields, written the way the column shows them: the box - //draws three lines, without the slashes + //Three lines, each field on its own: the box draws them one under + //the other, without any separator QCOMPARE(real_t->cableTarget(), (QVector{QStringLiteral("Boiler room"), QStringLiteral("Cabinet 3"), @@ -465,7 +472,7 @@ private slots: const QModelIndex cable_cell = model_cable.index( 0, TerminalStripModel::CableTarget); QCOMPARE(model_cable.data(cable_cell).toString(), - QStringLiteral("Plant 9 / Cabinet 9 / -S9")); + QStringLiteral("Plant 9\nCabinet 9\n-S9")); QVERIFY(!(model_cable.flags(cable_cell) & Qt::ItemIsEditable)); //And the automatic target was never written in the element QCOMPARE(element->elementData().m_informations @@ -486,10 +493,11 @@ private slots: QVERIFY(model_back.flags(back_cell) & Qt::ItemIsEditable); } - // A text typed in by hand is written the way the fields are shown in - // the column, "Installation / Location / Label": the box under the - // cable draws one line per field, without the slashes, exactly like a - // target worked out from the project. + // A text typed in by hand is written one line per field, and only a + // line break breaks it into fields: the box under the cable draws + // one line per field, exactly like a target worked out from the + // project, while a "/" written inside a field is part of it and + // gives one single line holding it. void typedTargetGivesOneLinePerField() { const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); @@ -509,10 +517,11 @@ private slots: QCOMPARE(real_t->cableTarget(), QVector{QStringLiteral("Boiler room")}); - //Three fields: three lines, and no slash anywhere in them + //Three fields, written one per line: three lines, and no + //separator anywhere in them typed = element->elementData(); typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(), - QStringLiteral("Plant 9 / Cabinet 9 / -S9")); + QStringLiteral("Plant 9\nCabinet 9\n-S9")); element->setElementData(typed); QCOMPARE(real_t->cableTarget(), (QVector{QStringLiteral("Plant 9"), @@ -524,7 +533,187 @@ private slots: //The column shows the text back the way it was written TerminalStripModel model(project.terminalStrip().first()); QCOMPARE(model.data(model.index(0, TerminalStripModel::CableTarget)) - .toString(), QStringLiteral("Plant 9 / Cabinet 9 / -S9")); + .toString(), QStringLiteral("Plant 9\nCabinet 9\n-S9")); + + //A slash inside a field, and one typed between two words of a + //field kept in one line: both stay part of the field, the cell + //shows them untouched and the box holds one single line each + typed = element->elementData(); + typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(), + QStringLiteral("Betrieb 1/2")); + element->setElementData(typed); + QCOMPARE(real_t->cableTarget(), + QVector{QStringLiteral("Betrieb 1/2")}); + + typed = element->elementData(); + typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(), + QStringLiteral("Betrieb 1/2 / Keller")); + element->setElementData(typed); + QCOMPARE(real_t->cableTarget(), + QVector{QStringLiteral("Betrieb 1/2 / Keller")}); + TerminalStripModel slashed(project.terminalStrip().first()); + QCOMPARE(slashed.data(slashed.index(0, TerminalStripModel::CableTarget)) + .toString(), QStringLiteral("Betrieb 1/2 / Keller")); + + //The line breaks written by hand are the fields again, whatever + //the fields themselves contain + typed = element->elementData(); + typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(), + QStringLiteral("Betrieb 1/2\nKeller\n-K4")); + element->setElementData(typed); + QCOMPARE(real_t->cableTarget(), + (QVector{QStringLiteral("Betrieb 1/2"), + QStringLiteral("Keller"), + QStringLiteral("-K4")})); + } + + // The Target cell is edited line by line: its editor holds one line + // per field and hands the text back exactly as the cell holds it. + // Return writes a new line (the cell is multi line), Ctrl+Return + // gives the cell back without waiting for the focus to go somewhere + // else, and the row of the table is as tall as the lines the cell + // shows, so none of them is cut off. + void targetCellIsEditedLineByLine() + { + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + TerminalStripEditor editor(&project); + editor.setCurrentStrip(project.terminalStrip().first()); + QTableView *table = editor.findChild( + QStringLiteral("m_table_widget")); + QVERIFY2(table && table->model(), "the editor has its table"); + QAbstractItemModel *model = table->model(); + QVERIFY(model->rowCount() > 1); + const QModelIndex cell = model->index(0, TerminalStripModel::CableTarget); + QVERIFY(model->flags(cell) & Qt::ItemIsEditable); + + const QString typed{QStringLiteral("Betrieb 1/2\nKeller\n-K4")}; + QVERIFY(model->setData(cell, typed, Qt::EditRole)); + QCOMPARE(model->data(cell).toString(), typed); + + //The editor of the table is the one taking a cell of several + //lines, and one of its own does the very same thing for the + //keys, without a view in between + QVERIFY(qobject_cast( + table->itemDelegate())); + TerminalStripModelDelegate delegate; + + QStyleOptionViewItem option; + option.rect = QRect(0, 0, qMax( + 50, table->columnWidth(TerminalStripModel::CableTarget)), 400); + option.font = table->font(); + option.fontMetrics = QFontMetrics(option.font); + + QWidget *cell_editor = delegate.createEditor(table, option, cell); + QVERIFY(cell_editor); + auto *text_edit = qobject_cast(cell_editor); + QVERIFY2(text_edit, "the Target cell takes a multi line editor"); + QCOMPARE(text_edit->objectName(), + QStringLiteral("terminal_cable_target")); + + //The editor gives the lines back exactly as the cell holds + //them: nothing joined, nothing split again + delegate.setEditorData(cell_editor, cell); + QCOMPARE(text_edit->toPlainText(), typed); + delegate.setModelData(cell_editor, model, cell); + QCOMPARE(model->data(cell).toString(), typed); + + //Return writes a new line in the cell, and hands nothing over + //yet: Ctrl+Return is the key giving the cell back + QSignalSpy committed(&delegate, &TerminalStripModelDelegate::commitData); + QSignalSpy closed(&delegate, &TerminalStripModelDelegate::closeEditor); + text_edit->moveCursor(QTextCursor::End); + + QKeyEvent return_key{QEvent::KeyPress, Qt::Key_Return, + Qt::NoModifier}; + QCoreApplication::sendEvent(cell_editor, &return_key); + QCOMPARE(text_edit->toPlainText(), typed + QLatin1Char('\n')); + QCOMPARE(committed.count(), 0); + QCOMPARE(closed.count(), 0); + + QKeyEvent ctrl_return_key{QEvent::KeyPress, Qt::Key_Return, + Qt::ControlModifier}; + QCoreApplication::sendEvent(cell_editor, &ctrl_return_key); + QCOMPARE(committed.count(), 1); + QCOMPARE(closed.count(), 1); + delete cell_editor; + } + + // The row of the table follows the lines its Target cell holds: the + // cell is never cut off, however many lines it takes, the row grows + // to hold them all and shrinks back afterwards, while the rows + // around keep the height they have always had. + void targetRowGrowsWithTheLinesOfItsCell() + { + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + TerminalStripEditor editor(&project); + editor.setCurrentStrip(project.terminalStrip().first()); + QTableView *table = editor.findChild( + QStringLiteral("m_table_widget")); + QVERIFY2(table && table->model(), "the editor has its table"); + QAbstractItemModel *model = table->model(); + QVERIFY(model->rowCount() > 1); + const QModelIndex cell = model->index(0, TerminalStripModel::CableTarget); + QVERIFY(model->flags(cell) & Qt::ItemIsEditable); + QVERIFY2(model->data(cell).toString().isEmpty(), + "no target to start with, the row is the usual one"); + + const QModelIndex other_cell = model->index( + 1, TerminalStripModel::CableTarget); + const int base_row = table->rowHeight(cell.row()); + const int base_other_row = table->rowHeight(other_cell.row()); + QVERIFY(base_row > 0); + + //Three lines in the cell: the row holds them all, at the + //font of the table -- the row was already taller than that + //for what else the terminal draws, so it may stay as it is + const QString typed{QStringLiteral("Betrieb 1/2\nKeller\n-K4")}; + QVERIFY(model->setData(cell, typed, Qt::EditRole)); + QCOMPARE(model->data(cell).toString(), typed); + + const auto heightOfLines = [table](int lines_) { + return lines_ * table->fontMetrics().height(); + }; + const int three_line_row = table->rowHeight(cell.row()); + QVERIFY2(three_line_row >= qMax(base_row, heightOfLines(3)), + "the row must be as tall as both the lines the cell " + "shows and what it held before, or something is cut off"); + QVERIFY2(table->rowHeight(other_cell.row()) >= base_other_row, + "the rows around are not pushed down"); + + //From here on the rows have been worked out once: what the + //other rows hold is the height they keep + const int other_row = table->rowHeight(other_cell.row()); + + //Many more lines than anything else in the row: the row grows + //to hold them, and the rows around stay where they are + const QString many{QStringLiteral( + "Plant 6\nCabinet 6\n-K6\nZone 6\nLevel 6\nEnd 6")}; + QVERIFY(model->setData(cell, many, Qt::EditRole)); + const int six_line_row = table->rowHeight(cell.row()); + QVERIFY2(six_line_row >= heightOfLines(6), + "the row must be as tall as the lines the cell shows, " + "or the fields below the first one are cut off"); + QVERIFY2(six_line_row > qMax(base_row, three_line_row), + "a cell which took six lines must take a taller row " + "than the one it had before"); + QVERIFY2(table->rowHeight(other_cell.row()) == other_row, + "the rows around keep their own height"); + + //Back to one single line: the row follows again + QVERIFY(model->setData(cell, QStringLiteral("Keller"), Qt::EditRole)); + QVERIFY2(table->rowHeight(cell.row()) < six_line_row, + "the row follows the cell back to a single line"); + QVERIFY2(table->rowHeight(cell.row()) + <= base_row + table->fontMetrics().height(), + "the row is back to the height it had before"); + QVERIFY2(table->rowHeight(cell.row()) >= table->fontMetrics().height(), + "the single line left is still shown"); } };