From 5baf5bec603ecff510160414e8356651bcc2f3dd Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Tue, 20 Oct 2026 16:22:15 +0200 Subject: [PATCH] Set a target box lower when another one is in its way Two cables of one strip end close to each other, and their boxes are wider than the room between the two cables: a long Installation is enough for that, and the two boxes ended up written the one over the other -- a picture nobody can read, and no way to tell which box belongs to which cable. The boxes of a strip are now all measured before any of them is drawn, and placed from the left to the right: a box which would reach into one already placed is set just low enough to stand clear of it, its stub drawn longer. Nothing is cut down to fit -- the text keeps its font and the box keeps its width, it is the box which moves. The strip reserves the room for the lower box too, with the same rule and the very cells the drawing works on (Helpers::cableBoxesExtraDepth (), both styles now build one list of cells for the painting and for the reserved depth), so a lower box is never cut off by the drawing area. tst_cabletarget paints a strip with one cable ending on a target and the same strip with a second cable ending on one: the second picture draws a whole box lower, and the strip is deeper by exactly the room that lower box needs. --- .../realisticterminalstripdrawer.cpp | 84 +++-- .../GraphicsItem/terminalstripdrawer.cpp | 329 +++++++++++++++--- .../GraphicsItem/terminalstripdrawerhelpers.h | 7 + tests/qttest/tst_cabletarget.cpp | 105 +++++- 4 files changed, 453 insertions(+), 72 deletions(-) diff --git a/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.cpp b/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.cpp index b1ee952e6..26e68688c 100644 --- a/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.cpp +++ b/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.cpp @@ -542,6 +542,57 @@ bool haveCable(const AbstractTerminalStripInterface *strip) return false; } + /** + * @brief cableCells + * The cells the cables are drawn from : one per terminal of the strip + * carrying a cable, spread over the width of its body, all of them + * leaving the strip at its bottom edge. The paint and the depth the + * strip reserves under it both work on this list, so a box set lower + * than the others is never cut off by the drawing area. + */ + QVector cableCells(const AbstractTerminalStripInterface &strip, + const TerminalStripLayoutPattern &pattern) + { + const qreal s_{pattern.m_realistic_px_per_mm}; + const qreal length_{pattern.m_realistic_length_mm * s_}; + + //Left end stop and strip name : the terminals start after them + qreal x_{endStopWidthMm(pattern) * s_}; + if (pattern.m_realistic_group_marker) { + x_ += group_marker_width_mm * s_; + } + + QVector cells; + for (const auto &g_ : geometry(&strip)) + { + const qreal w_{g_.width_mm * s_}; + const int levels_{static_cast(g_.reals.size())}; + + //One wire exit by level, spread over the width of the body + for (int k = 0 ; k < levels_ ; ++k) + { + const auto &real_t{g_.reals.at(k)}; + if (!real_t || real_t->cable().isEmpty()) { + continue; + } + Helpers::CableCell cell_; + cell_.x = x_ + w_ * (k + 0.5) / levels_; + cell_.bottom = length_; + cell_.cable = real_t->cable(); + cell_.wire = real_t->cableWire(); + cell_.shield = real_t->isShield(); + cell_.terminal = real_t; + cells.append(cell_); + } + + x_ += w_; + if (g_.separator_after) { + x_ += separator_width_mm * s_; + } + } + return cells; + } + } //End anonymous namespace QRectF boundingRect(const AbstractTerminalStripInterface *strip, @@ -562,11 +613,16 @@ QRectF boundingRect(const AbstractTerminalStripInterface *strip, qreal extra_bottom_{0}; if (pattern.m_cable_wire_length > 0 && haveCable(strip)) { - //4 is the diameter of the circle at the end of the wires + //4 is the diameter of the circle at the end of the wires. + //A box standing in the way of another one is drawn lower than + //the end zone, and the room for it is reserved too (same rule, + //same cells as the drawing) or it is cut off extra_bottom_ = pattern.m_cable_wire_length + std::max(0, pattern.m_cable_length) + std::max(0, pattern.m_cable_end_length) - + 4; + + 4 + + Helpers::cableBoxesExtraDepth(cableCells(*strip, pattern), + pattern); } return QRectF{0, 0, width_mm_ * s_, length_ + extra_bottom_}; } @@ -628,7 +684,11 @@ void paint(QPainter *painter, } QHash> bridges_; - QVector cable_cells_; + //The cells the cables are drawn from, worked out once for the whole + //strip: the depth the strip reserves under it is built on the same + //list (boundingRect()), so a box set lower than another one is + //never cut off + QVector cable_cells_{cableCells(strip, pattern)}; for (const auto &g_ : geometry(&strip)) { @@ -637,24 +697,6 @@ void paint(QPainter *painter, const auto first_{g_.reals.first()}; const int levels_{static_cast(g_.reals.size())}; - //Cables (hoses) of this terminal : one wire exit by level, spread over - //the width of the body, all of them leaving the strip at its bottom edge - for (int k = 0 ; k < levels_ ; ++k) - { - const auto &real_t{g_.reals.at(k)}; - if (!real_t || real_t->cable().isEmpty()) { - continue; - } - Helpers::CableCell cell_; - cell_.x = x_ + w_ * (k + 0.5) / levels_; - cell_.bottom = length_; - cell_.cable = real_t->cable(); - cell_.wire = real_t->cableWire(); - cell_.shield = real_t->isShield(); - cell_.terminal = real_t; - cable_cells_.append(cell_); - } - //A terminal chosen in the catalog is drawn with its own clamp, so that a strip //mixing screw and spring terminals is drawn as it is. The spring-cage clamps //(opened with a screwdriver) are drawn like the push-in ones. diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp index 52cd984e0..06877d9bb 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp +++ b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp @@ -263,6 +263,168 @@ void drawConnections(QPainter *painter, const QRectF &terminal_rect, qreal lengt } } +/** + One box at the end of a cable : which terminals the cable has and + where the box goes. The boxes of a strip are all measured before any + of them is drawn, so a box reaching into another one can be set + lower than it instead of being written over it. +*/ +struct TargetBox +{ + int first{-1}; /// lines; /// other.cable_x - other.width / 2 + && top < other.bottom() && bottom() > other.top; + } +}; + +/** + * @brief boxFont + * The font of a box naming where a cable goes : the font of the drawing, + * shrunk only while the lines of the target do not fit the height left + * for the box. The width never shrinks anything -- a line too long for + * the cross bar gives a wider box, it never gives a smaller text. + * @param room : the height left for the box + * @return the font the lines are written with + */ +QFont boxFont(const QFont &font, const QVector &lines, qreal room) +{ + QFont box_font{font}; + if (box_font.pixelSize() <= 0 && box_font.pointSizeF() > 0) { + box_font.setPixelSize(qMax(4, qRound(box_font.pointSizeF() * 96.0 / 72.0))); + } + const auto fits_the_height = [&lines, room](const QFont &font_) { + return QFontMetricsF{font_}.height() * lines.size() + 4 <= room; + }; + while (!fits_the_height(box_font) && box_font.pixelSize() > 4) { + box_font.setPixelSize(box_font.pixelSize() - 1); + } + return box_font; +} + +/** + * @brief targetBoxes + * Where the boxes naming where the cables go stand, and how big they + * are. Every box is measured first -- the lines of the target at the + * font of the drawing, a width of the widest line or of the cross bar + * the cable ends on, whichever is wider -- and they are then placed + * from the left to the right : a box which would reach into one + * already placed is set just low enough to stand clear of it, its stub + * drawn longer. Two cables of one strip therefore never write their + * boxes the one over the other, and no text is ever cut down to fit. + * @return one entry per cable which has something to name at its end + */ +QVector targetBoxes(const QVector &cells, + const TerminalStripLayoutPattern &pattern, + const QFont &font) +{ + QVector boxes; + const qreal wire_length{pattern.m_cable_wire_length}; + if (wire_length <= 0) { + return boxes; + } + const qreal cable_length{qMax(0, pattern.m_cable_length)}; + const qreal end_length{qMax(0, pattern.m_cable_end_length)}; + const qreal stub{qMin(6, end_length)}; + if (end_length - stub <= 4) { + return boxes; + } + constexpr qreal box_gap{2}; + + auto first{0}; + while (first < cells.size()) + { + if (cells.at(first).cable.isEmpty()) { + ++first; + continue; + } + + //Find the last terminal of the cable, the same groups the + //drawing makes + auto last{first}; + while (last + 1 < cells.size() && + cells.at(last + 1).cable == cells.at(first).cable) { + ++last; + } + + QVector wires, shields; + qreal max_bottom{cells.at(first).bottom}; + for (auto i = first ; i <= last ; ++i) + { + max_bottom = qMax(max_bottom, cells.at(i).bottom); + if (cells.at(i).shield) { + shields << i; + } else { + wires << i; + } + } + //A cable with only a shield is drawn like a cable with only one wire + if (wires.isEmpty()) { + wires = shields; + } + + const auto &first_wire{cells.at(wires.first())}; + TargetBox box; + box.first = first; + box.bar_2_y = max_bottom + wire_length + cable_length; + box.min_x = first_wire.x; + box.max_x = cells.at(wires.last()).x; + box.cable_x = (box.min_x + box.max_x) / 2; + box.lines = first_wire.terminal ? first_wire.terminal->cableTarget() + : QVector(); + + if (!box.lines.isEmpty()) + { + const QFontMetricsF metrics{boxFont(font, box.lines, end_length - stub)}; + box.width = qMax(box.max_x - box.min_x, 16); + for (const QString &line_ : std::as_const(box.lines)) { + box.width = qMax(box.width, metrics.horizontalAdvance(line_) + 6); + } + box.height = metrics.height() * box.lines.size() + 4; + box.top = box.bar_2_y + stub; + boxes << box; + } + + first = last + 1; + } + + //Place them : a box reaching into one already placed goes just + //under it, and the boxes set aside settle down again with it + for (auto i = 0 ; i < boxes.size() ; ++i) + { + bool settled{false}; + for (auto guard = 0 ; !settled && guard < boxes.size() + 2 ; ++guard) + { + settled = true; + for (auto j = 0 ; j < i ; ++j) + { + if (boxes.at(i).reachesInto(boxes.at(j)) + && boxes.at(i).top < boxes.at(j).bottom() + box_gap) + { + boxes[i].top = boxes.at(j).bottom() + box_gap; + settled = false; + } + } + } + } + return boxes; +} + /** * @brief drawCables * Draw the cables under the terminals. Consecutive terminals who have the same cable name @@ -333,6 +495,15 @@ void drawCables(QPainter *painter, const QVector &cells, const Termin painter->restore(); }; + //Where every box stands, worked out for the whole strip before + //any of them is drawn: a box reaching into another one is set + //lower than it, never written over it + QHash box_of; + const auto boxes{targetBoxes(cells, pattern, painter->font())}; + for (const TargetBox &box_ : boxes) { + box_of.insert(box_.first, box_); + } + auto first{0}; while (first < cells.size()) { @@ -387,52 +558,42 @@ void drawCables(QPainter *painter, const QVector &cells, const Termin //Location and Label (BMK) of the piece it lands on, the same //three fields the cable list shows. The box spans the cross bar //and never shrinks its text down to fit: when its lines are - //longer than the cross bar, the box is the one which grows. + //longer than the cross bar, the box is the one which grows. A + //box standing in the way of another one is set lower instead, + //its stub drawn longer, so no box is ever written over another. painter->drawLine(QPointF{min_x, bar_2_y}, QPointF{max_x, bar_2_y}); const qreal stub{qMin(6, end_length)}; - painter->drawLine(QPointF{cable_x, bar_2_y}, QPointF{cable_x, bar_2_y + stub}); - - const auto target_lines{wires.first().terminal ? wires.first().terminal->cableTarget() - : QVector()}; - if (!target_lines.isEmpty() && end_length - stub > 4) + const TargetBox box{box_of.value(first)}; + if (box.lines.isEmpty()) { + painter->drawLine(QPointF{cable_x, bar_2_y}, + QPointF{cable_x, bar_2_y + stub}); + } + else + { + //The stub goes down to the box, which stands lower than the + //end zone when another box of this strip was in its way + painter->drawLine(QPointF{cable_x, bar_2_y}, + QPointF{cable_x, box.top}); + //The font of the drawing, shrunk only while the lines do not //fit the height the settings leave for the box - const qreal room{end_length - stub}; - QFont box_font{painter->font()}; - if (box_font.pixelSize() <= 0 && box_font.pointSizeF() > 0) { - box_font.setPixelSize(qMax(4, qRound(box_font.pointSizeF() * 96.0 / 72.0))); - } - const auto fits_the_height = [&target_lines, room](const QFont &font_) { - return QFontMetricsF{font_}.height() * target_lines.size() + 4 <= room; - }; - while (!fits_the_height(box_font) && box_font.pixelSize() > 4) { - box_font.setPixelSize(box_font.pixelSize() - 1); - } - - //As wide as the cross bar the cable ends on -- the strip of - //one single terminal has no width to take, so a small floor - //keeps the box drawn at all -- and wider still when the lines - //of the target are longer than that + const QFont box_font{boxFont(painter->font(), box.lines, + end_length - stub)}; const QFontMetricsF box_metrics{box_font}; - qreal box_width{qMax(max_x - min_x, 16)}; - for (const QString &line_ : target_lines) { - box_width = qMax(box_width, box_metrics.horizontalAdvance(line_) + 6); - } - - const qreal box_height{box_metrics.height() * target_lines.size() + 4}; - const QRectF box{cable_x - box_width / 2, bar_2_y + stub, box_width, box_height}; + const QRectF box_rect{cable_x - box.width / 2, box.top, + box.width, box.height}; painter->save(); painter->setFont(box_font); - painter->drawRect(box); - for (auto i = 0 ; i < target_lines.size() ; ++i) + painter->drawRect(box_rect); + for (auto i = 0 ; i < box.lines.size() ; ++i) { - painter->drawText(QRectF{box.left() + 3, - box.top() + 2 + i * box_metrics.height(), - box.width() - 6, box_metrics.height()}, + painter->drawText(QRectF{box_rect.left() + 3, + box_rect.top() + 2 + i * box_metrics.height(), + box_rect.width() - 6, box_metrics.height()}, Qt::AlignCenter, - target_lines.at(i)); + box.lines.at(i)); } painter->restore(); } @@ -505,6 +666,53 @@ QRectF terminalRect(const TerminalStripLayoutPattern &pattern, return rect_; } +/** + * @brief cableCells + * The cells the cables are drawn from : one per real terminal of the + * strip, in the order they stand. The paint loop and the depth the + * strip reserves under its terminals both work on this list, so a box + * set lower than the others is never cut off by the drawing area. + * @param strip + * @param pattern + * @return the cells to hand over to Helpers::drawCables() + */ +QVector cableCells(const AbstractTerminalStripInterface &strip, + const TerminalStripLayoutPattern &pattern) +{ + QVector cells; + qreal x_offset{pattern.m_header_rect.width() + pattern.m_spacer_rect.width()}; + for (const auto &physical_t : strip.physicalTerminal()) + { + const QVector> real_terminal_vector{physical_t->realTerminals()}; + const auto real_t_count{real_terminal_vector.size()}; + const auto offset_{4 - real_t_count}; + + for (auto i=0 ; i= 4) { + break; + } + + const auto terminal_rect{terminalRect(pattern, index_, + real_terminal_vector[i])}; + CableCell cell; + cell.x = x_offset + terminal_rect.width()/2; + cell.bottom = terminal_rect.y() + terminal_rect.height(); + if (real_terminal_vector[i]) + { + cell.cable = real_terminal_vector[i]->cable(); + cell.wire = real_terminal_vector[i]->cableWire(); + cell.shield = real_terminal_vector[i]->isShield(); + cell.terminal = real_terminal_vector[i]; + } + cells.append(cell); + x_offset += terminal_rect.width(); + } + } + return cells; +} + /** * @brief The TerminalColors struct * Colors used to fill the rectangle of a terminal. @@ -936,6 +1144,22 @@ void Helpers::drawCables(QPainter *painter, const QVector &cells, ::TerminalStripDrawer::drawCables(painter, cells, pattern); } +qreal Helpers::cableBoxesExtraDepth(const QVector &cells, + const TerminalStripLayoutPattern &pattern) +{ + const qreal end_length{qMax(0, pattern.m_cable_end_length)}; + qreal extra{0}; + for (const auto &box_ : ::TerminalStripDrawer::targetBoxes(cells, pattern, + pattern.font())) + { + //How much deeper than the end zone the strip reserves this box + //stands -- the zone is the room of the box, a box set lower + //than the others needs room the strip must reserve too + extra = qMax(extra, box_.bottom() - (box_.bar_2_y + end_length)); + } + return qMax(0, extra); +} + /** * @brief TerminalStripDrawer::paint * @param painter @@ -1041,7 +1265,12 @@ void TerminalStripDrawer::paint(QPainter *painter) QRectF xref_rect; QHash> bridges_anchor_points; - QVector cable_cells; + //The cells the cables are drawn from, worked out once for the + //whole strip: the depth the strip reserves under its terminals + //is built on the same list (boundingRect()), so a box set lower + //than the others is never cut off + QVector cable_cells{cableCells(*m_strip, *m_pattern)}; + auto cell_index{0}; m_hovered_xref = hoverTerminal{}; @@ -1099,19 +1328,13 @@ void TerminalStripDrawer::paint(QPainter *painter) terminal_rect = terminalRect(*m_pattern, index_, real_terminal_vector[i]); - //Cable (hose) of this terminal - CableCell cable_cell; - if (real_terminal_vector[i]) - { - cable_cell.x = x_offset + terminal_rect.width()/2; - cable_cell.bottom = terminal_rect.y() + terminal_rect.height(); - cable_cell.cable = real_terminal_vector[i]->cable(); - cable_cell.wire = real_terminal_vector[i]->cableWire(); - cable_cell.shield = real_terminal_vector[i]->isShield(); - cable_cell.terminal = real_terminal_vector[i]; - } + //Cable (hose) of this terminal, and where this + //terminal stands: the very cell the depth of the strip + //was worked out from, so the drawing and the room it + //is given can never disagree + const CableCell &cable_cell{cable_cells.at(cell_index++)}; + x_offset = cable_cell.x - terminal_rect.width()/2; const bool have_cable{m_pattern->m_cable_wire_length > 0 && !cable_cell.cable.isEmpty()}; - cable_cells.append(cable_cell); //Draw terminal rect, filled with the color of the terminal (if any) const auto colors_{terminalColors(real_terminal_vector[i])}; const auto fill_color_{colors_.fill}; @@ -1412,12 +1635,18 @@ QRectF TerminalStripDrawer::boundingRect() const if (have_cable) { - //4 is the diameter of the circle at the end of the wires + //4 is the diameter of the circle at the end of the wires. + //A box standing in the way of another one is drawn lower + //than the end zone, and the room for it is reserved too + //(same rule, same cells as the drawing) or it is cut off extra_bottom = std::max(extra_bottom, m_pattern->m_cable_wire_length + std::max(0, m_pattern->m_cable_length) + std::max(0, m_pattern->m_cable_end_length) - + 4); + + 4 + + Helpers::cableBoxesExtraDepth( + cableCells(*m_strip, *m_pattern), + *m_pattern)); } } diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripdrawerhelpers.h b/sources/TerminalStrip/GraphicsItem/terminalstripdrawerhelpers.h index 494262358..d39bc76b6 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripdrawerhelpers.h +++ b/sources/TerminalStrip/GraphicsItem/terminalstripdrawerhelpers.h @@ -58,6 +58,13 @@ namespace TerminalStripDrawer //in terminalstripdrawer.cpp for the whole drawing) void drawCables(QPainter *painter, const QVector &cells, const TerminalStripLayoutPattern &pattern); + //How much deeper than the end zone (m_cable_end_length) the boxes of + //the cables need: a box reaching into another one is set lower than + //the others, and the strip must reserve the room for it or the lower + //box is cut off. The same rule the drawing uses, worked out of the + //same cells, so the two can never disagree. + qreal cableBoxesExtraDepth(const QVector &cells, + const TerminalStripLayoutPattern &pattern); } } diff --git a/tests/qttest/tst_cabletarget.cpp b/tests/qttest/tst_cabletarget.cpp index 1504b4761..36095fd31 100644 --- a/tests/qttest/tst_cabletarget.cpp +++ b/tests/qttest/tst_cabletarget.cpp @@ -53,7 +53,10 @@ 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. + line per field, with no slash in 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. Both drawing styles are covered: the schematic one and the realistic one (the strip seen from above), which paint the same @@ -178,6 +181,7 @@ private slots: void wireLabelStaysOutOfTheTerminal(); void targetBoxKeepsTheDrawingFont(); void typedTargetIsDrawnInItsOwnLines(); + void boxesOfTwoCablesAreSetApart(); void realisticStyleDrawsTheCable(); }; @@ -612,6 +616,105 @@ void tst_cabletarget::typedTargetIsDrawnInItsOwnLines() "three fields must give a box two lines deeper than one field"); } +/** + Two cables of one strip end close to each other, and their boxes are + wider than the room between the two cables: the box standing in the + way of the other is set lower than it, its stub drawn longer -- the + two boxes are never written the one over the other, and the text is + never cut down to fit. The strip reserves the room for the lower box + too, or the drawing would be cut off there. +*/ +void tst_cabletarget::boxesOfTwoCablesAreSetApart() +{ + QVERIFY(wireCableToTheContact()); + + //The second cable of the strip : its wires only join the strip's + //own terminals, so it names nothing and takes what is typed in + Cable *cable_ = m_project->newCable(); + QVERIFY(cable_); + Conductor *inside_ = conductor( + QStringLiteral("{584b452e-a885-491a-98f1-fd348610b5e0}")); + QVERIFY(inside_); + claimCable(inside_, cable_->uuid()); + + const auto b_ = terminalInStrip( + QStringLiteral("{4861e268-d344-413e-a86f-034ea5ada16c}")); + QVERIFY(b_); + Element *second_ = b_->element(); + QVERIFY(second_); + + auto pattern_ = QSharedPointer{new TerminalStripLayoutPattern}; + const auto strips_ = m_project->terminalStrip(); + QVERIFY(!strips_.isEmpty()); + TerminalStripDrawer::TerminalStripDrawer drawer{ + QSharedPointer{ + new TerminalStripDrawer::TrueTerminalStrip{strips_.first()}}, + pattern_}; + + const auto render = [&drawer](const QRectF &bounds) { + QImage image{bounds.size().toSize() + QSize{4, 4}, + QImage::Format_ARGB32_Premultiplied}; + image.fill(Qt::transparent); + QPainter painter{&image}; + painter.translate(-bounds.topLeft() + QPointF{2, 2}); + drawer.paint(&painter); + painter.end(); + return image; + }; + const auto lowest_row = [](const QImage &image_) { + qreal lowest{-1}; + for (auto y = 0 ; y < image_.height() ; ++y) + { + for (auto x = 0 ; x < image_.width() ; ++x) + { + if (image_.pixel(x, y) != qRgba(0, 0, 0, 0)) { + lowest = y; + break; + } + } + } + return lowest; + }; + + //One cable with a target : one box, in the end zone + const QRectF one_bounds{drawer.boundingRect()}; + const qreal one_row{lowest_row(render(one_bounds))}; + QVERIFY(one_row >= 0); + + //The second cable gets a target of its own : its box is wider + //than the distance between the two cables and would be written + //over the first one + ElementData second_data_{second_->elementData()}; + second_data_.m_informations.addValue(RealTerminal::cableInfoKey(), + QStringLiteral("X98")); + second_data_.m_informations.addValue(RealTerminal::cableTargetInfoKey(), + QStringLiteral("Plant 9 / Cabinet 9 / -S9")); + second_->setElementData(second_data_); + QCOMPARE(b_->cableTarget(), + (QVector{QStringLiteral("Plant 9"), + QStringLiteral("Cabinet 9"), + QStringLiteral("-S9")})); + + const QRectF two_bounds{drawer.boundingRect()}; + const qreal two_row{lowest_row(render(two_bounds))}; + + //The box in the way is set lower : the strip draws a whole box + //deeper than it did with one single box -- the second box stands + //under the first one, one box height plus the gap between them + const QFontMetricsF metrics{pattern_->font()}; + const qreal box_height{metrics.height() * 3 + 4}; + QVERIFY2(two_row - one_row > box_height, + "a box reaching into another one must be set lower, not " + "written over it"); + + //And the strip is deep enough for it, or the lower box would be + //cut off by the drawing area + QVERIFY2(two_bounds.height() > one_bounds.height(), + "the strip reserves no room for the box it sets lower"); + QVERIFY2(two_bounds.bottom() - one_bounds.bottom() > 0.5 * box_height, + "the room reserved for the lower box is too small"); +} + int main(int argc, char **argv) { QStandardPaths::setTestModeEnabled(true);