chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using System.Collections.Immutable;
using System.ComponentModel;
using System.Linq;
namespace OpenTap
{
    class MixinMemberMenuModel : IMenuModel, IMemberMenuModel
    {
        readonly MixinMemberData member;
        public MixinMemberMenuModel(MixinMemberData member)
        {
            this.member = member;
        }
 
        public object[] Source { get; set; }
 
        IMemberData IMemberMenuModel.Member => member;
        
        public bool TestPlanAllowsEdit => Source
            .OfType<ITestStepParent>()
            .FirstNonDefault(step => step as TestPlan ?? step.GetParent<TestPlan>())
            ?.AllowEdit ?? true;
 
        public bool StepLocked => Source.OfType<ITestStep>().Any(x => x.ChildTestSteps.IsReadOnly);
        
        [Display("Modify Mixin...", "Modify custom setting.", Order: 2.0, Group: "Mixins")]
        [Browsable(true)]
        [IconAnnotation(IconNames.ModifyMixin)]
        [EnabledIf(nameof(TestPlanAllowsEdit), true)]
        [EnabledIf(nameof(StepLocked), false, HideIfDisabled = true)]
        public void ModifyMixin()
        {
            var src = member.Source;
            var targetType = TypeData.GetTypeData(Source.First());
            var builders = MixinFactory.GetMixinBuilders(targetType).ToImmutableArray();
            builders = builders.Replace(builders.FirstOrDefault(x => x.GetType() == src.GetType()), src);
            src.Initialize(targetType);
 
            // use DisplayAttribute if present
            var displayName = member.GetAttribute<DisplayAttribute>()?.Name;
            // Otherwise fall back to member name
            if (displayName == null)
                displayName = member.Name;
 
            var ui = new MixinBuilderUi(builders.ToArray(), src)
            {
                InitialMixinName = displayName
            };
 
            UserInput.Request(ui);
 
            if (ui.Submit == MixinBuilderUi.OkCancel.Cancel)
                return; // cancel
 
            var selectedMixinBuilder = ui.SelectedItem;
            var serializer = new TapSerializer();
 
            foreach (var src2 in Source)
            {
                // save the current value of the mixin so that it can be used
                // note that the type may have to change since ToDynamicMember may decide to return a new type of member.
                var currentValue = member.GetValue(src2);
                
                // make sure to unload the mixin before calling ToDynamicMember
                MixinFactory.UnloadMixin(src2, member);
 
                // Clone the mixin builder before applying it - Each mixin member keeps track of their own builder.
                var mem = serializer.Clone(selectedMixinBuilder).ToDynamicMember(TypeData.GetTypeData(src2));
 
                DynamicMember.AddDynamicMember(src2, mem);
 
                // if the saved value fits the member type, use it, Otherwise create a new value for it. 
                if (currentValue != null && TypeData.GetTypeData(currentValue).DescendsTo(mem.TypeDescriptor))
                {
                    mem.SetValue(src2, currentValue);
                }
                else
                {
                    mem.SetValue(src2, mem.NewInstance());
                }
            }
        }
 
        [Display("Remove Mixin", "Remove custom setting.", Order: 2.0, Group: "Mixins")]
        [Browsable(true)]
        [IconAnnotation(IconNames.RemoveMixin)]
        [EnabledIf(nameof(TestPlanAllowsEdit), true)]
        [EnabledIf(nameof(StepLocked), false, HideIfDisabled = true)]
        public void RemoveMixin()
        {
            foreach (var src in Source)
                MixinFactory.UnloadMixin(src, member);
        }
    }
}