A terminal's label is written as it is read off the plan, "X1:1", and
the symbol draws it as one field. On a long strip that field is long
enough to reach into the next terminal, and a user who wants the number
next to the screw and the label out of the way has no way to say so.
Settings > General > Appearance now has a "Display of the terminal
labelling" group with two choices: the label and its terminal number in
one field, the way it has always been drawn, or the two split. The
first is what every drawing gets, so nothing moves until the user asks
for it, and choosing the other one redraws the drawings already open --
nothing needs to be closed and opened again.
A symbol made for the split carries a text field bound to the new
"Terminal number" information (in the element editor's list, next to
the label, and as the %{terminal_number} variable of a composite text):
the label field then shows "X1" and the number field "1", each placed
where the symbol wants it, and hideable when it must be. A symbol which
has no such field -- every symbol drawn until now -- gets the number on
its own line inside the label field, so switching the setting on can
never make a number vanish, and never needs a symbol to be touched
first. A field which becomes, or stops being, the number field tells
the label fields of its symbol to draw themselves again.
Only the drawing reacts. What the element stores, the numbering, the
sorting in the terminal strip editor, the automatic strip import and
every export keep the whole label: the split is read where the text is
drawn (Element::labelForDisplay() and friends), not where the label is
made.
The new test covers both choices, with and without the number field, a
label with no colon at all, and the way back to the whole label. The
full test suite passes (124/124).
The editor asks its model for every cell of every row, over and over
(paint, flags, row height, resize to contents). Three answers used to
cost far more than the little they tell:
- a row was found by walking m_physical_data each time, which made a
layout O(rows^2 * columns);
- the bridge cells walked to the head of the strip for each neighbour
of each step, and every step filled the full row data, which reads
the cables of the whole project (the target column of the terminal
strip editor);
- without any bridge on the strip, that walk still ran to its end.
Rows are now found through an index filled once with m_physical_data,
the bridge walk steps from row to row and fills only the three fields
it reads (level, bridge, identity), and it is skipped when the strip
has no bridge at all.
Nothing about what the editor shows, edits or saves changes: the model
data, the bridge pixmaps and the strip order are the same, only the
time to answer is not. Measured on a 302 row strip with one bridge
(20 rounds, offscreen, off the UI): all flags 372 ms to 6.6 ms, all
display cells 1195 ms to 31 ms, all decoration 7623 ms to 106 ms
(a bridge cell 251 us to 2.5 us), about 65 times faster overall.
The full test suite passes (121/121).
Every QElectroTech on a computer shares elements_cache.sqlite. The
cache set PRAGMA locking_mode = EXCLUSIVE, so a QElectroTech that had
read the file once kept it locked until it closed. A second one -- a
build in another folder, or a build beside a released version, which
SingleApplication lets run because its key includes the program's path
and version -- could not write to it: each write waited SQLite's 5 s
busy timeout and failed, on the GUI thread, twice for every symbol the
collection showed. The report's log is exactly that: an empty
QSqlError every 5.0 s for three minutes.
Use SQLite's normal locking, which holds a lock only while a statement
runs, and give this connection no busy timeout: the cache only saves
time, so a busy file is a cache miss, never a wait. Print the failing
query's error, not the connection's, which was always empty.
Measured with the real program running and a second process writing to
its cache: before, the write fails "database is locked" after 5.01 s;
after, it succeeds in 0.02 s. tst_elementcachelock opens two caches on
one file; it fails before the fix with the report's empty QSqlError.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rows-only numbering reads cost roughly 2x master's wall-clock under
ASan (~2065s measured solo on an idle machine, vs master's
~1038-1071s). Two timeouts were tight enough to fail on that
legitimate cost alone: the script's own subprocess timeout and
ctest's TIMEOUT property for the test. Raised both with real margin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
elementAutoNumIdToLink() queried numbering_scheme per symbol even when
many symbols on unbuilt folios share the same scheme; setUpFormula()
fetched the same scheme's rows twice (elementAutoNumCurrentFormula()
then elementAutoNum() separately). Both are real but didn't resolve
the tst_lazyfolios ASan timeout on their own -- see QETZ-DB-FIRST-PLAN.md's
2b-3 row for the remaining open question.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
QETProject no longer holds its numbering schemes and their settings in
hashes and members of its own: every accessor reads and writes the
project database's numbering tables (QETZ-DB-FIRST stage 2b-3), which
the save already writes from. projectDataBase gains per-scheme calls
(numberingSchemes, numberingScheme, setNumberingScheme,
removeNumberingScheme, renameNumberingScheme, the scheme uuid and
title, and the settings); setNumbering() is what reading the file
writes, in one go once it is all read.
QET_CHECK_NUMBERING_ROWS goes: there is one copy left to compare. The
test now checks the scheme list after each change through the script
API, that no part row is left without its scheme, and the saved file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each Increment spin box had a fixed maximum width of 55 px. The Windows
11 style puts the up and down arrows side by side, which leaves almost
nothing of 55 px for the number (#1566). The boxes now take the width
their style asks for.
Test: tst_autonumincrementwidth runs the editor under a style that
widens spin boxes the way the Windows 11 style does and checks each
Increment box is at least as wide as the style needs: 55 px against 81
px before this change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1533 stopped the panel shrinking to its title bar when the selection
had nothing to edit, so that it would keep the height the user gave it
(#1439). Users want the room back for the Collections and Auto numbering
panels while nothing is selected (#1566).
The panel's layout is back to QLayout::SetMaximumSize, so the empty
panel shrinks as before. Just before its last editor goes, the panel
keeps the heights of every docked panel in its area; when an editor
comes back it gives them back with QMainWindow::resizeDocks, the call a
separator drag makes. Giving only this panel its height back fell 35 px
short in the test: the neighbours had been split again from their size
hints and gave up only part of the room.
Test: tst_selectionpanelheight now checks, twice over, that the empty
panel shrinks and that selecting an element again brings back the height
the user gave it. Without the restore the panel comes back at 470 px
instead of 560; on master the empty panel does not shrink.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QETZ-DB-FIRST stage 2b-2. The save writes the numbering schemes and
their settings from the project database's numbering rows (2b-1)
instead of the project's own lists. The project still keeps the rows
in step after each change, and QET_CHECK_NUMBERING_ROWS still says
whether the two agree, until the lists go (2b-3).
tst_elementautonumids::numberingRowsFollowEveryChange also checks the
saved file: the current symbol and wire numbering and the schemes. With
setCurrrentElementAutonum() not writing the rows, a save straight after
it now names the old scheme in use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A --run script is stopped after 30 s: on industrial.qet the 23 saves
took 23 s natively and did not finish under AddressSanitizer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QETZ-DB-FIRST stage 2b-1. The numbering schemes of wires, symbols,
folios and cables, and the settings saved with them (the scheme in use
of each kind, freezing new labels, auto-break, the cable axis and
question), get three tables of the project database: numbering_scheme,
numbering_part (one row per part, in order) and numbering_setting.
QETProject writes them when it reads its file and after each change of
its numbering, from every setter; nothing reads them yet.
The save writes the numbering through writeNumberingXml(), the code it
had moved out of writeDefaultPropertiesXml(), so the same writer can
take the rows once the save moves to them (2b-2).
QET_CHECK_NUMBERING_ROWS=1 writes the numbering from the project and
from the rows at each save and says whether the two differ.
tst_elementautonumids::numberingRowsFollowEveryChange checks it after
each of 23 changes; with one setter not writing the rows, one save
differs and it fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A double click on a linked folio report showed the other end's folio
and highlighted it, but left the mouse pointer where it was, on every
platform, while only Wayland forbids a program to move it. On Windows,
X11 and macOS the pointer is now put on the end jumped to, once the
folio shown has its size (next turn of the event loop), so that a
second double click jumps back. On Wayland the highlight alone still
shows where the jump landed.
The pointing hand over a report's label text also stayed after a
click: mousePressEvent() puts the text colour back, and the cursor was
only unset with the colour in hoverLeaveEvent(). It is now always
unset when the mouse leaves the text.
A target typed in by hand was split into its fields on " / ", which
broke as soon as a field itself contained a slash: an installation
named "ASP/01" gave the box two lines with half a name in each, and
nothing to tell a separator apart from the name. Only a line break now
breaks the text into fields (RealTerminal::cableTarget()), and the
Target cell shows the fields the very same way, one line each
(modelRealTerminalData::targetText()), so the cell and the box under
the cable always read the same text.
The Target cell is edited line by line to go with that: it takes a
multi line editor where Return writes a new line, Ctrl+Return hands
the cell back to the table and Escape throws the text away
(TerminalStripModelDelegate), and the rows of the table are as tall as
the lines their cell shows, so the fields below the first one are never
cut off -- a cell without a target keeps the row it has always had.
The text copied to the clipboard keeps one line per row, the fields of
such a cell joined with " / " again, or a spreadsheet would take them
for several rows.
tst_terminalcableauto reads the three fields of a target of the
drawing back one line each, checks a "/" typed inside a field stays
part of it -- one line holding it, slashes and all -- checks the
editor of the cell is the multi line one, hands its text back
untouched, gives the cell back on Ctrl+Return while Return keeps
writing a new line, and checks the rows grow with the lines the cell
holds and shrink back afterwards. tst_cabletarget paints a strip whose
cable ends on a field containing a slash: the box is no deeper than
with one plain field.
Two cables of one strip end close to each other, and their boxes are
wider than the room between the two cables: a long Installation is
enough for that, and the two boxes ended up written the one over the
other -- a picture nobody can read, and no way to tell which box
belongs to which cable.
The boxes of a strip are now all measured before any of them is drawn,
and placed from the left to the right: a box which would reach into one
already placed is set just low enough to stand clear of it, its stub
drawn longer. Nothing is cut down to fit -- the text keeps its font and
the box keeps its width, it is the box which moves.
The strip reserves the room for the lower box too, with the same rule
and the very cells the drawing works on (Helpers::cableBoxesExtraDepth
(), both styles now build one list of cells for the painting and for
the reserved depth), so a lower box is never cut off by the drawing
area.
tst_cabletarget paints a strip with one cable ending on a target and
the same strip with a second cable ending on one: the second picture
draws a whole box lower, and the strip is deeper by exactly the room
that lower box needs.
Reading where a cable lands meant going back to the drawing: the strip
editor said which cable a terminal carries and which core, but never
what the cable goes to. A single core (1x16) could not be followed
from the terminal to its piece at all.
The new Target column names it: Installation, Location and Label (BMK)
of the piece the wires land on, past this strip, joined in one line --
the very three fields the strip drawing writes in the box under its
cable, in the same order (RealTerminal::cableTarget()). It is worked
out of the project like the Cable cells and so follows their rule: a
target the drawing names is read only, for only the drawing decides
it, and a cell nothing is worked out for takes an edit, so a target
the drawing cannot find -- a cable landing on a piece with no
Installation, Location nor Label set, a cable going to another strip
-- is still worth naming by hand. The text typed in is stored in the
element (RealTerminal::cableTargetInfoKey()) and kept until a cable
works a target out again, and back once that cable goes, exactly like
a cable designation typed by hand: the automatic value is never
written, so no stale text can be left behind. The column takes the
width of its own text, so a long Installation is never cut off.
A text typed in is written the way the fields are shown in the column,
"Installation / Location / Label", and gives one line per field: the
box under the cable draws three lines without any slash in them, just
like a target worked out from the project, instead of one long line
carrying its separators as text.
tst_terminalcableauto checks the column is empty -- and editable --
without a cable, reads "Plant 7 / Cabinet 7 / -S7" with one, goes
empty -- and editable again -- when the core is removed, takes no edit
while the drawing names the target, and is headed "Target"; a second
case checks a target typed in by hand is taken by the cell, stored by
Apply, shown again by a fresh model, set aside for the read only
target of the drawing once a cable lands somewhere worth naming, and
back once that cable goes; a third one the line per field. tst_cable
target paints a strip whose cable ends on one field and the same strip
ending on three, the second box two lines deeper than the first.
Narrower, a long word ran out of the text's box while the short lines
were still aligned inside the too narrow width: on a right aligned or
centred text a lone "1" stood in the middle under its words, and moved
on every step. Found on a real plan (-XS20, "Steckdosen Windfang, 1").
The probe checks that no line runs out of the text after narrowing, and
also for a text aligned bottom left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The text moves on every step of a drag to keep its alignment point in
place. Mapping the mouse with its current transform made the delta grow
with every step, so a right or centre aligned text ran away: 90 px of
mouse gave +1220 px of width. The probe now drags in 30 steps and checks
the width grows by exactly as much as the mouse moved.
Reported by ispyisail in the review of #1555.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Which point of a symbol's text stays in place while its width is
dragged now follows the text's alignment, the nine points of its
alignment dialog, rather than the handle: a text aligned top right
keeps its top right corner whichever handle is dragged, one centred
keeps its middle, and a text that wraps grows away from that point.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Narrowing a symbol's text with the left handle until it wraps onto
more lines moved it up: the point kept in place was the middle of the
opposite edge, and the middle moves down as the text grows. Keep the
top of the opposite edge instead, or the middle or the bottom for a
text aligned that way, for both handles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dragging the left width handle of a symbol's text changed its width but
kept its position, which is its left edge: the right edge moved with the
drag and the text jumped away from where the user held it. Now the edge
opposite the dragged handle stays in place, as in a drawing program and
as the corner handles of a free text already do (TextResizeHandles). It
is found along the text's own baseline, so a rotated text behaves the
same. Undo restores width and position together, with TextResizeCommand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Placing a group switches its alignment, which lays its texts out anew.
Undoing to AlignJustify, the default of the "Label and comment" group,
lays out nothing, so the texts stayed at their placed offsets. The
positions of the group's texts are now part of the undo step. The probe
reproduces the case from a group with freely placed texts.
Reported by ispyisail in the review of #1563.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
The strip seen from above drew no cable at all: it had no zone for one
and no code to draw it, so a strip drawn realistically told nothing
about where its cables go while the same strip drawn schematically did.
The drawing itself was already shared between nothing -- it lived in
one style only, so it is now Helpers::drawCables() with its cells, and
both styles hand it the terminals they show. The realistic style
collects one wire exit per level spread over the width of the terminal
body, all of them leaving the strip at its bottom edge, and reserves
the zone under the strip for them as soon as one terminal carries a
cable, exactly like the schematic style does.
tst_cabletarget checks the realistic style paints under the body of
the strip when a terminal carries a cable, and drops that zone when it
does not.
The box hugged its own text, so it stayed small whatever room the end
zone gave, and its font shrank until the three lines squeezed into it.
It now spans the cross bar the cable bundle ends on -- the width the
strip already draws there -- and keeps the font of the drawing: when
its lines (Installation, Location and Label) are longer than that cross
bar, it is the box which grows, the text is never cut down to fit a box.
The end zone of the default layout leaves the room those three lines
need at that font, so a target is not squeezed by the drawing itself.
The setting still rules it: a smaller zone shrinks the text again.
tst_cabletarget checks the box is drawn three lines tall at the font
of the drawing, and that the default zone leaves that room.
Feedback on a real plan: zooming in on the end jumped to was too much.
A jump now shows the target's folio whole, as a page (the view's fit),
selects the target and shades it and its texts blue for three seconds,
so that the eye finds the label too. One function, Element::jumpTo(),
serves the double click on the arrow and on the text.
The mouse pointer is not moved onto the target: Wayland does not let a
program move the pointer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The number of a wire was written from its mark towards the terminal
without any regard for the room left: a colour code like 'gnge' ran
into the drawing of the terminal. The length of the wires setting
moves the mark, but every project has to be tuned to its longest
number by hand, and a short length leaves no room at all.
Shrink the font while the number is longer than the room between the
mark and the terminal, and clip away what still does not fit even at
the smallest size, so a number never crosses into the terminal -- the
wire length and the font stay the settings to make the drawing
pleasant, they are just no longer able to break it.
tst_cabletarget paints the strip with a long number and without any
and checks the two pictures are the same above the bottom of the
terminal: the number is drawn below it or not at all.
The strip graphic fanned the wires out again after the cable, which
shows each core leaving the cable but says nothing about where it is
going -- the far end was unnamed. Keep the bundle together at the end
now: a short stub and a box with the Installation, Location and Label
(BMK) of the piece the cable lands on, the same three fields the cable
list computes for a target (cablelistrows.cpp), so the strip and the
list can never disagree.
The target is the first piece past the strip's own terminals the
wires of the cable land on, in plan order; a report is no arrival
(the cable continues on the linked sheet), several cores on one
piece are one landing, and a cable which lands on nothing but the
strip itself gets no box. The lines shrink to the end zone the
Terminal plan settings give (Cable end length), so the box never
grows past the strip the bounding rect reserved.
RealTerminal::cableTarget() works the whole out: the cable is the one
the wires drawn to the terminal carry, a name typed by hand is
looked up among the cables of the project, and the box is drawn for
the first wire of the cable group. tst_cabletarget checks the value
rule (own strip no target, spare no target, no cable no target) and
that painting with and without a target gives two different
pictures.
The strip sorted by label: X1:1, X1:2 ... with X1:N and X1:PE at the
end whatever the plan looks like, so a strip drawn across pages came
out in an order no cable follows. Sort it the way the plan is drawn
instead -- folio by folio, then left to right -- in both places that
order a strip: the auto import's own sort of the terminals it groups,
and SortTerminalStripCommand, which the import pushes for a strip it
just creates and which the editor's sort button and
qet.sortTerminalStrip() push as well. The two share one comparator
(SortTerminalStripCommand::planOrderLessThan), so they cannot fight
each other. A spare stands on no folio, has no place in the plan order
and goes last, by label; the label breaks the remaining ties, so two
terminals at the very same spot keep a stable order.
A linked folio report now shows a pointing hand under the mouse, on its
arrow as well as on the text that shows its label (which already turned
blue), and a double click on the arrow goes to the other end, as a
double click on the text did. Before, a double click on the arrow opened
the report's properties, which stay reachable from the context menu and
the properties panel.
The end jumped to is highlighted for two seconds, with the same blue as
the hover highlight but independent of it: the hover highlight is
cleared as soon as the mouse leaves the end jumped from, so on the same
folio the user saw nothing mark where the jump landed.
The text of a linked slave that shows its master's label gets the same
blue and hand, since a double click on it already jumped to the master.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>