Commit Graph

4190 Commits

Author SHA1 Message Date
elevatormind 78caeaec48 Merge branch 'qelectrotech:master' into no-sqlite 2026-09-21 07:34:32 +02:00
Magnus Hellströmer 27dea3ffab refactor: use VACUUM INTO for database export
Remove the native SQLite backup API and direct SQLite library
dependency.
2026-09-20 20:49:02 +02:00
Laurent Trinques 8e3a7a6ab6 Merge pull request #927 from ispyisail/fix/923-snap-element-text
Snap a device's text to the grid when it is dragged (#923)
2026-09-20 20:10:13 +02:00
Laurent Trinques 740271c993 Merge pull request #938 from arummler/fix-translation-syntax
Fix various singular/plural cosntructions
2026-09-20 20:08:53 +02:00
Laurent Trinques 1bec2700bd Merge pull request #924 from Kellermorph/auto-numbering-new-project
Add global auto-numbering rules to QElectroTech settings
2026-09-20 20:07:16 +02:00
Laurent Trinques 268e9c7bd9 Merge pull request #959 from jp2images/fix-dark-canvas-item-updates
Keep the dark canvas on QGraphicsView's own update path
2026-09-20 20:02:50 +02:00
Laurent Trinques 264a0f677f Merge pull request #964 from jp2images/remove-stray-sbom-files
Remove two files that slipped into #944
2026-09-20 20:02:27 +02:00
Jeff Patterson 04463f0954 Darken the hover ink on a light button face
QETStyle::hoverColor() lightened the highlight color until it read at
3.5:1 against the Light role. On a dark face that is the way to go; on a
light face lightening only fades the ink, so with a pale platform accent
that QET keeps (macOS's green selection color, black selection text) the
loop ran to white and every hovered line-art icon vanished. The ink now
moves away from the face, darker on a light face, lighter on a dark one,
and falls back to the button text color if twenty steps are not enough.

The hover test gets a row with that accent on each palette, and a new
test sweeps accents across hues and lightness on both palettes and
requires the hover ink to read at 3:1 on the face.

Fixes #962
2026-09-20 12:45:07 -05:00
Laurent Trinques 3245919c19 Merge pull request #961 from jp2images/fix-small-page-icons
Give every configuration page an icon at page size
2026-09-20 19:44:00 +02:00
Jeff Patterson 55ad2eccbf Remove two files that slipped into #944
sources/qetsbom.cpp and sources/qetsbom.h were untracked local files
that a directory-wide add swept into the rebuilt #944 commit. Nothing
references them; the build does not compile them.
2026-09-20 12:43:57 -05:00
Jeff Patterson 8b2548d601 Keep the dark canvas on QGraphicsView's own update path
Laurent found that moving an element on a #954 build left its terminals'
help lines behind at every step, on both palettes. The view listened to
QGraphicsScene::changed() so that render() would keep the scene's updates
flowing, and any receiver on that signal puts the scene on its Qt 4.4
compatibility path, which erases a moved item's own old rect only:
children bigger than their parent stay on screen. Master hides that with
FullViewportUpdate, which repaints the whole viewport on every change.

Paint the inverted folio through QGraphicsView::paintEvent() instead, with
IndirectPainting set for that call and the draw hooks painting into a
viewport-sized image, and hand the scene the viewport when the items are
drawn so it records where each item was painted, including a child whose
geometry is set while its parent paints. The listener and the
full-viewport update go. Two tests move a parent with a sheet-wide child,
read the backing store, and require the repaint to be a partial one.
2026-09-20 12:43:50 -05:00
Jeff Patterson 16d89f64fd Refresh style-sheet widgets after a live palette switch
Switching the system between light and dark while QET runs changed
the palette of every plain widget but left widgets that carry a style
sheet in the colors they were created with: the folio tab bar stayed
light in dark mode, and after a dark-to-light switch its Add folio and
chevron buttons hovered as a near-black box with the icon lost inside
it. QApplication::setPalette() does not reach a widget with a style
sheet; QStyleSheetStyle resolved its palette once, when the sheet was
applied, and keeps it. Seventeen call sites set a sheet on a widget and
five .ui files carry one, so any of them could show the stale palette.

QET::Palette::refreshStyleSheets() re-applies each such widget's own
sheet, which makes QStyleSheetStyle resolve it against the palette now
in force. QETApp::useSystemPalette() calls it after installing the
palette, so both the OS color scheme change and the "use system
colors" setting are covered.

tests/qttest/tst_qetpalette: a tab widget with the folio tab bar's
sheet is still drawn in the old colors after setPalette(), which is the
defect, and follows the palette after refreshStyleSheets(), in both
directions.

Fixes #943.
2026-09-20 12:20:01 -05:00
Jeff Patterson ac78c51b70 Give every configuration page an icon at page size
The settings and project dialogs list their pages with 64 or 128 pixel
icons, and two pages had theirs at 22 pixels only: the terminal-strip
page and the shortcuts page, which borrowed configure-toolbars. Both get
a 128 pixel icon drawn in the style of the other page icons, the
shortcuts page under its own name, configure-shortcuts. The SVG sources
sit beside the PNGs.

On a dark palette the Printing and Export pages were small too: their
128 pixel icons exist in the light theme only, and Qt inherits by name,
not by size, so the dark theme's small copies were scaled up instead.
make_icon_themes.py now aliases the light files of the sizes a dark name
lacks, when they read on the dark window at 3:1.

A test asks the theme for every page icon at 128 pixels, on both
palettes.

Fixes #960
2026-09-20 10:44:09 -05:00
Laurent Trinques d1808c7a89 Fix Draw the folio with inverted lightness on a dark palette- #954 2026-09-20 13:07:57 +02:00
Laurent Trinques 242f134e0f Revert "Revert "Draw the folio with inverted lightness on a dark palette"" 2026-09-20 13:05:13 +02:00
Laurent Trinques 75fafd5acc Revert "Draw the folio with inverted lightness on a dark palette" 2026-09-20 06:57:56 +02:00
Jeff Patterson 3e34a6d55f Fill the dark canvas buffer and keep one scene connection
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.
2026-09-19 18:27:00 -05:00
Jeff Patterson 50792ba1ad Repaint the whole dark canvas when the application palette changes
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.
2026-09-19 18:27:00 -05:00
Jeff Patterson 6404612014 Follow the application palette, not the view's, on the dark canvas
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.
2026-09-19 18:27:00 -05:00
Jeff Patterson eaf15faaa3 Move the dark canvas into PaletteGraphicsView and test it directly
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.
2026-09-19 18:27:00 -05:00
Jeff Patterson b8c9e670c4 Keep scene updates flowing on the dark canvas and soften its grid
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.
2026-09-19 18:26:45 -05:00
Jeff Patterson 85dc638a42 Draw the folio with inverted lightness on a dark palette
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.
2026-09-19 18:26:45 -05:00
Laurent Trinques dfe56cddbd Merge pull request #928 from ispyisail/fix/903-escape-cancels-text-tool
Let Escape cancel the text tool, like every other placement (#903)
2026-09-19 23:43:31 +02:00
Laurent Trinques eb7abdfbc6 Merge pull request #946 from jp2images/feature-panels-follow-palette
Let the Collections and Projects panels follow the palette
2026-09-19 20:24:05 +02:00
Jeff Patterson 4e43cde063 Give icons a hover state through a proxy style
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.
2026-09-19 12:26:06 -05:00
Jeff Patterson 0395f90f75 Let the Collections and Projects panels follow the palette
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.
2026-09-19 12:22:24 -05:00
Kellermorph 7fa1ff35b8 Add global auto-numbering rules to QElectroTech settings
Add a 'Numérotation auto' tab to the global settings page (Settings >
Nouveau projet) where users can define default auto-numbering rules
for Conducteurs, Eléments, and Folios. These rules are automatically
transferred to every new project created.

Changes:
- Add NumerotationContext::saveToSettings()/loadFromSettings() static
  helpers for persisting named numerotation contexts via QSettings
- Add 'Numérotation auto' tab to NewDiagramPage with three sub-tabs
  using SelectAutonumW widgets (same UI as project properties)
- Add save/remove/persist slots for conductor, element, and folio
  contexts with immediate QSettings persistence on every change
- NewDiagramPage::applyConf() saves autonum settings when editing
  global defaults (no project)
- QETProject constructor loads global autonum settings from QSettings
  for new empty projects
2026-09-19 13:47:28 +02:00
Laurent Trinques a6b4c3c673 Merge pull request #930 from ispyisail/feat/923-forum-auto-conductor-shortcut
List auto conductor creation in the Shortcuts page
2026-09-19 13:04:44 +02:00
Andre Rummler 7e3420b1a5 Avoid constructing sentences. 2026-09-19 10:39:54 +02:00
Andre Rummler 183035d5ac Replace enumerator list. 2026-09-19 09:15:28 +02:00
Andre Rummler eb4109bb11 Replace manual enumerator sentence with qt trasnlator automatism. 2026-09-19 09:08:25 +02:00
Andre Rummler 717bf57677 Improve plural marking from previous commit. 2026-09-19 09:08:00 +02:00
Andre Rummler 861e5de25e Use qt own enumeration translation function. Fix plural forms. 2026-09-19 08:31:34 +02:00
ispyisail 70599c4ae1 List auto conductor creation in the Shortcuts page
"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
2026-09-19 09:18:04 +12:00
ispyisail e42ebdc861 Add a conductor colour button to the Schéma toolbar (#461)
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
2026-09-19 09:02:58 +12:00
ispyisail 3c1fb2fe72 Let Escape cancel the text tool, like every other placement (#903)
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>
2026-09-19 08:32:58 +12:00
ispyisail 95d0e523fe Snap a device text to the grid when it is dragged (#923)
Dragging an element's text -- its label, article number, any of its
information fields -- moved it in free one-unit steps while everything
else in the editor snapped to the grid. Reported by pki791 in #923 for
labels moved with Shift.

QET moves a text with the mouse along five paths. Four snap and let Ctrl
place freely:

  DiagramTextItem::mouseMoveEvent        an independent text
  ElementTextItemGroup::mouseMoveEvent   a group of element texts
  ElementTextsMover::continueMovement    every OTHER selected element text
  QetGraphicsItem::setPos                elements, images, shapes

DynamicElementTextItem::mouseMoveEvent, the text actually under the
cursor, ended "setPos(new_pos)" with no grid and no modifier check. It is
otherwise the same function as the group's, which is why this reads as an
omission rather than a decision: the line this adds is that function's,
character for character.

The inconsistency was visible in one gesture. With two element texts
selected and one of them dragged, ElementTextsMover skips the driver item
and snaps the rest, so the text under the cursor was the only one on the
folio that did not land on the grid.

Verified on a virtual display (Xvfb + openbox) against a two-lamp fixture,
grid 10, reading the saved positions rather than the screen:

  Shift+drag the label      before (32.95, -11.55) -> (7.95, 23.45) off-grid
                            after                  -> (10, 20)      on-grid
  the co-selected label     (10, -10) -> (50, 20) on-grid, before and after
  Shift to grab, then Ctrl  -> (7.95, 23.45) off-grid, free placement kept

The last line matters: moving an element text needs Shift at press, and
the modifier is read at move time, so Ctrl still places freely -- press
with Shift, hold Ctrl to drag. Holding both from the press is a different
gesture, reserved by DiagramView::isCtrlShifting() for the view's mode
switch, and does not move the text at all. Nothing that was possible
before is lost.

Worth knowing when reviewing: 470 of the 492 element texts in the 24
example projects (95.5 %) sit off the grid today, because element
definitions place their default text at fractional offsets. The first
drag of almost any existing label will pull it onto the grid, by at most
half a grid step.

ctest 12/12, Qt 6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 08:06:45 +12:00
Laurent Trinques 2da516667e Merge pull request #918 from Kellermorph/fix-colour-selection
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m41s
Persist QColorDialog custom colors across application restarts
2026-09-18 19:36:25 +02:00
Laurent Trinques b62b118d45 Merge pull request #919 from jp2images/feature-icon-themes
Add a "qet-dark" icon theme for dark palettes
2026-09-18 19:00:47 +02:00
Laurent Trinques b0b7814015 Merge pull request #925 from arummler/fix-undo-stack
Fix issues with undo stack
2026-09-18 18:54:11 +02:00
Andre Rummler 059d179d9b Fix:
a) Project stayed in modified state even after undoing all changes.
b) Saving the project deleted the undo stack.
2026-09-18 18:28:48 +02:00
Kellermorph fa52aab149 Persist QColorDialog custom colors across application restarts
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.
2026-09-18 13:53:14 +02:00
Andre Rummler 54e61d482d Fix: "group rotate" of elements detached the conductors as those were not rotated. 2026-09-18 12:35:08 +02:00
Jeff Patterson 800ddfbcbe Add a "qet-dark" icon theme for dark palettes
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.
2026-09-18 05:06:51 -05:00
Laurent Trinques 0258d1a74f Merge pull request #917 from ispyisail/fix/913-paste-under-cursor
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m43s
Paste under the cursor, and restore the folio context menu after a paste (#913)
2026-09-18 10:26:00 +02:00
ispyisail dd0c194a3c Paste under the cursor, and give the context menu back (#913)
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>
2026-09-18 19:57:59 +12:00
Laurent Trinques 0067ba1dca Merge pull request #905 from ispyisail/harden/crash-reporter
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m1s
Harden the crash reporter: keep every dump, and say what crashed
2026-09-18 04:59:45 +02:00
ispyisail 16220af012 Merge remote-tracking branch 'upstream/master' into harden/crash-reporter 2026-09-18 14:31:54 +12:00
ispyisail f8c1b5206a Address review on #905: offered-list semantics, dump ordering, FreeBSD, legacy dumps
Five things raised in review, plus tests for the parts that were only
described in prose.

clearPendingCrashDump() did not do what its comment said. It called
pendingCrashDumpFiles() again at clear time, so it deleted whatever was
in the directory then, not what had been offered. The offer sits inside a
modal dialog that stays open as long as the user reads it, and
SingleApplication keys its socket on the binary path, so a second
QElectroTech build running alongside is a separate process that can crash
and write a dump in that window. Re-listing deleted that dump unseen --
the exact failure this change exists to fix. The list is now taken once
in QETApp::checkCrashDump() and passed to both
pendingCrashDumpContents() and clearPendingCrashDump().

The ring is now written before the backtrace. backtrace() unwinds through
libgcc, which calls dl_iterate_phdr and takes the loader lock; warming it
in install() removes the allocation but not the lock. Crashing inside
dlopen() (Qt plugin loading), or on a corrupted stack, could therefore
hang or re-fault the handler at the backtrace and lose the ring with it.
Order is now header, signal, ring, backtrace, so the cheapest and most
valuable part is already on disk before anything that can block. The
class comment claimed the handler takes no locks; that was not strictly
true and now says so.

QET_CRASH_BACKTRACE comes from find_package(Backtrace) rather than
__has_include(<execinfo.h>). The header exists on FreeBSD but backtrace()
lives in libexecinfo there, so the probe compiled and the link failed.

A crash_dump.log left by a pre-#905 version is migrated into crashes/ at
startup, named from its own mtime. Otherwise upgrading stranded it: the
new code never looks at that path, so the dump from the crash that
prompted the upgrade would sit there unoffered forever.

Also from the review: dumps are capped at the 10 newest, so a crash loop
cannot fill the log directory before any dialog is shown; crashDumpDir()
no longer creates the directory as a side effect of a const getter
(ensureCrashDumpDir() does that for the callers that write); and redact()
now masks an AppImage's per-run /tmp/.mount_XXXXXX prefix, which
backtrace_symbols_fd() writes into every frame.

Two test executables, both of which were checked to fail against the
behaviour they replace:

- tst_crashhandler covers CrashHandler::formatInt(), which had no
  coverage at all despite running only inside a signal handler, where
  nothing can assert: zero, negatives, INT_MIN (negated through unsigned,
  since -INT_MIN is UB), INT_MAX, truncation and a zero-sized buffer,
  each checked against a sentinel-filled buffer so a write past the
  reported length fails.
- tst_crashdumps covers the bookkeeping: ordering, empty dumps, the
  exclusion of this run's own path, the cap, concatenation of every
  offered dump, that clearing deletes only what was offered, and what
  redact() masks. qetlogger.cpp needs exactly one symbol from the
  application, QETApp::dataDir(), which the test supplies itself.

Not addressed here: the timestamp in crash_<timestamp>_<pid> is the
launch time, not the crash time -- correct as observed, and the commit
message that implied otherwise was the thing that was wrong. Resolvable
QET frames for AppImage/Flatpak/Snap/Debian need -rdynamic and archived
debug symbols, which is a packaging discussion, not this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 14:31:49 +12:00
ispyisail eea939c31c Merge remote-tracking branch 'upstream/master' into fix/904-quit-during-modal 2026-09-18 13:34:58 +12:00