mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-05 18:54:14 +02:00
Merge pull request #1311 from ispyisail/feature/xref-stack-option
Add a setting for stacking cross references at the folio bottom
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