From 9c3c19a316a8eb08a4c64644188733680ca98841 Mon Sep 17 00:00:00 2001 From: DaTweety <129504390+DaTweety@users.noreply.github.com> Date: Mon, 19 Oct 2026 17:55:12 +0200 Subject: [PATCH] Place symbol texts: the icon shows where the texts go Each of the nine points has its own icon: a grey square for the symbol and two bars for the lines of text, where they will go and lined up as they will be. The grid shows them instead of arrows, and the toolbar button takes the icon of the point chosen last, so that one sees where a click on it puts the texts. Co-Authored-By: Claude Opus 5.5 --- sources/qetdiagrameditor.cpp | 37 ++++++++++-------- sources/symboltextplacement.cpp | 41 ++++++++++++++++++++ sources/symboltextplacement.h | 8 ++++ tests/qttest/symbol_text_placement_probe.cpp | 33 ++++++++++++++++ 4 files changed, 103 insertions(+), 16 deletions(-) diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index 75290f55e..34bf5a2f9 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -494,7 +494,12 @@ void QETDiagramEditor::setUpActions() //The texts of the selected symbols, label and notes, as one block at //one of nine points around the symbol: the nine points of the text //alignment dialog. A click places them at the point chosen last. - m_place_texts = new QAction(QET::Icons::AlignRight, tr("Place symbol texts"), this); + const auto chosen_point = []() { + return Qt::Alignment(QSettings().value(QStringLiteral("diagrameditor/place_texts_point"), + int(Qt::AlignLeft | Qt::AlignVCenter)).toInt()); + }; + //The button shows where its click will put the texts + m_place_texts = new QAction(SymbolTextPlacement::icon(chosen_point()), tr("Place symbol texts"), this); m_place_texts->setStatusTip(tr("Place the label and the notes of the selected symbols as one block beside them", "status bar tip")); ShortcutManager::instance().registerAction(m_place_texts, "diagrameditor.place_symbol_texts", tr("Diagram editors"), QKeySequence()); { @@ -503,21 +508,22 @@ void QETDiagramEditor::setUpActions() auto *grid = new QGridLayout(grid_widget); grid->setContentsMargins(6, 6, 6, 6); grid->setSpacing(2); - const struct { Qt::Alignment where; const char *arrow; const char *tip; } points[] = { - {Qt::AlignLeft | Qt::AlignTop, "\u2196", QT_TR_NOOP("Above, left")}, - {Qt::AlignHCenter | Qt::AlignTop, "\u2191", QT_TR_NOOP("Above")}, - {Qt::AlignRight | Qt::AlignTop, "\u2197", QT_TR_NOOP("Above, right")}, - {Qt::AlignLeft | Qt::AlignVCenter, "\u2190", QT_TR_NOOP("Left")}, - {Qt::AlignCenter, "\u25CF", QT_TR_NOOP("On the symbol")}, - {Qt::AlignRight | Qt::AlignVCenter, "\u2192", QT_TR_NOOP("Right")}, - {Qt::AlignLeft | Qt::AlignBottom, "\u2199", QT_TR_NOOP("Below, left")}, - {Qt::AlignHCenter | Qt::AlignBottom, "\u2193", QT_TR_NOOP("Below")}, - {Qt::AlignRight | Qt::AlignBottom, "\u2198", QT_TR_NOOP("Below, right")}}; + const struct { Qt::Alignment where; const char *tip; } points[] = { + {Qt::AlignLeft | Qt::AlignTop, QT_TR_NOOP("Above, left")}, + {Qt::AlignHCenter | Qt::AlignTop, QT_TR_NOOP("Above")}, + {Qt::AlignRight | Qt::AlignTop, QT_TR_NOOP("Above, right")}, + {Qt::AlignLeft | Qt::AlignVCenter, QT_TR_NOOP("Left")}, + {Qt::AlignCenter, QT_TR_NOOP("On the symbol")}, + {Qt::AlignRight | Qt::AlignVCenter, QT_TR_NOOP("Right")}, + {Qt::AlignLeft | Qt::AlignBottom, QT_TR_NOOP("Below, left")}, + {Qt::AlignHCenter | Qt::AlignBottom, QT_TR_NOOP("Below")}, + {Qt::AlignRight | Qt::AlignBottom, QT_TR_NOOP("Below, right")}}; int n = 0; for (const auto &point : points) { auto *button = new QToolButton(grid_widget); - button->setText(QString::fromUtf8(point.arrow)); + button->setIcon(SymbolTextPlacement::icon(point.where)); + button->setIconSize(QSize(24, 24)); button->setToolTip(tr(point.tip)); button->setObjectName(QStringLiteral("place_texts_%1").arg(n)); button->setFixedSize(28, 28); @@ -526,6 +532,7 @@ void QETDiagramEditor::setUpActions() connect(button, &QToolButton::clicked, this, [this, menu, where]() { menu->close(); QSettings().setValue(QStringLiteral("diagrameditor/place_texts_point"), int(where)); + m_place_texts->setIcon(SymbolTextPlacement::icon(where)); placeSymbolTexts(where); }); grid->addWidget(button, n / 3, n % 3); @@ -536,10 +543,8 @@ void QETDiagramEditor::setUpActions() menu->addAction(grid_action); m_place_texts->setMenu(menu); } - connect(m_place_texts, &QAction::triggered, this, [this]() { - const int where = QSettings().value(QStringLiteral("diagrameditor/place_texts_point"), - int(Qt::AlignLeft | Qt::AlignVCenter)).toInt(); - placeSymbolTexts(Qt::Alignment(where)); + connect(m_place_texts, &QAction::triggered, this, [this, chosen_point]() { + placeSymbolTexts(chosen_point()); }); //Duplicate: copy the selection and place it at a configured, diff --git a/sources/symboltextplacement.cpp b/sources/symboltextplacement.cpp index c43ee178e..7dfec5033 100644 --- a/sources/symboltextplacement.cpp +++ b/sources/symboltextplacement.cpp @@ -22,7 +22,11 @@ #include "qetgraphicsitem/element.h" #include "qetgraphicsitem/elementtextitemgroup.h" +#include #include +#include +#include +#include #include namespace { @@ -127,3 +131,40 @@ QUndoCommand *SymbolTextPlacement::place(const QList &elements, Qt::A } return undo; } + +QIcon SymbolTextPlacement::icon(Qt::Alignment where) +{ + const Qt::Alignment lines = linesFor(where); + const QPalette palette = QApplication::palette(); + QIcon icon; + for (int size : {16, 22, 24, 32, 48}) + { + QPixmap pixmap(size, size); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + painter.setRenderHint(QPainter::Antialiasing); + painter.scale(size / 24.0, size / 24.0); + + //The symbol + painter.setPen(Qt::NoPen); + painter.setBrush(palette.color(QPalette::Mid)); + painter.drawRect(QRectF(7, 7, 10, 10)); + + //Two lines of text, a long one over a short one + const qreal right = (where & Qt::AlignLeft) ? 5.5 : (where & Qt::AlignRight) ? 24 : 18.5; + const qreal left = (where & Qt::AlignLeft) ? 0 : (where & Qt::AlignRight) ? 18.5 : 5.5; + const qreal top = (where & Qt::AlignTop) ? 0 : (where & Qt::AlignBottom) ? 18.5 : 9.25; + painter.setBrush(palette.color(QPalette::WindowText)); + for (int i = 0; i < 2; ++i) + { + const qreal width = i == 0 ? right - left : (right - left) * 0.6; + const qreal x = lines == Qt::AlignRight ? right - width + : lines == Qt::AlignLeft ? left + : (left + right - width) / 2; + painter.drawRect(QRectF(x, top + i * 3, width, 2.5)); + } + painter.end(); + icon.addPixmap(pixmap); + } + return icon; +} diff --git a/sources/symboltextplacement.h b/sources/symboltextplacement.h index eb1d9c0a7..5100c98d3 100644 --- a/sources/symboltextplacement.h +++ b/sources/symboltextplacement.h @@ -18,6 +18,7 @@ #ifndef SYMBOLTEXTPLACEMENT_H #define SYMBOLTEXTPLACEMENT_H +#include #include #include @@ -47,6 +48,13 @@ namespace SymbolTextPlacement nullptr when none of the symbols has a text */ QUndoCommand *place(const QList &elements, Qt::Alignment where); + + /** + @return a picture of @a where: a grey square for the symbol, and + two bars for the lines of text where they will go, lined up as + they will be (left of the symbol, aligned right) + */ + QIcon icon(Qt::Alignment where); } #endif // SYMBOLTEXTPLACEMENT_H diff --git a/tests/qttest/symbol_text_placement_probe.cpp b/tests/qttest/symbol_text_placement_probe.cpp index e0902ecb6..5f29402b0 100644 --- a/tests/qttest/symbol_text_placement_probe.cpp +++ b/tests/qttest/symbol_text_placement_probe.cpp @@ -11,6 +11,8 @@ // placed in the folio's directions, and one undo puts everything back. #include #include +#include +#include #include #include #include @@ -141,6 +143,37 @@ int main(int argc, char **argv) for (DynamicElementTextItem *t : other->dynamicTextItems()) t->setVisible(false); check(SymbolTextPlacement::place({other}, Qt::AlignLeft) == nullptr, "a symbol without texts gives no undo step"); } + // The icons show where the texts go: bars left of the grey square + // for "left", right of it for "right", above it for "above". + auto dark = [](const QImage &img, const QRect &r) { + int n = 0; + for (int y = r.top(); y <= r.bottom(); ++y) + for (int x = r.left(); x <= r.right(); ++x) { + const QColor c = img.pixelColor(x, y); + if (c.alpha() > 128 && c.lightness() < 60) ++n; + } + return n; + }; + const QImage left = SymbolTextPlacement::icon(Qt::AlignLeft | Qt::AlignVCenter).pixmap(24, 24).toImage(); + const QImage right = SymbolTextPlacement::icon(Qt::AlignRight | Qt::AlignVCenter).pixmap(24, 24).toImage(); + const QImage above = SymbolTextPlacement::icon(Qt::AlignHCenter | Qt::AlignTop).pixmap(24, 24).toImage(); + check(dark(left, QRect(0, 8, 7, 8)) > 10 && dark(left, QRect(17, 0, 7, 24)) == 0, "the icon for left has its bars left of the symbol"); + check(dark(right, QRect(17, 8, 7, 8)) > 10 && dark(right, QRect(0, 0, 7, 24)) == 0, "the icon for right has them right of it"); + check(dark(above, QRect(6, 0, 12, 7)) > 10 && dark(above, QRect(0, 17, 24, 7)) == 0, "the icon for above has them above it"); + + //All nine side by side, to look at + QImage sheet(3 * 52, 3 * 52, QImage::Format_ARGB32); + sheet.fill(Qt::white); + QPainter p(&sheet); + const Qt::Alignment nine[] = { + Qt::AlignLeft | Qt::AlignTop, Qt::AlignHCenter | Qt::AlignTop, Qt::AlignRight | Qt::AlignTop, + Qt::AlignLeft | Qt::AlignVCenter, Qt::AlignCenter, Qt::AlignRight | Qt::AlignVCenter, + Qt::AlignLeft | Qt::AlignBottom, Qt::AlignHCenter | Qt::AlignBottom, Qt::AlignRight | Qt::AlignBottom}; + for (int i = 0; i < 9; ++i) + p.drawPixmap((i % 3) * 52 + 2, (i / 3) * 52 + 2, SymbolTextPlacement::icon(nine[i]).pixmap(48, 48)); + p.end(); + if (argc > 3) sheet.save(QString::fromLocal8Bit(argv[3]) + "/icons.png"); + std::puts("PASS"); } catch (const std::exception &error)