Skip to content

FIX: GetBindingDisplayString returns empty for composites filtered by group-based binding mask [UUM-141423]#2414

Open
Pauliusd01 wants to merge 1 commit intodevelopfrom
fix/uum-141423-getbindingdisplaystring-composite-mask
Open

FIX: GetBindingDisplayString returns empty for composites filtered by group-based binding mask [UUM-141423]#2414
Pauliusd01 wants to merge 1 commit intodevelopfrom
fix/uum-141423-getbindingdisplaystring-composite-mask

Conversation

@Pauliusd01
Copy link
Copy Markdown
Collaborator

Description

InputActionRebindingExtensions.GetBindingDisplayString(InputAction, InputBinding, InputBinding.DisplayStringOptions) was returning an empty string for composite bindings (e.g. 1DAxis, 2DVector, ButtonWithOneModifier) whenever the supplied binding mask filtered by control-scheme group, because the per-binding loop skipped isPartOfComposite entries and then tested the mask directly against the composite header — which carries no group metadata of its own — causing the whole composite to drop out of the result. The fix performs a fall-through scan over the composite's part siblings and promotes the composite atomically when at least one of its parts matches the mask, reusing the exact scan idiom already used by the integer-index renderer further down the same file. The user-visible effect is that rebinding UIs and similar callers now correctly render composite display strings under group-based masks. Hypothesis was approved by input-fix-judge on pass 2 — pass 1 rejected the original Step 5b Potential fix, input-fix-deep-dive then re-investigated and proposed this alternative, and pass 2 of the judge approved this hypothesis before the user invoked this skill directly.

Testing status & QA

  • Added automated test Actions_WhenGettingDisplayTextForBindingsOnAction_CompositeIsIncludedWhenAtLeastOnePartMatchesBindingMask in Assets/Tests/InputSystem/CoreTests_Actions.cs ([Test], CoreTestsFixture).
  • QA should re-run the manual repro steps from the linked Jira ticket: https://jira.unity3d.com/browse/UUM-141423

Overall Product Risks

  • Complexity: low
  • Halo Effect: low (internal-only edit confined to a single method, with no signature, serialization, or native/threading/unsafe surface touched)

Comments to reviewers

Auto-approved by input-fix-judge (pass-2): file and method exist, the mechanism is in the method body (lines 321-323), the fall-through reuses the existing scan idiom at lines 444-448 of the same file with no signature/exception/serialization change, no nearby test asserts the buggy current behaviour, no native/threading/unsafe touches in the method or its direct callees at one level, and the previously-cited mixed-group composite edge case is now explicitly addressed via documented atomic-composite semantics grounded in the line-440-492 renderer's existing convention. This hypothesis came from input-fix-deep-dive's independent root-cause analysis after the original triage Step 5b Potential fix was rejected on pass 1; pass 2 of the judge approved this alternative. The chain only routes hypotheses that the judge could not find any concrete reason to reject (read-only checklist of file/method existence, mechanism location, public API impact, existing test coverage, side-effects, and ignored edge cases). Reviewers should still treat this as a normal review — the chain skips anything the judge rejects but does not certify correctness.

Checklist

Before review:

  • Changelog entry added.
    • Explains the change in Changed, Fixed, Added sections.
    • For API change contains an example snippet and/or migration example.
    • JIRA ticket linked, example (case %%). If it is a private issue, just add the case ID without a link.
    • Jira port for the next release set as "Resolved".
  • Tests added/changed, if applicable.
    • Functional tests Area_CanDoX, Area_CanDoX_EvenIfYIsTheCase, Area_WhenIDoX_AndYHappens_ThisIsTheResult.
    • Performance tests.
    • Integration tests.
  • Docs for new/changed API's.
    • Xmldoc cross references are set correctly.
    • Added explanation how the API works.
    • Usage code examples added.
    • The manual is updated, if needed.

During merge:

  • Commit message for squash-merge is prefixed with one of the list:
    • NEW: ___.
    • FIX: ___.
    • DOCS: ___.
    • CHANGE: ___.
    • RELEASE: 1.1.0-preview.3.

… group-based binding mask [UUM-141423]

Auto-generated by input-fix-pr-prep after input-fix-judge approved the hypothesis from input-fix-deep-dive (after triage Step 5b was rejected on pass 1).

InputActionRebindingExtensions.GetBindingDisplayString(InputAction, InputBinding, InputBinding.DisplayStringOptions) returns empty when the binding mask filters by group, because the per-binding loop skips isPartOfComposite parts but then tests the mask against composite headers which carry no group metadata, so the whole composite drops out.

Jira: https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-141423
Judge rationale: file and method exist, the mechanism is in the method body (lines 321-323), the fall-through reuses the existing scan idiom at lines 444-448 of the same file with no signature/exception/serialization change, no nearby test asserts the buggy current behaviour, no native/threading/unsafe touches in the method or its direct callees at one level, and the previously-cited mixed-group composite edge case is now explicitly addressed via documented atomic-composite semantics grounded in the line-440-492 renderer's existing convention.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Pauliusd01 Pauliusd01 self-assigned this May 5, 2026
@Pauliusd01 Pauliusd01 marked this pull request as ready for review May 5, 2026 12:26
@Pauliusd01 Pauliusd01 requested a review from u-pr May 5, 2026 12:27
@Pauliusd01
Copy link
Copy Markdown
Collaborator Author

@u-pr can you review this PR

Copy link
Copy Markdown
Contributor

@u-pr u-pr Bot left a comment

Choose a reason for hiding this comment

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

Perfect

The pull request is of excellent quality. I have identified one minor optimization in InputActionRebindingExtensions.cs to simplify a loop that currently performs a two-pass scan.

🤖 Helpful? 👍/👎

anyPartMatches = true;
break;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

low

Although this logic intentionally reuses the scan idiom from the integer-index renderer below, it computes lastPartIndex to find the end of the composite parts and then iterates through the parts a second time to find a match.

Since lastPartIndex is not used further in this block (e.g., it is not used to advance the outer loop's i), you can simplify this into a single loop. This avoids evaluating the composite parts twice and makes the code slightly more concise.

Have you considered merging the bounds check into the for loop condition?

Suggested change
}
var anyPartMatches = false;
for (var partIndex = i + 1; partIndex < bindings.Count && bindings[partIndex].isPartOfComposite; ++partIndex)
{
if (bindingMask.Matches(bindings[partIndex]))
{
anyPartMatches = true;
break;
}
}

🤖 Helpful? 👍/👎

Copy link
Copy Markdown
Contributor

@u-pr u-pr Bot left a comment

Choose a reason for hiding this comment

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

Perfect

The code quality is high. One minor improvement is suggested regarding the maintainability of documentation comments.

🤖 Helpful? 👍/👎

continue;
{
// Composites are filtered atomically: any matching part promotes the whole
// composite, consistent with how the integer-index renderer at lines 440-492
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

low

Have you considered referencing the method name or logical section instead of specific line numbers (lines 440-492)? Hardcoded line numbers in comments tend to become outdated quickly as the file is modified and code is added or removed above this section.

🤖 Helpful? 👍/👎

@u-pr
Copy link
Copy Markdown
Contributor

u-pr Bot commented May 5, 2026

The fix correctly addresses the issue where composite bindings were being filtered out by group-based masks because the composite header itself lacks group metadata. By scanning the composite's parts and "promoting" the composite if any part matches the mask, you've ensured that the GetBindingDisplayString overload that takes a bindingMask behaves consistently with how composites are rendered as atomic units in other parts of the system.

The implementation follows existing patterns in InputActionRebindingExtensions.cs and the added test case reproduces the reported issue. I've suggested a small addition to the test suite to verify the "atomic" behavior for mixed-group composites.

🤖 Helpful? 👍/👎

continue;
var lastPartIndex = i + 1;
while (lastPartIndex < bindings.Count && bindings[lastPartIndex].isPartOfComposite)
++lastPartIndex;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This scan idiom is consistent with the implementation at line 444 (in the index-based GetBindingDisplayString overload).

One minor detail: since the outer loop already skips isPartOfComposite (line 321), this inner loop correctly identifies the boundaries of the current composite without interfering with the outer loop's progression.

📚 Additional Context

The following files were referenced for additional context:

  • Packages/com.unity.inputsystem/InputSystem/Runtime/Actions/InputActionRebindingExtensions.cs

🤖 Helpful? 👍/👎

.With("Positive", "<Keyboard>/d", groups: "Keyboard");

Assert.That(action.GetBindingDisplayString(InputBinding.MaskByGroup("Keyboard")),
Is.EqualTo("A/D"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This test confirms the fix for the common case where all parts share the same group. To fully verify the "atomic promotion" logic mentioned in the PR description, consider adding a test case for a composite with mixed groups, for example:

[Test]
public void Actions_WhenGettingDisplayTextForMixedGroupComposite_IsIncludedWhenAnyPartMatches()
{
    var action = new InputAction();
    action.AddCompositeBinding("2DVector")
        .With("Up", "<Keyboard>/w", groups: "Keyboard")
        .With("Down", "<Gamepad>/dpad/down", groups: "Gamepad");

    // Should return "W/D-Pad Down" even though only the 'Up' part matches the 'Keyboard' mask
    Assert.That(action.GetBindingDisplayString(InputBinding.MaskByGroup("Keyboard")),
        Is.EqualTo("W/D-Pad Down"));
}

🤖 Helpful? 👍/👎

@codecov-github-com
Copy link
Copy Markdown

codecov-github-com Bot commented May 5, 2026

Codecov Report

Attention: Patch coverage is 90.90909% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../Runtime/Actions/InputActionRebindingExtensions.cs 88.23% 2 Missing ⚠️
@@             Coverage Diff             @@
##           develop    #2414      +/-   ##
===========================================
+ Coverage    78.13%   78.15%   +0.02%     
===========================================
  Files          483      761     +278     
  Lines        98770   138878   +40108     
===========================================
+ Hits         77169   108542   +31373     
- Misses       21601    30336    +8735     
Flag Coverage Δ
inputsystem_MacOS_2022.3_project 75.40% <90.90%> (-0.01%) ⬇️
inputsystem_MacOS_6000.0 5.33% <0.00%> (+0.02%) ⬆️
inputsystem_MacOS_6000.0_project 77.29% <90.90%> (-0.01%) ⬇️
inputsystem_MacOS_6000.3 5.33% <0.00%> (+0.02%) ⬆️
inputsystem_MacOS_6000.3_project 77.29% <90.90%> (+0.02%) ⬆️
inputsystem_MacOS_6000.4 5.34% <0.00%> (+0.02%) ⬆️
inputsystem_MacOS_6000.4_project 77.30% <90.90%> (+0.02%) ⬆️
inputsystem_MacOS_6000.5 5.32% <0.00%> (+0.02%) ⬆️
inputsystem_MacOS_6000.5_project 77.33% <90.90%> (+0.01%) ⬆️
inputsystem_MacOS_6000.6 5.32% <0.00%> (+0.02%) ⬆️
inputsystem_MacOS_6000.6_project 77.33% <90.90%> (+0.02%) ⬆️
inputsystem_Ubuntu_2022.3_project 75.31% <90.90%> (+<0.01%) ⬆️
inputsystem_Ubuntu_6000.0 5.34% <0.00%> (+0.02%) ⬆️
inputsystem_Ubuntu_6000.0_project 77.20% <90.90%> (-0.01%) ⬇️
inputsystem_Ubuntu_6000.3 5.34% <0.00%> (+0.02%) ⬆️
inputsystem_Ubuntu_6000.3_project 77.20% <90.90%> (+0.02%) ⬆️
inputsystem_Ubuntu_6000.4 5.34% <0.00%> (+0.02%) ⬆️
inputsystem_Ubuntu_6000.4_project 77.21% <90.90%> (+0.02%) ⬆️
inputsystem_Ubuntu_6000.5 5.33% <0.00%> (+0.02%) ⬆️
inputsystem_Ubuntu_6000.5_project 77.24% <90.90%> (+0.01%) ⬆️
inputsystem_Ubuntu_6000.6 5.33% <0.00%> (+0.02%) ⬆️
inputsystem_Ubuntu_6000.6_project 77.24% <90.90%> (+0.02%) ⬆️
inputsystem_Windows_2022.3 5.36% <0.00%> (+0.02%) ⬆️
inputsystem_Windows_2022.3_project 75.53% <90.90%> (-0.01%) ⬇️
inputsystem_Windows_6000.0 5.33% <0.00%> (+0.02%) ⬆️
inputsystem_Windows_6000.0_project 77.42% <90.90%> (-0.01%) ⬇️
inputsystem_Windows_6000.3 5.33% <0.00%> (+0.02%) ⬆️
inputsystem_Windows_6000.3_project 77.42% <90.90%> (+0.02%) ⬆️
inputsystem_Windows_6000.4 5.34% <0.00%> (+0.02%) ⬆️
inputsystem_Windows_6000.4_project 77.42% <90.90%> (+0.02%) ⬆️
inputsystem_Windows_6000.5 5.32% <0.00%> (+0.02%) ⬆️
inputsystem_Windows_6000.5_project 77.46% <90.90%> (+0.01%) ⬆️
inputsystem_Windows_6000.6 5.32% <0.00%> (+0.02%) ⬆️
inputsystem_Windows_6000.6_project 77.47% <90.90%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
Assets/Tests/InputSystem/CoreTests_Actions.cs 98.41% <100.00%> (+<0.01%) ⬆️
.../Runtime/Actions/InputActionRebindingExtensions.cs 82.50% <88.23%> (ø)

... and 232 files with indirect coverage changes

ℹ️ Need help interpreting these results?

@Pauliusd01 Pauliusd01 removed the request for review from u-pr May 5, 2026 13:24
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.

1 participant