From 0a577eaabf97d3005cd4289a4d5eeff6be4385f5 Mon Sep 17 00:00:00 2001 From: DaTweety <129504390+DaTweety@users.noreply.github.com> Date: Mon, 19 Oct 2026 16:27:50 +0200 Subject: [PATCH] Place symbol texts: label and notes as one block, at one of nine points A new command, "Place symbol texts", for the selected symbols: their label and the notes beside it go as one block next to the drawing of the symbol, 3 px away, at one of the nine points of the text alignment dialog, chosen in a 3 x 3 grid that opens from the toolbar button and from the Edit menu. A click on the button places them at the point chosen last (left of the symbol at first). Left of the symbol the lines are aligned right, so that they hang on it and a longer text grows away from it (their alignment is set too); right of it aligned left; above, below and on it centred. The lines keep their order and their spacing, a group of texts is placed as one line with the same alignment, a turned symbol is placed in the folio's directions, and everything is one undo step. Element::drawingRect() gives the area the drawing covers, tighter than the bounding rect, which is rounded up to the grid. A toolbar command with a menu now shows as a button with an arrow beside it. Translations for all languages. Co-Authored-By: Claude Opus 5.5 --- cmake/qet_compilation_vars.cmake | 2 + lang/qet_ar.ts | 64 ++++++++ lang/qet_ca.ts | 64 ++++++++ lang/qet_cs.ts | 64 ++++++++ lang/qet_da.ts | 64 ++++++++ lang/qet_de.ts | 64 ++++++++ lang/qet_el.ts | 64 ++++++++ lang/qet_en.ts | 64 ++++++++ lang/qet_es.ts | 64 ++++++++ lang/qet_fi.ts | 64 ++++++++ lang/qet_fr.ts | 64 ++++++++ lang/qet_hr.ts | 64 ++++++++ lang/qet_hu.ts | 64 ++++++++ lang/qet_it.ts | 64 ++++++++ lang/qet_ja.ts | 64 ++++++++ lang/qet_ko.ts | 64 ++++++++ lang/qet_mn.ts | 64 ++++++++ lang/qet_nb.ts | 64 ++++++++ lang/qet_nl.ts | 64 ++++++++ lang/qet_nl_BE.ts | 64 ++++++++ lang/qet_nl_NL.ts | 64 ++++++++ lang/qet_no.ts | 64 ++++++++ lang/qet_pl.ts | 64 ++++++++ lang/qet_pt.ts | 64 ++++++++ lang/qet_pt_BR.ts | 64 ++++++++ lang/qet_ro.ts | 64 ++++++++ lang/qet_rs.ts | 64 ++++++++ lang/qet_ru.ts | 64 ++++++++ lang/qet_sk.ts | 64 ++++++++ lang/qet_sl.ts | 64 ++++++++ lang/qet_sr.ts | 64 ++++++++ lang/qet_sv.ts | 64 ++++++++ lang/qet_tr.ts | 64 ++++++++ lang/qet_uk.ts | 64 ++++++++ lang/qet_zh.ts | 64 ++++++++ lang/qet_zh_TW.ts | 64 ++++++++ sources/diagramtoolbarsettings.cpp | 12 +- sources/qetdiagrameditor.cpp | 76 +++++++++ sources/qetdiagrameditor.h | 4 +- sources/qetgraphicsitem/element.cpp | 12 ++ sources/qetgraphicsitem/element.h | 2 + sources/symboltextplacement.cpp | 129 +++++++++++++++ sources/symboltextplacement.h | 52 ++++++ tests/qttest/CMakeLists.txt | 9 + .../qttest/run_symbol_text_placement_probe.py | 60 +++++++ tests/qttest/symbol_text_placement_probe.cpp | 154 ++++++++++++++++++ 46 files changed, 2750 insertions(+), 2 deletions(-) create mode 100644 sources/symboltextplacement.cpp create mode 100644 sources/symboltextplacement.h create mode 100755 tests/qttest/run_symbol_text_placement_probe.py create mode 100644 tests/qttest/symbol_text_placement_probe.cpp diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index eebec6489..44a3186cb 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -386,6 +386,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/autobreakconductor.cpp ${QET_DIR}/sources/autobreakconductor.h + ${QET_DIR}/sources/symboltextplacement.cpp + ${QET_DIR}/sources/symboltextplacement.h ${QET_DIR}/sources/diagramevent/diagrameventaddelement.cpp ${QET_DIR}/sources/diagramevent/diagrameventaddelement.h diff --git a/lang/qet_ar.ts b/lang/qet_ar.ts index 70f4baf34..558ba8c2a 100644 --- a/lang/qet_ar.ts +++ b/lang/qet_ar.ts @@ -11972,6 +11972,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + وضع نصوص الرمز + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + وضع التسمية والملاحظات للرموز المحددة ككتلة واحدة بجانبها + + + + Above, left + أعلى، يسار + + + + Above + أعلى + + + + Above, right + أعلى، يمين + + + + Left + يسار + + + + On the symbol + على الرمز + + + + Right + يمين + + + + Below, left + أسفل، يسار + + + + Below + أسفل + + + + Below, right + أسفل، يمين + @@ -24776,4 +24832,12 @@ The following variables can be used: + + SymbolTextPlacement + + + Place symbol texts + وضع نصوص الرمز + + diff --git a/lang/qet_ca.ts b/lang/qet_ca.ts index 3a1cf99e5..1ba3ea190 100644 --- a/lang/qet_ca.ts +++ b/lang/qet_ca.ts @@ -12054,6 +12054,62 @@ Opcions disponibles: QETDiagramEditor + + + Place symbol texts + Col·locar els textos del símbol + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Col·loca l'etiqueta i les notes dels símbols seleccionats com un sol bloc al costat + + + + Above, left + A dalt, a l'esquerra + + + + Above + A dalt + + + + Above, right + A dalt, a la dreta + + + + Left + Esquerra + + + + On the symbol + Sobre el símbol + + + + Right + Dreta + + + + Below, left + A baix, a l'esquerra + + + + Below + A baix + + + + Below, right + A baix, a la dreta + @@ -24823,4 +24879,12 @@ associar el nom "volta" amb el valor "1745" substituirà %{v Què : + + SymbolTextPlacement + + + Place symbol texts + Col·locar els textos del símbol + + diff --git a/lang/qet_cs.ts b/lang/qet_cs.ts index 47a96f8b9..f37ca087d 100644 --- a/lang/qet_cs.ts +++ b/lang/qet_cs.ts @@ -12069,6 +12069,62 @@ Dostupné volby: QETDiagramEditor + + + Place symbol texts + Umístit texty symbolu + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Umístí označení a poznámky vybraných symbolů jako jeden blok vedle nich + + + + Above, left + Nahoře vlevo + + + + Above + Nahoře + + + + Above, right + Nahoře vpravo + + + + Left + Vlevo + + + + On the symbol + Na symbolu + + + + Right + Vpravo + + + + Below, left + Dole vlevo + + + + Below + Dole + + + + Below, right + Dole vpravo + Displ&ay @@ -24871,4 +24927,12 @@ Přiřazení názvu "volta" a hodnoty "1745" v záhlaví vý Co: + + SymbolTextPlacement + + + Place symbol texts + Umístit texty symbolu + + diff --git a/lang/qet_da.ts b/lang/qet_da.ts index 51cad3a5b..38f41e744 100644 --- a/lang/qet_da.ts +++ b/lang/qet_da.ts @@ -11921,6 +11921,62 @@ Kommandovalg: QETDiagramEditor + + + Place symbol texts + Placer symboltekster + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Placerer mærkat og noter for de valgte symboler som én blok ved siden af dem + + + + Above, left + Over, til venstre + + + + Above + Over + + + + Above, right + Over, til højre + + + + Left + Venstre + + + + On the symbol + På symbolet + + + + Right + Højre + + + + Below, left + Under, til venstre + + + + Below + Under + + + + Below, right + Under, til højre + Displ&ay @@ -24552,4 +24608,12 @@ Eksempel: forbind navn "volta" og værdi "1745" erstat %{vol Hvad: + + SymbolTextPlacement + + + Place symbol texts + Placer symboltekster + + diff --git a/lang/qet_de.ts b/lang/qet_de.ts index bad84db1e..747912959 100644 --- a/lang/qet_de.ts +++ b/lang/qet_de.ts @@ -12040,6 +12040,62 @@ Verfügbare Optionen: QETDiagramEditor + + + Place symbol texts + Symboltexte platzieren + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Setzt BMK und Anmerkungen der ausgewählten Symbole als einen Block neben das Symbol + + + + Above, left + Oben links + + + + Above + Oben + + + + Above, right + Oben rechts + + + + Left + Links + + + + On the symbol + Auf dem Symbol + + + + Right + Rechts + + + + Below, left + Unten links + + + + Below + Unten + + + + Below, right + Unten rechts + Copy text format @@ -24876,4 +24932,12 @@ die Variable "volta" kombiniert mit dem Wert "1745" lässt i Was: + + SymbolTextPlacement + + + Place symbol texts + Symboltexte platzieren + + diff --git a/lang/qet_el.ts b/lang/qet_el.ts index 294546ad7..73f0aaf39 100644 --- a/lang/qet_el.ts +++ b/lang/qet_el.ts @@ -12012,6 +12012,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Τοποθέτηση κειμένων συμβόλου + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Τοποθετεί την ετικέτα και τις σημειώσεις των επιλεγμένων συμβόλων ως ένα μπλοκ δίπλα τους + + + + Above, left + Πάνω, αριστερά + + + + Above + Πάνω + + + + Above, right + Πάνω, δεξιά + + + + Left + Αριστερά + + + + On the symbol + Πάνω στο σύμβολο + + + + Right + Δεξιά + + + + Below, left + Κάτω, αριστερά + + + + Below + Κάτω + + + + Below, right + Κάτω, δεξιά + Displ&ay @@ -24747,4 +24803,12 @@ The following variables can be used: Τι: + + SymbolTextPlacement + + + Place symbol texts + Τοποθέτηση κειμένων συμβόλου + + diff --git a/lang/qet_en.ts b/lang/qet_en.ts index d46a8ab2d..595e603c1 100644 --- a/lang/qet_en.ts +++ b/lang/qet_en.ts @@ -12052,6 +12052,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Place symbol texts + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Place the label and the notes of the selected symbols as one block beside them + + + + Above, left + Above, left + + + + Above + Above + + + + Above, right + Above, right + + + + Left + Left + + + + On the symbol + On the symbol + + + + Right + Right + + + + Below, left + Below, left + + + + Below + Below + + + + Below, right + Below, right + Displ&ay @@ -24888,4 +24944,12 @@ The following variables can be used: What: + + SymbolTextPlacement + + + Place symbol texts + Place symbol texts + + diff --git a/lang/qet_es.ts b/lang/qet_es.ts index c7c2bc00b..123e65718 100644 --- a/lang/qet_es.ts +++ b/lang/qet_es.ts @@ -12055,6 +12055,62 @@ Opciones disponibles: QETDiagramEditor + + + Place symbol texts + Colocar los textos del símbolo + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Coloca la etiqueta y las notas de los símbolos seleccionados como un solo bloque a su lado + + + + Above, left + Arriba, izquierda + + + + Above + Arriba + + + + Above, right + Arriba, derecha + + + + Left + Izquierda + + + + On the symbol + Sobre el símbolo + + + + Right + Derecha + + + + Below, left + Abajo, izquierda + + + + Below + Abajo + + + + Below, right + Abajo, derecha + No modification @@ -24826,4 +24882,12 @@ asociar el nombre de "volta" y el valor "1745" reemplazara % Que: + + SymbolTextPlacement + + + Place symbol texts + Colocar los textos del símbolo + + diff --git a/lang/qet_fi.ts b/lang/qet_fi.ts index 725f5019e..a3cbacc62 100644 --- a/lang/qet_fi.ts +++ b/lang/qet_fi.ts @@ -11694,6 +11694,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Sijoita symbolin tekstit + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Sijoittaa valittujen symbolien tunnuksen ja huomautukset yhtenä lohkona niiden viereen + + + + Above, left + Yllä vasemmalla + + + + Above + Yllä + + + + Above, right + Yllä oikealla + + + + Left + Vasemmalla + + + + On the symbol + Symbolin päällä + + + + Right + Oikealla + + + + Below, left + Alla vasemmalla + + + + Below + Alla + + + + Below, right + Alla oikealla + @@ -24228,4 +24284,12 @@ The following variables can be used: + + SymbolTextPlacement + + + Place symbol texts + Sijoita symbolin tekstit + + diff --git a/lang/qet_fr.ts b/lang/qet_fr.ts index fe3cb9ade..6c49e975d 100644 --- a/lang/qet_fr.ts +++ b/lang/qet_fr.ts @@ -12064,6 +12064,62 @@ Options disponibles : QETDiagramEditor + + + Place symbol texts + Placer les textes du symbole + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Place le repère et les notes des symboles sélectionnés en un seul bloc à côté d'eux + + + + Above, left + En haut à gauche + + + + Above + En haut + + + + Above, right + En haut à droite + + + + Left + À gauche + + + + On the symbol + Sur le symbole + + + + Right + À droite + + + + Below, left + En bas à gauche + + + + Below + En bas + + + + Below, right + En bas à droite + Copy text format @@ -24904,4 +24960,12 @@ associer le nom "volta" et la valeur "1745" remplacera %{vol Quoi : + + SymbolTextPlacement + + + Place symbol texts + Placer les textes du symbole + + diff --git a/lang/qet_hr.ts b/lang/qet_hr.ts index d1965e3cc..8d36a9dbd 100644 --- a/lang/qet_hr.ts +++ b/lang/qet_hr.ts @@ -11786,6 +11786,62 @@ Dostupne opcije: QETDiagramEditor + + + Place symbol texts + Postavi tekstove simbola + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Postavlja oznaku i napomene odabranih simbola kao jedan blok pokraj njih + + + + Above, left + Gore lijevo + + + + Above + Gore + + + + Above, right + Gore desno + + + + Left + Lijevo + + + + On the symbol + Na simbolu + + + + Right + Desno + + + + Below, left + Dolje lijevo + + + + Below + Dolje + + + + Below, right + Dolje desno + @@ -24386,4 +24442,12 @@ The following variables can be used: + + SymbolTextPlacement + + + Place symbol texts + Postavi tekstove simbola + + diff --git a/lang/qet_hu.ts b/lang/qet_hu.ts index a52dd1e40..ea6bca5ad 100644 --- a/lang/qet_hu.ts +++ b/lang/qet_hu.ts @@ -11979,6 +11979,62 @@ Elérhető lehetőségek: QETDiagramEditor + + + Place symbol texts + Szimbólumszövegek elhelyezése + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + A kijelölt szimbólumok jelölését és megjegyzéseit egy blokként melléjük helyezi + + + + Above, left + Fent balra + + + + Above + Fent + + + + Above, right + Fent jobbra + + + + Left + Balra + + + + On the symbol + A szimbólumon + + + + Right + Jobbra + + + + Below, left + Lent balra + + + + Below + Lent + + + + Below, right + Lent jobbra + @@ -24666,4 +24722,12 @@ A következő változók használhatók: Adatai : + + SymbolTextPlacement + + + Place symbol texts + Szimbólumszövegek elhelyezése + + diff --git a/lang/qet_it.ts b/lang/qet_it.ts index 0c98a3da9..64641487d 100644 --- a/lang/qet_it.ts +++ b/lang/qet_it.ts @@ -12026,6 +12026,62 @@ Ecco gli ultimi messaggi registrati prima della chiusura — puoi salvarli per i QETDiagramEditor + + + Place symbol texts + Posiziona i testi del simbolo + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Posiziona la sigla e le note dei simboli selezionati come un unico blocco accanto a essi + + + + Above, left + In alto a sinistra + + + + Above + In alto + + + + Above, right + In alto a destra + + + + Left + A sinistra + + + + On the symbol + Sul simbolo + + + + Right + A destra + + + + Below, left + In basso a sinistra + + + + Below + In basso + + + + Below, right + In basso a destra + No modification @@ -24779,4 +24835,12 @@ si possono utilizzare le seguenti variabili: Conduttore + + SymbolTextPlacement + + + Place symbol texts + Posiziona i testi del simbolo + + diff --git a/lang/qet_ja.ts b/lang/qet_ja.ts index 6cd2f459b..99c819f1f 100644 --- a/lang/qet_ja.ts +++ b/lang/qet_ja.ts @@ -11953,6 +11953,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + シンボルのテキストを配置 + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + 選択したシンボルのラベルと注記を1つのブロックとして横に配置します + + + + Above, left + 左上 + + + + Above + 上 + + + + Above, right + 右上 + + + + Left + 左 + + + + On the symbol + シンボル上 + + + + Right + 右 + + + + Below, left + 左下 + + + + Below + 下 + + + + Below, right + 右下 + @@ -24642,4 +24698,12 @@ The following variables can be used: 項目 : + + SymbolTextPlacement + + + Place symbol texts + シンボルのテキストを配置 + + diff --git a/lang/qet_ko.ts b/lang/qet_ko.ts index 9ba95a949..8bd2c7497 100644 --- a/lang/qet_ko.ts +++ b/lang/qet_ko.ts @@ -11955,6 +11955,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + 심볼 텍스트 배치 + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + 선택한 심볼의 라벨과 메모를 하나의 블록으로 옆에 배치합니다 + + + + Above, left + 위, 왼쪽 + + + + Above + 위 + + + + Above, right + 위, 오른쪽 + + + + Left + 왼쪽 + + + + On the symbol + 심볼 위 + + + + Right + 오른쪽 + + + + Below, left + 아래, 왼쪽 + + + + Below + 아래 + + + + Below, right + 아래, 오른쪽 + Displ&ay @@ -24592,4 +24648,12 @@ The following variables can be used: 검색어 : + + SymbolTextPlacement + + + Place symbol texts + 심볼 텍스트 배치 + + diff --git a/lang/qet_mn.ts b/lang/qet_mn.ts index c67b519c3..576800b31 100644 --- a/lang/qet_mn.ts +++ b/lang/qet_mn.ts @@ -11876,6 +11876,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Тэмдгийн текстийг байрлуулах + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Сонгосон тэмдгүүдийн шошго, тэмдэглэлийг нэг блок болгон хажууд нь байрлуулна + + + + Above, left + Дээд зүүн + + + + Above + Дээд + + + + Above, right + Дээд баруун + + + + Left + Зүүн + + + + On the symbol + Тэмдэг дээр + + + + Right + Баруун + + + + Below, left + Доод зүүн + + + + Below + Доод + + + + Below, right + Доод баруун + @@ -24472,4 +24528,12 @@ The following variables can be used: Юу : + + SymbolTextPlacement + + + Place symbol texts + Тэмдгийн текстийг байрлуулах + + diff --git a/lang/qet_nb.ts b/lang/qet_nb.ts index c87188f36..5c3d9fc6c 100644 --- a/lang/qet_nb.ts +++ b/lang/qet_nb.ts @@ -11891,6 +11891,62 @@ Tilgjengelige opsjoner: QETDiagramEditor + + + Place symbol texts + Plasser symboltekster + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Plasserer merke og merknader for de valgte symbolene som én blokk ved siden av dem + + + + Above, left + Over, til venstre + + + + Above + Over + + + + Above, right + Over, til høyre + + + + Left + Venstre + + + + On the symbol + På symbolet + + + + Right + Høyre + + + + Below, left + Under, til venstre + + + + Below + Under + + + + Below, right + Under, til høyre + @@ -24508,4 +24564,12 @@ variablen "volta" kombiniert med verdi "1745" I tegningsmale Hva: + + SymbolTextPlacement + + + Place symbol texts + Plasser symboltekster + + diff --git a/lang/qet_nl.ts b/lang/qet_nl.ts index 302ecee1f..08c5e8d7d 100644 --- a/lang/qet_nl.ts +++ b/lang/qet_nl.ts @@ -11917,6 +11917,62 @@ Opties : QETDiagramEditor + + + Place symbol texts + Symbolteksten plaatsen + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Plaatst het label en de opmerkingen van de geselecteerde symbolen als één blok ernaast + + + + Above, left + Boven, links + + + + Above + Boven + + + + Above, right + Boven, rechts + + + + Left + Links + + + + On the symbol + Op het symbool + + + + Right + Rechts + + + + Below, left + Onder, links + + + + Below + Onder + + + + Below, right + Onder, rechts + Displ&ay @@ -24549,4 +24605,12 @@ Door de associatie van de naam "volta" met de waarde "1745" Welke : + + SymbolTextPlacement + + + Place symbol texts + Symbolteksten plaatsen + + diff --git a/lang/qet_nl_BE.ts b/lang/qet_nl_BE.ts index 9969bcc7a..5c96dced5 100644 --- a/lang/qet_nl_BE.ts +++ b/lang/qet_nl_BE.ts @@ -12039,6 +12039,62 @@ Beschikbare opties: QETDiagramEditor + + + Place symbol texts + Symbolteksten plaatsen + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Plaatst het label en de opmerkingen van de geselecteerde symbolen als één blok ernaast + + + + Above, left + Boven, links + + + + Above + Boven + + + + Above, right + Boven, rechts + + + + Left + Links + + + + On the symbol + Op het symbool + + + + Right + Rechts + + + + Below, left + Onder, links + + + + Below + Onder + + + + Below, right + Onder, rechts + Displ&ay @@ -24791,4 +24847,12 @@ De volgende variabelen kunnen gebruikt worden: Wat : + + SymbolTextPlacement + + + Place symbol texts + Symbolteksten plaatsen + + diff --git a/lang/qet_nl_NL.ts b/lang/qet_nl_NL.ts index dca5baef9..d44405c00 100644 --- a/lang/qet_nl_NL.ts +++ b/lang/qet_nl_NL.ts @@ -9936,6 +9936,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Symbolteksten plaatsen + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Plaatst het label en de opmerkingen van de geselecteerde symbolen als één blok ernaast + + + + Above, left + Boven, links + + + + Above + Boven + + + + Above, right + Boven, rechts + + + + Left + Links + + + + On the symbol + Op het symbool + + + + Right + Rechts + + + + Below, left + Onder, links + + + + Below + Onder + + + + Below, right + Onder, rechts + @@ -21749,4 +21805,12 @@ The following variables can be used: + + SymbolTextPlacement + + + Place symbol texts + Symbolteksten plaatsen + + diff --git a/lang/qet_no.ts b/lang/qet_no.ts index 16a35735c..46cbdd84d 100644 --- a/lang/qet_no.ts +++ b/lang/qet_no.ts @@ -11694,6 +11694,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Plasser symboltekster + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Plasserer merke og merknader for de valgte symbolene som én blokk ved siden av dem + + + + Above, left + Over, til venstre + + + + Above + Over + + + + Above, right + Over, til høyre + + + + Left + Venstre + + + + On the symbol + På symbolet + + + + Right + Høyre + + + + Below, left + Under, til venstre + + + + Below + Under + + + + Below, right + Under, til høyre + @@ -24228,4 +24284,12 @@ The following variables can be used: + + SymbolTextPlacement + + + Place symbol texts + Plasser symboltekster + + diff --git a/lang/qet_pl.ts b/lang/qet_pl.ts index 2bd93b3e5..fdee219fe 100644 --- a/lang/qet_pl.ts +++ b/lang/qet_pl.ts @@ -12141,6 +12141,62 @@ Dostępne opcje: QETDiagramEditor + + + Place symbol texts + Rozmieść teksty symbolu + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Umieszcza oznaczenie i uwagi zaznaczonych symboli jako jeden blok obok nich + + + + Above, left + U góry po lewej + + + + Above + U góry + + + + Above, right + U góry po prawej + + + + Left + Po lewej + + + + On the symbol + Na symbolu + + + + Right + Po prawej + + + + Below, left + Na dole po lewej + + + + Below + Na dole + + + + Below, right + Na dole po prawej + Displ&ay @@ -25099,4 +25155,12 @@ skojarzenie nazwy "volta" i wartości "1745" spowoduje zast Dane: + + SymbolTextPlacement + + + Place symbol texts + Rozmieść teksty symbolu + + diff --git a/lang/qet_pt.ts b/lang/qet_pt.ts index 9c5def165..228fa076a 100644 --- a/lang/qet_pt.ts +++ b/lang/qet_pt.ts @@ -11927,6 +11927,62 @@ Opções disponiveis: QETDiagramEditor + + + Place symbol texts + Posicionar os textos do símbolo + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Posiciona a etiqueta e as notas dos símbolos selecionados como um só bloco ao lado deles + + + + Above, left + Em cima, à esquerda + + + + Above + Em cima + + + + Above, right + Em cima, à direita + + + + Left + À esquerda + + + + On the symbol + Sobre o símbolo + + + + Right + À direita + + + + Below, left + Em baixo, à esquerda + + + + Below + Em baixo + + + + Below, right + Em baixo, à direita + Displ&ay @@ -24678,4 +24734,12 @@ Podem ser usadas as seguintes variáveis: O quê: + + SymbolTextPlacement + + + Place symbol texts + Posicionar os textos do símbolo + + diff --git a/lang/qet_pt_BR.ts b/lang/qet_pt_BR.ts index 212342d9d..a4b82f8e9 100644 --- a/lang/qet_pt_BR.ts +++ b/lang/qet_pt_BR.ts @@ -12030,6 +12030,62 @@ Opções disponíveis: QETDiagramEditor + + + Place symbol texts + Posicionar os textos do símbolo + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Posiciona a etiqueta e as notas dos símbolos selecionados como um só bloco ao lado deles + + + + Above, left + Acima, à esquerda + + + + Above + Acima + + + + Above, right + Acima, à direita + + + + Left + À esquerda + + + + On the symbol + Sobre o símbolo + + + + Right + À direita + + + + Below, left + Abaixo, à esquerda + + + + Below + Abaixo + + + + Below, right + Abaixo, à direita + @@ -24869,4 +24925,12 @@ associar o nome "variável_personalizada" e o valor "1745" v O que: + + SymbolTextPlacement + + + Place symbol texts + Posicionar os textos do símbolo + + diff --git a/lang/qet_ro.ts b/lang/qet_ro.ts index 9697d297e..725f28f87 100644 --- a/lang/qet_ro.ts +++ b/lang/qet_ro.ts @@ -11800,6 +11800,62 @@ Opțiuni disponibile: QETDiagramEditor + + + Place symbol texts + Plasează textele simbolului + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Plasează eticheta și notele simbolurilor selectate ca un singur bloc lângă ele + + + + Above, left + Sus, stânga + + + + Above + Sus + + + + Above, right + Sus, dreapta + + + + Left + Stânga + + + + On the symbol + Pe simbol + + + + Right + Dreapta + + + + Below, left + Jos, stânga + + + + Below + Jos + + + + Below, right + Jos, dreapta + @@ -24404,4 +24460,12 @@ asociind numele "volta" cu valoarea "1745" se va înlocui %{ + + SymbolTextPlacement + + + Place symbol texts + Plasează textele simbolului + + diff --git a/lang/qet_rs.ts b/lang/qet_rs.ts index 3dd8f1181..f98b83674 100644 --- a/lang/qet_rs.ts +++ b/lang/qet_rs.ts @@ -11738,6 +11738,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Postavi tekstove simbola + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Postavlja oznaku i napomene izabranih simbola kao jedan blok pored njih + + + + Above, left + Gore levo + + + + Above + Gore + + + + Above, right + Gore desno + + + + Left + Levo + + + + On the symbol + Na simbolu + + + + Right + Desno + + + + Below, left + Dole levo + + + + Below + Dole + + + + Below, right + Dole desno + @@ -24330,4 +24386,12 @@ The following variables can be used: + + SymbolTextPlacement + + + Place symbol texts + Postavi tekstove simbola + + diff --git a/lang/qet_ru.ts b/lang/qet_ru.ts index ecfbbe428..0376d4638 100644 --- a/lang/qet_ru.ts +++ b/lang/qet_ru.ts @@ -12132,6 +12132,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Разместить тексты символа + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Размещает обозначение и примечания выбранных символов одним блоком рядом с ними + + + + Above, left + Сверху слева + + + + Above + Сверху + + + + Above, right + Сверху справа + + + + Left + Слева + + + + On the symbol + На символе + + + + Right + Справа + + + + Below, left + Снизу слева + + + + Below + Снизу + + + + Below, right + Снизу справа + @@ -24979,4 +25035,12 @@ The following variables can be used: Где: + + SymbolTextPlacement + + + Place symbol texts + Разместить тексты символа + + diff --git a/lang/qet_sk.ts b/lang/qet_sk.ts index 3c66aeac1..99b7650d1 100644 --- a/lang/qet_sk.ts +++ b/lang/qet_sk.ts @@ -11921,6 +11921,62 @@ Dostupné možnosti: QETDiagramEditor + + + Place symbol texts + Umiestniť texty symbolu + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Umiestni označenie a poznámky vybraných symbolov ako jeden blok vedľa nich + + + + Above, left + Hore vľavo + + + + Above + Hore + + + + Above, right + Hore vpravo + + + + Left + Vľavo + + + + On the symbol + Na symbole + + + + Right + Vpravo + + + + Below, left + Dole vľavo + + + + Below + Dole + + + + Below, right + Dole vpravo + Displ&ay @@ -24734,4 +24790,12 @@ Používajú sa nasledujúce premenné: Čo: + + SymbolTextPlacement + + + Place symbol texts + Umiestniť texty symbolu + + diff --git a/lang/qet_sl.ts b/lang/qet_sl.ts index e879adf61..349d867f7 100644 --- a/lang/qet_sl.ts +++ b/lang/qet_sl.ts @@ -11782,6 +11782,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Postavi besedila simbola + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Postavi oznako in opombe izbranih simbolov kot en blok poleg njih + + + + Above, left + Zgoraj levo + + + + Above + Zgoraj + + + + Above, right + Zgoraj desno + + + + Left + Levo + + + + On the symbol + Na simbolu + + + + Right + Desno + + + + Below, left + Spodaj levo + + + + Below + Spodaj + + + + Below, right + Spodaj desno + @@ -24432,4 +24488,12 @@ The following variables can be used: + + SymbolTextPlacement + + + Place symbol texts + Postavi besedila simbola + + diff --git a/lang/qet_sr.ts b/lang/qet_sr.ts index 47460d10d..2c0703bd3 100644 --- a/lang/qet_sr.ts +++ b/lang/qet_sr.ts @@ -11738,6 +11738,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Постави текстове симбола + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Поставља ознаку и напомене изабраних симбола као један блок поред њих + + + + Above, left + Горе лево + + + + Above + Горе + + + + Above, right + Горе десно + + + + Left + Лево + + + + On the symbol + На симболу + + + + Right + Десно + + + + Below, left + Доле лево + + + + Below + Доле + + + + Below, right + Доле десно + @@ -24330,4 +24386,12 @@ The following variables can be used: + + SymbolTextPlacement + + + Place symbol texts + Постави текстове симбола + + diff --git a/lang/qet_sv.ts b/lang/qet_sv.ts index 8cd8e0913..f2690c1d0 100644 --- a/lang/qet_sv.ts +++ b/lang/qet_sv.ts @@ -11922,6 +11922,62 @@ Tillgängliga alternativ : QETDiagramEditor + + + Place symbol texts + Placera symboltexter + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Placerar märkning och anteckningar för de valda symbolerna som ett block bredvid dem + + + + Above, left + Ovanför, vänster + + + + Above + Ovanför + + + + Above, right + Ovanför, höger + + + + Left + Vänster + + + + On the symbol + På symbolen + + + + Right + Höger + + + + Below, left + Nedanför, vänster + + + + Below + Nedanför + + + + Below, right + Nedanför, höger + @@ -24574,4 +24630,12 @@ du associerar namnet ”volta” med värdet ”1745” kommer %{volta} att ers Vad: + + SymbolTextPlacement + + + Place symbol texts + Placera symboltexter + + diff --git a/lang/qet_tr.ts b/lang/qet_tr.ts index 50e850b91..a987082d2 100644 --- a/lang/qet_tr.ts +++ b/lang/qet_tr.ts @@ -11984,6 +11984,62 @@ Mevcut seçenekler: QETDiagramEditor + + + Place symbol texts + Sembol metinlerini yerleştir + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Seçili sembollerin etiketini ve notlarını tek blok olarak yanlarına yerleştirir + + + + Above, left + Üst, sol + + + + Above + Üst + + + + Above, right + Üst, sağ + + + + Left + Sol + + + + On the symbol + Sembolün üstünde + + + + Right + Sağ + + + + Below, left + Alt, sol + + + + Below + Alt + + + + Below, right + Alt, sağ + @@ -24681,4 +24737,12 @@ Aşağıdaki değişkenler kullanılabilir: Ne ile: + + SymbolTextPlacement + + + Place symbol texts + Sembol metinlerini yerleştir + + diff --git a/lang/qet_uk.ts b/lang/qet_uk.ts index c48faea7d..7b5bdc34e 100644 --- a/lang/qet_uk.ts +++ b/lang/qet_uk.ts @@ -12089,6 +12089,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + Розмістити тексти символу + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + Розміщує позначення та примітки вибраних символів одним блоком поруч із ними + + + + Above, left + Згори ліворуч + + + + Above + Згори + + + + Above, right + Згори праворуч + + + + Left + Ліворуч + + + + On the symbol + На символі + + + + Right + Праворуч + + + + Below, left + Знизу ліворуч + + + + Below + Знизу + + + + Below, right + Знизу праворуч + @@ -24919,4 +24975,12 @@ The following variables can be used: Де: + + SymbolTextPlacement + + + Place symbol texts + Розмістити тексти символу + + diff --git a/lang/qet_zh.ts b/lang/qet_zh.ts index 1f6f232c3..d9420fc1e 100644 --- a/lang/qet_zh.ts +++ b/lang/qet_zh.ts @@ -11987,6 +11987,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + 放置符号文本 + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + 将所选符号的标签和注释作为一个整体放在其旁边 + + + + Above, left + 左上 + + + + Above + 上 + + + + Above, right + 右上 + + + + Left + 左 + + + + On the symbol + 在符号上 + + + + Right + 右 + + + + Below, left + 左下 + + + + Below + 下 + + + + Below, right + 右下 + @@ -24681,4 +24737,12 @@ The following variables can be used: 类型: + + SymbolTextPlacement + + + Place symbol texts + 放置符号文本 + + diff --git a/lang/qet_zh_TW.ts b/lang/qet_zh_TW.ts index 797f74cb8..09e05c3e0 100644 --- a/lang/qet_zh_TW.ts +++ b/lang/qet_zh_TW.ts @@ -11987,6 +11987,62 @@ Available options: QETDiagramEditor + + + Place symbol texts + 放置符號文字 + + + + Place the label and the notes of the selected symbols as one block beside them + status bar tip + 將所選符號的標籤與註解作為一個區塊放在其旁邊 + + + + Above, left + 左上 + + + + Above + 上 + + + + Above, right + 右上 + + + + Left + 左 + + + + On the symbol + 在符號上 + + + + Right + 右 + + + + Below, left + 左下 + + + + Below + 下 + + + + Below, right + 右下 + @@ -24681,4 +24737,12 @@ The following variables can be used: 型別: + + SymbolTextPlacement + + + Place symbol texts + 放置符號文字 + + diff --git a/sources/diagramtoolbarsettings.cpp b/sources/diagramtoolbarsettings.cpp index 95964b227..d49ef3e38 100644 --- a/sources/diagramtoolbarsettings.cpp +++ b/sources/diagramtoolbarsettings.cpp @@ -17,6 +17,9 @@ */ #include "diagramtoolbarsettings.h" +#include +#include + #include "shortcutmanager.h" #include @@ -198,7 +201,9 @@ QStringList DiagramToolbarSettings::defaultIds(const QString &name) //the shapes of the "Ajouter" toolbar. sep, QStringLiteral("diagrameditor.add_cable"), - QStringLiteral("widget:conductor_color")}; + QStringLiteral("widget:conductor_color"), + sep, + QStringLiteral("diagrameditor.place_symbol_texts")}; } if (name == QLatin1String("adding")) { //add_pdf is skipped when built without QtPdf, as before @@ -330,6 +335,11 @@ void DiagramToolbarSettings::fill(QToolBar *toolbar, const QStringList &ids, pending_separator = false; } toolbar->addAction(action); + //A command with a menu of choices: a click does it again, + //the arrow beside it opens the choices + if (action->menu()) + if (auto *button = qobject_cast(toolbar->widgetForAction(action))) + button->setPopupMode(QToolButton::MenuButtonPopup); } } diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index 527623837..75290f55e 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -20,6 +20,7 @@ #include "container/qetzip.h" #include "utils/previousversions.h" #include "shownkinds.h" +#include "symboltextplacement.h" #ifdef QET_HAS_SCRIPTING #include "scripting/qetscripting.h" #include "scripting/scriptlibrary.h" @@ -33,6 +34,7 @@ #include #include #include +#include #include "ElementsCollection/elementscollectionwidget.h" #include "ElementsCollection/elementpickerpopup.h" #include "shortcutbarsettings.h" @@ -489,6 +491,57 @@ void QETDiagramEditor::setUpActions() connect(m_copy_text_format, &QAction::triggered, this, &QETDiagramEditor::copyTextFormat); connect(m_apply_text_format, &QAction::triggered, this, &QETDiagramEditor::applyTextFormat); + //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); + 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()); + { + auto *menu = new QMenu(this); + auto *grid_widget = new QWidget(menu); + 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")}}; + int n = 0; + for (const auto &point : points) + { + auto *button = new QToolButton(grid_widget); + button->setText(QString::fromUtf8(point.arrow)); + button->setToolTip(tr(point.tip)); + button->setObjectName(QStringLiteral("place_texts_%1").arg(n)); + button->setFixedSize(28, 28); + button->setAutoRaise(true); + const Qt::Alignment where = point.where; + connect(button, &QToolButton::clicked, this, [this, menu, where]() { + menu->close(); + QSettings().setValue(QStringLiteral("diagrameditor/place_texts_point"), int(where)); + placeSymbolTexts(where); + }); + grid->addWidget(button, n / 3, n % 3); + ++n; + } + auto *grid_action = new QWidgetAction(menu); + grid_action->setDefaultWidget(grid_widget); + 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)); + }); + //Duplicate: copy the selection and place it at a configured, //grid-step offset immediately -- no interactive follow-the- //cursor step, unlike Ctrl+V above. That is deliberate (#991): @@ -1585,6 +1638,7 @@ void QETDiagramEditor::setUpMenu() menu_edition -> addAction(m_paste); menu_edition -> addAction(m_copy_text_format); menu_edition -> addAction(m_apply_text_format); + menu_edition -> addAction(m_place_texts); menu_edition -> addAction(m_paste_origin); menu_edition -> addAction(m_paste_element_info); menu_edition -> addAction(m_duplicate); @@ -3187,6 +3241,7 @@ void QETDiagramEditor::slot_updateComplexActions() m_find_element->setEnabled(selected_elements_count == 1); m_copy_text_format->setEnabled(selected_elements_count >= 1); m_apply_text_format->setEnabled(!ro && selected_elements_count >= 1 && ElementTextFormatPattern::hasPattern()); + m_place_texts->setEnabled(!ro && selected_elements_count >= 1); m_mirror_horizontal->setEnabled(!ro && selected_elements_count); m_mirror_vertical->setEnabled(!ro && selected_elements_count); @@ -5014,6 +5069,27 @@ void QETDiagramEditor::copyTextFormat() * @brief QETDiagramEditor::applyTextFormat * Applies the stored text formatting template to all selected elements in the active diagram. */ +/** + @brief QETDiagramEditor::placeSymbolTexts + Place the texts of the selected symbols as one block at @a where, + one of nine points around each symbol, in one undo step. + @param where : see SymbolTextPlacement::place() +*/ +void QETDiagramEditor::placeSymbolTexts(Qt::Alignment where) +{ + DiagramView *dv = currentDiagramView(); + if (!dv || !dv->diagram() || dv->diagram()->isReadOnly()) return; + + QList elements; + for (QGraphicsItem *item : dv->diagram()->selectedItems()) + if (Element *element = qgraphicsitem_cast(item)) + elements.append(element); + if (elements.isEmpty()) return; + + if (QUndoCommand *undo = SymbolTextPlacement::place(elements, where)) + dv->diagram()->undoStack().push(undo); +} + void QETDiagramEditor::applyTextFormat() { if (!ElementTextFormatPattern::hasPattern()) return; diff --git a/sources/qetdiagrameditor.h b/sources/qetdiagrameditor.h index df1d2c1bb..e848d212b 100644 --- a/sources/qetdiagrameditor.h +++ b/sources/qetdiagrameditor.h @@ -210,6 +210,7 @@ class QETDiagramEditor : public QETMainWindow void subWindowActivated(QMdiSubWindow *subWindows); void copyTextFormat(); void applyTextFormat(); + void placeSymbolTexts(Qt::Alignment where); private slots: void updateTextGridButton(); @@ -225,7 +226,8 @@ class QETDiagramEditor : public QETMainWindow *m_paste_element_info, ///< Copy the information of the copied element onto the selected elements (#1375) *m_insert_last_element = nullptr, ///< Place the last placed element again *m_copy_text_format = nullptr, ///< Copy label/text formatting template - *m_apply_text_format = nullptr; ///< Apply label/text formatting template + *m_apply_text_format = nullptr, ///< Apply label/text formatting template + *m_place_texts = nullptr; ///< Place the texts of the selected symbols at one of nine points QActionGroup m_row_column_actions_group, /// Action related to add/remove rows/column in diagram diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index 982b1ed22..93b81870a 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -598,6 +598,18 @@ void Element::paint( /** @return Le rectangle delimitant le contour de l'element */ +/** + @brief Element::drawingRect + @return the area its drawing covers, in this symbol's coordinates: + tighter than boundingRect(), which is rounded up to the grid. The + bounding rect for a symbol with nothing drawn. +*/ +QRectF Element::drawingRect() const +{ + const QRect drawn = m_picture.isNull() ? QRect() : m_picture.boundingRect(); + return drawn.isEmpty() ? boundingRect() : QRectF(drawn); +} + QRectF Element::boundingRect() const { return(QRectF(QPointF(-hotspot_coord.x(), -hotspot_coord.y()), diff --git a/sources/qetgraphicsitem/element.h b/sources/qetgraphicsitem/element.h index a18653d3b..29c29bb1f 100644 --- a/sources/qetgraphicsitem/element.h +++ b/sources/qetgraphicsitem/element.h @@ -185,6 +185,8 @@ class Element : public QetGraphicsItem QPixmap pixmap(); QPoint setHotspot(QPoint); QPoint hotspot() const; + /// The area the symbol is drawn on, in its own coordinates + QRectF drawingRect() const; void editProperty() override; static bool valideXml(QDomElement &); static QUuid savedTerminalUuid(const QList &records, int index); diff --git a/sources/symboltextplacement.cpp b/sources/symboltextplacement.cpp new file mode 100644 index 000000000..c43ee178e --- /dev/null +++ b/sources/symboltextplacement.cpp @@ -0,0 +1,129 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "symboltextplacement.h" + +#include "QPropertyUndoCommand/qpropertyundocommand.h" +#include "qetgraphicsitem/dynamicelementtextitem.h" +#include "qetgraphicsitem/element.h" +#include "qetgraphicsitem/elementtextitemgroup.h" + +#include +#include + +namespace { + + /// How the lines of the block line up, for a block placed at @a where +Qt::Alignment linesFor(Qt::Alignment where) +{ + if (where & Qt::AlignLeft) return Qt::AlignRight; + if (where & Qt::AlignRight) return Qt::AlignLeft; + return Qt::AlignHCenter; +} + + /// One line of the block: a text, or a group of texts +struct Line +{ + QObject *object; ///< for its properties + QGraphicsItem *item; ///< for where it stands + QPointF old_pos; + QVariant old_alignment; +}; + + /// Move @a item so that the top left of its box in the folio is at @a to +void moveTo(QGraphicsItem *item, const QPointF &to) +{ + const QPointF by = to - item->sceneBoundingRect().topLeft(); + QGraphicsItem *parent = item->parentItem(); + const QPointF step = parent ? parent->mapFromScene(by) - parent->mapFromScene(QPointF(0, 0)) + : by; + item->setPos(item->pos() + step); +} + +bool placeOne(Element *element, Qt::Alignment where, QUndoCommand *parent) +{ + const Qt::Alignment lines_alignment = linesFor(where); + + QList lines; + for (DynamicElementTextItem *text : element->dynamicTextItems()) + if (!text->parentGroup() && text->isVisible()) + lines.append({text, text, text->pos(), text->property("alignment")}); + for (ElementTextItemGroup *group : element->textGroups()) + if (group->isVisible()) + lines.append({group, group, group->pos(), group->property("alignment")}); + if (lines.isEmpty()) + return false; + + //The lines first take the alignment of the block: a group lays its + //texts out anew, and later changes of a text keep that side. + for (const Line &line : lines) + { + Qt::Alignment a = line.old_alignment.value(); + if (qobject_cast(line.object)) + a = lines_alignment == Qt::AlignHCenter ? Qt::AlignVCenter : lines_alignment; + else + a = (a & Qt::AlignVertical_Mask) | lines_alignment; + line.object->setProperty("alignment", QVariant::fromValue(a)); + } + + QRectF block; + for (const Line &line : lines) + block |= line.item->sceneBoundingRect(); + + const QRectF body = element->mapRectToScene(element->drawingRect()); + const qreal gap = SymbolTextPlacement::gap; + QRectF to = block; + if (where & Qt::AlignLeft) to.moveRight(body.left() - gap); + else if (where & Qt::AlignRight) to.moveLeft(body.right() + gap); + else to.moveLeft(body.center().x() - block.width() / 2); + if (where & Qt::AlignTop) to.moveBottom(body.top() - gap); + else if (where & Qt::AlignBottom) to.moveTop(body.bottom() + gap); + else to.moveTop(body.center().y() - block.height() / 2); + + for (const Line &line : lines) + { + const QRectF box = line.item->sceneBoundingRect(); + const qreal y = to.top() + box.top() - block.top(); + const qreal x = lines_alignment == Qt::AlignRight ? to.right() - box.width() + : lines_alignment == Qt::AlignLeft ? to.left() + : to.center().x() - box.width() / 2; + moveTo(line.item, QPointF(x, y)); + + const QVariant alignment = line.object->property("alignment"); + if (alignment != line.old_alignment) + new QPropertyUndoCommand(line.object, "alignment", line.old_alignment, alignment, parent); + if (line.item->pos() != line.old_pos) + new QPropertyUndoCommand(line.object, "pos", line.old_pos, line.item->pos(), parent); + } + return true; +} + +} // namespace + +QUndoCommand *SymbolTextPlacement::place(const QList &elements, Qt::Alignment where) +{ + auto *undo = new QUndoCommand( + QCoreApplication::translate("SymbolTextPlacement", "Place symbol texts")); + bool any = false; + for (Element *element : elements) + any |= placeOne(element, where, undo); + if (!any || !undo->childCount()) { + delete undo; + return nullptr; + } + return undo; +} diff --git a/sources/symboltextplacement.h b/sources/symboltextplacement.h new file mode 100644 index 000000000..eb1d9c0a7 --- /dev/null +++ b/sources/symboltextplacement.h @@ -0,0 +1,52 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef SYMBOLTEXTPLACEMENT_H +#define SYMBOLTEXTPLACEMENT_H + +#include +#include + +class Element; +class QUndoCommand; + +/** + Place the texts of a symbol -- its label and the notes beside it -- as + one block at one of nine points around its drawing, the nine of the + text alignment dialog. Left of the symbol, the lines are aligned right, + so that the block hangs on the symbol and a longer text grows away + from it; right of it, aligned left; above, below and on it, centred. + The lines keep their order and their spacing; a group of texts is + placed as one line of the block, with the same alignment. +*/ +namespace SymbolTextPlacement +{ + /// Room between the drawing of the symbol and the block + constexpr double gap = 3; + + /** + @param elements : the symbols whose texts to place + @param where : the side of the drawing, as a 9-point alignment: + Qt::AlignLeft | Qt::AlignVCenter is left of the symbol, halfway + up; Qt::AlignCenter is on it + @return one undo step that places them (already placed), or + nullptr when none of the symbols has a text + */ + QUndoCommand *place(const QList &elements, Qt::Alignment where); +} + +#endif // SYMBOLTEXTPLACEMENT_H diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 83249d7a1..6984d23ed 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -122,6 +122,15 @@ if(Python3_Interpreter_FOUND AND CMAKE_GENERATOR STREQUAL "Ninja") --fixture ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/linked_reports.qet) set_tests_properties(tst_reportjumpintegration PROPERTIES TIMEOUT 300 RUN_SERIAL TRUE ENVIRONMENT "QT_QPA_PLATFORM=offscreen") + # "Place symbol texts": label and notes as one block at one of nine + # points beside the drawing, aligned to hang on it; one undo step. + add_test(NAME tst_symboltextplacementintegration + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_symbol_text_placement_probe.py + --build ${CMAKE_BINARY_DIR} --binary $ + --source ${CMAKE_CURRENT_SOURCE_DIR}/symbol_text_placement_probe.cpp + --fixture ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/unlinked_contact_label.qet) + set_tests_properties(tst_symboltextplacementintegration PROPERTIES TIMEOUT 300 RUN_SERIAL TRUE + ENVIRONMENT "QT_QPA_PLATFORM=offscreen") endif() if(Python3_Interpreter_FOUND AND CMAKE_GENERATOR STREQUAL "Ninja") add_test(NAME tst_darkimageintegration diff --git a/tests/qttest/run_symbol_text_placement_probe.py b/tests/qttest/run_symbol_text_placement_probe.py new file mode 100755 index 000000000..789954e6b --- /dev/null +++ b/tests/qttest/run_symbol_text_placement_probe.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Link the symbol text placement integration probe using an existing Ninja app build. + +Reuses app objects without duplicating the app or introducing a core-library +refactor. No application source or existing executable is replaced. The +generated manifest, probe binary, reports and export fixtures stay in build/. +""" +import argparse +import pathlib +import re +import shutil +import subprocess +import sys + +parser = argparse.ArgumentParser() +parser.add_argument('--build', required=True) +parser.add_argument('--binary', required=True) +parser.add_argument('--source', required=True) +parser.add_argument('--fixture', required=True) +args = parser.parse_args() +build = pathlib.Path(args.build).resolve() +binary = pathlib.Path(args.binary).resolve() +source = pathlib.Path(args.source).resolve() +fixture = pathlib.Path(args.fixture).resolve() +manifest = (build / 'build.ninja').read_text(encoding='utf-8') +match = re.search(r'^build (\S*?/sources/main\.cpp\.(?:obj|o)):', manifest, re.MULTILINE) +if not match: + sys.exit('Cannot find the application main object in the Ninja build.') +main_object = match.group(1) +probe_object = main_object.replace('main.cpp.', 'symbol_text_placement_probe.cpp.') +probe_binary = binary.with_name('symbol_text_placement_probe' + binary.suffix) +def escape(path): + return pathlib.Path(path).as_posix().replace('$', '$$').replace(':', '$:').replace(' ', '$ ') +main_source = source.parents[2] / 'sources' / 'main.cpp' +manifest = manifest.replace(main_object, probe_object) +manifest = manifest.replace(escape(main_source), escape(source)) +if binary.suffix: + manifest = manifest.replace(binary.name, probe_binary.name) +else: + old_target = escape(binary.name if binary.parent == build else binary) + new_target = escape(probe_binary.name if probe_binary.parent == build else probe_binary) + manifest = manifest.replace('build ' + old_target + ':', 'build ' + new_target + ':') + old_file = binary.name if binary.parent == build else binary.as_posix() + new_file = probe_binary.name if probe_binary.parent == build else probe_binary.as_posix() + manifest = re.sub(r'^ TARGET_FILE = ' + re.escape(old_file) + r'$', + ' TARGET_FILE = ' + new_file, manifest, flags=re.MULTILINE) +probe_manifest = build / 'symbol-text-placement-probe.ninja' +probe_manifest.write_text(manifest, encoding='utf-8') +ninja = shutil.which('ninja') +if not ninja: + sys.exit('Ninja is required for the integration probe.') +target = probe_binary.name if probe_binary.parent == build else str(probe_binary) +subprocess.run([ninja, '-C', str(build), '-f', probe_manifest.name, target], check=True) +output = build / 'symbol-text-placement' +output.mkdir(exist_ok=True) +report = output / 'results.txt' +result = subprocess.run([str(probe_binary), str(fixture), str(report), str(output)], timeout=90) +if report.exists(): + print(report.read_text(encoding='utf-8', errors='replace')) +sys.exit(result.returncode) diff --git a/tests/qttest/symbol_text_placement_probe.cpp b/tests/qttest/symbol_text_placement_probe.cpp new file mode 100644 index 000000000..e0902ecb6 --- /dev/null +++ b/tests/qttest/symbol_text_placement_probe.cpp @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Placing the texts of a symbol at one of nine points, linked against the +// application's objects. +// +// "Place symbol texts" puts the label and the notes of a symbol as one +// block beside its drawing, 3 px away, at one of the nine points of the +// text alignment dialog. Left of the symbol the lines are aligned right, +// so that they hang on it and a longer text grows away from it; right of +// it aligned left; above it centred. The lines keep their order and their +// spacing, a group of texts is placed as one line, a turned symbol is +// placed in the folio's directions, and one undo puts everything back. +#include +#include +#include +#include +#include +#include +#include + +#include "../../sources/diagram.h" +#include "../../sources/qetproject.h" +#include "../../sources/qetmessagebox.h" +#include "../../sources/symboltextplacement.h" +#include "../../sources/qetgraphicsitem/element.h" +#include "../../sources/qetgraphicsitem/dynamicelementtextitem.h" +#include "../../sources/qetgraphicsitem/elementtextitemgroup.h" + +static void check(bool value, const char *message) +{ + if (!value) throw std::runtime_error(message); + std::printf("ok: %s\n", message); +} + +static bool same(qreal a, qreal b) { return qAbs(a - b) < 0.6; } + +static QRectF body(Element *e) { return e->mapRectToScene(e->drawingRect()); } + +static QRectF block(const QList &texts) +{ + QRectF r; + for (DynamicElementTextItem *t : texts) r |= t->sceneBoundingRect(); + return r; +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + std::freopen(argv[2], "w", stdout); + + QTemporaryDir settings; + QSettings::setDefaultFormat(QSettings::IniFormat); + QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settings.path()); + QCoreApplication::setOrganizationName("QETSymbolTextPlacementRegression"); + QETProject::setBackupEnabled(false); + QET::QetMessageBox::setNonInteractive(true); + QFontDatabase::addApplicationFont(":/fonts/LiberationSans-Regular.ttf"); + + try + { + QETProject project(QString::fromLocal8Bit(argv[1])); + check(project.state() == QETProject::Ok, "fixture opens"); + project.buildFolios(project.diagrams()); + Diagram *scene = project.diagrams().first(); + Element *element = scene->elements().first(); + check(element && !element->drawingRect().isEmpty(), "the fixture has a drawn symbol"); + check(element->drawingRect().width() <= element->boundingRect().width(), + "the drawing is no wider than the frame"); + + //The symbol's own texts and three of ours, to the right of it + for (DynamicElementTextItem *t : element->dynamicTextItems()) t->setVisible(false); + QList texts; + const QStringList lines {"-FI1", "40 A / 30 mA, type A", "FI UG"}; + for (int i = 0; i < lines.size(); ++i) { + auto *t = new DynamicElementTextItem(element); + t->setTextFrom(DynamicElementTextItem::UserText); + t->setText(lines[i]); + t->setPos(80 + 5 * i, -20 + 12 * i); + element->addDynamicTextItem(t); + texts << t; + } + QList start; + for (DynamicElementTextItem *t : texts) start << t->pos(); + const QRectF before = block(texts); + + // Left of the symbol, halfway up: aligned right, 3 px away. + QUndoCommand *undo = SymbolTextPlacement::place({element}, Qt::AlignLeft | Qt::AlignVCenter); + check(undo, "placing gives one undo step"); + scene->undoStack().push(undo); + QRectF b = block(texts); + check(same(b.right(), body(element).left() - SymbolTextPlacement::gap), "left: the block ends 3 px before the drawing"); + check(same(b.center().y(), body(element).center().y()), "left: halfway up the drawing"); + bool right_aligned = true, keeps_order = true; + for (int i = 0; i < texts.size(); ++i) { + right_aligned &= same(texts[i]->sceneBoundingRect().right(), b.right()); + right_aligned &= bool(texts[i]->alignment() & Qt::AlignRight); + if (i) keeps_order &= same(texts[i]->sceneBoundingRect().top() - texts[i-1]->sceneBoundingRect().top(), 12); + } + check(right_aligned, "left: the lines are aligned right, on the screen and as their alignment"); + check(keeps_order, "the lines keep their order and their spacing"); + check(same(b.height(), before.height()), "the block keeps its height"); + + scene->undoStack().undo(); + bool back = true; + for (int i = 0; i < texts.size(); ++i) back &= texts[i]->pos() == start[i] && !(texts[i]->alignment() & Qt::AlignRight); + check(back, "one undo puts every text and its alignment back"); + scene->undoStack().redo(); + + // Right of it: aligned left. Above it: centred. + scene->undoStack().push(SymbolTextPlacement::place({element}, Qt::AlignRight | Qt::AlignVCenter)); + b = block(texts); + check(same(b.left(), body(element).right() + SymbolTextPlacement::gap), "right: the block starts 3 px after the drawing"); + check(same(texts[1]->sceneBoundingRect().left(), b.left()) && (texts[1]->alignment() & Qt::AlignLeft), + "right: the lines are aligned left"); + scene->undoStack().push(SymbolTextPlacement::place({element}, Qt::AlignHCenter | Qt::AlignTop)); + b = block(texts); + check(same(b.bottom(), body(element).top() - SymbolTextPlacement::gap), "above: the block ends 3 px above the drawing"); + check(same(texts[0]->sceneBoundingRect().center().x(), body(element).center().x()), "above: the lines are centred on it"); + + // A turned symbol: left in the folio is still left. + element->setRotation(90); + scene->undoStack().push(SymbolTextPlacement::place({element}, Qt::AlignLeft | Qt::AlignVCenter)); + b = block(texts); + check(same(b.right(), body(element).left() - SymbolTextPlacement::gap), "turned symbol: the block is left of it in the folio"); + element->setRotation(0); + + // A group of texts is one line of the block, aligned like it. + ElementTextItemGroup *group = element->addTextGroup(QStringLiteral("g")); + element->addTextToGroup(texts[1], group); + element->addTextToGroup(texts[2], group); + scene->undoStack().push(SymbolTextPlacement::place({element}, Qt::AlignLeft | Qt::AlignVCenter)); + QRectF with_group = texts[0]->sceneBoundingRect() | group->sceneBoundingRect(); + check(same(with_group.right(), body(element).left() - SymbolTextPlacement::gap) + && same(group->sceneBoundingRect().right(), with_group.right()), + "a group is placed as one line of the block"); + check(group->alignment() == Qt::AlignRight, "and is aligned right too"); + + // A symbol with no texts: nothing to undo. + Element *other = nullptr; + for (Element *e : scene->elements()) if (e != element) other = e; + if (other) { + for (DynamicElementTextItem *t : other->dynamicTextItems()) t->setVisible(false); + check(SymbolTextPlacement::place({other}, Qt::AlignLeft) == nullptr, "a symbol without texts gives no undo step"); + } + std::puts("PASS"); + } + catch (const std::exception &error) + { + std::printf("FAIL: %s\n", error.what()); + std::fflush(stdout); + return 1; + } + std::fflush(stdout); + return 0; +}