View Issue Details

IDProjectCategoryView StatusLast Update
0004560NoesisGUIStudiopublic2025-11-24 18:20
Reporterdstewart Assigned Tomaherne  
PrioritynormalSeverityminor 
Status resolvedResolutionfixed 
Product VersionStudio_Beta 
Target VersionStudio_Beta 
Summary0004560: Changes to Opacity Masks Featuring a RadialGradient Don't Log Their Chosen Adorner Positions In A Storyboard
Description

Currently, changing the Directional Adorner of a RadialGradient used within the context of an Opacity Mask can act uncontrollably, and not reliably log the position of the Rotation from centre, as set by the user.

See attached video.

Note that the 100% repro I've been able to get in this video, and in the attached file occurs adjusting the Adorner within a frame on a Storyboard. However, its also worth noting that I believe I've also encountered the inverse, where its possble to adjust the adorner in the Storyboard, but not in the 'No Storyboard Applied' state.

Note that in an equivalent setup, the same issues don't occur with LinearGradients.


Repro Steps:

1) Download and open the attached Studio Project.

2) With the MainPage.xaml open, navigate to the Animation/Storyboards tab, and select the "MyAnimation" Storyboard.

3) With the Timeline open, in the Navigator, select 'Grid0', and navigate to its Properties Panel, and click on its OpacityMask Property.
This will open the Brush Panel, and expose the RadialGradients Adorners on the Page.

4) Grab the 'end' tip of the RadialGradient's adorner, and attempt to move it to a different position (in both angle, and length)

5) Observe the end of the adorner 'snapping' back to its original angle, but conserving the new length.

Expected Results: The RadialGradient adorner adheres to the user-selected position.

Attached Files
PlatformAny

Activities

maherne

maherne

2025-11-24 15:44

developer   ~0011458

This patch is a fix for this issue.
While recording in the Storyboard, BrushAdorner was sending the PropertyUpdateRequest for setting the Transform/RelativeTransform to CreateUpdatePropertyCommand with canAnimate set to true, leading it to be added to an AnimatePropertyCommand.
Transforms are supposed to always be set with an UpdatePropertyCommand.
In the patch, if recording in a Storyboard, a UpdatePropertyCommand is always created when setting a Transform/RelativeTransform.

BrushAdorner.cpp.patch (4,310 bytes)   
Index: Native/Src/Packages/Gui/Studio/Src/BrushAdorner.cpp
===================================================================
--- Native/Src/Packages/Gui/Studio/Src/BrushAdorner.cpp	(revision 16345)
+++ Native/Src/Packages/Gui/Studio/Src/BrushAdorner.cpp	(working copy)
@@ -19,6 +19,7 @@
 #include "XamlDocumentAdornerManager.h"
 #include "ZoomBorder.h"
 #include "StyleScope.h"
+#include "LinkedCommand.h"
 
 #include <NsGui/DrawingContext.h>
 #include <NsGui/Pen.h>
@@ -720,6 +721,7 @@
         Ptr<RadialGradientBrush> bClone = DynamicPtrCast<RadialGradientBrush>(
             Helper::Clone(b, nullptr, false, mDocument->GetResourceMapper()));
 
+        PropertyUpdateRequest transformPropUpdate;
         Vector<PropertyUpdateRequest> propUpdates;
 
         Property* prop;
@@ -757,7 +759,7 @@
                     }
                     else
                     {
-                        propUpdates.EmplaceBack(NSS(Brush),
+                        transformPropUpdate = PropertyUpdateRequest(NSS(Brush),
                             NSS(Transform), false, prop->GetOwnerNode()->GetByteIndex());
                     }
                 }
@@ -776,7 +778,7 @@
                     }
                     else
                     {
-                        propUpdates.EmplaceBack(NSS(Brush),
+                        transformPropUpdate = PropertyUpdateRequest(NSS(Brush),
                             NSS(RelativeTransform), false, prop->GetOwnerNode()->GetByteIndex(),
                             group);
                     }
@@ -794,7 +796,7 @@
                     }
                     else
                     {
-                        propUpdates.EmplaceBack(NSS(Brush),
+                        transformPropUpdate = PropertyUpdateRequest(NSS(Brush),
                             NSS(RelativeTransform), false, prop->GetOwnerNode()->GetByteIndex());
                     }
                 }
@@ -813,12 +815,17 @@
                     }
                     else
                     {
-                        propUpdates.EmplaceBack(NSS(Brush),
+                        transformPropUpdate = PropertyUpdateRequest(NSS(Brush),
                             NSS(Transform), false, prop->GetOwnerNode()->GetByteIndex(),
                             group);
                     }
                 }
             }
+            if (!mProperty->GetScopeDocument()->IsRecordingAnimation())
+            {
+                propUpdates.EmplaceBack(transformPropUpdate);
+                transformPropUpdate = PropertyUpdateRequest();
+            }
         }
 
         if (CHECK_UPDATE(BrushUpdate_RadialOrigin))
@@ -1027,7 +1034,7 @@
         {
             mProperty->GetSetValueCommand()->Execute(bClone);
         }
-        else if (!propUpdates.Empty())
+        else if (!propUpdates.Empty() || !transformPropUpdate.prop.IsNull())
         {
             XamlDocument* runtimeDoc = mProperty->GetRuntimeDocument();
             XamlDocument* scopeDoc = mProperty->GetScopeDocument();
@@ -1077,11 +1084,33 @@
                     mProperty->ResetValue();
                 }
             }
+
+            Ptr<XamlUndoRedoCommand> updateTransformCmd;
+            if (!transformPropUpdate.prop.IsNull())
+            {
+                updateTransformCmd = Helper::CreateUpdatePropertyCommand(
+                    runtimeDoc, scopeDoc, transformPropUpdate, false, false, false);
+                updateTransformCmd->Execute();
+                if (propUpdates.Empty())
+                {
+                    updateTransformCmd->Commit();
+                    return;
+                }
+            }
             
             Ptr<XamlUndoRedoCommand> cmd = Helper::CreateUpdatePropertyCommand(runtimeDoc, scopeDoc,
                 propUpdates);
             cmd->Execute();
-            cmd->Commit();
+
+            if (updateTransformCmd != nullptr)
+            {
+                Ptr<LinkedCommand> linkedCommand = *new LinkedCommand(updateTransformCmd, cmd);
+                linkedCommand->Commit();
+            }
+            else
+            {
+                cmd->Commit();
+            }
         }
     }
     else if (mProperty->GetIsLinearGradientBrush())
BrushAdorner.cpp.patch (4,310 bytes)   
maherne

maherne

2025-11-24 18:20

developer   ~0011462

Fixed in r16349

Issue History

Date Modified Username Field Change
2025-11-21 15:44 dstewart New Issue
2025-11-21 15:44 dstewart File Added: StudioFrameZeroAnimation.zip
2025-11-21 15:44 dstewart File Added: 2025-11-21 09-37-41.mp4
2025-11-21 15:45 dstewart Description Updated
2025-11-21 15:48 jsantos Assigned To => sfernandez
2025-11-21 15:48 jsantos Status new => assigned
2025-11-21 15:49 jsantos Target Version => Studio_Beta
2025-11-24 15:44 maherne Note Added: 0011458
2025-11-24 15:44 maherne File Added: BrushAdorner.cpp.patch
2025-11-24 18:20 maherne Assigned To sfernandez => maherne
2025-11-24 18:20 maherne Status assigned => resolved
2025-11-24 18:20 maherne Resolution open => fixed
2025-11-24 18:20 maherne Note Added: 0011462