QElectroTech now writes qet-assistant.json in the standard data folder
when an editor opens, and again whenever the stored scripts or the
scripting setting change: its version and program, every folder (data,
settings, scripts, the element and title block collections), which
features are on, every call a script can make (QetScriptApi::signatures(),
apiSignatures() without a running script), and the stored scripts with
their ids, files, icons and shortcuts, plus those refused and why. On
quit it says nothing is running any more.
The qet MCP server reads it rather than guessing each folder per
platform, which is wrong as soon as QElectroTech runs with --data-dir:
the file stays where the server can find it and names the folders
actually in use. Readable by the user only.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every .js file in the "scripts" folder of the user's data folder that
starts with a // ==QETScript== header becomes a command: in Projet >
Scripts, as a button on a new Scripts toolbar, and, because it is
registered with ShortcutManager as diagrameditor.script.<file name>, in
the shortcut settings, the shortcut bar (S) and command search. The
header gives its name, icon (a file next to the script or a theme icon;
a tile with its initials otherwise), tooltip, default shortcut and when
it is enabled (always, with a selection, with a conductor selected).
The folder is watched, so a script added, edited or deleted while QET is
open appears, changes or goes without a restart. A file with a header
that cannot be used gets no button; the Scripts menu lists it with the
reason. The menu also opens the folder, and holds "Exécuter un script…".
A click runs the script on the current project as one undo step named
after it, and asks to switch scripting on first, like "Exécuter un
script…" does: scripting stays off by default.
ShortcutManager::unregisterAction() takes a command out of the lists
when its script is deleted, and lets it come back under a new name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ctrl+V moves the pasted group under the pointer as before. The new
Ctrl+Shift+V ("Coller au point d'origine", diagrameditor.paste_origin)
leaves the group exactly where it was copied from and warps the OS
cursor to the group's grid-snapped origin, so the paste baseline and
the physical cursor agree and the group starts following the mouse from
its original place instead of jumping to the pointer.
The cursor warp is only trusted as the baseline when it verifiably
lands (QCursor::pos() agrees with the target): QCursor::setPos() needs
pointer focus and is a silent no-op on compositors without
wp_pointer_warp_v1. Otherwise m_baseline_captured stays false and
moveTo() re-baselines on the first real mouse move, so a refused warp
costs at most the first movement rather than flinging the group across
the folio. An origin outside the viewport is scrolled into view first,
since the compositor rejects warp targets outside the window.
The diagram toolbar gets an "Ajouter un arc" button next to the
ellipse. Click one end, then click the other end at the height the arc
should reach: the result is a half arc bulging up or down from the line
between the two clicks (Shift: a true half circle). It is not filled,
since a fill would close it into a half disc.
A selected half arc shows one more handle, in the middle of the curve.
Dragging it makes the arc deeper or flatter while both ends stay put;
dragging it across the line between the ends turns the arc over. The
handle is hidden whenever the arc is not a half arc on a horizontal or
vertical diameter, because only then is "keep the ends, move the
middle" one well-defined change.
Nothing new is stored: an arc stays an Ellipse with a start and end
angle, exactly as the existing arc handles of an ellipse save it, so
files read by older versions are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scrolls the folio so the point under the mouse becomes the centre of the
view, keeping the current zoom. Requested on the forum (topic 3199) for
moving around a zoomed-in folio the way KiCad and EasyEDA allow;
"Zoom sur le contenu" (Ctrl+8) was the closest existing command, but it
also refits the zoom.
The command is in the Affichage menu and in the shortcut editor. It has
no default key: F4 and the single letters are already taken or would
clash with typing. It does nothing when the cursor is not over the view,
so choosing it from the menu is harmless. Near the folio border the view
cannot scroll past the edge, so the point ends up as close to the centre
as the scroll bars allow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A template (.qetmak) was previewed and placed offset from the cursor by
the position its items had on the folio it was saved from: the preview
pixmap kept that offset, and addMacro() added it back before handing the
position to fromXml(). A template saved from the middle or the lower
right of a folio therefore landed far below and to the right of the
click, often off the folio, which reads as "the template does not drop"
(forum topic 3190). Anchor both the preview and the placement on the
template's own top-left corner instead.
fromXml() skips translating for a null position, so a click on the folio
origin is nudged by a fraction of a pixel that the grid snap removes.
Also:
- a template saved with "Create a template" only appeared in the
templates tab after reloading the collections or restarting; it is
now added to the tab of every open editor when it is saved;
- the placement status bar message ended in an untranslated
"(Makro-Anker)"; it now reads "x : y" like element placement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The seven Edit > Align commands (Snap to grid and the six align
commands, discussion #1069) had no icons: the menu, the context menu,
command search and the shortcut bar showed them as bare text.
They are pixel-grid SVGs in ico/scalable/ drawn like the folio and
Add PDF icons: 24 pixel canvas, art on the inner 22 pixel grid, one
currentColor ink so misc/make_icon_themes.py writes the dark copies.
Each align icon is a guide line with two boxes of different lengths
against it; the centre ones show the guide only between the boxes.
Snap to grid is a faint grid with one box sitting exactly on a cell.
The boxes have a solid outline and a 35 % tinted inside, which keeps
the guide the strongest mark at 16 pixels.
Names follow the freedesktop icon names (align-horizontal-left, ...).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rectangles, ellipses, lines and polygons were left out of the Align
submenu: with only shapes selected every command was greyed out, and a
shape outside a group was ignored when aligning it with a symbol.
Shapes now take part like pictures. Their edges are the shape as drawn
(the new QetShapeItem::sceneOutlineRect(), without the pen, the 6 px
selection margin or the wider hover outline; the old code used
sceneBoundingRect() for grouped shapes and so aligned them 6 px off).
The point that goes on the grid is the top-left corner of that box: a
rectangle's corner, an ellipse's box. pos() is not used, because a
shape drawn with Ctrl held or rotated has its corners off the grid
while pos() is on it.
The menu's enable rule now counts what the command counts, a group as
one. Before, two symbols in one group enabled the six align commands,
which then did nothing and only said so in the status bar.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Group and Ungroup are enabled in slot_updateComplexActions(), which ran
only when the selection changed. Grouping, ungrouping and undoing either
leave the selection as it is, and so does right-clicking an item that is
already selected, so the actions kept the state from before. The
right-click menu hides disabled actions, so it went on showing Group on a
selection that was now one group (and Ungroup after ungrouping), and the
one that would work was not in the menu at all.
Diagram::setItemGroup() is the one place an item's group changes (group,
ungroup, undo, redo, paste), so it now emits itemGroupChanged() and the
editor refreshes its actions on it, beside the selectionChanged
connection.
Checked in the GUI on two free texts, saving after each step: right-click
> Group, right-click again, Ctrl+Z, right-click again. Before, the second
and third right-clicks offered Group again and did nothing (both saves
still grouped). After, they offered Ungroup and ungrouped (0 grouped
texts in both saves); Group, Ungroup, Group also round-trips.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A wire whose terminals have uuids is saved against them, and on load it
is reattached to a terminal with that uuid or dropped, with only a qDebug
line. Re-importing a changed symbol and choosing "replace" swapped the
project's definition for one whose terminal uuids differ; the placed
symbols kept the old ones until the project was reopened, so every wire
on them was lost at the next open, silently, and gone for good at the
next save.
- XmlElementCollection::copyElement(), where an embedded definition is
overwritten, carries each old terminal uuid onto the new terminal at
the same place and orientation (TerminalUuids::keep()). Terminals that
moved, and new ones, keep their own.
- Diagram::fromXml() records wires it could not reattach, logs them, and
the editor lists them in one warning after opening a project.
Measured on 2612_ats_singlephase.qet with the stored splice's terminal
uuids made to differ from the collection's: replace, save, reopen loads
34 of 131 wires on master, 131 with this change (GUI, both arms).
tst_terminaluuids covers keep() and runs the real loader.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Discussion #1070 proposed that rotate, like move, copy and delete, works on
the whole group once one of its items is clicked. Rotate (Space) turned
each member on its own spot instead, so rotating a group pulled it apart:
two grouped texts side by side ended up each turned in place, no longer
side by side.
When the selection is exactly one whole group -- wires aside, which follow
their symbols -- Rotate now turns it as one piece around its centre, as
"Pivoter le groupe" (Shift+Space) already does
(ItemGroups::soleWholeGroup()). Any other selection, including a single
member picked out of its group, rotates as before.
In the GUI, on two grouped texts selected by one click: Space on master
leaves both where they were, turned; here it gives exactly what
Shift+Space gives on both (both texts swung around the group's centre).
tst_itemgroups: 4 new checks; without the whole-group condition, a
picked member counts as a group and fails. ctest 24/24.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Edit > Aligner gains six commands, also in the selection's context menu
and the command search: Aligner à gauche, Centrer horizontalement,
Aligner à droite, Aligner en haut, Centrer verticalement, Aligner en bas.
One undo step; wires follow their symbols. Second stage of discussion
#1069, on top of "Aligner sur la grille".
Left/right/top/bottom line the edges up on the outermost one. The two
centre commands line the items up on the mean of their centres, and a
symbol's centre is its origin point, not the middle of its drawing: in
the collection, vertical two-terminal symbols almost always have their
terminals on the origin's axis, so this puts their wires on one line.
A picture is aligned by the picture itself, without its caption
(imageRect() becomes public for this).
A group (#1070) lines up as one piece: its edges are its members'
together, and every member moves by the same amount, so the group keeps
its shape; shapes inside a group come along.
Each item moves only across the line it is aligned on, and lands on the
grid its drag uses, so aligning never takes a symbol off the grid. Two
symbols whose edges sit at different distances from their origins
cannot both be exactly on the line and on the grid; they end up within
half a grid step of it.
The commands need two items (Aligner sur la grille still needs one).
Locked items stay put and the status bar says so. If nothing moves, no
undo step is pushed and the status bar says the selection is already
aligned as far as the grid allows. The geometry is in alignment.h,
tested by tst_alignment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The "Orienter les textes" dialog always opened at 0, so turning a text
at 90 degrees by a little meant typing the angle in again. It now opens
at the angle every selected text and text group shares, and at 0 as
before when they differ.
The shortcut bar showed only a command's short name as its tooltip,
which for this one ("Choose texts orientation") does not say what it
does. It now adds the command's status tip on a second line: "Rotate
selected texts to a specific angle".
Checked in the GUI on a folio holding one text at 90 degrees: master
opens the dialog at 0.00, this at 90.00.
Fixes#1082
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both branches added an undo command, an include and a test target at
the same spots; kept both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Symbols, free texts, shapes and pictures can be grouped from the Edit
menu, the selection's context menu or the command search. Clicking one
member selects the group, so moving, copying and deleting act on all of
it; Ctrl+click on a member deselects the group; a rubber band touching
part of a group selects all of it when released. No default shortcut:
Ctrl+G is "jump to element".
A group is not an object in the scene. Each member keeps its place and
carries the group's uuid (QGraphicsItem::data()), saved as a "group"
attribute written only when set: a project without groups saves exactly
as before, and older versions open a grouped one and ignore the groups.
Re-parenting under a QGraphicsItemGroup would have made every member's
position group-relative; ElementTextItemGroup already needs nine special
cases for that.
- Selection is completed on clicks and at the end of a rubber band, not
on every selectionChanged(): export, search and Tab select items
themselves and must not have groups pulled back in.
- Project database: group_uuid on element, shape, independent_text and
image, kept in step by projectDataBase::itemGroupChanged().
- Paste and folio duplication give each source group one new uuid.
- Undo of Ungroup restores each item's exact group.
Builds on #1065 (uuids and database rows for texts, shapes and images).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Edit > Aligner > Aligner sur la grille, also in the selection's
context menu and the command search, puts each selected item back where
dragging it would have left it: symbols and pictures on the folio grid,
free texts on the text grid. One undo step; wires follow their symbols.
Symbols leave the grid through the fine nudge (Alt+arrow, 1 px), and
nothing put them back: 508 of the 3,678 symbols in the example projects
are off the 10 px grid. First stage of discussion #1069.
The status bar says how many items moved, or that the selection was
already on the grid, and how many locked items were left in place.
Shapes are left out: they are made of several points and no single one
is the obvious one to snap.
The snap never reads the keyboard, unlike Diagram::snapToGrid(), so a
user who binds the action to a shortcut with Ctrl gets the grid, not
pixel rounding. The geometry is in alignment.h, tested by tst_alignment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Enter on a folio runs the last drawing or placing command again, as it
does in SolidWorks: the last tool from the add-item family (text, line,
rectangle, terminal strip plan...) or the last element placed, however
it was placed -- dragged, double-clicked, picked, or from the shortcut
bar.
Enter is handled in DiagramView::keyPressEvent, not bound as a shortcut,
so it keeps working everywhere else: search fields, the collection
tree, dialogs. On the folio it is left alone while a tool is running or
an item has the focus (a text being edited), and with any modifier held.
The same command is in Édition, named after what it will do --
"Répéter : Ajouter une ligne", "Répéter : insérer « Diode »" -- and
registered with ShortcutManager (diagrameditor.repeat_last_command, no
default key), so it can be bound or put on the shortcut bar.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 1e06ce97961ee18734ddaaa9b72b0303a169b9b2)
After a click that selects something on a folio, a small row of commands
appears just above and to the right of the cursor, as the SolidWorks
context toolbar does. It fades as the mouse moves away and is gone past
200 pixels; a click elsewhere, the wheel, a key press or an emptied
selection hide it too. Clicking a command leaves it up, so rotate can be
clicked again.
The commands are the shortcut bar's for that selection -- elements or
conductors -- at most eight, so customising the bar customises this as
well. It is a child of the view's viewport, never a window, and never
takes the focus.
It is not shown after a drag (moving items, a rubber band), while
placing or drawing (Diagram::eventInterfaceIsRunning()), on
a read-only folio, or when switched off with the new General option
"Afficher les commandes près de la sélection" (diagrameditor/
context_toolbar, on by default).
ShortcutBarSettings::contextFor() now decides the context for both the
bar and this toolbar.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 78f3c1a2bc5c3cf1f7beae4bd60aefec20847d6d)
Right-click the shortcut bar, or click the "…" button at its end, and it
turns into a small window holding two lists: the bar's commands, left
to right, and every other command. Drag a command onto the bar, off
it, or to another place on it; a double click moves it to the other
list. Terminé saves and shows the bar again where it was, with the
result; Annuler, Esc or closing the window leaves it as it was.
"Valeurs par défaut" puts back the defaults for this context.
A Qt::Popup closes on a press outside it and holds the mouse grab, so
the bar is re-shown as a Qt::Tool window for the time of the edit.
Only QSettings is written, through ShortcutBarSettings, never the
project's undo stack.
The popup now looks the commands up itself (popUpShortcutBar(pos,
context)) instead of being handed actions, since it has to rebuild
them after an edit. ShortcutBarSettings::availableIds() lists what can
go on the bar, shared with the configuration page. An empty bar still
shows its "…" button, so it can be filled again.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 4145d4bec1d403126b2a6587105aea47092db8a2)
Pressing S on a folio opens the element picker at the cursor with a row
of commands above it, chosen by what is selected, like the SolidWorks
shortcut bar:
nothing selected insert last element, element picker, text, line,
rectangle, terminal strip plan, paste, folio
properties
elements selected rotate, rotate texts, edit, copy, cut, delete
only conductors reset path, edit, delete
Each row is a list of ShortcutManager ids, so any registered command can
go on it and the bar carries no command list of its own. The lists are
in QSettings (diagrameditor/shortcut_bar/<context>); a context the user
has not changed follows the defaults. A disabled command keeps its
place, greyed, so a row looks the same each time. Clicking a button
closes the bar and triggers the action.
A new configuration page, "Barre de raccourcis", edits the three lists:
add, remove and reorder any diagram editor command.
To make that possible:
- ShortcutManager::action(id, owner) returns the action a given window
registered under an id, since each editor window registers its own.
- The add-item actions (text, image, shapes, terminal strip plan) are
registered as diagrameditor.add_<kind>, with no default key. They also
appear in the Shortcuts page and can now be bound.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 53c1e213282b9f7226ca1c09e81703113236bb88)
Insert opens a small picker where the mouse is, with its search field
focused. Type to search the whole collection, Enter to place the best
hit (it is preselected), Up/Down to choose another, Esc to close. The
chosen element goes into the usual placement mode, so it can be placed
several times and repeated with A.
With the field empty the picker shows a palette: the elements of a
folder, as an icon grid. The palette is a folder rather than a setting
or a file format. Subfolders are read in, the 01_/02_ filename prefixes
the shipped collection already uses give the order, and sharing it is
putting it in the company collection. Only the path is stored,
"elementscollection/palette-path", defaulting to the user collection.
It is read each time the picker opens, capped at 60 entries and three
folder levels.
The picker builds no second collection model. It asks the Collections
panel's rankedSearch(), so both give the same results in the same order
and startup is unchanged.
"Insérer un élément…" is in the Édition menu, registered with
ShortcutManager on Insert, which nothing else uses, and disabled with no
folio open or on a read-only project.
Discussion #676.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 01e1e0303b96380e710da8d4bb2300b21d01e938)
Ajouter une ligne, un rectangle, une ellipse... had no ShortcutManager
id, so the command search could not list them. Give each one an id with
no default key; they can also be bound in the Shortcuts page now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ctrl+Shift+M (Édition → "Rechercher une commande…") opens a small
search box at the cursor listing every command of the diagram editor
window, as SolidWorks' "Search Commands" and the command palette of
many editors do. Typing narrows it, best match first: name starting
with the text, then a word starting with it, then containing it.
Matching ignores case, accents and mnemonic "&", so "editer" finds
"Éditer l'item sélectionné". Each row shows the command's key when it
has one, which also teaches the keys. Disabled commands are listed,
greyed, and cannot be run. Enter runs the highlighted one after
closing the box; Esc closes.
The list is ShortcutManager's registry, restricted to the actions this
window owns (ShortcutManager::action(id, owner)), so a second editor
window's commands never appear and nothing has to be listed by hand.
Ctrl+Shift+P, the usual key for this, is already the autonumbering
dock's.
tst_commandsearch covers the folding, the ranking, that another
window's commands are left out and that a disabled command does not
run; both behaviours were checked to fail the test when broken.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Affichage › "Afficher les limites des cases" draws a faint dashed line
at every column and row limit of the folio border, so that zoomed into
the middle of a folio you can see where a cell ends, not only which one
the headers name. Off by default, remembered once set
(diagrameditor/cell_lines).
Drawn by DiagramView::drawBackground(), under every item and only in
the view: printing and export render the Diagram, never the view, so
they cannot pick the lines up. PaletteGraphicsView gains scenePainter()
so a subclass can draw there on the inverted (dark palette) path too.
The lines follow the border's own cell positions, and each direction is
hidden when the folio hides that header.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DiagramEventAddElement is already a good placement mode: the element
follows the cursor on the grid, a left click drops it, Space rotates it,
and it stays loaded for a run of the same symbol. Its only caller was
DiagramView::handleElementDrop(), so it could be reached only by
finishing a drag. Double-clicking a symbol in the Collections dock
opened the element editor instead.
- DiagramView::startElementPlacement() is split out of
handleElementDrop(). defaultPlacementPos() uses the cursor when it is
over the view and the centre of the visible area otherwise.
- ElementsCollectionWidget emits insertElementRequested() on double
click, or Enter on the highlighted item. The host decides which view
receives it, so the widget can later be reused outside the editor.
- When there is nowhere to place it (no folio open, read-only project),
the element editor opens, as a double click did before.
- "Insérer le dernier élément" (Édition menu, default key A) places the
last element again. DiagramView reports every placement it starts, so
an element dropped by drag counts too. Macros are not remembered.
A rather than Space: Space rotates the pending element inside placement
mode and is bound three more times in this editor. The key is a
ShortcutManager default and can be changed in the Shortcuts page.
Double click placing is a behaviour change, so it has a preference,
"elementscollection/double-click-inserts" (default true), shown in
Configuration as an opt-out: "Double-cliquer dans la collection ouvre
l'éditeur d'élément au lieu de l'insérer".
Discussions #676 and #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Right-clicking an empty folio offered Paste here, Folio properties and
four row/column actions. Four of six entries change the folio's layout,
which is rarely wanted and easy to hit by mistake, while nothing in the
menu helps draw.
The empty-folio menu now holds the "Ajouter" submenu (text, image,
shapes, terminal strip plan -- the same actions as the Edition menu and
toolbar), then Folio properties, then the row and column actions in a
"Lignes et colonnes" submenu. The selection menu is unchanged.
A submenu whose actions are all disabled is dropped, the same way
disabled actions already are.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Drop 1:2.5: on a grid of 10 it steps by 4, which misses 10, so texts on
two elements 10 apart could never line up. Every remaining divisor is a
whole number, which tst_textgrid checks.
The divisor list and the snapping arithmetic move to the header-only
textgrid.h so the toolbar menu, the preferences page and the test share
them. The preferences page gets the same choice under Grille + Clavier;
QETApp::textGridChanged keeps every editor's toolbar button in step.
While element texts are dragged, the status bar names the text grid and
says to release Shift and hold Ctrl for free placement -- Ctrl+Shift
together is the pan shortcut.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Texts snapped to the folio grid, so the first drag of an off-grid label
pulled it sideways by up to half a grid step (discussion #1020). Texts
now snap to a fraction of the folio grid, chosen from a "Textes 1:N"
button in the View toolbar and a menu under Affichage: Off, 1:1, 1:2,
1:2.5, 1:5, 1:10. Because the step divides the folio grid, texts on
different elements still line up. Ctrl still places a text freely.
1:1 is the default and matches the previous behaviour. The setting is
stored in QSettings; nothing changes in saved projects.
All five text movers switch together: element texts, text groups,
texts moved with a selection, conductor texts and independent texts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
Affichage > "Garder les en-têtes visibles" adds a bar along the top and
the left of the diagram view that repeats the folio's column numbers and
row letters, aligned with the cells at any zoom, so they stay in sight
when the folio's own headers are scrolled away. Off by default; the
choice is stored as diagrameditor/cell_rulers.
The bars are CellRuler widgets in the view's margins
(setViewportMargins), not scene items, so printing and PDF/PNG/DXF
export never see them, and they paint with the application palette
outside of the dark-palette inversion. They keep a constant thickness;
when cells get narrower than their labels, only every 2nd, 5th, 10th...
label is written. A bar is hidden when the folio hides that header.
Showing or hiding them keeps the centre of the view where it was.
The labels come from BorderCellLabels, now also used by
BorderTitleBlock::draw(), so the bars and the border cannot disagree.
PNG export of all 133 folios of the examples is pixel-identical to
master, with border-columns_0 true and false.
Known limits: changing border-columns_0 repaints the bars at the next
scroll or zoom; the menu toggle updates the views of its own editor
window only, like the grid toggle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
After a crash, QElectroTech offers to reopen its recovery files. If one
cannot be read, answering OK crashed the program instead of showing the
"could not open" warning: QETProject(KAutoSaveFile *) takes ownership of
the file and deletes it on failure, and openBackupFiles() then read the
file name from the deleted object to build the warning. Read the name
first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
RotateTextsCommand called QDialog::exec() from inside its constructor, so
the command could not be built without a human answering a dialog. That
made it untestable headlessly, undrivable from any script or test harness,
and it is why bugtracker #312 (PR #707) shipped with its save/reload
round-trip unverified -- the symptom could not be reproduced without a GUI.
The command now takes the angle as a parameter and does no asking. Two
statics carry the interactive half:
hasSelectedTexts(diagram) -- is there anything to rotate
askRotation(rotation) -- open the dialog, false if cancelled
The single call site in QETDiagramEditor asks first, then builds the
command, so the user-visible behaviour is unchanged: same dialog, same
title, same no-dialog-on-empty-selection. Keeping askRotation() in this
class also keeps the QObject tr() context, so existing translations of
"Orienter les textes sélectionnés" are not invalidated.
Also guards undo()/redo() against a null m_anim_group. When nothing is
selected the constructor calls setObsolete(true) without ever creating the
animation group, and QUndoStack::push() calls redo() before discarding an
obsolete command -- a latent null dereference on that path.
Verified headlessly, which was the point: driving the command through a
scratch --test-ops op on examples/741.qet (67 conductors), rotation
attributes written on save go 0 -> 67 with the #707 fix present and stay
at 0 with it reverted, while the reverted build instead writes userx on
all 67. That is bugtracker #312 reproduced and fixed under test for the
first time.