The inverted painting, the rubber band replay and the changed()
receiver lived in DiagramView, which the unit tests cannot link, so the
update-flag regression was only covered through a stand-in view. They
now live in PaletteGraphicsView, a QGraphicsView subclass with no
other dependency, and DiagramView derives from it. The view tells a
subclass through paintingInverted(bool) when it renders for an
inverted display; DiagramView forwards that to the diagram. The grid
dot rule moves out of Diagram::drawBackground into
QET::Palette::gridDotColor().
tst_qetpalette now links the real class: gridDotColorSoftensInvertedDots,
paletteViewFollowsThePalette (light sheet, dark sheet at text contrast
with a red box still red and the paintingInverted calls in order, back
to light), paletteViewKeepsSceneUpdatesFlowing (three whole-scene
updates and a selection each repaint, scene set after construction),
paletteViewDrawsTheRubberBand.
On a dark palette the folio view paints through QGraphicsView::render()
instead of onto its viewport. In that case QGraphicsView never clears
the scene's "update everything" flag, and while the flag is set every
further QGraphicsScene::update() and item update is dropped: from the
second Diagram::update() on, the grid toggle, the white/gray toggle and
even a selection waited for an unrelated repaint. With a receiver on
QGraphicsScene::changed() the scene clears the flag before it emits, so
DiagramView now connects an empty receiver in its constructor.
While the view paints for inversion, Diagram draws the grid dots a
third of the way from the sheet color to black, so they come out as a
soft gray on the dark sheet instead of as bright as the ink. Printing
and export never take that path.
Test in tst_qetpalette: sceneUpdatesReachARenderedView.
a) paste
- conductor does not move
- pressing escape does not abort
- pressing anything during the process crashes the program instead of aborting
b) similar issue with the escape not working fixed for graphics items, texxt fields (and probably others)
Diagram::m_uuid was created in the constructor and never written, so a
folio got a new uuid on every load. Inside a running instance that is
enough (the project database keys on it), but nothing outside it could
tell which folio is which: in the file, folios were only identified by
their position.
Motivation
More and more .qet projects live in version control -- a Git repository
on GitHub or GitLab, reviewed through pull requests, sometimes edited
by several people -- or are synchronised through a cloud or key-value
store. A .qet file is plain XML, so in principle it can be diffed,
merged and split up, but only if the same folio can be recognised in
two versions of the file. Today it cannot:
- Inserting, deleting or reordering a folio shifts every following
<diagram> element. A line-based diff, and GitHub's review view, then
pair up unrelated folios and show far more change than was made.
- A three-way merge of two branches that both touched the project has
no way to match "folio 3" on one side with "folio 3" on the other if
either side reordered folios.
- Any tool that wants to say "folio X changed in this commit", keep
per-folio history, lock a single folio, or store folios as separate
objects has nothing stable to key on. The title and the folio number
are user-editable and not unique.
Element uuids are already persisted and used for cross-folio links, so
the file format already relies on uuids for identity; the folio itself
was the missing piece. A stable folio uuid is the prerequisite for
later work towards better version control support: per-folio diffs and
locks (check-out / check-in), and possibly storing a project as a
directory with one file per folio.
Change
Write the uuid as an attribute of <diagram> when the whole content is
saved, and restore it first thing when the project is loaded, before any
item is created. Older versions ignore the attribute, so files stay
readable in both directions.
Folios without a uuid: why not a random one
The obvious migration -- keep the random uuid created by the
constructor and save it -- conflicts with #754 / #779: saving an
unmodified project must give the same bytes every time. Every example
project predates the attribute, so each load would invent different
uuids and write them out. Measured on the 24 example projects (resaved
3-4x each from the same original, QT_HASH_SEED=0 so that QDom's
attribute order is stable, isolated HOME per run):
upstream master 23/24 byte-identical
persist, random uuid 0/24
persist, derived uuid (this) 23/24
The remaining project, schema_indus.qet, differs only in element uuids,
the known residual #779 leaves for elements; its folio uuid is stable.
This is the same problem #779 solved for conductors by not writing an
invented uuid back at all. That is not an option here: legacy folios
would never get a persistent uuid, which is the whole point of the
change. Instead, a folio without a uuid gets a name-based (version 5)
uuid, derived only from data read from the file:
QUuid::createUuidV5(<fixed QET folio namespace>,
"legacy" + project title
+ position of the folio in the file
+ folio title)
- The same input file always yields the same uuids, so resaving an
unmodified legacy project stays reproducible.
- The uuid is derived once, at load time, and saved from then on. After
that it is read, never recomputed: renaming, reordering or editing
the folio later does not change it. Renaming in the same session as
the migration does not change it either, since it was derived from
the title as loaded.
- Two people opening the same legacy file on different branches get
the same uuid for each folio, even if one of them reorders or renames
folios before saving. With random uuids the two branches would
disagree about every folio and a later merge could not match them.
- The folio content is deliberately not part of the name: QDom keeps
attributes in a hash whose iteration order changes between runs, so
hashing the content would need a canonical form for no real gain.
Folios are only guaranteed unique within their project. Two unrelated
legacy projects with the same title and the same first folio title get
the same uuid for that folio; anything keying folios globally has to
combine the folio uuid with a project identifier. (The project uuid is
not persisted yet; that is a separate change.)
Duplicated uuids
A hand-edited or merged file can contain the same uuid twice, e.g. a
folio copied by duplicating its XML block. Since the uuid is used as a
key, the second folio gets a derived uuid as well ("duplicate" + the
clashing uuid + the same inputs as above), so this case is
reproducible too. Should a derived uuid ever be taken already, which
takes a hand-crafted file, the name is salted with a counter until it
is free.
The namespace uuid is fixed in the code and must never change, or every
legacy folio would get a different uuid.
Tests (Qt 6.4, offscreen, qelectrotech --resave / --set-titleblock)
- 24 example projects, 3-4 resaves each from the same original: results
above; all folio uuids identical across runs, no duplicates within
any project.
- Resaving an already migrated file is byte-identical to the first
output.
- Renaming a folio in a migrated file keeps its uuid.
- Swapping two <diagram> blocks in a migrated file: each uuid moves
with its folio.
- Migrating and renaming in the same run (--set-titleblock title=...
on a legacy file) gives the same uuids as a plain resave.
- A file with a duplicated uuid: the second folio gets a new uuid, the
same one on every run.
- A migrated file opened with upstream master loads normally; the
attribute is ignored and dropped on save.
Refs #754, #779
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDyt4txaott5JyPNGQaeVp
Implements the second pillar of #574: keyboard-driven selection on the
diagram canvas.
Tab / Shift+Tab select the next / previous item on the current
diagram, cycling through items() (z-order) and wrapping at either
end. If nothing is selected, Tab selects the first item and
Shift+Tab the last. Skipped while a text item has focus, for the
same reason arrow-key movement already guards on !focusItem().
Candidates use the same "what counts as a real selectable diagram
item" filter (QetGraphicsItem / DiagramTextItem / Conductor) already
established by Diagram::invertSelection(), so the cycling order
always matches what a user could reach by clicking.
Getting Tab to actually reach the scene needed two separate fixes,
each independently discovered by empirical testing rather than
assumption:
- QWidget (DiagramView) intercepts Tab/Backtab for widget focus-chain
traversal before generating a key event at all. Overriding
DiagramView::focusNextPrevChild() to return false disables that.
- QGraphicsScene (Diagram) has its own, separate item-focus-chain
traversal, checked before keyPressEvent() is ever reached. The
obvious fix -- overriding Diagram::focusNextPrevChild() the same
way -- silently does nothing on Qt 5, because
QGraphicsScene::focusNextPrevChild() only becomes virtual in Qt 6
(guarded by the QT6_VIRTUAL macro); a compile error surfaced this
immediately when attempted directly, rather than shipping a fix
that worked on Qt 6 and silently no-opped on Qt 5. Intercepting
QEvent::KeyPress in Diagram::event() instead is virtual on every Qt
version and sidesteps the scene's internal traversal entirely.
Also adds Diagram::selectAllConductors() / selectAllTextFields(),
wired up as two new actions in the existing select_all /
select_nothing / select_invert action group in
qetdiagrameditor.cpp, so they appear in the Edit menu and go through
the same QAction -> data() -> selectGroupTriggered() dispatch as the
existing selection commands.
Verified end-to-end in a real running session (Xvfb + xdotool) with
a multi-transistor schematic: Tab/Shift+Tab correctly move a single
selection forward/backward through elements and text fields
(confirmed via the properties panel updating to each new item and
the visual selection box moving on canvas); Tab/Shift+Tab from no
selection correctly select the first/last item; "Select all
conductors" and "Select all text fields" each correctly select every
matching item and deselect everything else.
See discussion #574.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When copying and pasting selected areas, right-aligned dynamic text in
report and slave elements was not displayed correctly. The text
insertion point was always shifted to the left by the text width.
To correct this, the insertion point of dynamicElementTextItems is reset
to its origin insertion point before writing to clipboard.
Remove setter function : void BorderTitleBlock::setTitle(const QString
&title)
Remove singal diagramTitleChanged from BorderTitleBlock and use instead
the signal informationChanged.
Before this commit :
ElementPropertiesWidget emit a signal of Diagram to edit an element, and
the signal goes up from Diagram -> DiagramView -> ProjectView ->
QetDiagramEditor and QetDiagramEditor call a static function.
Now :
ElementPropertiesWidget call the static function itself and that all.
All unnecessary signals are removed.
diagram editor :
normal displacement "adjustable from 1 to 30"
(keys : Left,Right,Up,Down)
fine displacement "adjustable from 1 to 10"
(ALT + keys : Left,Right,Up,Down ), thanks Erik for his patch
git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@5749 bfdf4180-ca20-0410-9c96-a3a8aa849046
This avoid to parse the same element definition each time user drop the same element in the diagram.
Only the first element build the picture, all other get the created picture which is shared.
2- For use the "implicite shared" QPicture and QPixmap of element, now this isn't the element who build her picture, but a dedicated class who made only this job : build the QPicture and QPixmap of elements.
3- With the two previous novelty, the class CustomElement and GhostElement are became useless, and so was removed.
Some few member function of CustomElement was moved to Element.
git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@5492 bfdf4180-ca20-0410-9c96-a3a8aa849046