示例#1
0
        /// <summary>
        /// Once RemoteConfigData and SyncTargets are retrieved, render them in the ScrollView.
        /// First show unmapped parameters in a list, ending in a TextField+Button to add a new
        /// unmapped parameter.
        /// Then show all the discovered SyncTargets in hierarchy view, and highlight which ones
        /// are not synced with RemoteConfig.
        /// </summary>
        public void RenderParameters()
        {
            InitHeaders();
            var offset = valueScrollView.scrollOffset;

            valueScrollView.Clear();
            topLevelElement = new TemplateContainer();
            valueScrollView.Add(topLevelElement);

            // Render unmapped targets, if any.
            var unmappedParamsSection = new TemplateContainer();

            // Create Unmapped Parameters header section.
            var unmappedParamsHeader = new Box();

            unmappedParamsHeader.AddToClassList(headersClassName);
            var unmappedLabel = new Label("Unmapped Parameters");

            unmappedParamsHeader.Add(unmappedLabel);
            unmappedParamsSection.Add(unmappedParamsHeader);
            foreach (var param in unmappedParams.OrderBy(p => p.Key))
            {
                var unmappedTarget = new UnmappedSyncElement(param);
                unmappedParamsSection.Add(unmappedTarget);
            }

            // Add a section with TextField and Button to create a new unmapped Parameter.
            var newKeyContainer = new TemplateContainer();

            newKeyContainer.AddToClassList(columnClassName);
            newKeyContainer.AddToClassList(rowClassName);
            var newUnmappedParamText = "New Unmapped Param";
            var newKeyField          = new TextField {
                value = newUnmappedParamText
            };

            newKeyContainer.Add(newKeyField);

            var newUnmappedButton = new Button(() => {
                if (string.IsNullOrWhiteSpace(newKeyField.value))
                {
                    Debug.LogWarning("Cannot create parameter with null/whitespace key.");
                    return;
                }
                if (RemoteConfigData.parameters.ContainsKey(newKeyField.value))
                {
                    Debug.LogWarning($"A parameter with key {newKeyField.value} already exists.");
                    return;
                }
                var newParam          = RemoteConfigData.GetOrCreateParameter(newKeyField.value);
                var newUnmappedTarget = new UnmappedSyncElement(newParam);
                // Insert the new unmapped key at the end of the unmapped keys list.
                var index = unmappedParamsSection.IndexOf(newKeyContainer);
                unmappedParamsSection.Insert(index, newUnmappedTarget);
                newKeyField.value = newUnmappedParamText;
                // Apply column sizing to newly created SyncTargetElement.
                newUnmappedTarget
                .Query(null, columnClassName)
                .ForEach(col => col.style.minWidth = col.style.maxWidth = columnSize);
            });

            newUnmappedButton.text = "+";
            newUnmappedButton.AddToClassList("flex-0");
            newKeyContainer.Add(newUnmappedButton);
            unmappedParamsSection.Add(newKeyContainer);
            topLevelElement.Add(unmappedParamsSection);

            // Create a SyncGroupElement for the top-level SyncTargetContainer. SyncGroupElement and
            // the various SyncTypeElement classes handle the logic for creating the hierarchy UI.
            topLevelSyncTarget = new SyncGroupElement(SyncTargets);
            topLevelElement.Add(topLevelSyncTarget);

            // Below the ScrollView area, show a set of buttons to sync to/from RC and reset local changes.
            buttonContainer.Clear();
            buttonContainer.Add(uploadButton);
            buttonContainer.Add(downloadButton);
            buttonContainer.Add(resetChangesButton);

            // Reset UI by scrolling to previous scroll position and sizing the newly created columns.
            valueScrollView.scrollOffset = offset;
            lastTabWidth = position.width;
            ResizeColumns();
        }
        /// <summary>
        /// Constructor which initializes UI elements for the given SyncTargetContainer as well as its
        /// children, indenting nested sync targets as appropriate.
        /// </summary>
        public SyncGroupElement(SyncItem syncItem) : base(syncItem)
        {
            if (!(syncItem is SyncTargetContainer))
            {
                Debug.LogWarning(
                    $"Cannot create SyncGroupElement from SyncTarget {syncItem.FullKeyString}");
                return;
            }

            // The top-level group (the SyncTargetContainer of all the discovered SyncTargets) has a
            // different appearance than nested SyncTargetContainers.
            VisualElement topLevelContainer = syncItem.FullKeyString.Length == 0 ?
                                              CreateTopLevelGroupElement() :
                                              CreateNestedGroupElement();

            // For each SyncItem child of this container, add a SyncTargetElement or SyncGroupElement
            // child element with an increased indent.
            // List SyncTargets before nested SyncTargetContainers for visual clarity.
            foreach (var kv in container.Items.OrderByDescending(target => target.Value is SyncTarget))
            {
                SyncItem    child            = kv.Value;
                SyncElement childSyncElement = null;
                if (child is SyncTarget)
                {
                    var target = child as SyncTarget;
                    if (target.Field.FieldType == typeof(bool))
                    {
                        childSyncElement = new SyncTypeElement <bool, Toggle>(target);
                    }
                    else if (target.Field.FieldType == typeof(double))
                    {
                        childSyncElement = new SyncTypeElement <double, DoubleField>(target);
                    }
                    else if (target.Field.FieldType == typeof(int))
                    {
                        childSyncElement = new SyncTypeElement <int, IntegerField>(target);
                    }
                    else if (target.Field.FieldType == typeof(string))
                    {
                        childSyncElement = new SyncTypeElement <string, TextField>(target);
                    }
                    else
                    {
                        Debug.Log(
                            $"Invalid type for sync target {target.FullKeyString}: {target.Field.FieldType}.");
                        continue;
                    }
                }
                else if (child is SyncTargetContainer)
                {
                    childSyncElement = new SyncGroupElement(child);
                }
                topLevelContainer.Add(childSyncElement);
            }

            if (syncItem.FullKeyString.Length == 0)
            {
                return;
            }

            // For non-top-level SyncGroupElements, add a "Sync All" toggle, enabled if all descendents
            // are synced.
            // Start unchecked if any sync Toggles in tree are unchecked.
            // Add listener to all sync Toggle descendents to update this Sync All Toggle as appropriate.
            var descendentSyncToggles = this
                                        .Query <SyncTypeElement>()
                                        .ToList()
                                        .Select(el => el.SyncToggle)
                                        .ToList();

            descendentSyncToggles.ForEach(el => {
                el.RegisterValueChangedCallback(UpdateSyncAllToggle);
            });
            unsyncedDescendents = descendentSyncToggles
                                  .Where(el => !el.value)
                                  .ToList();
            CreateSyncAllToggle(topLevelContainer, unsyncedDescendents.Count == 0);
        }