mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-06 19:54:13 +02:00
Cross references at the folio bottom: stack only when texts overlap, add a checkbox (#1287 follow-up)
The stacking added in #1287 tested the crosses' bounding rects, so crosses whose frames or empty margins touched were raised although no text was hidden (the list display case found in review). The texts each cross really draws are now recorded while its geometry is built, and a cross is raised only when one of its texts would be drawn over a text of another one. A new "Empiler les références dont les textes se chevauchent" checkbox in the cross reference properties, per type, turns the stacking off; the crosses of that type are then only centred, as before #1287. It is saved in the project (xref attribute "stackoverlapping") and in the settings, and is on when absent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,7 @@ XRefProperties::XRefProperties()
|
||||
m_show_power_ctc = true;
|
||||
m_show_terminal_name = true;
|
||||
m_show_all_configured_slaves = false;
|
||||
m_stack_overlapping = true;
|
||||
m_display = Cross;
|
||||
m_snap_to = Bottom;
|
||||
m_prefix_keys << "power" << "delay" << "switch";
|
||||
@@ -83,6 +84,7 @@ void XRefProperties::toSettings(QSettings &settings,
|
||||
settings.setValue(prefix % "showpowerctc", m_show_power_ctc);
|
||||
settings.setValue(prefix % "showterminalname", m_show_terminal_name);
|
||||
settings.setValue(prefix % "showallconfiguredslaves", m_show_all_configured_slaves);
|
||||
settings.setValue(prefix % "stackoverlapping", m_stack_overlapping);
|
||||
QString display = m_display == Cross? "cross" : "contacts";
|
||||
settings.setValue(prefix % "displayhas", display);
|
||||
QString snap = m_snap_to == Bottom? "bottom" : "label";
|
||||
@@ -117,6 +119,7 @@ void XRefProperties::fromSettings(const QSettings &settings,
|
||||
m_show_power_ctc = settings.value(prefix % "showpowerctc", true).toBool();
|
||||
m_show_terminal_name = settings.value(prefix % "showterminalname", true).toBool();
|
||||
m_show_all_configured_slaves = settings.value(prefix % "showallconfiguredslaves", false).toBool();
|
||||
m_stack_overlapping = settings.value(prefix % "stackoverlapping", true).toBool();
|
||||
QString display = settings.value(prefix % "displayhas", "cross").toString();
|
||||
display == "cross"? m_display = Cross : m_display = Contacts;
|
||||
QString snap = settings.value(prefix % "snapto", "label").toString();
|
||||
@@ -148,6 +151,7 @@ QDomElement XRefProperties::toXml(QDomDocument &xml_document) const
|
||||
xml_element.setAttribute("showpowerctc", m_show_power_ctc? "true" : "false");
|
||||
xml_element.setAttribute("showterminalname", m_show_terminal_name? "true" : "false");
|
||||
xml_element.setAttribute("showallconfiguredslaves", m_show_all_configured_slaves? "true" : "false");
|
||||
xml_element.setAttribute("stackoverlapping", m_stack_overlapping? "true" : "false");
|
||||
QString display = m_display == Cross? "cross" : "contacts";
|
||||
xml_element.setAttribute("displayhas", display);
|
||||
QString snap = m_snap_to == Bottom? "bottom" : "label";
|
||||
@@ -181,6 +185,7 @@ bool XRefProperties::fromXml(const QDomElement &xml_element) {
|
||||
m_show_power_ctc = xml_element.attribute("showpowerctc") == "true";
|
||||
m_show_terminal_name = xml_element.attribute("showterminalname", "true") == "true";
|
||||
m_show_all_configured_slaves = xml_element.attribute("showallconfiguredslaves", "false") == "true";
|
||||
m_stack_overlapping = xml_element.attribute("stackoverlapping", "true") == "true";
|
||||
QString display = xml_element.attribute("displayhas", "cross");
|
||||
display == "cross"? m_display = Cross : m_display = Contacts;
|
||||
QString snap = xml_element.attribute("snapto", "label");
|
||||
@@ -228,6 +233,7 @@ bool XRefProperties::operator ==(const XRefProperties &xrp) const{
|
||||
return (m_show_power_ctc == xrp.m_show_power_ctc
|
||||
&& m_show_terminal_name == xrp.m_show_terminal_name
|
||||
&& m_show_all_configured_slaves == xrp.m_show_all_configured_slaves
|
||||
&& m_stack_overlapping == xrp.m_stack_overlapping
|
||||
&& m_display == xrp.m_display
|
||||
&& m_snap_to == xrp.m_snap_to
|
||||
&& m_prefix == xrp.m_prefix
|
||||
|
||||
@@ -63,6 +63,9 @@ class XRefProperties : public PropertiesInterface
|
||||
void setShowAllConfiguredSlaves (const bool a) {m_show_all_configured_slaves = a;}
|
||||
bool showAllConfiguredSlaves () const {return m_show_all_configured_slaves;}
|
||||
|
||||
void setStackOverlapping (const bool a) {m_stack_overlapping = a;}
|
||||
bool stackOverlapping () const {return m_stack_overlapping;}
|
||||
|
||||
void setDisplayHas (const DisplayHas dh) {m_display = dh;}
|
||||
DisplayHas displayHas () const {return m_display;}
|
||||
|
||||
@@ -92,6 +95,7 @@ class XRefProperties : public PropertiesInterface
|
||||
bool m_show_power_ctc;
|
||||
bool m_show_terminal_name;
|
||||
bool m_show_all_configured_slaves;
|
||||
bool m_stack_overlapping;
|
||||
DisplayHas m_display;
|
||||
SnapTo m_snap_to;
|
||||
Qt::AlignmentFlag m_xref_pos;
|
||||
|
||||
@@ -268,6 +268,7 @@ void CrossRefItem::updateLabel()
|
||||
m_shape_path = QPainterPath();
|
||||
prepareGeometryChange();
|
||||
m_bounding_rect = QRectF();
|
||||
m_text_rects.clear();
|
||||
|
||||
// Build geometry and m_hovered_contacts_map using a QImage-backed
|
||||
// painter so font metrics match the screen painter in paint().
|
||||
@@ -343,9 +344,12 @@ void CrossRefItem::autoPos()
|
||||
@brief CrossRefItem::stackAtBottom
|
||||
Places every cross reference of this folio that snaps to the bottom.
|
||||
Each one is centred under its element at the bottom of the folio, and
|
||||
when two would overlap, the one of the higher element goes above the
|
||||
other: several coils in one column get their crosses stacked in the
|
||||
same order as the coils, instead of all on the same spot.
|
||||
when its texts would be drawn over the texts of another one, the one
|
||||
of the higher element goes above the other: several coils in one
|
||||
column get their crosses stacked in the same order as the coils,
|
||||
instead of all on the same spot. Crosses that only touch, or overlap
|
||||
without any text over text, stay where they are.
|
||||
The types whose properties do not ask for stacking are only centred.
|
||||
*/
|
||||
void CrossRefItem::stackAtBottom()
|
||||
{
|
||||
@@ -374,41 +378,56 @@ void CrossRefItem::stackAtBottom()
|
||||
return a->m_element->uuid() < b->m_element->uuid();
|
||||
});
|
||||
|
||||
struct Placed { QRectF rect; QList<QRectF> texts; };
|
||||
const qreal gap = 5;
|
||||
QList<QRectF> placed;
|
||||
QList<Placed> placed;
|
||||
for (CrossRefItem *xref : std::as_const(xrefs)) {
|
||||
const qreal offset = xref->m_properties.offset();
|
||||
QGIUtility::centerToBottomDiagram(xref, xref->m_element,
|
||||
offset <= 40 ? 5 : offset);
|
||||
if (xref->boundingRect().isEmpty()) continue;
|
||||
if (xref->boundingRect().isEmpty()
|
||||
|| !xref->m_properties.stackOverlapping())
|
||||
continue;
|
||||
|
||||
// Move up past every cross already placed that it would overlap.
|
||||
// A list of contacts has an empty margin in its bounding rect:
|
||||
// only its content is tested, so side by side lists do not
|
||||
// count as overlapping.
|
||||
const bool list = xref->m_properties.displayHas()
|
||||
== XRefProperties::Contacts;
|
||||
QRectF rect = xref->sceneBoundingRect();
|
||||
if (list)
|
||||
rect.adjust(list_margin_left, 0, -list_margin_right, 0);
|
||||
const qreal bottom = rect.bottom();
|
||||
Placed self;
|
||||
self.rect = xref->sceneBoundingRect();
|
||||
const QTransform to_scene = xref->sceneTransform();
|
||||
for (const QRectF &text : std::as_const(xref->m_text_rects))
|
||||
self.texts << to_scene.mapRect(text);
|
||||
const qreal bottom = self.rect.bottom();
|
||||
|
||||
// Move up past every cross already placed whose texts it would
|
||||
// draw over. Each move goes strictly up, so this ends.
|
||||
for (bool moved = true; moved; ) {
|
||||
moved = false;
|
||||
for (const QRectF &other : std::as_const(placed))
|
||||
if (rect.left() < other.right() && other.left() < rect.right()
|
||||
&& rect.top() < other.bottom() + gap
|
||||
&& other.top() - gap < rect.bottom()) {
|
||||
rect.moveBottom(other.top() - gap);
|
||||
moved = true;
|
||||
}
|
||||
for (const Placed &other : std::as_const(placed)) {
|
||||
const qreal dy = other.rect.top() - gap - self.rect.bottom();
|
||||
if (dy >= 0 || !textsOverlap(self.texts, other.texts))
|
||||
continue;
|
||||
self.rect.translate(0, dy);
|
||||
for (QRectF &text : self.texts) text.translate(0, dy);
|
||||
moved = true;
|
||||
}
|
||||
}
|
||||
if (rect.bottom() != bottom)
|
||||
if (self.rect.bottom() != bottom)
|
||||
xref->setPos(xref->parentItem()->mapFromScene(
|
||||
xref->scenePos() + QPointF(0, rect.bottom() - bottom)));
|
||||
placed << rect;
|
||||
xref->scenePos() + QPointF(0, self.rect.bottom() - bottom)));
|
||||
placed << self;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CrossRefItem::textsOverlap
|
||||
@return true if one of the rects of a intersects one of the rects of b
|
||||
*/
|
||||
bool CrossRefItem::textsOverlap(const QList<QRectF> &a, const QList<QRectF> &b)
|
||||
{
|
||||
for (const QRectF &ra : a)
|
||||
for (const QRectF &rb : b)
|
||||
if (ra.intersects(rb)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CrossRefItem::sceneEvent
|
||||
@param event
|
||||
@@ -1156,9 +1175,9 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
painter.setFont(QETApp::diagramTextsFont(4));
|
||||
QRectF bt(0, offset, 24, 10);
|
||||
if (terminal_names.size() >= 1)
|
||||
painter.drawText(bt, Qt::AlignLeft|Qt::AlignTop, terminal_names[0]);
|
||||
drawText(painter, bt, Qt::AlignLeft|Qt::AlignTop, terminal_names[0]);
|
||||
if (terminal_names.size() >= 2)
|
||||
painter.drawText(bt, Qt::AlignRight|Qt::AlignTop, terminal_names[1]);
|
||||
drawText(painter, bt, Qt::AlignRight|Qt::AlignTop, terminal_names[1]);
|
||||
painter.setFont(QETApp::diagramTextsFont(5));
|
||||
}
|
||||
|
||||
@@ -1241,7 +1260,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
QRectF text_rect = painter.boundingRect(QRectF(30, offset, 5, 10), Qt::AlignLeft | Qt::AlignVCenter, str);
|
||||
if (!str.isEmpty())
|
||||
{
|
||||
painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, str);
|
||||
drawText(painter, text_rect, Qt::AlignLeft | Qt::AlignVCenter, str);
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
}
|
||||
|
||||
@@ -1286,13 +1305,13 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
painter.setFont(QETApp::diagramTextsFont(4));
|
||||
// Storage order set above: [0]=NC, [1]=NO, [2]=Common
|
||||
if (terminal_names.size() >= 2)
|
||||
painter.drawText(QRectF(0, offset, 8, 8),
|
||||
drawText(painter, QRectF(0, offset, 8, 8),
|
||||
Qt::AlignLeft|Qt::AlignTop, terminal_names[1]); // NO top-left
|
||||
if (terminal_names.size() >= 3)
|
||||
painter.drawText(QRectF(16, offset+4, 8, 6),
|
||||
drawText(painter, QRectF(16, offset+4, 8, 6),
|
||||
Qt::AlignRight|Qt::AlignTop, terminal_names[2]); // Common right
|
||||
if (terminal_names.size() >= 1)
|
||||
painter.drawText(QRectF(0, offset+9, 8, 6),
|
||||
drawText(painter, QRectF(0, offset+9, 8, 6),
|
||||
Qt::AlignLeft|Qt::AlignTop, terminal_names[0]); // NC bottom-left
|
||||
painter.setFont(QETApp::diagramTextsFont(5));
|
||||
}
|
||||
@@ -1325,7 +1344,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
str);
|
||||
if (!str.isEmpty())
|
||||
{
|
||||
painter.drawText(text_rect,
|
||||
drawText(painter, text_rect,
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
str);
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
@@ -1361,7 +1380,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
str);
|
||||
if (!str.isEmpty())
|
||||
{
|
||||
painter.drawText(text_rect,
|
||||
drawText(painter, text_rect,
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
str);
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
@@ -1454,7 +1473,7 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
|
||||
QSize(middle_cross, 1)),
|
||||
Qt::AlignLeft,
|
||||
str);
|
||||
painter.drawText(bounding, Qt::AlignLeft, str);
|
||||
drawText(painter, bounding, Qt::AlignLeft, str);
|
||||
|
||||
if (m_update_map) {
|
||||
QString pos_str = elementPositionText(elmt, true);
|
||||
@@ -1534,7 +1553,7 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
|
||||
QSize(middle_cross, 1)),
|
||||
Qt::AlignRight,
|
||||
str);
|
||||
painter.drawText(bounding, Qt::AlignRight, str);
|
||||
drawText(painter, bounding, Qt::AlignRight, str);
|
||||
|
||||
if (m_update_map) {
|
||||
QString pos_str = elementPositionText(elmt, true);
|
||||
@@ -1582,7 +1601,7 @@ void CrossRefItem::AddExtraInfo(QPainter &painter, const QString& type)
|
||||
r,
|
||||
Qt::TextWordWrap | Qt::AlignHCenter,
|
||||
text);
|
||||
painter.drawText(text_bounding,
|
||||
drawText(painter, text_bounding,
|
||||
Qt::TextWordWrap | Qt::AlignHCenter,
|
||||
text);
|
||||
|
||||
@@ -1598,6 +1617,21 @@ void CrossRefItem::AddExtraInfo(QPainter &painter, const QString& type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CrossRefItem::drawText
|
||||
Draws text in rect with painter, like QPainter::drawText. While the
|
||||
geometry is built (m_update_map), also stores the rect really covered
|
||||
by the text, so stackAtBottom() can tell texts drawn over each other.
|
||||
*/
|
||||
void CrossRefItem::drawText(QPainter &painter, const QRectF &rect,
|
||||
int flags, const QString &text)
|
||||
{
|
||||
QRectF drawn;
|
||||
painter.drawText(rect, flags, text, &drawn);
|
||||
if (m_update_map && !text.isEmpty())
|
||||
m_text_rects << painter.transform().mapRect(drawn);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CrossRefItem::NOElements
|
||||
@return The linked elements of m_element which are open or switch contact.
|
||||
|
||||
@@ -59,6 +59,8 @@ class CrossRefItem : public QGraphicsObject
|
||||
void init();
|
||||
void setUpConnection();
|
||||
void stackAtBottom();
|
||||
static bool textsOverlap(const QList<QRectF> &a,
|
||||
const QList<QRectF> &b);
|
||||
|
||||
public:
|
||||
enum { Type = UserType + 1009 };
|
||||
@@ -138,6 +140,8 @@ class CrossRefItem : public QGraphicsObject
|
||||
const QStringList &master_labels = QStringList());
|
||||
void fillCrossRef(QPainter &painter);
|
||||
void AddExtraInfo(QPainter &painter, const QString&);
|
||||
void drawText(QPainter &painter, const QRectF &rect, int flags,
|
||||
const QString &text);
|
||||
QList<Element *> NOElements() const;
|
||||
QList<Element *> NCElements() const;
|
||||
|
||||
@@ -150,6 +154,7 @@ class CrossRefItem : public QGraphicsObject
|
||||
int m_drawed_contacts;
|
||||
bool m_update_map = false;
|
||||
QMultiMap <Element *, QRectF> m_hovered_contacts_map;
|
||||
QList <QRectF> m_text_rects; //rects of the drawn texts (local coords)
|
||||
Element *m_hovered_contact = nullptr;
|
||||
DynamicElementTextItem *m_text = nullptr;
|
||||
ElementTextItemGroup *m_group = nullptr;
|
||||
|
||||
@@ -146,6 +146,7 @@ void XRefPropertiesWidget::saveProperties(int index) {
|
||||
xrp.setShowPowerContac(ui->m_show_power_cb->isChecked());
|
||||
xrp.setShowTerminalName(ui->m_show_terminal_name_cb->isChecked());
|
||||
xrp.setShowAllConfiguredSlaves(ui->m_show_all_slaves_cb->isChecked());
|
||||
xrp.setStackOverlapping(ui->m_stack_overlapping_cb->isChecked());
|
||||
xrp.setPrefix("power", ui->m_power_prefix_le->text());
|
||||
xrp.setPrefix("delay", ui->m_delay_prefix_le->text());
|
||||
xrp.setPrefix("switch", ui->m_switch_prefix_le->text());
|
||||
@@ -201,6 +202,8 @@ void XRefPropertiesWidget::updateDisplay()
|
||||
ui->m_snap_to_cb->setCurrentIndex(ui->m_snap_to_cb->findData("label"));
|
||||
ui->m_offset_sb->setEnabled(false);
|
||||
}
|
||||
ui->m_stack_overlapping_cb->setChecked(xrp.stackOverlapping());
|
||||
ui->m_stack_overlapping_cb->setEnabled(ui->m_offset_sb->isEnabled());
|
||||
|
||||
if(xrp.getXrefPos() == Qt::AlignTop) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("top"));
|
||||
else if(xrp.getXrefPos() == Qt::AlignLeft) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("left"));
|
||||
@@ -233,6 +236,7 @@ void XRefPropertiesWidget::updateDisplay()
|
||||
ui->m_display_has_cross_rb->setVisible(!is_plc);
|
||||
ui->m_show_terminal_name_cb->setVisible(!is_plc);
|
||||
ui->m_show_all_slaves_cb->setVisible(!is_plc);
|
||||
ui->m_stack_overlapping_cb->setVisible(!is_plc);
|
||||
ui->m_cross_properties_gb->setVisible(!is_plc);
|
||||
}
|
||||
|
||||
@@ -261,4 +265,5 @@ void XRefPropertiesWidget::enableOffsetSB(int i){
|
||||
ui->m_offset_sb->setEnabled(false);
|
||||
else
|
||||
ui->m_offset_sb->setEnabled(true);
|
||||
ui->m_stack_overlapping_cb->setEnabled(ui->m_offset_sb->isEnabled());
|
||||
}
|
||||
|
||||
@@ -89,6 +89,16 @@
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="m_stack_overlapping_cb">
|
||||
<property name="toolTip">
|
||||
<string>Quand les textes de deux références croisées se chevauchent, celle de l'élément le plus haut est placée au-dessus de l'autre.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Empiler les références dont les textes se chevauchent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
@@ -330,6 +340,7 @@
|
||||
<tabstop>m_type_cb</tabstop>
|
||||
<tabstop>m_snap_to_cb</tabstop>
|
||||
<tabstop>m_offset_sb</tabstop>
|
||||
<tabstop>m_stack_overlapping_cb</tabstop>
|
||||
<tabstop>m_xrefpos_cb</tabstop>
|
||||
<tabstop>m_display_has_contacts_rb</tabstop>
|
||||
<tabstop>m_display_has_cross_rb</tabstop>
|
||||
|
||||
Reference in New Issue
Block a user