Compare commits

...

10 Commits

Author SHA1 Message Date
Laurent Trinques c5edd0a54b CI: try to fix 404 error on downlad page
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m11s
2026-09-22 08:19:23 +02:00
Laurent Trinques 3a13287ba9 CI: delete auto-doxygen.yml 2026-09-22 08:05:53 +02:00
Laurent Trinques 4888ae87f3 Merge pull request #978 from Kellermorph/feature/refresh-all
fix: update composite text %{label} when folio changes
2026-09-22 07:53:36 +02:00
Laurent Trinques 59344eb565 Merge pull request #977 from ispyisail/fix/591-text-resize-handles
Fix #591's resize handles: unreachable via plain click, wrong position
2026-09-22 07:51:32 +02:00
Kellermorph 8985babfe7 fix: update composite text %{label} when folio changes
DynamicElementTextItem::updateLabel() resolved %{label} in composite
text using the stale value from elementInformations()["label"], which
is only set once at load time and never updated when the folio/page
number changes.

Use element->actualLabel() instead, which resolves the label formula
(including %F, %f, %id) against the current folio at call time.
2026-09-21 21:45:03 +02:00
ispyisail 29d16c3337 Fix #591's resize handles: reachable only via Shift/right-click, wrong position
Two bugs reported by @arummler on #591 after merge:

  "It works but to select the text field one has to right click on
  it...I think there are competing handlers or something."
  "the drag elements should be on the border of the box. In the
  moment they appear directly left and right from the text."

Both reproduced headlessly (scripts/qet-gui-dialog.sh) against a fresh
build of current master and root-caused before touching anything.

Selection: DynamicElementTextItem::mousePressEvent() forwards a plain
click (no Shift) straight to parentElement()->mousePressEvent(), by
design and pre-existing -- it's what lets dragging a symbol by its own
label move the whole symbol rather than just the label. That's correct
and untouched here. But it means a plain click leaves the *parent*
selected, not the text, and #591's handles were wired only to the
text's own ItemSelectedHasChanged -- so they were only reachable via
Shift+click or a right-click's context menu (which happens to select
the item under the cursor for its own context menu, unrelated to the
Shift path), neither of which anyone reaches for to resize a text.
Confirmed with screenshots at each step, including that Shift+click
already reached the existing (if misplaced) handles correctly.

Fix: DynamicElementTextItem::refreshResizeHandlesVisibility() shows the
handles when either the text itself or its parent element is selected,
and Element gets an itemChange() override (it had none) that calls it
on each of its own texts when the element's own selection changes. Both
sides driven from itemChange(), Qt's own hook for exactly this and the
same one already used for the text's own selection.

First attempt drove this from paint() instead, since the PR's own
updateResizeHandlesPos() already runs there. That crashed reproducibly
(SIGABRT) on deselecting a text: paint() runs while QGraphicsScene
iterates its item list to draw it, and addResizeHandles()/
removeResizeHandles() mutate that list via QGraphicsScene::addItem()/
removeItem(), which cannot safely happen mid-iteration. Caught it with
the same headless repro before it went anywhere near a PR, moved the
logic to itemChange(), and re-ran the full sequence -- select, resize,
undo, deselect, twice through -- clean.

Position: updateResizeHandlesPos() placed the handles on frameRect(),
which is a box sized to the text's natural (idealWidth()) content and
then re-centred inside boundingRect() -- it does not grow with
textWidth(). Once a text has been widened, frameRect() stays tight
around the glyphs while boundingRect() -- the box QGraphicsView actually
outlines as the selection, and the box a user drags relative to -- grows
around it, leaving the handles stranded well inside the visible
selection border. Fix: position them on boundingRect() instead, which
does track textWidth(); confirmed by widening a text and checking the
handle lands exactly on the new edge rather than partway across it.

Verified headlessly end to end on the original report's own element
("motor off" on grafcet.qet, folio 1): a single plain left-click (no
Shift, no right-click) now shows both handles at the true box border;
dragging resizes correctly and the handle tracks the growing edge;
Ctrl+Z restores the -1 auto-width sentinel and the handles stay at the
reverted position; clicking away removes them; repeated twice with no
crash. Qt 6.10.2, ctest 12/12, no new warnings in either changed file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 07:30:39 +12:00
Laurent Trinques 61f5e5e502 Merge pull request #972 from arummler/fix-pugixml-linking
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m42s
fix PUGIXML linking
2026-09-21 19:36:28 +02:00
Laurent Trinques 77bc9ed8e4 Merge pull request #976 from arummler/untrack-qch
Stop tracking *.qch via LFS.
2026-09-21 19:35:48 +02:00
Andre Rummler 9b26d5dc6a Stop tracking *.qch via LFS. 2026-09-21 17:57:59 +02:00
Andre Rummler b76d8ce8a1 Fix: remove PUGIXML files from compilation fileset. This is redundant using the target approach and leads sometimes to failures. 2026-09-21 13:29:40 +02:00
9 changed files with 107 additions and 85 deletions
-1
View File
@@ -1 +0,0 @@
*.qch filter=lfs diff=lfs merge=lfs -text
-67
View File
@@ -1,67 +0,0 @@
name: Auto-build doxygen docs
on:
push:
tags:
- '**'
jobs:
doxygen:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'
show-progress: ''
- name: Setup and run doxygen
run: sudo apt update && sudo apt install doxygen graphviz qhelpgenerator-qt5 -y
- name: Set up Git LFS
run: |
git lfs install
git lfs track "*.qch"
- name: Run doxygen
run: doxygen Doxyfile
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.MR_TOKEN }}
commit-message: update QCH file
committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
title: Update QCH Help file
body: |
- Updating QT Help file following commit ${{ github.sha }}.
- Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
branch: update-qch
labels: |
qch
cicd
delete-branch: true
add-paths: doc/*.qch
- uses: actions/upload-pages-artifact@v3
with:
path: ${{ github.workspace }}/doc/html/
deploy:
# Add a dependency to the build job
needs: doxygen
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
# Deploy to the github-pages environment
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
# Specify runner + deployment step
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4 # or specific "vX.X.X" version tag for this action
+14 -1
View File
@@ -381,7 +381,20 @@ jobs:
deploy-pages: deploy-pages:
needs: build-msi needs: build-msi
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: always() && needs.build-msi.result == 'success' # Regenerate the page whenever the MSI job actually ran (success OR
# packaging/signing failure) — not only on full success.
# generate-page.py queries the published assets on the "nightly"
# release itself (line 407: `gh release view nightly --json assets`),
# so it already handles a missing MSI natively (empty MSI_NAME -> no
# MSI button on the page). The page only needs the exe/zip already
# published by windows-build.yml, independent of the MSI's fate.
# Only "skipped"/"cancelled" are excluded: if build-msi never ran at
# all (e.g. Windows Build itself failed), there is nothing new to
# publish and regenerating the page would be pointless.
if: >
always() &&
needs.build-msi.result != 'skipped' &&
needs.build-msi.result != 'cancelled'
permissions: permissions:
contents: write contents: write
pages: write pages: write
-1
View File
@@ -318,7 +318,6 @@ target_include_directories(
${QET_DIR}/sources/NameList ${QET_DIR}/sources/NameList
${QET_DIR}/sources/NameList/ui ${QET_DIR}/sources/NameList/ui
${QET_DIR}/sources/utils ${QET_DIR}/sources/utils
${QET_DIR}/pugixml/src
${QET_DIR}/sources/dataBase ${QET_DIR}/sources/dataBase
${QET_DIR}/sources/dataBase/ui ${QET_DIR}/sources/dataBase/ui
${QET_DIR}/sources/factory/ui ${QET_DIR}/sources/factory/ui
-4
View File
@@ -507,10 +507,6 @@ set(QET_SRC_FILES
${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.cpp ${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.cpp
${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.h ${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.h
${QET_DIR}/pugixml/src/pugiconfig.hpp
${QET_DIR}/pugixml/src/pugixml.cpp
${QET_DIR}/pugixml/src/pugixml.hpp
${QET_DIR}/sources/qetgraphicsitem/conductor.cpp ${QET_DIR}/sources/qetgraphicsitem/conductor.cpp
${QET_DIR}/sources/qetgraphicsitem/conductor.h ${QET_DIR}/sources/qetgraphicsitem/conductor.h
${QET_DIR}/sources/qetgraphicsitem/conductortextitem.cpp ${QET_DIR}/sources/qetgraphicsitem/conductortextitem.cpp
@@ -732,6 +732,16 @@ void DynamicElementTextItem::paint(QPainter *painter, const QStyleOptionGraphics
{ {
DiagramTextItem::paint(painter, option, widget); DiagramTextItem::paint(painter, option, widget);
//Only ever repositions already-existing sibling items here --
//never adds or removes one. paint() runs while QGraphicsScene is
//iterating its item list to draw it, and mutating that list mid
//-iteration (which addResizeHandles()/removeResizeHandles() do,
//through QGraphicsScene::addItem()/removeItem()) crashes. An
//earlier version of this fix called them from here and crashed
//qelectrotech reproducibly on deselecting a text (SIGABRT); see
//refreshResizeHandlesVisibility() for where that now happens
//instead -- itemChange(), Qt's own safe hook for exactly this,
//already used below for this item's own selection.
if (m_left_resize_handle || m_right_resize_handle) if (m_left_resize_handle || m_right_resize_handle)
updateResizeHandlesPos(); updateResizeHandlesPos();
@@ -826,10 +836,7 @@ QVariant DynamicElementTextItem::itemChange(QGraphicsItem::GraphicsItemChange ch
} }
else if (change == QGraphicsItem::ItemSelectedHasChanged) else if (change == QGraphicsItem::ItemSelectedHasChanged)
{ {
if (value.toBool()) refreshResizeHandlesVisibility();
addResizeHandles();
else
removeResizeHandles();
} }
else if (change == QGraphicsItem::ItemSceneHasChanged && !scene()) else if (change == QGraphicsItem::ItemSceneHasChanged && !scene())
{ {
@@ -878,6 +885,31 @@ bool DynamicElementTextItem::sceneEventFilter(QGraphicsItem *watched, QEvent *ev
return false; return false;
} }
/**
@brief DynamicElementTextItem::refreshResizeHandlesVisibility
Show the resize handles when this text is selected directly, OR when its
parent element is -- which is what an ordinary click without Shift
selects (DynamicElementTextItem::mousePressEvent() forwards a plain
click to the parent, so dragging a symbol by its label moves the whole
symbol; a pre-existing, unrelated behaviour, left untouched here).
Without this, the handles were reachable only via Shift+click or a
right-click's context menu, neither of which a user reaches for to
resize a text field (qelectrotech#591, reported by @arummler).
Called from itemChange() -- both this item's own ItemSelectedHasChanged,
below, and Element::itemChange() on the parent's, which calls this on
every one of its texts. Not from paint(): see the comment there for why
that crashed.
*/
void DynamicElementTextItem::refreshResizeHandlesVisibility()
{
const bool handles_wanted = isSelected() || (m_parent_element && m_parent_element->isSelected());
if (handles_wanted && !m_left_resize_handle)
addResizeHandles();
else if (!handles_wanted && m_left_resize_handle)
removeResizeHandles();
}
/** /**
@brief DynamicElementTextItem::addResizeHandles @brief DynamicElementTextItem::addResizeHandles
Create and show the two width-resize handles (left/right edge of Create and show the two width-resize handles (left/right edge of
@@ -917,20 +949,32 @@ void DynamicElementTextItem::removeResizeHandles()
/** /**
@brief DynamicElementTextItem::updateResizeHandlesPos @brief DynamicElementTextItem::updateResizeHandlesPos
Keep the two resize handles at the vertical middle of frameRect()'s left Keep the two resize handles at the vertical middle of boundingRect()'s
and right edges, in scene coordinates -- called on every paint() so it left and right edges, in scene coordinates -- called on every paint() so
stays correct across every kind of change that can move this item or it stays correct across every kind of change that can move this item or
change its size (position, rotation, font, text, textWidth...) without change its size (position, rotation, font, text, textWidth...) without
needing a dedicated hook for each one. needing a dedicated hook for each one.
Deliberately boundingRect(), not frameRect(): frameRect() is a tight box
around the text's own natural (idealWidth()) size, re-centred inside
boundingRect() -- it does not grow with textWidth(). Once a text has
been widened, that leaves a growing gap between the tight frame and the
dashed selection outline QGraphicsView draws at boundingRect(), which is
the box a user actually sees and expects a resize handle to sit on
(qelectrotech#591, reported by @arummler: "the drag elements should be
on the border of the box"). boundingRect() reflects the full
textWidth() (it is QGraphicsTextItem's own, driven by the document's
laid-out size), so the handles now track the box that is visibly
resized rather than the text glyphs inside it.
*/ */
void DynamicElementTextItem::updateResizeHandlesPos() void DynamicElementTextItem::updateResizeHandlesPos()
{ {
if (!m_left_resize_handle || !m_right_resize_handle) if (!m_left_resize_handle || !m_right_resize_handle)
return; return;
QRectF fr = frameRect(); QRectF br = boundingRect();
m_left_resize_handle->setPos(mapToScene(QPointF(fr.left(), fr.center().y()))); m_left_resize_handle->setPos(mapToScene(QPointF(br.left(), br.center().y())));
m_right_resize_handle->setPos(mapToScene(QPointF(fr.right(), fr.center().y()))); m_right_resize_handle->setPos(mapToScene(QPointF(br.right(), br.center().y())));
} }
/** /**
@@ -1289,9 +1333,15 @@ void DynamicElementTextItem::updateLabel()
if(m_text_from == ElementInfo && element) { if(m_text_from == ElementInfo && element) {
setPlainText(element->actualLabel()); QString new_label = element->actualLabel();
if (toPlainText() != new_label) {
setPlainText(new_label);
}
} }
else if (m_text_from == CompositeText) { else if (m_text_from == CompositeText) {
// Use actualLabel() to ensure %{label} reflects the current
// resolved label (e.g. after a folio/page-number change)
dc.addValue(QStringLiteral("label"), element->actualLabel());
setPlainText(autonum::AssignVariables::replaceVariable(m_composite_text, dc)); setPlainText(autonum::AssignVariables::replaceVariable(m_composite_text, dc));
} }
} }
@@ -121,6 +121,12 @@ class DynamicElementTextItem : public DiagramTextItem
void setRotationPointCenter(bool set); void setRotationPointCenter(bool set);
bool rotationPointCenter() const; bool rotationPointCenter() const;
//Called by Element::itemChange() when the PARENT's selection
//changes, so the parent can keep each of its texts' resize
//handles in sync with its own selection state. Public for that;
//see the .cpp for why it exists.
void refreshResizeHandlesVisibility();
protected: protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
+25
View File
@@ -1664,6 +1664,31 @@ void Element::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
update(); update();
} }
/**
@brief Element::itemChange
On ItemSelectedHasChanged, tell each of this element's own dynamic texts
to re-check whether its resize handles should be showing --
DynamicElementTextItem::refreshResizeHandlesVisibility() shows them when
either the text itself or its parent (this) is selected. An ordinary
click with no Shift selects the parent, not the text
(DynamicElementTextItem::mousePressEvent() forwards it), so without this
a plain click on a symbol never showed the resize handles this PR adds
to its texts (qelectrotech#591, reported by @arummler) -- only
Shift+click or a right-click's context menu did, since those are the
paths that leave the text itself selected.
*/
QVariant Element::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == QGraphicsItem::ItemSelectedHasChanged)
{
const QList<DynamicElementTextItem *> texts = dynamicTextItems();
for (DynamicElementTextItem *deti : texts) {
deti->refreshResizeHandlesVisibility();
}
}
return QetGraphicsItem::itemChange(change, value);
}
/** /**
@brief Element::setUpFormula @brief Element::setUpFormula
Set up the formula used to create the label of this element Set up the formula used to create the label of this element
+1
View File
@@ -255,6 +255,7 @@ class Element : public QetGraphicsItem
QGraphicsSceneMouseEvent *event) override; QGraphicsSceneMouseEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *) override; void hoverEnterEvent(QGraphicsSceneHoverEvent *) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override; void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
protected: protected:
//ATTRIBUTES related to linked element //ATTRIBUTES related to linked element