QGraphicsView::render() paints only what the scene draws, so a scene
without a background brush left the off-screen buffer uninitialized
and the inversion turned that memory into noise. The buffer is now
filled white first, which the inversion turns into the Base color.
listenToScene() connected a new receiver on every setScene() call and
never dropped the previous scene's. It now keeps a single connection
and replaces it.
Test in tst_qetpalette: paletteViewFillsWhatTheSceneLeavesBlank.
After a live light/dark switch only the sheet changed colors; the
viewport around it kept the previous palette. Qt repaints a widget on
an application palette change only when the widget's own palette
changed with it, and under the folio tab widget's style sheet it does
not, so the only repaints came from the scene and covered the scene
rectangle alone. PaletteGraphicsView now watches the application
object for ApplicationPaletteChange, the one receiver Qt always
notifies, and repaints its whole viewport.
The test paletteViewFollowsTheApplicationUnderAStyleSheet now also
requires a full-viewport repaint after each switch, before anything
asks the view for a rendering.
The folio tab widget carries a style sheet, and QStyleSheetStyle pins
the palette of every widget under it to the application palette in
force when the sheet was applied. After a live light/dark switch the
view's own palette() is therefore stale: the folio kept its dark sheet
after a switch to light, and kept its white sheet after a switch to
dark. PaletteGraphicsView now reads the application palette both for
the decision to invert and for the sheet and ink colors.
Test in tst_qetpalette: paletteViewFollowsTheApplicationUnderAStyleSheet
puts the view in a tab widget with a style sheet and switches the
application palette to dark and back.
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.
On a dark palette the folio stayed a white sheet with black ink, and
the white/gray toggle only darkened the sheet while the ink stayed
black. DiagramView now renders each repaint into an image and inverts
its lightness before blitting it: white becomes the palette's Base,
black becomes its Text, and colored conductors and elements keep their
hue. The document, printing and export are untouched; only the screen
rendering changes, and only while the palette is dark.
QET::Palette::invertLightness does the inversion in one integer pass
(adding 255 - max - min to the three channels inverts the HSL lightness
and keeps hue and saturation), then stretches the result between the
sheet and ink colors through three lookup tables. A 4K viewport costs
about 9 ms in a release build. QGraphicsView::render() skips the
selection rubber band, so the view draws it again after the inversion.
Tests in tst_qetpalette: invertLightnessMapsSheetAndInk,
invertedViewReadsOnDarkSheet, invertLightnessSpeed.
Hovering a tool button changed only its frame, and on the dark palette
Fusion's hover frame is too faint to notice, so nothing told the user
which button was under the pointer (GitHub #870, PR 3 of the plan).
QETStyle wraps the running style. For QIcon::Active it returns line-art
icons tinted in the palette's highlight color, lightened until the tint
reads at 3:1 on the Light role, which is the top of Fusion's hover
gradient. Colored icons keep their colors, using the same line-art rule
as misc/make_icon_themes.py. Every other mode goes to the base style.
Fusion also asks for QIcon::Active for the icon of a highlighted menu
item and paints it on the highlight bar, where the tint would vanish.
The generated pixmap is cached per icon and cannot tell a menu from a
toolbar, so drawControl(CE_MenuItem) hands the base style an icon whose
Active pixmap is in the HighlightedText color instead.
QETApp::initStyle() installs the proxy on every platform, keeping the
base style's object name so the Fusion checks still match.
tests/qttest/tst_qeticons: hovering changes the icon ink to the tint and
stays at 3:1 on both palettes, on a raised button and on a checked one
drawn sunken, where the frame gives no hover cue at all; a colored icon is unchanged on hover; a
highlighted menu item's icon reads at 3:1 on the highlight bar.
inkcontrast.h gains background() and ink() helpers for those checks.
tst_menubarkeyboard's control case presses Alt+F and expects the File
menu to open. On macOS it never did, for two reasons unrelated to the
F10 shortcut it guards: a QMenuBar is native there, so its menus live
in the system menu bar where QTest key events do not reach them, and Qt
does not turn "&File" into an Alt+F mnemonic on macOS at all. The test
has failed on every Mac build since bd6bed8d6 added it.
On macOS the test now uses an in-window menu bar and switches auto
mnemonics on for its own process, which runs the same QMenuBar code the
other platforms exercise. What it still does not prove on macOS is the
native bar: QETMainWindow::activateMenuBar() calls setActiveAction() on
a bar the system draws, and only F10 in the running application can
say what that does there. Other platforms are unchanged.
Fixes#948.
Both panels forced a light palette on themselves (white rows, black
text, their own selection blue) so that element previews, which are
black line art drawn for the white sheet, would stay visible on a dark
desktop (bugtracker 335). On a dark palette the two docks were the
only white windows left.
The forced palettes are gone. Element previews are now kept as drawn,
on a transparent background, and adapted where they are shown:
ElementPreviewDelegate, installed on the collection tree, hands the
view a copy with its lightness inverted when the palette is dark
(QET::Palette::forPalette), so black ink becomes the palette's light
gray while colored icons such as folders stay as they are; the drag
pixmap is adapted the same way. A light palette shows the previews
untouched. This fixes bugtracker 335 on every dark desktop rather than
masking it with a white panel.
The preview cache stored the old white-sheet pictures; it records the
format now and drops a cache written before this change once. The
amber "show this directory" highlight sets black text so it reads on
both palettes. The Projects panel only shows icons from the icon
theme, which has a dark variant, so nothing else changes there.
tests/qttest/tst_qetpalette: the line-art rule tells ink from color;
inversion keeps hue and alpha; a preview reads at 3:1 on the Base
color of both palettes; in a tree on the dark palette the delegate
inverts a line-art icon and leaves a colored one alone.
Fixes#945.
The folio icons (Add, Remove, Properties, New folio, Title block
template) were anti-aliased gray page drawings, and the previous commit
left them untouched on the dark palette, where their soft gray fills
read blurry next to the line-art icons. They are now pixel-grid SVGs in
ico/scalable/ in the style of the Add PDF icon: a landscape sheet with
a title block line, a plus or minus badge in the corner, text lines for
properties, a filled title block for the template. Same 24 pixel canvas
as pdf-import.svg, same currentColor recoloring for the dark theme.
Only the 22 pixel PNGs go: five files leave ico/22x22 and both .qrc
files, and the alias list in misc/make_icon_themes.py that exposed
three of them under a second name is down to conductor2.png. The 16
pixel files stay, so menus and the projects panel keep their icons at
that size, and the 128 pixel diagram.png stays for the configuration
page list.
tests/qttest/tst_qeticons: every file in ico/scalable/ resolves in both
themes at 22, 24, 32 and 64 pixels, dark ink on light and light ink on
dark, with no 22 pixel PNG left beside it; the light-art check reads
the folio family at 16 pixels, where the page art remains.
The "Add PDF" action used ico/22x22/pdf-import.png, a white page with a
red PDF mark that sat apart from its neighbors "Add text" and "Add
image", both gray line art in a square frame with a plus. It is now
ico/scalable/pdf-import.svg, the same pixel design as insert-image.png:
a frame, the letters PDF, a plus in the corner. One file serves every
slot and stays sharp on high-DPI screens; the PNG is gone from both
.qrc files.
The canvas is 24 pixels with the art offset by one, like the Breeze
SVGs already in the theme. Fusion's toolbar slot is 24 pixels: a 22
pixel PNG is drawn unscaled inside it, but a scalable icon is rendered
at the slot size, and a 22 pixel grid stretched to 24 puts every one
pixel line between pixels and reads blurry.
The file uses currentColor like the Breeze SVGs already in the theme,
so misc/make_icon_themes.py produces the dark copy the same way. A new
ico/scalable/ folder holds QET's own vector icons; the direction
arummler asked for in #690.
tests/qttest/tst_qeticons: the icon resolves in both themes at 16, 22,
32 and 64 pixels, dark ink on the light theme and light ink on the dark
one, and no 22 pixel PNG remains.
misc/make_icon_themes.py sorted icons by saturation alone, so a white
page with a small red mark counted as line art and its dark copy turned
the page black: the PDF import icon read black on black (#919,
Kellermorph), and the folio, diagram and label icons came out as dark
pages with a light border.
An icon whose visible pixels are at least 30% near white is now "light
art" and inherits from the qet theme untouched; it already reads on a
dark toolbar. The generator also removes dark files it no longer
produces, so a reclassified icon falls back to the light theme instead
of keeping a stale copy. Thirteen files leave ico/themes/qet-dark.
tests/qttest/tst_qeticons: every dark theme file, taken as its mean
visible color, reaches 3:1 on the dark palette's window color; the
lightest-pixel check it replaces let a black page with a light border
through. Asking the dark theme for pdf-import, diagram, label, the
folio icons and diagram_bg returns the light art.
"Est il possible dans les raccourcis d'ajouter un pour création
automatique de conducteur ? Je n'utilise pas par défaut, mais
ponctuellement c'est très pratique." -- oc67, an electrician, on the
forum (viewtopic.php?pid=23296).
The action itself has existed for a long time: m_auto_conductor is a
checkable QAction in the Schéma toolbar and the Project menu. It was
simply never handed to ShortcutManager, so it did not appear in
Configuration > Raccourcis and there was no way to reach it from the
keyboard. This registers it.
No default sequence is set. That is the request read literally -- he
asked for it to be *in* the shortcuts list so he can bind it himself --
and it avoids spending one of the few free keys on a setting many people
never touch. The Shortcuts page already treats "no shortcut" as a normal
state: it renders an empty field and its quick filter can list actions
with and without a binding separately.
Verified on a virtual display. With shortcuts/diagrameditor.auto_conductor
set to Ctrl+Alt+A:
Configuration > Raccourcis, filtered on "conducteur", lists
"Création automatique de conducteur(s)" under "Éditeur de schémas"
showing that binding.
Mouse parked away from the toolbar, pressing it twice: the toolbar
button changes on each press and returns to its starting appearance
after the second, so the key toggles the setting exactly as clicking
the button does.
Two things that misled the first run, recorded so the next person does
not repeat them: F7 is already registered to panel.move_diagram_downx100
in the elements panel, and a screenshot taken with the pointer resting on
the button shows its hover state, not its checked state.
ctest 12/12, Qt 6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CaKympWT3owLotCpEN2CFj
An electrician on the forum draws 400 V and 24 V circuits in the same
folio and wants the colour to be one click away
(qelectrotech.org/forum, viewtopic pid=23296). Today the quickest route
is F2, which opens a colour dialog and refuses to act unless exactly one
conductor is selected, so colouring a run means one conductor, one
dialog, at a time.
This adds a swatch button beside the auto-conductor actions. Picking a
colour does two things:
- recolours every conductor currently selected, as ONE undo step;
- becomes the colour of the next conductor drawn, through the
LastUsedStyle mechanism #888 already added and Conductor's
constructor already reads.
Either half is useful alone: with nothing selected it just sets the pen
for what comes next.
The menu lists the colours the trade names -- the three phases, neutral,
earth, and the ones used for control and extra-low-voltage circuits --
then any custom colours picked this session, then the full colour
dialog. A colour already in the standard list is not repeated under
"recently used".
Nothing is written to the project or to QSettings. That is deliberate:
it is the same session-scoped "what did I just use" idea as
LastUsedStyle, so it adds no persisted state and no file-format change.
Named presets stored per project -- what #461 actually asks for -- are a
larger feature that needs a maintainer decision first; the question is
still open on that issue since 21 June.
Verified on a virtual display against examples/Habitat-Schemas_developpes.qet,
reading colours back from the saved project rather than the screen:
select all on folio 1, pick Rouge
21 conductors {none:1, #ff5500:2, #ff0000:6, #00aa00:5, #0000ff:7}
-> all 21 #ff0000
one Ctrl+Z
-> back to the original five-colour mix, exactly
pick Marron with nothing selected, then draw a conductor
-> the new conductor is #7b3f00
ctest 12/12, Qt 6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CaKympWT3owLotCpEN2CFj
Issue #903 reported that Escape stopped cancelling an in-progress
placement. PR #899 fixed that by letting Escape through to the active
tool whenever Diagram::eventInterfaceIsRunning(), and 863ac5f0a tightened
it to isRunning() so a second Escape cannot retrigger an abort already in
flight. That covers seven of the eight tools. It cannot cover the eighth.
eventInterfaceIsRunning() is m_event_interface && isRunning(), and
isRunning() returns m_running. DiagramEventAddText is the only class
under sources/diagramevent/ that never sets m_running, so the guard reads
false for the whole time the tool is armed and DiagramView::keyPressEvent
keeps swallowing Escape for its own selection/focus handling.
It is also the only one of the eight with no RightButton branch --
right-clicking the folio with the text tool armed opens the folio context
menu. So this tool currently has no way to cancel at all: the only way
out is to pick a different tool, and any stray click drops a text field
the user did not want.
The tool is armed from the moment it is attached, so m_running is set in
the constructor and cleared where the text is placed, before finish().
Measured on a virtual display against a fixture holding one free text
field, counting diagram-level text fields in the saved project:
arm the text tool, click 2 fields places, as it should
arm it, press Escape, then click 2 fields before -- not cancelled
1 field after -- cancelled
The same run on master's rectangle tool cancels correctly, which is what
made the text tool look fixed when it was not.
ctest 12/12, Qt 6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Qt's QColorDialog loads custom colors from QSettings on startup but
never writes them back, so user-defined colors in the color picker
are lost when the application exits.
Save and load the 16 custom color slots explicitly via QSettings
in QETApp's constructor (after initStyle) and destructor (before
other settings are flushed). This covers every QColorDialog usage
in the application transparently.
Also fixes a QColorDialog memory leak in DiagramView.
189 of QET's 266 fixed-size icons are black line art with no dark
variant, so on a dark palette they were black on a dark toolbar, and
Fusion's disabled rendering lightened them into something more readable
than the enabled state (GitHub #466, #870; bugtracker 335 for the
element panels, which keep their own fix).
The theme "qet-dark" holds light-ink copies of the line-art icons in
ico/themes/qet-dark, generated by misc/make_icon_themes.py. Colored
icons are not copied; the theme inherits them from "qet". An icon counts
as line art when fewer than 20% of its visible pixels are saturated. The
copies keep hue and alpha and invert lightness, scaled so each icon's
darkest ink becomes (220,220,220), the dark palette's text color. The
eight SVG icons get their color replaced the same way.
QETApp::applyIconTheme() picks "qet-dark" for a dark palette and "qet"
otherwise. It runs from initIconTheme(), again from initStyle() once the
palette is final, and on the OS color scheme switch. Icons created with
QIcon::fromTheme() re-resolve on their next paint, so nothing else
changes.
With light-ink files, Fusion's own disabled rendering comes out dimmer
than enabled with no extra code.
tests/qttest/tst_qeticons: every name resolves in both themes, every
dark file has light ink, and a Fusion tool button shows its icon at 3:1
in both themes with disabled weaker than enabled.
Two fixes in the placement tool Ctrl+V starts.
Paste appeared on top of the original, not under the cursor. 55c2c0df9
added the placement tool precisely so the copy would not land invisibly
on top of what was copied, but 53a0f07ca then warped the pointer to the
group's grid-snapped origin -- which is the original's position -- so the
copy reappeared exactly over the original until the mouse was moved. The
start_pos the caller computes from the cursor was left unread: three
mentions in the file, all declaration or comment. Reported on #913,
where it reads as Ctrl+V pasting in place.
Move the group to the cursor instead of the cursor to the group. Both put
the copy under the pointer; only one of them takes the pointer away from
where the user put it. start_pos is honoured, the items are translated
once at construction, their conductors re-routed before anything is
drawn, and the movement baseline is set there too rather than waiting for
the first mouse move.
That made the baseline sentinel matter, so it is now the
m_baseline_captured flag the header already declared and nothing used,
rather than m_initial_cursor.isNull() -- which cannot tell "not set yet"
from a baseline that is legitimately scene (0,0).
Separately: one Ctrl+V killed the folio's right-click menu for the rest
of the session. init() sets Qt::NoContextMenu so a right click cancels
the placement instead of opening a menu over it, and nothing ever set it
back, taking "Coller ici", "Collage multiple", the folio properties and
add/remove column/row with it. Every other DiagramEvent* class restores
the policy in its destructor; this one did not. Confirmed against an
unmodified master build: the menu opens before a paste and not after one.
It matters here because "use the right-click menu instead" is the answer
people are given when Ctrl+V does not place where they wanted.
Verified on a built binary driven through a virtual X display, against
examples/741.qet and convertisseur.qet:
- the pointer does not move across Ctrl+V (1300,870 before and after;
on master it jumps to the original at 798,455), and the copy is under it
- a multi-element selection keeps its layout and its conductors re-route
- Escape and right-click both cancel, leaving nothing behind and nothing
on the undo stack; click and Return both commit; one undo removes the
whole paste and redo restores it
- with the pointer outside the viewport the copy lands at the viewport
centre, visible, and follows correctly once the mouse enters
- pasting into a different folio from the one copied from works
- the context menu opens before a paste, after a cancelled one, and after
a committed one, with "Coller ici" present
ctest 9/9.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>