Skip to content

UV Editor: UV component selection (issue #460)#760

Open
fernandotonon wants to merge 4 commits into
masterfrom
feature/uv-component-selection-460
Open

UV Editor: UV component selection (issue #460)#760
fernandotonon wants to merge 4 commits into
masterfrom
feature/uv-component-selection-460

Conversation

@fernandotonon

@fernandotonon fernandotonon commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds UV-vertex / UV-edge / UV-face component selection to the UV editor panel (Slice B of Epic: UV — UV Map editing #458)
  • Selection state lives on UVEditorController (not SelectionSet); supports click, Shift-add, Ctrl-toggle, and drag-box select
  • Toolbar mode buttons + 1/2/3 shortcuts (panel focus)
  • Read-only context island highlight when 3D Edit Mode selects components on the same entity (no live sync — bidirectional sync was removed after it caused main-thread freezes)
  • Box rules: vertices fully enclosed; edges/faces any touched
  • 14 unit tests covering pick, multi-select, box select, context islands, and edit-mode coexistence

Closes #460

Review feedback addressed

  • Face pick gated by pickRadiusUv (no arbitrary face on empty click)
  • UV corner welding via undirected mesh-edge keys
  • Filtered submesh views preserve original global vertex/triangle offsets (for context-island mapping)
  • N-gon fan triangles share one meshGlobalTri for context lookup
  • Live Edit Mode sync removed (replaced by read-only island tint only)

Test plan

  • ./build_local/bin/UnitTests --gtest_filter="UVEditorControllerTest.*" (14/14 pass)
  • Manual: open UV panel, verify V/E/F modes, Shift/Ctrl multi-select, box select marquee
  • Manual: 3D Edit Mode face selection tints the owning UV island without changing UV selection
  • Manual: UV picking does not freeze the app

Summary by CodeRabbit

  • New Features

    • UV editing is now interactive with vertex/edge/face selection modes, keyboard shortcuts, and mode buttons.
    • Added modifier-aware picking and box selection, plus selection clearing and zoom-aware hit detection.
    • Selection highlighting now renders selected elements and shows related UV “context island” faces.
  • Bug Fixes

    • Selection overlays stay in sync as the active mesh, selection mode, or UV selection changes.
  • Tests

    • Added coverage for picking, shift-click behavior, box-selection rules, and context island highlighting.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9c1ef0a1-4fd6-4fce-b218-182224cbb409

📥 Commits

Reviewing files that changed from the base of the PR and between feda14f and 3573075.

📒 Files selected for processing (1)
  • src/UVEditorController.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/UVEditorController.cpp

📝 Walkthrough

Walkthrough

The UV editor panel and controller now support vertex, edge, and face selection, box selection, selection overlays, context-island highlighting, and edit-mode synchronization. The controller also rebuilds UV caches and the test suite covers the new selection behaviors.

Changes

UV component selection editor

Layer / File(s) Summary
Controller selection API
src/UVEditorController.h, src/UVEditorController.cpp
The controller exposes UV selection mode, revision, counts, enums, and QML selection-entry methods, and emits selection notifications.
UV cache rebuild
src/UVEditorController.cpp
The controller rebuilds UV triangle, vertex, and edge caches from entity data, assigns global indices, deduplicates welded UV geometry, and resets selection state during mesh refresh.
Selection interaction and sync
src/UVEditorController.cpp
The controller handles modifier-aware point and box selection, exposes selected UV geometry to QML, and recomputes context-island faces from Edit Mode selection changes.
Panel controls and overlays
qml/UVEditorPanel.qml
The panel adds selection-mode shortcuts and buttons, rebuilds cached overlays on selection changes, draws context-island and selection overlays, and switches between point and box selection on mouse release.
Selection tests
src/UVEditorController_test.cpp
The tests cover face and vertex picking, box select rules, selection-mode signaling, context-island highlights, and selection behavior during edit mode.

Sequence Diagram(s)

sequenceDiagram
  participant UVEditorPanel
  participant UVEditorController
  participant EditModeController
  UVEditorPanel->>UVEditorController: setSelectionMode()
  UVEditorController-->>UVEditorPanel: selectionModeChanged
  UVEditorPanel->>UVEditorController: pickAt() / boxSelect()
  UVEditorController->>UVEditorController: applySelectionSet()
  UVEditorController-->>UVEditorPanel: uvSelectionChanged
  EditModeController-->>UVEditorController: editSelectionChanged
  UVEditorController->>UVEditorController: updateContextIslandsFromEdit()
  UVEditorController-->>UVEditorPanel: uvSelectionChanged
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • Epic: UV — UV Map editing #458: The PR implements UV vertex/edge/face selection, selection-mode wiring, drag-box selection, and overlays that match the issue scope.

Possibly related PRs

  • fernandotonon/QtMeshEditor#759: This PR extends the same qml/UVEditorPanel.qml and src/UVEditorController.* UV panel/controller path that PR 759 established.
  • fernandotonon/QtMeshEditor#281: This PR listens to EditModeController selection changes, which PR 281’s edit-mode selection machinery drives.

Poem

I hop through UVs, neat and bright,
With V, E, F buttons in sight.
I drag a box and wiggle my nose,
The selected islands glow in rows. 🐇
Hoppity! The editor dances just right.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Most selection and highlighting requirements are met, but the required off-by-default bidirectional sync toggle was removed. Restore the UV/Edit Mode sync toggle and safe bidirectional propagation, or revise the linked issue if sync is intentionally out of scope.
Docstring Coverage ⚠️ Warning Docstring coverage is 2.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately describes the main change: UV component selection in the UV editor.
Description check ✅ Passed The description covers the feature summary, behavior changes, and test plan, though it omits some template sections.
Out of Scope Changes check ✅ Passed The changes stay focused on UV selection, highlighting, controller wiring, and tests, with no clear unrelated additions.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/uv-component-selection-460

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2432c6f5d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/UVEditorController.cpp Outdated
Comment on lines +931 to +932
int globalVertOffset = 0;
int globalTriIndex = 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve original submesh offsets for sync indices

When only a later submesh is selected, filteredEditableMesh removes the earlier submeshes, but the new sync IDs start from zero here. The meshGlobalTri and meshGlobalVert values therefore point at components in the first submesh, so with Sync enabled a UV pick in submesh 1 pushes/pulls the wrong Edit Mode face or vertex. Keep the original submesh's global vertex/triangle offsets when building the filtered UV cache.

Useful? React with 👍 / 👎.

Comment thread src/UVEditorController.cpp Outdated
Comment on lines +1021 to +1022
const uint64_t key = (packUvKey(tri.u[e], tri.v[e]) << 1)
^ packUvKey(tri.u[n], tri.v[n]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use an undirected key when welding shared UV edges

Adjacent triangles normally store their shared edge in opposite directions, but this key is order-dependent, so the two occurrences do not land in the same bucket and occs.size() stays 1. As a result, shared UV corners are not welded and vertex/edge selection splits across every triangle boundary even inside one continuous UV island. Build the occurrence key from sorted endpoint UV keys and weld reversed corners.

Useful? React with 👍 / 👎.

Comment thread src/UVEditorController.cpp Outdated
Comment on lines +334 to +335
if (bestFace >= 0)
faces.insert(bestFace);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Respect the pick radius before selecting a face

In FaceMode, a click outside every triangle still keeps the nearest centroid in bestFace and inserts it unconditionally, so an empty-space click even far from the UV layout selects an arbitrary face instead of clearing/no-oping; edge and vertex modes already guard with pickRadiusSq. Gate the centroid fallback by the pick radius, or only insert when pointInTriangle matched.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/UVEditorController.cpp (1)

104-125: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add breadcrumbs for the new user-facing UV selection actions.

setSelectionMode() logs a breadcrumb, but the new sync toggle, clear selection, click-pick, and box-select actions do not. As per coding guidelines, “Add SentryReporter::addBreadcrumb(category, message) calls for user-facing actions and significant operations.”

Proposed breadcrumb coverage
 void UVEditorController::setSelectionSyncEnabled(bool on)
 {
     if (m_selectionSyncEnabled == on)
         return;
+    SentryReporter::addBreadcrumb(QStringLiteral("ui.action"),
+        QStringLiteral("UV editor selection sync: %1").arg(on ? QStringLiteral("on")
+                                                              : QStringLiteral("off")));
     m_selectionSyncEnabled = on;
@@
 void UVEditorController::clearUvSelection()
 {
     if (m_selectedUvVerts.isEmpty() && m_selectedUvEdges.isEmpty() && m_selectedUvFaces.isEmpty())
         return;
+    SentryReporter::addBreadcrumb(QStringLiteral("ui.action"),
+        QStringLiteral("UV editor clear selection"));
@@
 void UVEditorController::pickAt(double u, double v, int modifiers, double pickRadiusUv)
 {
     if (!m_hasMesh || m_uvTris.empty())
         return;
+    SentryReporter::addBreadcrumb(QStringLiteral("ui.action"),
+        QStringLiteral("UV editor pick selection"));
@@
 void UVEditorController::boxSelect(double uMin, double vMin, double uMax, double vMax, int modifiers)
 {
     if (!m_hasMesh)
         return;
+    SentryReporter::addBreadcrumb(QStringLiteral("ui.action"),
+        QStringLiteral("UV editor box selection"));

Also applies to: 307-413

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/UVEditorController.cpp` around lines 104 - 125, The new user-facing UV
selection actions in UVEditorController need Sentry breadcrumbs like
setSelectionMode() already does. Add SentryReporter::addBreadcrumb(category,
message) calls in the sync toggle path (setSelectionSyncEnabled), clear
selection (clearUvSelection), and the click-pick/box-select handling mentioned
in the diff so each significant user action is recorded with a clear
category/message. Use the existing breadcrumb pattern in UVEditorController to
place the calls near the action entry points and keep the messages descriptive
and consistent.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/UVEditorController.cpp`:
- Around line 941-943: The UV triangle emission path is using a per-triangle
counter for meshGlobalTri, but triangulated n-gons need to keep the original
mesh face id so sync/context lookups resolve to the correct Edit Mode face.
Update the UvTri creation logic in UVEditorController’s triangulation flow
(including the loop around sub.faces and the UvTri assignment) so every emitted
triangle for a source face carries that face’s original mesh id instead of the
incremented UV triangle index.
- Around line 1017-1043: The UV weld pass in UVEditorController’s
edge-occurrence loop is using a directed UV-only key, which misses shared edges
when adjacent triangles store opposite edge directions and can incorrectly merge
unrelated overlapping UVs. Change the edge matching logic around edgeOccMap and
packUvKey so it builds undirected keys from the actual mesh edge identity (using
the underlying vertex ids, not just UV coordinates), then keep the corner
welding in weldCorner gated by the UV epsilon check.
- Around line 317-335: The FaceMode picking logic in UVEditorController
currently falls back to the nearest triangle centroid even when no UV triangle
contains the click, which causes empty-space clicks to select an arbitrary face.
Update the selection path in the FaceMode block so it only inserts into faces
when pointInTriangle() actually matches, and otherwise leaves the selection
empty or no-ops consistently with the vertex/edge picking behavior in
UVEditorController.
- Around line 1178-1181: The UV rebuild path in
UVEditorController::buildFromEntity/clearUvSelection is pushing an empty
selection too early when sync is enabled and entity != prevEntity. Update the
rebuild flow so clearUvSelection does not call pushSelectionToEdit() in this
transition, or otherwise guard it until after the Edit Mode selection has been
pulled back, to avoid deselectAll() on the source selection before the sync
readback occurs.

---

Nitpick comments:
In `@src/UVEditorController.cpp`:
- Around line 104-125: The new user-facing UV selection actions in
UVEditorController need Sentry breadcrumbs like setSelectionMode() already does.
Add SentryReporter::addBreadcrumb(category, message) calls in the sync toggle
path (setSelectionSyncEnabled), clear selection (clearUvSelection), and the
click-pick/box-select handling mentioned in the diff so each significant user
action is recorded with a clear category/message. Use the existing breadcrumb
pattern in UVEditorController to place the calls near the action entry points
and keep the messages descriptive and consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ebadd4a7-a8a8-43b2-9738-9f540db73d76

📥 Commits

Reviewing files that changed from the base of the PR and between 464b8a3 and e2432c6.

📒 Files selected for processing (4)
  • qml/UVEditorPanel.qml
  • src/UVEditorController.cpp
  • src/UVEditorController.h
  • src/UVEditorController_test.cpp

Comment thread src/UVEditorController.cpp
Comment thread src/UVEditorController.cpp
Comment thread src/UVEditorController.cpp
Comment thread src/UVEditorController.cpp Outdated
@fernandotonon

Copy link
Copy Markdown
Owner Author

Addressed CodeRabbit/Codex review feedback in eb4da3e:

  • Face pick now respects pickRadiusUv for centroid fallback (empty-space clicks no longer select arbitrary faces)
  • UV corner welding uses undirected mesh-edge keys instead of directed UV-only keys
  • Filtered submesh views preserve original global vertex/triangle offsets for Edit Mode sync
  • N-gon fan triangles share one meshGlobalTri (first fan tri) for sync/context lookup
  • Entity-switch rebuild suppresses sync push before pull to avoid clearing Edit Mode selection

Added FacePickMissesEmptySpaceOutsideRadius test (19/19 UV tests pass locally).

CI unit-tests-linux failed with runner shutdown (SIGTERM) — re-running failed jobs.

@fernandotonon

Copy link
Copy Markdown
Owner Author

Follow-up in 570f207: removed live Edit Mode sync (Sync checkbox + push/pull). It was freezing the app because every UV pick rebuilt Edit Mode selection overlays on the main thread.

Review items — status:

Comment Status
Face pick radius (pointInTriangle / centroid fallback) Fixed in eb4da3e
Undirected mesh-edge weld keys Fixed in eb4da3e
Preserve submesh global offsets (filtered view) Fixed in eb4da3e — still used for context-island mapping
N-gon meshGlobalTri (one id per face) Fixed in eb4da3e
Don't push empty UV selection before pull N/A — sync removed entirely

Context island tint (3D → UV read-only) remains.

@fernandotonon

Copy link
Copy Markdown
Owner Author

Re-ran unit-tests-linux twice — both runs hit exit 143 (runner SIGTERM) before the suite finished. No ERROR: Suite lines or UV test failures in the logs. All platform builds pass. This matches the known CI timeout issue on the full test matrix.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/workflows/deploy.yml (1)

1441-1496: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Sharding silently halves SonarCloud coverage.

gcovr (Line 1441) and sonar-scanner (Line 1496) run only on shard 1, but shard 1 now executes only ~half of the UnitTests suites (the per-suite skip at Lines 1318-1324). The .gcda files for suites that run exclusively on shard 2 are therefore never produced on the shard-1 runner, and shard 2 uploads no coverage and there is no merge step. The result is that SonarCloud receives coverage for only the shard-1 subset (plus the "other test executables"), which can drop reported coverage substantially and trip the quality gate.

Two options:

  • Download shard-2's .gcda/coverage artifact and merge before running gcovr/sonar; or
  • Run the full (unsharded) UnitTests set on shard 1 purely for coverage, keeping shard 2 for parallelism/pass-fail only.

Please confirm the intended coverage semantics before merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 1441 - 1496, Shard 1 is only
generating and uploading coverage for a partial test set, so SonarCloud sees
incomplete coverage. Update the coverage flow in the deploy workflow around the
“Generate coverage data” and “Run sonar-scanner” steps so the shard-1 job either
merges shard-2 .gcda/coverage artifacts before running gcovr, or runs the full
UnitTests coverage suite on shard 1 while keeping sharding only for execution.
Use the existing shard-1 condition and coverage generation block to locate the
change.
src/UVEditorController.cpp (1)

976-985: 🗄️ Data Integrity & Integration | 🟠 Major

Pair welded corners by shared mesh vertex, not by c0/c1 order. EdgeOcc::c0/c1 keep each triangle’s local edge direction, and shared manifold edges are expected to run in opposite directions. The current a.c0↔b.c0 / a.c1↔b.c1 pairing can compare different mesh vertices and miss a valid weld, leaving adjacent triangles split. Match corners by meshGlobalVert, and add a regression test with two triangles that actually share a mesh edge; the existing seamed-quad test uses duplicated vertices and does not cover this path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/UVEditorController.cpp` around lines 976 - 985, The corner-welding logic
in the UVEditorController weld helper is pairing `a.c0/c1` with `b.c0/c1`, which
can mismatch shared manifold edges because `EdgeOcc::c0/c1` are local edge
directions. Update the welding in this block to match corners by shared
`meshGlobalVert` for each `EdgeOcc`, so the correct corresponding corners are
united regardless of edge orientation. Add a regression test around the relevant
UV welding path in `UVEditorController` using two triangles that truly share a
mesh edge, since the current seamed-quad coverage with duplicated vertices does
not exercise this case.
🧹 Nitpick comments (1)
.github/workflows/deploy.yml (1)

939-941: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Keep UNIT_TEST_SHARD_TOTAL in sync with the matrix.

UNIT_TEST_SHARD_TOTAL: 2 is duplicated from matrix.shard: [1, 2]. Adding/removing a shard requires editing both; deriving it from the matrix length (${{ strategy.job-total }}) removes the drift risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 939 - 941, Keep
UNIT_TEST_SHARD_TOTAL aligned with the shard matrix instead of hardcoding it. In
the deploy workflow’s unit test job, replace the duplicated total in the env
block with a value derived from the job matrix/strategy so changes to the shard
list only need to be made in one place; use the existing matrix shard
configuration and the job-total/strategy-derived value to keep the total in
sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/deploy.yml:
- Line 938: The workflow job is over-permissioned by using permissions:
read-all; narrow the permissions block to only the minimal read access the
analysis job actually needs. Update the relevant job in deploy.yml where
SONAR_TOKEN and GITHUB_TOKEN are used so it requests explicit repo read access
instead of every read scope, keeping least-privilege without changing the
analysis behavior.
- Around line 1430-1434: The workflow step using actions/setup-python is still
on the retired v4 tag, so update the Set up Python 3.8 for gcovr step to a
current Node 20-based release such as v6.3.0 and pin it to the release commit
SHA instead of the mutable major tag. Keep the existing matrix.shard condition
and python-version setting, and make the change in the setup-python action
reference only.

---

Outside diff comments:
In @.github/workflows/deploy.yml:
- Around line 1441-1496: Shard 1 is only generating and uploading coverage for a
partial test set, so SonarCloud sees incomplete coverage. Update the coverage
flow in the deploy workflow around the “Generate coverage data” and “Run
sonar-scanner” steps so the shard-1 job either merges shard-2 .gcda/coverage
artifacts before running gcovr, or runs the full UnitTests coverage suite on
shard 1 while keeping sharding only for execution. Use the existing shard-1
condition and coverage generation block to locate the change.

In `@src/UVEditorController.cpp`:
- Around line 976-985: The corner-welding logic in the UVEditorController weld
helper is pairing `a.c0/c1` with `b.c0/c1`, which can mismatch shared manifold
edges because `EdgeOcc::c0/c1` are local edge directions. Update the welding in
this block to match corners by shared `meshGlobalVert` for each `EdgeOcc`, so
the correct corresponding corners are united regardless of edge orientation. Add
a regression test around the relevant UV welding path in `UVEditorController`
using two triangles that truly share a mesh edge, since the current seamed-quad
coverage with duplicated vertices does not exercise this case.

---

Nitpick comments:
In @.github/workflows/deploy.yml:
- Around line 939-941: Keep UNIT_TEST_SHARD_TOTAL aligned with the shard matrix
instead of hardcoding it. In the deploy workflow’s unit test job, replace the
duplicated total in the env block with a value derived from the job
matrix/strategy so changes to the shard list only need to be made in one place;
use the existing matrix shard configuration and the job-total/strategy-derived
value to keep the total in sync.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ce7969fd-7d21-412e-896b-14cbe465f78a

📥 Commits

Reviewing files that changed from the base of the PR and between e2432c6 and d99ce55.

📒 Files selected for processing (5)
  • .github/workflows/deploy.yml
  • qml/UVEditorPanel.qml
  • src/UVEditorController.cpp
  • src/UVEditorController.h
  • src/UVEditorController_test.cpp
💤 Files with no reviewable changes (1)
  • qml/UVEditorPanel.qml

matrix:
shard: [1, 2]
timeout-minutes: 45
permissions: read-all

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Scope down permissions: read-all.

This job only needs to read the repo (and uses SONAR_TOKEN/GITHUB_TOKEN from secrets for analysis). read-all grants read on every scope unnecessarily; prefer the least-privilege set.

🔒 Suggested scoping
-    permissions: read-all
+    permissions:
+      contents: read
+      pull-requests: read
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions: read-all
permissions:
contents: read
pull-requests: read
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 938-938: overly broad permissions (excessive-permissions): uses read-all permissions

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml at line 938, The workflow job is
over-permissioned by using permissions: read-all; narrow the permissions block
to only the minimal read access the analysis job actually needs. Update the
relevant job in deploy.yml where SONAR_TOKEN and GITHUB_TOKEN are used so it
requests explicit repo read access instead of every read scope, keeping
least-privilege without changing the analysis behavior.

Source: Linters/SAST tools

Comment on lines 1430 to 1434
- name: Set up Python 3.8 for gcovr
if: matrix.shard == 1
uses: actions/setup-python@v4
with:
python-version: 3.8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major

🧩 Analysis chain

🌐 Web query:

What is the latest release tag and commit SHA of actions/setup-python, and which Node runtime does v4 vs v5 use?

💡 Result:

As of June 25, 2026, the latest release tag for actions/setup-python is v6.3.0 [1]. The commit SHA associated with the v6.3.0 release is a309ff8b426b58ec0e2a45f0f869d46889d02405 [2]. Regarding the Node runtime environments: - The v4 version of actions/setup-python used Node 16 [3]. - The v5 version of actions/setup-python updated the Node runtime to Node 20 [4][3].

Citations:


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/deploy.yml')
text = p.read_text()
lines = text.splitlines()
for i in range(1424, 1438):
    print(f"{i+1}: {lines[i]}")
PY

Repository: fernandotonon/QtMeshEditor

Length of output: 579


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/deploy.yml')
text = p.read_text()
lines = text.splitlines()
for i in range(1424, 1438):
    print(f"{i+1}: {lines[i]}")
PY

Repository: fernandotonon/QtMeshEditor

Length of output: 579


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/deploy.yml')
lines = p.read_text().splitlines()
for i in range(1370, 1455):
    print(f"{i+1}: {lines[i]}")
PY

Repository: fernandotonon/QtMeshEditor

Length of output: 4407


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/deploy.yml')
lines = p.read_text().splitlines()
for i in range(1370, 1455):
    print(f"{i+1}: {lines[i]}")
PY

Repository: fernandotonon/QtMeshEditor

Length of output: 4407


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/deploy.yml')
lines = p.read_text().splitlines()
for i in range(1370, 1455):
    print(f"{i+1}: {lines[i]}")
PY

Repository: fernandotonon/QtMeshEditor

Length of output: 4407


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/deploy.yml')
lines = p.read_text().splitlines()
for i in range(1370, 1455):
    print(f"{i+1}: {lines[i]}")
PY

Repository: fernandotonon/QtMeshEditor

Length of output: 4407


Update actions/setup-python to a Node 20 release and pin the SHA.

actions/setup-python@v4 uses the retired Node 16 runtime. Switch this step to a current release such as v6.3.0 and pin it to the release commit instead of the mutable major tag.

🧰 Tools
🪛 actionlint (1.7.12)

[error] 1432-1432: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 zizmor (1.26.1)

[error] 1432-1432: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 1430 - 1434, The workflow step
using actions/setup-python is still on the retired v4 tag, so update the Set up
Python 3.8 for gcovr step to a current Node 20-based release such as v6.3.0 and
pin it to the release commit SHA instead of the mutable major tag. Keep the
existing matrix.shard condition and python-version setting, and make the change
in the setup-python action reference only.

Source: Linters/SAST tools

fernandotonon and others added 3 commits June 26, 2026 02:50
Add independent UV-vertex/edge/face selection with click, multi-select, and
box-select; optional bidirectional sync with Edit Mode; and context island
highlighting when sync is off.

Co-authored-by: Cursor <cursoragent@cursor.com>
Gate face picks by radius, weld UV corners on undirected mesh edges,
preserve original submesh global indices for filtered views, map n-gon
fan triangles to one Edit Mode face id, and avoid pushing empty UV
selection during entity-switch rebuild before pull.

Co-authored-by: Cursor <cursoragent@cursor.com>
Bidirectional selection sync caused main-thread freezes (Edit Mode overlay
rebuilds on every pick). Drop the Sync checkbox and push/pull paths; UV
selection stays local and 3D Edit Mode selection still tints context islands.

Co-authored-by: Cursor <cursoragent@cursor.com>
@fernandotonon fernandotonon force-pushed the feature/uv-component-selection-460 branch from a7d3b32 to feda14f Compare June 26, 2026 06:50
Address CodeRabbit nitpick: log clear/pick/box selection alongside the
existing selection-mode breadcrumb.

Co-authored-by: Cursor <cursoragent@cursor.com>
@fernandotonon

Copy link
Copy Markdown
Owner Author

CI on rebased branch (feda14f) is green — unit-tests-linux passed in ~51m with master's single-job workflow (no matrix sharding).

Code review status (3573075):

Comment Status
Face pick respects pickRadiusUv Fixed in 86eeb17
Undirected mesh-edge weld keys (gv0/gv1 swap) Fixed in 86eeb17
Preserve submesh global vert/tri offsets Fixed in 86eeb17
N-gon fan shares one meshGlobalTri Fixed in 86eeb17
Don't push empty UV selection before pull N/A — live sync removed in feda14f
Sentry breadcrumbs for clear/pick/box Fixed in 3573075
deploy.yml matrix / permissions nitpicks N/A — CI changes dropped; using master workflow

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UV: Slice B — UV component selection (vertex/edge/face)

1 participant