Skip to content

Bug 610 - cannot pull wire from group.#621

Merged
highperformancecoder merged 8 commits intomasterfrom
bugfix-610-unable-to-pull-wire
Mar 11, 2026
Merged

Bug 610 - cannot pull wire from group.#621
highperformancecoder merged 8 commits intomasterfrom
bugfix-610-unable-to-pull-wire

Conversation

@highperformancecoder
Copy link
Owner

@highperformancecoder highperformancecoder commented Mar 11, 2026

This change is Reviewable

Summary by CodeRabbit

  • Bug Fixes

    • Improved edge I/O variable positioning, drawing, and hit-detection so edge variables can be reliably selected and used to start wire-dragging at all zoom levels; port clicks now register correctly. Selection works without requiring a prior redraw and rendering/selection are more consistent.
  • Tests

    • Added regression tests validating selection and wire-drag behavior for edge variables, including zoomed scenarios and selection without prior redraw.

Copilot AI review requested due to automatic review settings March 11, 2026 02:34
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

Warning

Rate limit exceeded

@highperformancecoder has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 47 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 32f533bc-9fbc-4148-9070-6805cbe8de9b

📥 Commits

Reviewing files that changed from the base of the PR and between a81c4a6 and 89b9d4d.

📒 Files selected for processing (1)
  • model/group.cc
📝 Walkthrough

Walkthrough

Adds positionEdgeVariables plumbing (frontend/backend), makes RenderVariable hit-testing zoom-aware, reorders Group hit-testing and introduces layout/position helpers for edge I/O variables, and adds regression tests validating selection and zoomed wire-pull behavior.

Changes

Cohort / File(s) Summary
Frontend API Layer
gui-js/libs/shared/src/lib/backend/minsky.ts
Added async positionEdgeVariables(): Promise<void> methods to Group and Selection that forward to the backend via this.$callMethod('positionEdgeVariables').
Render hit-test math
model/cairoItems.cc
Adjusted RenderVariable::inImage to divide coordinates by the variable's zoomFactor() before rotation, making hit-testing zoom-aware.
Group positioning & hit-testing
model/group.h, model/group.cc
Added void Group::positionEdgeVariables() const, introduced layoutEdgeVariables helper, reordered edge-variable hit-testing to run before the in/out-region early exit, updated select() and edge-variable drawing to use the new positioning logic.
Tests
test/testCanvas.cc
Added GroupIOTest tests (groupIOVarSelect, groupIOVarSelectZoomed) verifying edge I/O variable selection without prior draw and under non-default zoom.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as "UI Layer"
    participant Group as "Group\n(positionEdgeVariables / select)"
    participant Render as "RenderVariable\n(inImage)"
    User->>UI: Click at (x,y)
    UI->>Group: clickType(x,y)
    Group->>Group: positionEdgeVariables()
    Note over Group: compute world-space positions\nfor edge I/O variables (zoom-aware)
    Group->>Render: inImage(x,y) (coords normalized by zoom)
    alt hit detected
        Render-->>Group: hit
        Group-->>UI: selected variable / onPort
    else no hit
        Render-->>Group: miss
        Group-->>UI: continue other hit-tests
    end
    UI-->>User: selection result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I nudged the edge where wires may stray,
Zoomed-in hops find ports along the way.
No Cairo call, just math to place each dot,
A tiny rabbit tidy-up — click and slot!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Bug 610 - cannot pull wire from group' directly addresses the core issue being fixed: inability to pull wires from group edge variables, which is reflected in all file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix-610-unable-to-pull-wire

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 and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses a regression where users could not start wire dragging from a Group’s edge I/O variables (issue #610), particularly when the Group is zoomed and contents are displayed.

Changes:

  • Reorders Group::clickType() logic to test edge I/O variables before the inIORegion early-exit.
  • Adds Group::positionEdgeVariables() and invokes it from Group::select() to ensure edge-variable positions are valid for hit-testing even without a prior draw pass.
  • Fixes RenderVariable::inImage() hit-testing under zoom by accounting for zoomFactor().

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/testCanvas.cc Adds a regression test covering Group edge output-variable selection without a prior draw.
model/group.h Declares positionEdgeVariables() to support non-render hit-testing.
model/group.cc Implements edge-variable positioning for hit-testing; adjusts click-type evaluation order to fix issue #610.
model/cairoItems.cc Updates variable hit-testing to account for zoom.
gui-js/libs/shared/src/lib/backend/minsky.ts Exposes positionEdgeVariables() on Group/Selection in the generated TypeScript backend API wrapper.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +1225 to 1228
// Ensure edge variable positions are up-to-date before hit-testing.
// (Positions are otherwise only set during draw1edge() in a render pass.)
positionEdgeVariables();
for (auto& v: inVariables)
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

select() now unconditionally calls positionEdgeVariables(), which in turn calls margins() (updating each I/O var’s bounding box via Pango) and does moveTo() on every edge variable. Because Canvas::itemAt() hit-tests groups via clickType() (which now calls select()), this can add a lot of work even when (x,y) is nowhere near the group. Consider short-circuiting before positionEdgeVariables() (eg, return early when inVariables/outVariables are empty, and/or only update positions when (x,y) is within an expanded group bounds that could plausibly include edge variables).

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

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

@copilot open a new pull request to apply changes based on this feedback

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
test/testCanvas.cc (1)

111-128: Please also cover the zoomed wire-pull path.

This regression proves select() now positions edge variables before hit-testing, but it still probes the icon center at default zoom. That will not catch regressions in model/cairoItems.cc, Lines 90-97, or model/group.cc, Lines 910-927, which are the changes that actually unblock pulling a wire when zoomed. Please add a non-unit zoom case and assert grp->clickType(...) == ClickType::onPort (or the equivalent wire-drag path).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/testCanvas.cc` around lines 111 - 128, Add a test variant that verifies
the zoomed wire-pull path by calling grp->positionEdgeVariables() (or relying on
grp->select() which now positions them) at a non-default zoom and then asserting
the hit-test yields a port click; specifically, reuse the existing
expectedX/expectedY computation but set grp->setZoomFactor(...) (or
grp->zoomFactor setter) to a non-1 value, call grp->select(expectedX, expectedY)
(or directly call grp->clickType(expectedX, expectedY)) and assert the result
equals ClickType::onPort (or the existing enum/value used for wire-drag),
ensuring the test covers the code paths in model/cairoItems.cc and
model/group.cc that handle zoomed icon-center probing.
model/group.cc (1)

1188-1221: Factor the edge-variable placement math into one helper.

positionEdgeVariables() now duplicates the layout code in draw1edge() at model/group.cc, Lines 1048-1078. Keeping both copies in sync is what makes rendering and hit-testing agree, so this is worth centralizing before the next spacing tweak.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model/group.cc` around lines 1188 - 1221, The positioning math for edge
variables is duplicated between Group::positionEdgeVariables() and draw1edge();
extract the shared layout into a single helper (e.g., a new method like
Group::layoutEdgeVariables or a static/free helper used by both) that contains
the loop and calculations for xEdge, yOff, top/bottom, Rotate usage,
v->moveTo(), v->rotation(), and per-variable height/zoom adjustments; replace
the duplicated blocks in positionEdgeVariables() and the draw1edge() code (the
block at model/group.cc lines referenced in the comment) with calls into this
helper, passing the variables vector, computed xEdge (using
iWidth()/zoomFactor()/margins results), and any needed context (rotation(),
this->x()/this->y(), and zoom factors) so rendering and hit-testing share the
exact same placement logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@model/group.cc`:
- Around line 1188-1221: The positioning math for edge variables is duplicated
between Group::positionEdgeVariables() and draw1edge(); extract the shared
layout into a single helper (e.g., a new method like Group::layoutEdgeVariables
or a static/free helper used by both) that contains the loop and calculations
for xEdge, yOff, top/bottom, Rotate usage, v->moveTo(), v->rotation(), and
per-variable height/zoom adjustments; replace the duplicated blocks in
positionEdgeVariables() and the draw1edge() code (the block at model/group.cc
lines referenced in the comment) with calls into this helper, passing the
variables vector, computed xEdge (using iWidth()/zoomFactor()/margins results),
and any needed context (rotation(), this->x()/this->y(), and zoom factors) so
rendering and hit-testing share the exact same placement logic.

In `@test/testCanvas.cc`:
- Around line 111-128: Add a test variant that verifies the zoomed wire-pull
path by calling grp->positionEdgeVariables() (or relying on grp->select() which
now positions them) at a non-default zoom and then asserting the hit-test yields
a port click; specifically, reuse the existing expectedX/expectedY computation
but set grp->setZoomFactor(...) (or grp->zoomFactor setter) to a non-1 value,
call grp->select(expectedX, expectedY) (or directly call
grp->clickType(expectedX, expectedY)) and assert the result equals
ClickType::onPort (or the existing enum/value used for wire-drag), ensuring the
test covers the code paths in model/cairoItems.cc and model/group.cc that handle
zoomed icon-center probing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 909517e4-5444-4ec3-a9ac-0331315b89bd

📥 Commits

Reviewing files that changed from the base of the PR and between 235259d and 22ddea9.

📒 Files selected for processing (5)
  • gui-js/libs/shared/src/lib/backend/minsky.ts
  • model/cairoItems.cc
  • model/group.cc
  • model/group.h
  • test/testCanvas.cc

Copy link
Contributor

Copilot AI commented Mar 11, 2026

@highperformancecoder I've opened a new pull request, #622, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits March 11, 2026 03:27
…eVariables()

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@model/group.cc`:
- Around line 1080-1109: The draw pass must apply the same zoom used during
layout so rendered offsets match hit-test positions: in draw1edge()/Group::draw
where you compute t = v->bb.top(), b = v->bb.bottom() and update top/bottom,
scale those bounding-box values by the variable's zoom (e.g. t *=
v->zoomFactor(); b *= v->zoomFactor();) and stop dividing heights by zoom; use
v->height() when adjusting top and bottom (replace top -= v->height() /
v->zoomFactor() and bottom += v->height() / v->zoomFactor() with top -=
v->height() and bottom += v->height()). This keeps yOff/top/bottom in
world-space to match layoutEdgeVariables()/layout results.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa634275-0a0f-457c-8651-477e06f0ba57

📥 Commits

Reviewing files that changed from the base of the PR and between 22ddea9 and de8d4a6.

📒 Files selected for processing (2)
  • model/group.cc
  • test/testCanvas.cc
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/testCanvas.cc

@highperformancecoder highperformancecoder changed the title Fixes from Antigravity. Bug 610 - cannot pull wire from group. Mar 11, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
model/group.cc (1)

1080-1109: ⚠️ Potential issue | 🟠 Major

Keep draw1edge() in the same zoom space as layoutEdgeVariables().

layoutEdgeVariables() now computes yOff/top/bottom in world space, but this block switches back to unscaled bb.top()/bottom() and divides height() by zoom again. At non-unit zoom, the rendered edge ports drift away from the positions used for hit-testing. Group::draw() also has not scaled the Cairo context before drawEdgeVariables(), so the new comment here is misleading.

Suggested fix
-    // Iterate again solely to perform drawing steps.
-    // Group::draw has ALREADY called cairo_scale(cairo, z, z), so the `xEdge`
-    // parameter is properly matched to the visual coordinates.
-    // However, translating `yOff` locally during rendering requires tracking
-    // the variable bounding boxes WITHOUT pre-multiplying `z` onto them here.
     const double angle=rotation() * M_PI / 180.0;
     float top=0, bottom=0;
     for (size_t i=0; i<vars.size(); ++i)
       {
         auto& v=vars[i];
+        const float z=v->zoomFactor();
         
         float yOff = 0;
-        auto t = v->bb.top(), b = v->bb.bottom();
+        auto t = v->bb.top()*z, b = v->bb.bottom()*z;
         if (i > 0) yOff = i % 2 ? top - b : bottom - t;
         
         const cairo::CairoSave cs(cairo);
         cairo_translate(cairo,xEdge,yOff);
         // cairo context is already rotated, so antirotate
         cairo_rotate(cairo,-angle);
         v->draw(cairo);

         if (i == 0)
           {
             bottom = b;
             top = t;
           }
         else if (i % 2)
-          top -= v->height() / v->zoomFactor(); // Remove zoom as Group::draw supplies it
+          top -= v->height();
         else
-          bottom += v->height() / v->zoomFactor();
+          bottom += v->height();
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model/group.cc` around lines 1080 - 1109, The rendering loop is mixing zoom
spaces: layoutEdgeVariables() computes yOff/top/bottom in world (zoomed)
coordinates but this block reverts to unscaled values by calling
v->bb.top()/bottom() and then subtracting v->height()/v->zoomFactor(); to fix,
keep everything in the same (world/zoomed) space — stop undoing zoom by removing
the /v->zoomFactor() adjustments and use the bb/top/bottom and v->height() as-is
(or explicitly multiply by the current zoom if those members are in local
space), and update the comment near Group::draw()/drawEdgeVariables()/rotation()
to reflect that the Cairo context is not pre-scaled here so we must use
world-space values for positioning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@model/group.cc`:
- Around line 1080-1109: The rendering loop is mixing zoom spaces:
layoutEdgeVariables() computes yOff/top/bottom in world (zoomed) coordinates but
this block reverts to unscaled values by calling v->bb.top()/bottom() and then
subtracting v->height()/v->zoomFactor(); to fix, keep everything in the same
(world/zoomed) space — stop undoing zoom by removing the /v->zoomFactor()
adjustments and use the bb/top/bottom and v->height() as-is (or explicitly
multiply by the current zoom if those members are in local space), and update
the comment near Group::draw()/drawEdgeVariables()/rotation() to reflect that
the Cairo context is not pre-scaled here so we must use world-space values for
positioning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 365d9d8d-6e7b-476d-a1b7-2ed62de2fc1f

📥 Commits

Reviewing files that changed from the base of the PR and between cf496c7 and a81c4a6.

📒 Files selected for processing (1)
  • model/group.cc

@highperformancecoder highperformancecoder merged commit a82f9b1 into master Mar 11, 2026
7 checks passed
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.

3 participants