From fed204eb788f529285c02a5e58cab1beda15ea58 Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Sat, 17 Oct 2026 20:41:25 +0200 Subject: [PATCH] Terminal strip: clickable terminals in the realistic styles The realistic styles don't write the xrefs, so each terminal becomes a clickable zone, like the xref text of the schematic style: - double-click on a terminal goes to its element on the folio - the terminal under the mouse is outlined in blue, and a tool tip shows its label and its xref - the zones are exported as clickable links in the PDF A terminal with one level is clickable on its whole body; a terminal with levels is clickable on the rows of wire entries of each level. Only the terminals with an xref (element placed on a folio) are clickable. The zones use the same physical / real indexes as the schematic style, so the double-click and the PDF export need no other change. --- .../realisticterminalstripdrawer.cpp | 62 ++++++++++++++++++- .../realisticterminalstripdrawer.h | 9 ++- .../GraphicsItem/terminalstripdrawer.cpp | 33 +++++++++- .../GraphicsItem/terminalstripdrawer.h | 1 + .../GraphicsItem/terminalstripitem.cpp | 31 ++++++++++ .../GraphicsItem/terminalstripitem.h | 1 + 6 files changed, 133 insertions(+), 4 deletions(-) diff --git a/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.cpp b/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.cpp index 431b6c1e2..37dfa10de 100644 --- a/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.cpp +++ b/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.cpp @@ -66,6 +66,11 @@ struct PhysicalGeometry qreal width_mm{0}; bool separator_after{false}; //A separator plate is mounted after this terminal QVector> reals; + //Index of the physical terminal in the strip, and of each real terminal + //in the physical terminal : the same indexes than the schematic style, + //used to find the element of a clicked terminal. + int physical_index{-1}; + QVector real_indexes; }; /** @@ -80,18 +85,24 @@ QVector geometry(const AbstractTerminalStripInterface *strip) return geometry_; } + int physical_index_{-1}; for (const auto &physical_t : strip->physicalTerminal()) { + ++physical_index_; if (!physical_t) { continue; } PhysicalGeometry g_; + g_.physical_index = physical_index_; + int real_index_{-1}; for (const auto &real_t : physical_t->realTerminals()) { + ++real_index_; if (!real_t) { continue; } g_.reals.append(real_t); + g_.real_indexes.append(real_index_); g_.width_mm = std::max(g_.width_mm, real_t->widthMm()); g_.separator_after = g_.separator_after || real_t->hasSeparatorAfter(); if (g_.reals.size() == 4) { //Same limit than the schematic style @@ -384,7 +395,9 @@ void paint(QPainter *painter, const AbstractTerminalStripInterface &strip, const TerminalStripLayoutPattern &pattern, TerminalStripLayoutPattern::DrawStyle style, - bool preview_draw) + bool preview_draw, + const QPointF &hover_pos, + QVector *xref_rects) { const bool screw_{style == TerminalStripLayoutPattern::DrawStyle::RealisticScrew}; const qreal s_{pattern.m_realistic_px_per_mm}; @@ -509,6 +522,53 @@ void paint(QPainter *painter, drawVerticalText(painter, tag_, numbers_.join(QStringLiteral(" / ")), sectional_ ? QColor(Qt::white) : QColor(Qt::black)); + //Clickable zones : the whole terminal for a terminal with one level, + //the rows of wire entries of the level (top and bottom) for a terminal with levels. + //Only the terminals with an xref (element placed on a folio) are clickable. + QVector terminal_rects_; + for (int k = 0 ; k < levels_ ; ++k) + { + if (g_.reals.at(k)->xref().isEmpty()) { + continue; + } + const int physical_{g_.physical_index}; + const int real_{g_.real_indexes.at(k)}; + if (levels_ == 1) { + terminal_rects_.append({body_, physical_, real_}); + } else { + const qreal top_h_{length_ * (band_top - entries_top) / levels_}; + terminal_rects_.append({QRectF{x_, length_ * entries_top + k * top_h_, w_, top_h_}, physical_, real_}); + terminal_rects_.append({QRectF{x_, length_ * (1 - entries_top) - (k + 1) * top_h_, w_, top_h_}, physical_, real_}); + } + } + if (xref_rects) { + *xref_rects << terminal_rects_; + } + + //Highlight the terminal under the mouse, like the xref of the schematic style + if (!hover_pos.isNull()) + { + int hovered_real_{-1}; + for (const auto &r_ : std::as_const(terminal_rects_)) { + if (r_.rect.contains(hover_pos)) { + hovered_real_ = r_.real; + break; + } + } + if (hovered_real_ >= 0) + { + painter->save(); + painter->setPen(QPen(QColor(0, 90, 255), std::max(1.0, 0.4 * s_))); + painter->setBrush(Qt::NoBrush); + for (const auto &r_ : std::as_const(terminal_rects_)) { + if (r_.real == hovered_real_) { + painter->drawRect(r_.rect.adjusted(0.5, 0.5, -0.5, -0.5)); + } + } + painter->restore(); + } + } + //Bridge points : one channel by level for (int k = 0 ; k < levels_ ; ++k) { diff --git a/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.h b/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.h index 97afa0206..440195eaf 100644 --- a/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.h +++ b/sources/TerminalStrip/GraphicsItem/realisticterminalstripdrawer.h @@ -32,6 +32,11 @@ class QPainter; * The width of each terminal is its real width (mm) multiplied by the scale of the layout. * A terminal with several levels is drawn with one row of wire entries by level, * at the top and at the bottom. + * + * paint() fill @a xref_rects with the zone of each terminal which have an xref, + * with the same indexes than the schematic style : the item use them to go to the + * element on double click, to show the xref in a tool tip and to make clickable + * links in an exported PDF. The terminal under @a hover_pos is outlined in blue. */ namespace TerminalStripDrawer { @@ -43,7 +48,9 @@ namespace TerminalStripDrawer const AbstractTerminalStripInterface &strip, const TerminalStripLayoutPattern &pattern, TerminalStripLayoutPattern::DrawStyle style, - bool preview_draw = false); + bool preview_draw = false, + const QPointF &hover_pos = QPointF(), + QVector *xref_rects = nullptr); } } diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp index cefecbdd2..f348bee7c 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp +++ b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.cpp @@ -873,11 +873,24 @@ void TerminalStripDrawer::paint(QPainter *painter) { if (m_strip && m_pattern && m_draw_style != TerminalStripLayoutPattern::DrawStyle::Schematic) { - //No xref in the realistic styles + //The realistic styles don't write the xrefs, but each terminal + //is a clickable zone, like the xref text of the schematic style. m_united_xref_text_rect = QRectF(); m_xref_rects.clear(); m_hovered_xref = hoverTerminal{}; - Realistic::paint(painter, *m_strip, *m_pattern, m_draw_style, m_preview_draw); + Realistic::paint(painter, *m_strip, *m_pattern, m_draw_style, m_preview_draw, + m_mouse_hover_pos, &m_xref_rects); + for (const auto &xref_rect : std::as_const(m_xref_rects)) + { + m_united_xref_text_rect = m_united_xref_text_rect.isNull() ? + xref_rect.rect : + m_united_xref_text_rect.united(xref_rect.rect); + if (m_hovered_xref.physical < 0 && !m_mouse_hover_pos.isNull() + && xref_rect.rect.contains(m_mouse_hover_pos)) { + m_hovered_xref.physical = xref_rect.physical; + m_hovered_xref.real = xref_rect.real; + } + } return; } @@ -1369,6 +1382,22 @@ TerminalStripLayoutPattern::DrawStyle TerminalStripDrawer::drawStyle() const { return m_draw_style; } +/** + * @brief TerminalStripDrawer::xrefAt + * @param pos : position in the coordinates of the item + * @return the physical and real index of the terminal whose xref zone + * (drawn by the last call of paint()) contains @a pos, or -1 if none. + */ +hoverTerminal TerminalStripDrawer::xrefAt(const QPointF &pos) const +{ + for (const auto &xref_rect : m_xref_rects) { + if (xref_rect.rect.contains(pos)) { + return hoverTerminal{xref_rect.physical, xref_rect.real}; + } + } + return hoverTerminal{}; +} + void TerminalStripDrawer::setMouseHoverPos(const QPointF &pos) { m_last_mouse_pos_in_xrefs_rect = m_united_xref_text_rect.contains(m_mouse_hover_pos); diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.h b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.h index 04d92cec5..b23072fd6 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.h +++ b/sources/TerminalStrip/GraphicsItem/terminalstripdrawer.h @@ -130,6 +130,7 @@ namespace TerminalStripDrawer bool mouseHoverXref() const; bool needUpdate(); hoverTerminal hoveredXref() const; + hoverTerminal xrefAt(const QPointF &pos) const; QVector xrefRects() const; private: diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripitem.cpp b/sources/TerminalStrip/GraphicsItem/terminalstripitem.cpp index 1867b8032..07139e595 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripitem.cpp +++ b/sources/TerminalStrip/GraphicsItem/terminalstripitem.cpp @@ -142,12 +142,43 @@ void TerminalStripItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) if (m_drawer.needUpdate()) { update(); } + + //The realistic styles don't write the xref : show it in a tool tip + if (m_drawer.drawStyle() != TerminalStripLayoutPattern::DrawStyle::Schematic) { + setToolTip(xrefToolTip(m_drawer.xrefAt(hoverMousePos()))); + } } void TerminalStripItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { QetGraphicsItem::hoverLeaveEvent(event); m_drawer.setMouseHoverPos(QPointF{}); + setToolTip(QString()); + //Remove the highlight of the xref (or of the terminal) under the mouse + update(); +} + +/** + * @brief TerminalStripItem::xrefToolTip + * @param terminal : physical and real index of a terminal of the strip + * @return the text of the tool tip of @a terminal : its label and its xref, + * or an empty string if @a terminal is invalid. + */ +QString TerminalStripItem::xrefToolTip(const TerminalStripDrawer::hoverTerminal &terminal) const +{ + if (!m_strip || terminal.physical < 0 || terminal.real < 0) { + return QString(); + } + const auto physical_terminal{m_strip->physicalTerminal(terminal.physical)}; + if (physical_terminal.isNull()) { + return QString(); + } + const auto real_terminal{physical_terminal->realTerminal(terminal.real)}; + if (real_terminal.isNull() || real_terminal->Xref().isEmpty()) { + return QString(); + } + return tr("%1 : %2\nDouble-click to go to the element") + .arg(real_terminal->label(), real_terminal->Xref()); } void TerminalStripItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) diff --git a/sources/TerminalStrip/GraphicsItem/terminalstripitem.h b/sources/TerminalStrip/GraphicsItem/terminalstripitem.h index b4cd5d77f..b126dbddc 100644 --- a/sources/TerminalStrip/GraphicsItem/terminalstripitem.h +++ b/sources/TerminalStrip/GraphicsItem/terminalstripitem.h @@ -75,6 +75,7 @@ class TerminalStripItem : public QetGraphicsItem private: void setDefaultLayout(); + QString xrefToolTip(const TerminalStripDrawer::hoverTerminal &terminal) const; private: QPointer m_strip;