mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-20 15:24:14 +02:00
Compare commits
3 Commits
0067ba1dca
...
0258d1a74f
| Author | SHA1 | Date | |
|---|---|---|---|
| 0258d1a74f | |||
| dd0c194a3c | |||
| 3ba7de3284 |
@@ -292,9 +292,15 @@ jobs:
|
|||||||
# check: forks never have the SignPath secrets, and a fork-originated
|
# check: forks never have the SignPath secrets, and a fork-originated
|
||||||
# PR/run must never attempt a signing request.
|
# PR/run must never attempt a signing request.
|
||||||
# (cf. DieterMayerOSS:fix/msi-signing-fork-guard, d3f60c88)
|
# (cf. DieterMayerOSS:fix/msi-signing-fork-guard, d3f60c88)
|
||||||
|
# continue-on-error: SignPath's certificate is still pending validation
|
||||||
|
# (Sept 2026) — the signing request currently fails with a 500 on the
|
||||||
|
# SignPath side. Kept non-blocking so the nightly MSI still ships
|
||||||
|
# (unsigned) while the certificate is pending. Remove
|
||||||
|
# continue-on-error once the certificate is confirmed active.
|
||||||
- name: Sign MSI via SignPath
|
- name: Sign MSI via SignPath
|
||||||
id: sign
|
id: sign
|
||||||
if: github.repository == 'qelectrotech/qelectrotech-source-mirror' && env.SIGNPATH_API_TOKEN != ''
|
if: github.repository == 'qelectrotech/qelectrotech-source-mirror' && env.SIGNPATH_API_TOKEN != ''
|
||||||
|
continue-on-error: true
|
||||||
uses: signpath/github-action-submit-signing-request@v2
|
uses: signpath/github-action-submit-signing-request@v2
|
||||||
with:
|
with:
|
||||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||||
@@ -306,7 +312,22 @@ jobs:
|
|||||||
wait-for-completion: true
|
wait-for-completion: true
|
||||||
output-artifact-directory: 'dist\'
|
output-artifact-directory: 'dist\'
|
||||||
|
|
||||||
|
# If signing succeeded, SignPath already overwrote dist\*.msi with the
|
||||||
|
# signed copy (output-artifact-directory above) — nothing to do here.
|
||||||
|
# If it failed/was skipped, dist\*.msi is still the unsigned MSI from
|
||||||
|
# the "Build MSI" step, so the rest of the pipeline just ships that.
|
||||||
|
- name: Report signing status
|
||||||
|
if: always()
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
if ("${{ steps.sign.outcome }}" -eq "success") {
|
||||||
|
Write-Host "MSI signed successfully via SignPath."
|
||||||
|
} else {
|
||||||
|
Write-Warning "MSI signing skipped or failed (outcome: ${{ steps.sign.outcome }}) — shipping UNSIGNED MSI. Likely cause: SignPath certificate still pending validation."
|
||||||
|
}
|
||||||
|
|
||||||
- name: Upload signed MSI artifact
|
- name: Upload signed MSI artifact
|
||||||
|
if: always()
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: qelectrotech-windows-msi-${{ matrix.flavor }}
|
name: qelectrotech-windows-msi-${{ matrix.flavor }}
|
||||||
@@ -315,6 +336,7 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Delete old nightly .msi asset
|
- name: Delete old nightly .msi asset
|
||||||
|
if: always()
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
REPO: ${{ github.repository }}
|
REPO: ${{ github.repository }}
|
||||||
@@ -328,6 +350,7 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload MSI to nightly release
|
- name: Upload MSI to nightly release
|
||||||
|
if: always()
|
||||||
uses: softprops/action-gh-release@v3
|
uses: softprops/action-gh-release@v3
|
||||||
with:
|
with:
|
||||||
tag_name: nightly
|
tag_name: nightly
|
||||||
|
|||||||
@@ -59,9 +59,8 @@
|
|||||||
if (movable.isEmpty()) return;
|
if (movable.isEmpty()) return;
|
||||||
|
|
||||||
//Compute the top-left of all items' positions (not bounding
|
//Compute the top-left of all items' positions (not bounding
|
||||||
//rects) and snap to grid — used only for the initial cursor
|
//rects) and snap to grid: this is the point that gets placed
|
||||||
//warp. Items stay at their original XML positions;
|
//under the cursor, and the baseline moveTo() measures from.
|
||||||
//moveTo() handles grid-snapped movement via deltas.
|
|
||||||
QPointF top_left;
|
QPointF top_left;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto *item : movable) {
|
for (auto *item : movable) {
|
||||||
@@ -79,17 +78,42 @@
|
|||||||
Diagram::xGrid).toInt();
|
Diagram::xGrid).toInt();
|
||||||
const int yGrid = settings.value(QStringLiteral("diagrameditor/Ygrid"),
|
const int yGrid = settings.value(QStringLiteral("diagrameditor/Ygrid"),
|
||||||
Diagram::yGrid).toInt();
|
Diagram::yGrid).toInt();
|
||||||
const QPointF grid_origin(
|
const auto snapGrid = [xGrid, yGrid](const QPointF &p) -> QPointF {
|
||||||
qRound(top_left.x() / xGrid) * xGrid,
|
return QPointF(
|
||||||
qRound(top_left.y() / yGrid) * yGrid);
|
qRound(p.x() / xGrid) * xGrid,
|
||||||
|
qRound(p.y() / yGrid) * yGrid);
|
||||||
|
};
|
||||||
|
const QPointF grid_origin = snapGrid(top_left);
|
||||||
|
|
||||||
//Store each item's position. moveTo() applies a grid-snapped
|
//Move the group to the cursor, rather than the cursor to the
|
||||||
//delta from the baseline, so items preserve their layout and
|
//group. Both put the copy under the pointer, but warping the
|
||||||
//move in whole grid steps.
|
//pointer also drags it back to the original's position, so the
|
||||||
|
//copy appears exactly on top of what was copied until the mouse
|
||||||
|
//is moved -- which is the thing pasting under the cursor was
|
||||||
|
//meant to avoid (issue #913). Taking the pointer away from
|
||||||
|
//where the user put it is also its own surprise.
|
||||||
|
m_group_origin = snapGrid(start_pos);
|
||||||
|
const QPointF offset = m_group_origin - grid_origin;
|
||||||
|
|
||||||
|
//Store each item's position after the move. moveTo() applies a
|
||||||
|
//grid-snapped delta from the baseline to these, so items
|
||||||
|
//preserve their layout and move in whole grid steps.
|
||||||
for (auto *item : movable) {
|
for (auto *item : movable) {
|
||||||
|
item->setPos(item->pos() + offset);
|
||||||
m_relative_pos.insert(item, item->pos());
|
m_relative_pos.insert(item, item->pos());
|
||||||
}
|
}
|
||||||
m_group_origin = grid_origin;
|
|
||||||
|
//The conductors were laid out against the old terminal
|
||||||
|
//positions, so re-route them before anything is drawn.
|
||||||
|
const QList<Conductor *> conductors = m_content.conductors(DiagramContent::AnyConductor);
|
||||||
|
for (auto *conductor : conductors) {
|
||||||
|
conductor->updatePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
//The baseline is known now, so moveTo() does not have to
|
||||||
|
//capture one from the first mouse movement.
|
||||||
|
m_initial_cursor = m_group_origin;
|
||||||
|
m_baseline_captured = true;
|
||||||
|
|
||||||
m_diagram->clearSelection();
|
m_diagram->clearSelection();
|
||||||
for (auto *item : movable) {
|
for (auto *item : movable) {
|
||||||
@@ -101,11 +125,6 @@
|
|||||||
if (const auto qde = QETApp::diagramEditorAncestorOf(view)) {
|
if (const auto qde = QETApp::diagramEditorAncestorOf(view)) {
|
||||||
m_status_bar = qde->statusBar();
|
m_status_bar = qde->statusBar();
|
||||||
}
|
}
|
||||||
//Warp the cursor close to the group origin so the
|
|
||||||
//first mouseMoveEvent captures the correct baseline.
|
|
||||||
const QPoint view_pos = view->mapFromScene(m_group_origin);
|
|
||||||
const QPoint global_pos = view->viewport()->mapToGlobal(view_pos);
|
|
||||||
QCursor::setPos(global_pos);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showHint();
|
showHint();
|
||||||
@@ -129,6 +148,20 @@ DiagramEventAddPaste::~DiagramEventAddPaste()
|
|||||||
if (m_status_bar) {
|
if (m_status_bar) {
|
||||||
m_status_bar->clearMessage();
|
m_status_bar->clearMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Give the context menu back. init() turned it off so a right
|
||||||
|
//click would cancel the placement instead of opening a menu over
|
||||||
|
//it, and nothing turned it on again: one Ctrl+V left the folio's
|
||||||
|
//right-click menu dead for the rest of the session, taking
|
||||||
|
//"Coller ici", "Collage multiple" and the folio properties with
|
||||||
|
//it. Every other DiagramEvent* class restores it here; this one
|
||||||
|
//did not.
|
||||||
|
if (m_diagram) {
|
||||||
|
const auto views = m_diagram->views();
|
||||||
|
for (auto *view : views) {
|
||||||
|
view->setContextMenuPolicy(Qt::DefaultContextMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,9 +199,10 @@ void DiagramEventAddPaste::showHint()
|
|||||||
/**
|
/**
|
||||||
@brief DiagramEventAddPaste::moveTo
|
@brief DiagramEventAddPaste::moveTo
|
||||||
Compute a grid-snapped delta from the initial cursor position and
|
Compute a grid-snapped delta from the initial cursor position and
|
||||||
apply it to every item's grid-shifted position. This keeps all
|
apply it to every item's stored position. Working from a delta
|
||||||
items exactly on grid points regardless of modifier keys or
|
against a fixed baseline, rather than from the previous position,
|
||||||
sub-pixel cursor-warp rounding.
|
keeps all items exactly on grid points regardless of modifier keys
|
||||||
|
and stops rounding accumulating over a long drag.
|
||||||
*/
|
*/
|
||||||
void DiagramEventAddPaste::moveTo(const QPointF &scene_pos)
|
void DiagramEventAddPaste::moveTo(const QPointF &scene_pos)
|
||||||
{
|
{
|
||||||
@@ -184,14 +218,15 @@ void DiagramEventAddPaste::moveTo(const QPointF &scene_pos)
|
|||||||
qRound(p.y() / yGrid) * yGrid);
|
qRound(p.y() / yGrid) * yGrid);
|
||||||
};
|
};
|
||||||
|
|
||||||
//On the very first call, record the actual grid-snapped
|
//The constructor normally sets the baseline, having just put the
|
||||||
//cursor position as baseline. The cursor warp in the
|
//group there. This covers the case where it could not -- no view
|
||||||
//constructor goes through integer rounding (mapFromScene →
|
//to map through -- by taking the first cursor position instead.
|
||||||
//QPoint) so the real position may differ slightly from
|
//Tested with m_baseline_captured rather than
|
||||||
//m_initial_cursor. Using the actual scene position avoids
|
//m_initial_cursor.isNull(), which silently re-baselines when the
|
||||||
//a one-grid-unit jump on the first mouse movement.
|
//baseline is legitimately scene (0,0).
|
||||||
if (m_initial_cursor.isNull()) {
|
if (!m_baseline_captured) {
|
||||||
m_initial_cursor = snapGrid(scene_pos);
|
m_initial_cursor = snapGrid(scene_pos);
|
||||||
|
m_baseline_captured = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,18 @@ class DiagramEventAddPaste : public DiagramEventInterface
|
|||||||
///Each movable item's position relative to the group's top left,
|
///Each movable item's position relative to the group's top left,
|
||||||
///taken once so repeated moves cannot accumulate rounding drift.
|
///taken once so repeated moves cannot accumulate rounding drift.
|
||||||
QHash<QGraphicsItem *, QPointF> m_relative_pos;
|
QHash<QGraphicsItem *, QPointF> m_relative_pos;
|
||||||
///Top-left corner of the bounding rect of all movable items,
|
///Where the group's grid-snapped top left was put when the paste
|
||||||
///in scene coordinates, captured when the paste starts.
|
///started, in scene coordinates -- the cursor, so the copy
|
||||||
|
///appears under the pointer rather than on top of what was
|
||||||
|
///copied.
|
||||||
QPointF m_group_origin;
|
QPointF m_group_origin;
|
||||||
///Cursor position (scene coords) at the moment the paste starts,
|
///Cursor position (scene coords) the delta-based movement in
|
||||||
///so delta-based movement can compute offsets from the initial point.
|
///moveTo() measures from. Equal to m_group_origin, since the
|
||||||
|
///group is placed at the cursor.
|
||||||
QPointF m_initial_cursor;
|
QPointF m_initial_cursor;
|
||||||
///Set to true once the first moveTo() captures the real cursor position.
|
///Whether m_initial_cursor holds a usable baseline. A flag rather
|
||||||
|
///than testing m_initial_cursor.isNull(), which cannot tell "not
|
||||||
|
///set yet" from a baseline that is legitimately scene (0,0).
|
||||||
bool m_baseline_captured{false};
|
bool m_baseline_captured{false};
|
||||||
QPointer<QStatusBar> m_status_bar;
|
QPointer<QStatusBar> m_status_bar;
|
||||||
bool m_finished{false};
|
bool m_finished{false};
|
||||||
|
|||||||
Reference in New Issue
Block a user