diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp index 282729d30..fc343ac7c 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp +++ b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp @@ -299,19 +299,45 @@ void drawCables(QPainter *painter, const QVector &cells, const Termin const qreal text_height{font_metrics.height()}; //Draw a mark (small oblique line) on a wire and the number of the wire next to it. - //The text is written from the bottom to the top, at the left of the wire. - const auto draw_wire_mark = [&](qreal x, qreal mark_y, const QString &text, bool text_above) + //The text is written from the bottom to the top, at the left of the wire, starting + //above the mark and running towards the terminal: the font shrinks while the text + //is longer than the room left between the mark and the terminal, and what still + //does not fit is clipped away, so a long number never runs into the drawing of + //the terminal. The room left is @a terminal_bottom the bottom of that terminal. + const auto draw_wire_mark = [&](qreal x, qreal mark_y, qreal terminal_bottom, const QString &text) { painter->drawLine(QPointF{x - 3, mark_y + 3}, QPointF{x + 3, mark_y - 3}); if (text.isEmpty()) { return; } - const qreal text_width{font_metrics.horizontalAdvance(text)}; + //Room left between the mark and the terminal + const qreal room{mark_y - 4 - (terminal_bottom + 2)}; + if (room <= 0) { + return; + } + + QFont text_font{painter->font()}; + if (text_font.pixelSize() <= 0 && text_font.pointSizeF() > 0) { + text_font.setPixelSize(qMax(4, qRound(text_font.pointSizeF() * 96.0 / 72.0))); + } + while (QFontMetricsF{text_font}.horizontalAdvance(text) > room && + text_font.pixelSize() > 4) { + text_font.setPixelSize(text_font.pixelSize() - 1); + } + + const QFontMetricsF fitted_metrics{text_font}; + const qreal fitted_width{fitted_metrics.horizontalAdvance(text)}; + const qreal fitted_height{fitted_metrics.height()}; painter->save(); - painter->translate(x - 1, text_above ? mark_y - 4 : mark_y + 4 + text_width); + painter->setFont(text_font); + //The clip keeps the text under the terminal even when a number is + //so long that the smallest font does not fit it any more + painter->setClipRect(QRectF{x - 1 - fitted_height, terminal_bottom + 2, + fitted_height + 1, room}); + painter->translate(x - 1, mark_y - 4); painter->rotate(270); - painter->drawText(QRectF{0, -text_height, text_width + 2, text_height}, + painter->drawText(QRectF{0, -fitted_height, fitted_width + 2, fitted_height}, Qt::AlignLeft | Qt::AlignVCenter, text); painter->restore(); @@ -359,7 +385,7 @@ void drawCables(QPainter *painter, const QVector &cells, const Termin for (const auto &wire : std::as_const(wires)) { painter->drawLine(QPointF{wire.x, wire.bottom}, QPointF{wire.x, bar_1_y}); - draw_wire_mark(wire.x, qMax(wire.bottom + 4, bar_1_y - 10), wire.wire, true); + draw_wire_mark(wire.x, qMax(wire.bottom + 4, bar_1_y - 10), wire.bottom, wire.wire); } painter->drawLine(QPointF{min_x, bar_1_y}, QPointF{max_x, bar_1_y}); diff --git a/tests/qttest/tst_cabletarget.cpp b/tests/qttest/tst_cabletarget.cpp index b87385aca..1ca83a347 100644 --- a/tests/qttest/tst_cabletarget.cpp +++ b/tests/qttest/tst_cabletarget.cpp @@ -43,6 +43,10 @@ The graphic draws them in a small box under the cable, the bundle staying together (RealTerminal::cableTarget()). + Also here: the number of a wire must stay in the room left between + its mark and the terminal -- it shrinks, and what does not fit is + clipped, so it never runs into the drawing of the terminal. + fixtures/every_feature.qet has the strip X1 (elements 891b70f2 and 4861e268) and the contact fc9cc920 wired to 891b70f2 by the conductor 8bd16d7a; the test gives the contact the Installation, @@ -159,6 +163,7 @@ private slots: void ownStripIsNoTarget(); void spareHasNoTarget(); void paintsWithTargetBox(); + void wireLabelStaysOutOfTheTerminal(); }; void tst_cabletarget::init() @@ -298,6 +303,74 @@ void tst_cabletarget::paintsWithTargetBox() QVERIFY(with_box != without_box); } +/** + The number of a wire is written next to its mark, in the room left + between the mark and the terminal: it shrinks to fit there, and what + still does not fit is clipped away, so a long number never runs into + the drawing of the terminal. The two pictures -- one with a number + long enough to reach into the terminal, one without any -- must be + the same above the bottom of the terminal, and differ below it (the + number is drawn somewhere). +*/ +void tst_cabletarget::wireLabelStaysOutOfTheTerminal() +{ + QVERIFY(wireCableToTheContact()); + + 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 QRectF bounds{drawer.boundingRect()}; + QVERIFY(!bounds.isEmpty()); + + const auto render = [&drawer, &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); + return image; + }; + + //A number much longer than the room left over the mark + Element *terminal_ = element( + QStringLiteral("{891b70f2-2822-41c3-9f94-28efb448b5c4}")); + QVERIFY(terminal_); + ElementData terminal_data_{terminal_->elementData()}; + terminal_data_.m_informations.addValue( + RealTerminal::cableWireInfoKey(), + QStringLiteral("gnge-gnge-gnge-gnge-gnge")); + terminal_->setElementData(terminal_data_); + const QImage with_label{render()}; + + //The same strip, the wire carries no number at all + terminal_data_.m_informations.addValue(RealTerminal::cableWireInfoKey(), + QString()); + terminal_->setElementData(terminal_data_); + const QImage without_label{render()}; + + //The number is drawn: the two pictures differ + QVERIFY(with_label != without_label); + + //The terminals of this strip all stand on the last level of the + //pattern, so their bottom is the same rectangle + const qreal terminal_bottom{pattern_->m_terminal_rect.at(3).bottom()}; + const auto rows_to_check{qMin(qRound(terminal_bottom - bounds.topLeft().y() + 2), + with_label.height())}; + for (auto y = 0 ; y < rows_to_check ; ++y) + { + for (auto x = 0 ; x < with_label.width() ; ++x) + { + QCOMPARE(with_label.pixel(x, y), without_label.pixel(x, y)); + } + } +} + int main(int argc, char **argv) { QStandardPaths::setTestModeEnabled(true);