mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-20 07:14:13 +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
|
||||
# PR/run must never attempt a signing request.
|
||||
# (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
|
||||
id: sign
|
||||
if: github.repository == 'qelectrotech/qelectrotech-source-mirror' && env.SIGNPATH_API_TOKEN != ''
|
||||
continue-on-error: true
|
||||
uses: signpath/github-action-submit-signing-request@v2
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
@@ -306,7 +312,22 @@ jobs:
|
||||
wait-for-completion: true
|
||||
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
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: qelectrotech-windows-msi-${{ matrix.flavor }}
|
||||
@@ -315,6 +336,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Delete old nightly .msi asset
|
||||
if: always()
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
@@ -328,6 +350,7 @@ jobs:
|
||||
shell: pwsh
|
||||
|
||||
- name: Upload MSI to nightly release
|
||||
if: always()
|
||||
uses: softprops/action-gh-release@v3
|
||||
with:
|
||||
tag_name: nightly
|
||||
|
||||
@@ -59,9 +59,8 @@
|
||||
if (movable.isEmpty()) return;
|
||||
|
||||
//Compute the top-left of all items' positions (not bounding
|
||||
//rects) and snap to grid — used only for the initial cursor
|
||||
//warp. Items stay at their original XML positions;
|
||||
//moveTo() handles grid-snapped movement via deltas.
|
||||
//rects) and snap to grid: this is the point that gets placed
|
||||
//under the cursor, and the baseline moveTo() measures from.
|
||||
QPointF top_left;
|
||||
bool first = true;
|
||||
for (auto *item : movable) {
|
||||
@@ -79,17 +78,42 @@
|
||||
Diagram::xGrid).toInt();
|
||||
const int yGrid = settings.value(QStringLiteral("diagrameditor/Ygrid"),
|
||||
Diagram::yGrid).toInt();
|
||||
const QPointF grid_origin(
|
||||
qRound(top_left.x() / xGrid) * xGrid,
|
||||
qRound(top_left.y() / yGrid) * yGrid);
|
||||
const auto snapGrid = [xGrid, yGrid](const QPointF &p) -> QPointF {
|
||||
return QPointF(
|
||||
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
|
||||
//delta from the baseline, so items preserve their layout and
|
||||
//move in whole grid steps.
|
||||
//Move the group to the cursor, rather than the cursor to the
|
||||
//group. Both put the copy under the pointer, but warping the
|
||||
//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) {
|
||||
item->setPos(item->pos() + offset);
|
||||
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();
|
||||
for (auto *item : movable) {
|
||||
@@ -101,11 +125,6 @@
|
||||
if (const auto qde = QETApp::diagramEditorAncestorOf(view)) {
|
||||
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();
|
||||
@@ -129,6 +148,20 @@ DiagramEventAddPaste::~DiagramEventAddPaste()
|
||||
if (m_status_bar) {
|
||||
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
|
||||
Compute a grid-snapped delta from the initial cursor position and
|
||||
apply it to every item's grid-shifted position. This keeps all
|
||||
items exactly on grid points regardless of modifier keys or
|
||||
sub-pixel cursor-warp rounding.
|
||||
apply it to every item's stored position. Working from a delta
|
||||
against a fixed baseline, rather than from the previous position,
|
||||
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)
|
||||
{
|
||||
@@ -184,14 +218,15 @@ void DiagramEventAddPaste::moveTo(const QPointF &scene_pos)
|
||||
qRound(p.y() / yGrid) * yGrid);
|
||||
};
|
||||
|
||||
//On the very first call, record the actual grid-snapped
|
||||
//cursor position as baseline. The cursor warp in the
|
||||
//constructor goes through integer rounding (mapFromScene →
|
||||
//QPoint) so the real position may differ slightly from
|
||||
//m_initial_cursor. Using the actual scene position avoids
|
||||
//a one-grid-unit jump on the first mouse movement.
|
||||
if (m_initial_cursor.isNull()) {
|
||||
//The constructor normally sets the baseline, having just put the
|
||||
//group there. This covers the case where it could not -- no view
|
||||
//to map through -- by taking the first cursor position instead.
|
||||
//Tested with m_baseline_captured rather than
|
||||
//m_initial_cursor.isNull(), which silently re-baselines when the
|
||||
//baseline is legitimately scene (0,0).
|
||||
if (!m_baseline_captured) {
|
||||
m_initial_cursor = snapGrid(scene_pos);
|
||||
m_baseline_captured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,13 +79,18 @@ class DiagramEventAddPaste : public DiagramEventInterface
|
||||
///Each movable item's position relative to the group's top left,
|
||||
///taken once so repeated moves cannot accumulate rounding drift.
|
||||
QHash<QGraphicsItem *, QPointF> m_relative_pos;
|
||||
///Top-left corner of the bounding rect of all movable items,
|
||||
///in scene coordinates, captured when the paste starts.
|
||||
///Where the group's grid-snapped top left was put when the paste
|
||||
///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;
|
||||
///Cursor position (scene coords) at the moment the paste starts,
|
||||
///so delta-based movement can compute offsets from the initial point.
|
||||
///Cursor position (scene coords) the delta-based movement in
|
||||
///moveTo() measures from. Equal to m_group_origin, since the
|
||||
///group is placed at the 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};
|
||||
QPointer<QStatusBar> m_status_bar;
|
||||
bool m_finished{false};
|
||||
|
||||
Reference in New Issue
Block a user