Skip to content

Bugfix/uum 141406 fixing action overlay behavior#672

Open
lopezt-unity wants to merge 2 commits into
masterfrom
bugfix/uum-141406-fixing-action-overlay-behavior
Open

Bugfix/uum 141406 fixing action overlay behavior#672
lopezt-unity wants to merge 2 commits into
masterfrom
bugfix/uum-141406-fixing-action-overlay-behavior

Conversation

@lopezt-unity
Copy link
Copy Markdown
Collaborator

##Purpose of this PR

Jira: UUM-141406 (https://jira.unity3d.com/browse/UUM-141406)

This PR fixes two related issues with ProBuilder action overlays:

  1. Overlay Display Issue: When an action overlay was already displayed in the SceneView and a new action was
    triggered, the overlay would not properly refresh or display. The fix adds a workaround that toggles displayed from
    false to true to ensure the overlay is properly added/refreshed in the view.
  2. Live Preview Toggle Not Updating Fields: When users toggled the "Live Preview" setting during an action, input
    fields (FloatField, IntegerField, Vector3Field) would not dynamically update their isDelayed property. Users had to
    restart the action for the setting to take effect.

###Solution:

  • Introduced a delayedPreviewChanged event in PreviewActionManager that fires when the live preview setting changes
  • Updated all affected geometry actions (BevelEdges, ExtrudeEdges, ExtrudeFaces, OffsetElements, SubdivideEdges,
    WeldVertices, GrowSelection) to subscribe to this event and update their field properties dynamically
  • Fixed the overlay display issue with a toggle workaround in ActionSettings.cs
  • Removed the previous inefficient approach of destroying and recreating the entire overlay UI

##Release Notes

Fixed: UUM-141406 - Action overlays now display correctly when triggering a new action while an overlay is already shown in the SceneView.

##Functional Testing status

Manual Testing:

  • Tested all seven modified geometry actions (Bevel Edges, Extrude Edges, Extrude Faces, Offset Elements, Subdivide
    Edges, Weld Vertices, Grow Selection)
  • Verified toggling "Live Preview" immediately updates field behavior (delayed vs immediate input)
  • Verified starting actions when overlays are already displayed properly shows the new action overlay
  • Tested both delayed and immediate preview modes with each action
  • Confirmed field input behavior matches the live preview setting in all cases

Existing Automation:
ProBuilder has existing editor tests for action functionality. The overlay display and UI field behavior changes are
UI-specific and covered by manual testing.

##Performance Testing Status

Performance Impact: Minimal positive impact

The change replaces an inefficient approach (destroying and recreating the entire overlay UI hierarchy) with an
event-based approach that only updates the isDelayed property on existing fields. This reduces allocations and UI
rebuilds when toggling the live preview setting.

No performance testing required - this is a UI behavior fix with a minor efficiency improvement.

##Overall Product Risks

Technical Risk: 1
Isolated UI behavior changes in ProBuilder action overlays. The event-based approach is straightforward, and the
overlay display workaround is localized. Low risk of regression.

Halo Effect: 1
Changes are isolated to ProBuilder's action overlay system and the seven modified geometry actions. Does not affect
other ProBuilder functionality, other packages, or Unity Editor core systems.

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.

May require changes

This PR introduces static event subscriptions to PreviewActionManager.delayedPreviewChanged across several menu action settings panels. However, because these anonymous subscribers are not unregistered when the panels are destroyed, they will cause memory leaks and multiple handler invocations.

Summary of Findings

  • Static Event Memory Leaks (High Importance): Multiple menu action classes (BevelEdges, ExtrudeEdges, ExtrudeFaces, OffsetElements, SubdivideEdges, WeldVertices, and GrowSelection) register event handlers to the static event PreviewActionManager.delayedPreviewChanged without a mechanism to unsubscribe. These subscriptions should be cleaned up using the DetachFromPanelEvent callback on the root visual elements.

🤖 Helpful? 👍/👎

}
});

PreviewActionManager.delayedPreviewChanged += () =>
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.

high

Subscribing to a static event (PreviewActionManager.delayedPreviewChanged) using an anonymous delegate inside CreateSettingsContent will cause a memory leak. Because static events hold strong references to their subscribers, the floatField (and the entire parent VisualElement tree) will be kept alive in memory indefinitely even after the settings content is destroyed.

To prevent this, you should unsubscribe from the event when the visual element is detached. Have you considered registering a callback on the DetachFromPanelEvent to clean up the subscription?

Suggested change (apply manually):

            System.Action onDelayedPreviewChanged = () =>
            {
                floatField.isDelayed = PreviewActionManager.delayedPreview;
            };
            PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
            root.RegisterCallback<DetachFromPanelEvent>(evt =>
            {
                PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
            });

🤖 Helpful? 👍/👎 by guardian

floatField.tooltip = "Extrude Amount determines how far an edge will be moved along it's normal when extruding. This value can be negative.";
floatField.SetValueWithoutNotify(m_ExtrudeEdgeDistance);
floatField.RegisterCallback<ChangeEvent<float>>(OnExtrudeChanged);
PreviewActionManager.delayedPreviewChanged += () =>
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.

high

Subscribing to a static event (PreviewActionManager.delayedPreviewChanged) using an anonymous delegate inside CreateSettingsContent causes a memory leak because the static event holds onto the visual elements even after they are discarded.

You can avoid this by unsubscribing from the event when the element is detached from the panel.

Suggested change (apply manually):

            System.Action onDelayedPreviewChanged = () =>
            {
                floatField.isDelayed = PreviewActionManager.delayedPreview;
            };
            PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
            root.RegisterCallback<DetachFromPanelEvent>(evt =>
            {
                PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
            });

🤖 Helpful? 👍/👎 by guardian

});
root.Add(distanceField);

PreviewActionManager.delayedPreviewChanged += () =>
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.

high

Subscribing to a static event (PreviewActionManager.delayedPreviewChanged) using an anonymous delegate inside CreateSettingsContent causes a memory leak because the static event holds onto the visual elements even after they are discarded.

You can avoid this by unsubscribing from the event when the element is detached from the panel.

Suggested change (apply manually):

            System.Action onDelayedPreviewChanged = () =>
            {
                distanceField.isDelayed = PreviewActionManager.delayedPreview;
            };
            PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
            root.RegisterCallback<DetachFromPanelEvent>(evt =>
            {
                PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
            });

🤖 Helpful? 👍/👎 by guardian

PreviewActionManager.UpdatePreview();
});

PreviewActionManager.delayedPreviewChanged += () =>
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.

high

Subscribing to a static event (PreviewActionManager.delayedPreviewChanged) using an anonymous delegate inside CreateSettingsContent causes a memory leak because the static event holds onto the visual elements even after they are discarded.

You can avoid this by unsubscribing from the event when the element is detached from the panel.

Suggested change (apply manually):

            System.Action onDelayedPreviewChanged = () =>
            {
                distField.Query<FloatField>().ForEach(ff => ff.isDelayed = PreviewActionManager.delayedPreview);
            };
            PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
            root.RegisterCallback<DetachFromPanelEvent>(evt =>
            {
                PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
            });

🤖 Helpful? 👍/👎 by guardian

m_SubdivCount.tooltip = tooltip;
m_SubdivCount.RegisterCallback<ChangeEvent<int>>(OnCountChanged);
m_SubdivCount.style.width = 40;
PreviewActionManager.delayedPreviewChanged += () =>
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.

high

Subscribing to a static event (PreviewActionManager.delayedPreviewChanged) using an anonymous delegate inside CreateSettingsContent causes a memory leak because the static event holds onto the visual elements even after they are discarded.

You can avoid this by unsubscribing from the event when the element is detached from the panel.

Suggested change (apply manually):

            System.Action onDelayedPreviewChanged = () =>
            {
                m_SubdivCount.isDelayed = PreviewActionManager.delayedPreview;
            };
            PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
            root.RegisterCallback<DetachFromPanelEvent>(evt =>
            {
                PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
            });

🤖 Helpful? 👍/👎 by guardian

m_WeldDistance.SetValue(evt.newValue);
PreviewActionManager.UpdatePreview();
});
PreviewActionManager.delayedPreviewChanged += () =>
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.

high

Subscribing to a static event (PreviewActionManager.delayedPreviewChanged) using an anonymous delegate inside CreateSettingsContent causes a memory leak because the static event holds onto the visual elements even after they are discarded.

You can avoid this by unsubscribing from the event when the element is detached from the panel.

Suggested change (apply manually):

            System.Action onDelayedPreviewChanged = () =>
            {
                floatField.isDelayed = PreviewActionManager.delayedPreview;
            };
            PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
            root.RegisterCallback<DetachFromPanelEvent>(evt =>
            {
                PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
            });

🤖 Helpful? 👍/👎 by guardian

PreviewActionManager.UpdatePreview();
});

PreviewActionManager.delayedPreviewChanged += () =>
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.

high

Subscribing to a static event (PreviewActionManager.delayedPreviewChanged) using an anonymous delegate inside CreateSettingsContent causes a memory leak because the static event holds onto the visual elements even after they are discarded.

You can avoid this by unsubscribing from the event when the element is detached from the panel.

Suggested change (apply manually):

            System.Action onDelayedPreviewChanged = () =>
            {
                floatField.isDelayed = PreviewActionManager.delayedPreview;
            };
            PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
            root.RegisterCallback<DetachFromPanelEvent>(evt =>
            {
                PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
            });

🤖 Helpful? 👍/👎 by guardian

@codecov-github-com
Copy link
Copy Markdown

codecov-github-com Bot commented May 28, 2026

Codecov Report

Attention: Patch coverage is 0% with 34 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
Editor/MenuActions/Geometry/BevelEdges.cs 0.00% 5 Missing ⚠️
Editor/MenuActions/Geometry/ExtrudeFaces.cs 0.00% 5 Missing ⚠️
Editor/MenuActions/Geometry/OffsetElements.cs 0.00% 5 Missing ⚠️
Editor/MenuActions/Geometry/ExtrudeEdges.cs 0.00% 4 Missing ⚠️
Editor/MenuActions/Geometry/SubdivideEdges.cs 0.00% 4 Missing ⚠️
Editor/MenuActions/Geometry/WeldVertices.cs 0.00% 4 Missing ⚠️
Editor/MenuActions/Selection/GrowSelection.cs 0.00% 4 Missing ⚠️
Editor/Overlays/ActionSettings.cs 0.00% 3 Missing ⚠️
@@            Coverage Diff             @@
##           master     #672      +/-   ##
==========================================
+ Coverage   36.05%   37.41%   +1.35%     
==========================================
  Files         277      278       +1     
  Lines       34909    38959    +4050     
==========================================
+ Hits        12588    14575    +1987     
- Misses      22321    24384    +2063     
Flag Coverage Δ
probuilder_MacOS_6000.0 35.11% <0.00%> (+0.53%) ⬆️
probuilder_MacOS_6000.3 35.11% <0.00%> (+0.53%) ⬆️
probuilder_MacOS_6000.4 35.11% <0.00%> (+0.53%) ⬆️
probuilder_MacOS_6000.5 35.11% <0.00%> (+0.54%) ⬆️
probuilder_MacOS_6000.6 35.11% <0.00%> (?)
probuilder_Windows_6000.0 35.03% <0.00%> (-0.21%) ⬇️
probuilder_Windows_6000.3 35.03% <0.00%> (-0.21%) ⬇️
probuilder_Windows_6000.4 35.03% <0.00%> (-0.22%) ⬇️
probuilder_Windows_6000.5 35.03% <0.00%> (-0.21%) ⬇️
probuilder_Windows_6000.6 35.03% <0.00%> (?)

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

Files with missing lines Coverage Δ
Editor/Overlays/ActionSettings.cs 0.00% <0.00%> (ø)
Editor/MenuActions/Geometry/ExtrudeEdges.cs 8.42% <0.00%> (+1.27%) ⬆️
Editor/MenuActions/Geometry/SubdivideEdges.cs 5.55% <0.00%> (+2.00%) ⬆️
Editor/MenuActions/Geometry/WeldVertices.cs 5.60% <0.00%> (+1.79%) ⬆️
Editor/MenuActions/Selection/GrowSelection.cs 13.72% <0.00%> (+9.45%) ⬆️
Editor/MenuActions/Geometry/BevelEdges.cs 8.97% <0.00%> (+2.41%) ⬆️
Editor/MenuActions/Geometry/ExtrudeFaces.cs 13.55% <0.00%> (+2.33%) ⬆️
Editor/MenuActions/Geometry/OffsetElements.cs 2.81% <0.00%> (+0.95%) ⬆️

... and 76 files with indirect coverage changes

ℹ️ Need help interpreting these results?

Copy link
Copy Markdown
Contributor

@modrimkus-unity modrimkus-unity left a comment

Choose a reason for hiding this comment

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

LGTM!

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.

2 participants