public void AddControlItem(InputControlPickerDropdown dropdown, DeviceDropdownItem parent, ControlDropdownItem parentControl,
                                   InputControlLayout.ControlItem control, string device, string usage, bool searchable)
        {
            // for the Press control, show two variants, one for single touch presses, and another for multi-touch presses
            if (control.displayName == "Press")
            {
                dropdown.AddControlItem(this, parent, parentControl, new InputControlLayout.ControlItem
                {
                    name        = new InternedString("Press"),
                    displayName = new InternedString("Press (Single touch)"),
                    layout      = control.layout
                }, device, usage, searchable);

                dropdown.AddControlItem(this, parent, parentControl, new InputControlLayout.ControlItem
                {
                    name        = new InternedString("Press"),
                    displayName = new InternedString("Press (Multi-touch)"),
                    layout      = control.layout
                }, device, usage, searchable, "touch*/Press");
            }
            else
            {
                dropdown.AddControlItem(this, parent, parentControl, control, device, usage, searchable);
            }
        }
Exemple #2
0
        private void AddControlItem(DeviceDropdownItem parent, ControlDropdownItem parentControl,
                                    InputControlLayout.ControlItem control, string device, string usage, bool searchable)
        {
            // If it's an array, generate a control entry for each array element.
            for (var i = 0; i < (control.isArray ? control.arraySize : 1); ++i)
            {
                var name        = control.isArray ? control.name + i : control.name;
                var displayName = !string.IsNullOrEmpty(control.displayName)
                    ? (control.isArray ? control.displayName + i : control.displayName)
                    : name;

                var child = new ControlDropdownItem(parentControl, name, displayName,
                                                    device, usage, searchable);
                child.icon = EditorInputControlLayoutCache.GetIconForLayout(control.layout);
                var controlLayout = EditorInputControlLayoutCache.TryGetLayout(control.layout);

                if (LayoutMatchesExpectedControlLayoutFilter(control.layout))
                {
                    parent.AddChild(child);
                }
                else if (controlLayout.controls.Any(x => LayoutMatchesExpectedControlLayoutFilter(x.layout)))
                {
                    child.enabled = false;
                    parent.AddChild(child);
                }
                // Add children.
                if (controlLayout != null)
                {
                    AddControlTreeItemsRecursive(controlLayout, parent, device, usage,
                                                 searchable, child);
                }
            }
        }
Exemple #3
0
        private static void AddProcessors(InputControl control, ref InputControlLayout.ControlItem controlItem, string layoutName)
        {
            var processorCount = controlItem.processors.Count;

            for (var n = 0; n < processorCount; ++n)
            {
                var name = controlItem.processors[n].name;
                var type = InputProcessor.s_Processors.LookupTypeRegistration(name);
                if (type == null)
                {
                    throw new InvalidOperationException(
                              $"Cannot find processor '{name}' referenced by control '{controlItem.name}' in layout '{layoutName}'");
                }

                var processor = Activator.CreateInstance(type);

                var parameters = controlItem.processors[n].parameters;
                if (parameters.Count > 0)
                {
                    NamedValue.ApplyAllToObject(processor, parameters);
                }

                control.AddProcessor(processor);
            }
        }
Exemple #4
0
        string ConvertPotentialAliasToName(InputControlLayout layout, string nameOrAlias)
        {
            InternedString internedNameOrAlias = new InternedString(nameOrAlias);
            ReadOnlyArray <InputControlLayout.ControlItem> controls = layout.controls;

            for (int i = 0; i < controls.Count; i++)
            {
                InputControlLayout.ControlItem controlItem = controls[i];

                if (controlItem.name == internedNameOrAlias)
                {
                    return(nameOrAlias);
                }

                ReadOnlyArray <InternedString> aliases = controlItem.aliases;
                for (int j = 0; j < aliases.Count; j++)
                {
                    if (aliases[j] == nameOrAlias)
                    {
                        return(controlItem.name.ToString());
                    }
                }
            }
            return(nameOrAlias);
        }
Exemple #5
0
        private static void AddProcessors(InputControl control, ref InputControlLayout.ControlItem controlItem, string layoutName)
        {
            var processorCount = controlItem.processors.Count;

            for (var n = 0; n < processorCount; ++n)
            {
                var name = controlItem.processors[n].name;
                var type = InputControlProcessor.s_Processors.LookupTypeRegistration(name);
                if (type == null)
                {
                    throw new Exception(
                              string.Format("Cannot find processor '{0}' referenced by control '{1}' in layout '{2}'", name,
                                            controlItem.name, layoutName));
                }

                var processor = Activator.CreateInstance(type);

                var parameters = controlItem.processors[n].parameters;
                if (parameters.Count > 0)
                {
                    SetParameters(processor, parameters);
                }

                control.AddProcessor(processor);
            }
        }
Exemple #6
0
        private void AddChildControlIfMissing(InputControlLayout layout, InternedString variants, InputControl parent,
                                              ref bool haveChildrenUsingStateFromOtherControls,
                                              ref InputControlLayout.ControlItem controlItem)
        {
            ////TODO: support arrays (we may modify an entire array in bulk)

            // Find the child control.
            var child = InputControlPath.TryFindChild(parent, controlItem.name);

            if (child != null)
            {
                return;
            }

            // We're adding a child somewhere in the existing hierarchy. This is a tricky
            // case as we have to potentially shift indices around in the hierarchy to make
            // room for the new control.

            ////TODO: this path does not support recovering existing controls? does it matter?

            child = InsertChildControl(layout, variants, parent,
                                       ref haveChildrenUsingStateFromOtherControls, ref controlItem);

            // Apply layout change.
            if (!ReferenceEquals(child.parent, parent))
            {
                ComputeStateLayout(child.parent);
            }
        }
Exemple #7
0
        private static bool ControlLayoutMatchesPathComponent(ref InputControlLayout.ControlItem controlItem, ref PathParser parser)
        {
            // Match layout.
            var layout = parser.current.layout;

            if (layout.length > 0)
            {
                if (!StringMatches(layout, controlItem.layout))
                {
                    return(false);
                }
            }

            // Match usage.
            if (parser.current.usages != null)
            {
                // All of usages should match to the one of usage in the control
                for (int usageIndex = 0; usageIndex < parser.current.usages.Length; ++usageIndex)
                {
                    var usage = parser.current.usages[usageIndex];

                    if (usage.length > 0)
                    {
                        var usageCount      = controlItem.usages.Count;
                        var anyUsageMatches = false;
                        for (var i = 0; i < usageCount; ++i)
                        {
                            if (StringMatches(usage, controlItem.usages[i]))
                            {
                                anyUsageMatches = true;
                                break;
                            }
                        }

                        if (!anyUsageMatches)
                        {
                            return(false);
                        }
                    }
                }
            }

            // Match name.
            var name = parser.current.name;

            if (name.length > 0)
            {
                if (!StringMatches(name, controlItem.name))
                {
                    return(false);
                }
            }

            return(true);
        }
Exemple #8
0
        private InputControl InsertChildControl(InputControlLayout layout, InternedString variant, InputControl parent,
                                                ref bool haveChildrenUsingStateFromOtherControls,
                                                ref InputControlLayout.ControlItem controlItem)
        {
            var path = controlItem.name.ToString();

            // First we need to find the immediate parent from the given path.
            var indexOfSlash = path.LastIndexOf('/');

            if (indexOfSlash == -1)
            {
                throw new InvalidOperationException("InsertChildControl has to be called with a slash-separated path");
            }
            Debug.Assert(indexOfSlash != 0, "Could not find slash in path");
            var immediateParentPath = path.Substring(0, indexOfSlash);
            var immediateParent     = InputControlPath.TryFindChild(parent, immediateParentPath);

            if (immediateParent == null)
            {
                throw new InvalidOperationException(
                          $"Cannot find parent '{immediateParentPath}' of control '{controlItem.name}' in layout '{layout.name}'");
            }

            var controlName = path.Substring(indexOfSlash + 1);

            if (controlName.Length == 0)
            {
                throw new InvalidOperationException(
                          $"Path cannot end in '/' (control '{controlItem.name}' in layout '{layout.name}')");
            }

            // Make room in the device's child array.
            var childStartIndex = immediateParent.m_ChildStartIndex;

            if (childStartIndex == default)
            {
                // First child of parent.
                childStartIndex = m_Device.m_ChildrenForEachControl.LengthSafe();
                immediateParent.m_ChildStartIndex = childStartIndex;
            }
            var childIndex = childStartIndex + immediateParent.m_ChildCount;

            ShiftChildIndicesInHierarchyOneUp(m_Device, childIndex, immediateParent);
            ArrayHelpers.InsertAt(ref m_Device.m_ChildrenForEachControl, childIndex, null);
            ++immediateParent.m_ChildCount;

            // Insert the child.
            // NOTE: This may *add several* controls depending on the layout of the control we are inserting.
            //       The children will be appended to the child array.
            var control = AddChildControl(layout, variant, immediateParent,
                                          ref haveChildrenUsingStateFromOtherControls, controlItem, childIndex, controlName);

            return(control);
        }
Exemple #9
0
 private static void SetFormat(InputControl control, InputControlLayout.ControlItem controlItem)
 {
     control.m_StateBlock.format = controlItem.format;
     if (controlItem.sizeInBits == 0)
     {
         var primitiveFormatSize = InputStateBlock.GetSizeOfPrimitiveFormatInBits(controlItem.format);
         if (primitiveFormatSize != -1)
         {
             control.m_StateBlock.sizeInBits = (uint)primitiveFormatSize;
         }
     }
 }
Exemple #10
0
 public ControlTreeViewItem(InputControlLayout.ControlItem control, string prefix, string deviceId, string usage)
 {
     m_Device = deviceId;
     m_Usage  = usage;
     if (!string.IsNullOrEmpty(prefix))
     {
         m_ControlPath = prefix + "/";
         displayName   = prefix + "/";
     }
     m_ControlPath += control.name;
     displayName   += control.name;
     id             = controlPathWithDevice.GetHashCode();
 }
 public ControlTreeViewItem(InputControlLayout.ControlItem control, string prefix, string deviceId, string usage)  : base("")
 {
     m_Device = deviceId;
     m_Usage  = usage;
     if (!string.IsNullOrEmpty(prefix))
     {
         m_ControlPath = prefix + "/";
         name          = prefix + "/";
     }
     m_ControlPath   += control.name;
     name            += control.name;
     id               = controlPathWithDevice.GetHashCode();
     m_SearchableName = InputControlPath.ToHumanReadableString(controlPathWithDevice);
 }
Exemple #12
0
        private InputControl InsertChildControl(InputControlLayout layout, InternedString variant, InputControl parent,
                                                ref bool haveChildrenUsingStateFromOtherControls,
                                                ref InputControlLayout.ControlItem controlItem)
        {
            var path = controlItem.name.ToString();

            // First we need to find the immediate parent from the given path.
            var indexOfSlash = path.LastIndexOf('/');

            if (indexOfSlash == -1)
            {
                throw new ArgumentException("InsertChildControl has to be called with a slash-separated path", "path");
            }
            Debug.Assert(indexOfSlash != 0);
            var immediateParentPath = path.Substring(0, indexOfSlash);
            var immediateParent     = InputControlPath.TryFindChild(parent, immediateParentPath);

            if (immediateParent == null)
            {
                throw new Exception(
                          string.Format("Cannot find parent '{0}' of control '{1}' in layout '{2}'", immediateParentPath,
                                        controlItem.name, layout.name));
            }

            var controlName = path.Substring(indexOfSlash + 1);

            if (controlName.Length == 0)
            {
                throw new Exception(
                          string.Format("Path cannot end in '/' (control '{0}' in layout '{1}')", controlItem.name,
                                        layout.name));
            }

            // Make room in the device's child array.
            var childStartIndex = immediateParent.m_ChildrenReadOnly.m_StartIndex;
            var childIndex      = childStartIndex + immediateParent.m_ChildrenReadOnly.m_Length;

            ArrayHelpers.InsertAt(ref m_Device.m_ChildrenForEachControl, childIndex, null);
            ++immediateParent.m_ChildrenReadOnly.m_Length;

            // Insert the child.
            var control = AddChildControl(layout, variant, immediateParent, null,
                                          ref haveChildrenUsingStateFromOtherControls, ref controlItem, ref childIndex, controlName);

            // Adjust indices of control's that have been shifted around by our insertion.
            ShiftChildIndicesInHierarchyOneUp(parent, childIndex);

            return(control);
        }
Exemple #13
0
            private void AddControlItem(InputControlLayout.ControlItem control, TreeViewItem parent, ref int id)
            {
                var item = AddChild(parent, control.variants.IsEmpty() ? control.name : string.Format("{0} ({1})",
                    control.name, control.variants), ref id);

                ////TODO: fully merge TreeViewItems from isModifyingChildControlByPath control layouts into the control they modify

                ////TODO: allow clicking this field to jump to the layout
                if (!control.layout.IsEmpty())
                    AddChild(item, string.Format("Layout: {0}", control.layout), ref id);
                if (!control.variants.IsEmpty())
                    AddChild(item, string.Format("Variant: {0}", control.variants), ref id);
                if (control.format != 0)
                    AddChild(item, string.Format("Format: {0}", control.format), ref id);
                if (control.offset != InputStateBlock.kInvalidOffset)
                    AddChild(item, string.Format("Offset: {0}", control.offset), ref id);
                if (control.bit != InputStateBlock.kInvalidOffset)
                    AddChild(item, string.Format("Bit: {0}", control.bit), ref id);
                if (control.sizeInBits != 0)
                    AddChild(item, string.Format("Size In Bits: {0}", control.sizeInBits), ref id);
                if (control.isArray)
                    AddChild(item, string.Format("Array Size: {0}", control.arraySize), ref id);
                if (!string.IsNullOrEmpty(control.useStateFrom))
                    AddChild(item, string.Format("Use State From: {0}", control.useStateFrom), ref id);

                if (control.usages.Count > 0)
                    AddChild(item, "Usages: " + string.Join(", ", control.usages.Select(x => x.ToString()).ToArray()), ref id);
                if (control.aliases.Count > 0)
                    AddChild(item, "Aliases: " + string.Join(", ", control.aliases.Select(x => x.ToString()).ToArray()), ref id);

                if (control.parameters.Count > 0)
                {
                    var parameters = AddChild(item, "Parameters", ref id);
                    foreach (var parameter in control.parameters)
                        AddChild(parameters, parameter.ToString(), ref id);
                }

                if (control.processors.Count > 0)
                {
                    var processors = AddChild(item, "Processors", ref id);
                    foreach (var processor in control.processors)
                    {
                        var processorItem = AddChild(processors, processor.name, ref id);
                        foreach (var parameter in processor.parameters)
                            AddChild(processorItem, parameter.ToString(), ref id);
                    }
                }
            }
Exemple #14
0
        private static bool ControlLayoutMatchesPathComponent(ref InputControlLayout.ControlItem controlItem, ref PathParser parser)
        {
            // Match layout.
            var layout = parser.current.layout;

            if (layout.length > 0)
            {
                if (!StringMatches(layout, controlItem.layout))
                {
                    return(false);
                }
            }

            // Match usage.
            var usage = parser.current.usage;

            if (usage.length > 0)
            {
                var usageCount      = controlItem.usages.Count;
                var anyUsageMatches = false;
                for (var i = 0; i < usageCount; ++i)
                {
                    if (StringMatches(usage, controlItem.usages[i]))
                    {
                        anyUsageMatches = true;
                        break;
                    }
                }

                if (!anyUsageMatches)
                {
                    return(false);
                }
            }

            // Match name.
            var name = parser.current.name;

            if (name.length > 0)
            {
                if (!StringMatches(name, controlItem.name))
                {
                    return(false);
                }
            }

            return(true);
        }
Exemple #15
0
        private static void AddOptionalControlRecursive(InternedString layoutName, ref InputControlLayout.ControlItem controlItem)
        {
            Debug.Assert(!controlItem.isModifyingChildControlByPath);
            Debug.Assert(!controlItem.layout.IsEmpty());

            // Recurse into base.
            InternedString baseLayoutName;

            if (InputControlLayout.s_Layouts.baseLayoutTable.TryGetValue(layoutName, out baseLayoutName))
            {
                AddOptionalControlRecursive(baseLayoutName, ref controlItem);
            }

            // See if we already have this optional control.
            List <OptionalControl> list;
            var alreadyPresent = false;

            if (!s_OptionalControls.TryGetValue(layoutName, out list))
            {
                list = new List <OptionalControl>();
                s_OptionalControls[layoutName] = list;
            }
            else
            {
                // See if we already have this control.
                foreach (var item in list)
                {
                    if (item.name == controlItem.name && item.layout == controlItem.layout)
                    {
                        alreadyPresent = true;
                        break;
                    }
                }
            }
            if (!alreadyPresent)
            {
                list.Add(new OptionalControl {
                    name = controlItem.name, layout = controlItem.layout
                });
            }
        }
        private void InsertChildControlOverrides(InputControl parent, ref InputControlLayout.ControlItem controlItem)
        {
            if (m_ChildControlOverrides == null)
            {
                m_ChildControlOverrides = new Dictionary <string, InputControlLayout.ControlItem>();
            }

            var path          = InputControlPath.Combine(parent, controlItem.name);
            var pathLowerCase = path.ToLower();

            // See if there are existing overrides for the control.
            if (!m_ChildControlOverrides.TryGetValue(pathLowerCase, out var existingOverrides))
            {
                // So, so just insert our overrides and we're done.
                m_ChildControlOverrides[pathLowerCase] = controlItem;
                return;
            }

            // Yes, there's existing overrides so we have to merge.
            existingOverrides = existingOverrides.Merge(controlItem);
            m_ChildControlOverrides[pathLowerCase] = existingOverrides;
        }
Exemple #17
0
        private void InsertChildControlOverride(InputControl parent, ref InputControlLayout.ControlItem controlItem)
        {
            if (m_ChildControlOverrides == null)
            {
                m_ChildControlOverrides = new Dictionary <string, InputControlLayout.ControlItem>();
            }

            var path          = InputControlPath.Combine(parent, controlItem.name);
            var pathLowerCase = path.ToLower();

            // See if there are existing overrides for the control.
            if (!m_ChildControlOverrides.TryGetValue(pathLowerCase, out var existingOverrides))
            {
                // So, so just insert our overrides and we're done.
                m_ChildControlOverrides[pathLowerCase] = controlItem;
                return;
            }

            // Yes, there's existing overrides so we have to merge.
            // NOTE: The existing override's properties take precedence here. This is because
            //       the override has been established from higher up in the layout hierarchy.
            existingOverrides = existingOverrides.Merge(controlItem);
            m_ChildControlOverrides[pathLowerCase] = existingOverrides;
        }
Exemple #18
0
        private InputControl AddChildControl(InputControlLayout layout, InternedString variant, InputControl parent,
                                             ReadOnlyArray <InputControl>?existingChildren, ref bool haveChildrenUsingStateFromOtherControls,
                                             ref InputControlLayout.ControlItem controlItem, ref int childIndex, string nameOverride = null)
        {
            var    name          = nameOverride ?? controlItem.name;
            var    nameLowerCase = name.ToLower();
            var    nameInterned  = new InternedString(name);
            string path          = null;

            ////REVIEW: can we check this in InputControlLayout instead?
            if (string.IsNullOrEmpty(controlItem.layout))
            {
                throw new Exception(string.Format("Layout has not been set on control '{0}' in '{1}'",
                                                  controlItem.name, layout.name));
            }

            // See if there is an override for the control.
            InputControlLayout.ControlItem?controlOverride = null;
            if (m_ChildControlOverrides != null)
            {
                path = string.Format("{0}/{1}", parent.path, name);
                var pathLowerCase = path.ToLower();

                InputControlLayout.ControlItem match;
                if (m_ChildControlOverrides.TryGetValue(pathLowerCase, out match))
                {
                    controlOverride = match;
                }
            }

            // Get name of layout to use for control.
            var layoutName = controlItem.layout;

            if (controlOverride != null && !controlOverride.Value.layout.IsEmpty())
            {
                layoutName = controlOverride.Value.layout;
            }

            // See if we have an existing control that we might be able to re-use.
            InputControl existingControl = null;

            if (existingChildren != null)
            {
                var existingChildCount = existingChildren.Value.Count;
                for (var n = 0; n < existingChildCount; ++n)
                {
                    var existingChild = existingChildren.Value[n];
                    if (existingChild.layout == layoutName &&
                        existingChild.name.ToLower() == nameLowerCase)
                    {
                        existingControl = existingChild;
                        break;
                    }
                }
            }

            // Create control.
            InputControl control;

            try
            {
                control = InstantiateLayout(layoutName, variant, nameInterned, parent, existingControl);
            }
            catch (InputControlLayout.LayoutNotFoundException exception)
            {
                // Throw better exception that gives more info.
                throw new Exception(
                          string.Format("Cannot find layout '{0}' used in control '{1}' of layout '{2}'",
                                        exception.layout, name, layout.name),
                          exception);
            }

            // Add to array.
            m_Device.m_ChildrenForEachControl[childIndex] = control;
            ++childIndex;

            // Set display name.
            control.m_DisplayNameFromLayout = controlItem.displayName;

            // Set flags.
            control.m_IsNoisy = controlItem.isNoisy;

            // Pass state block config on to control.
            var usesStateFromOtherControl = !string.IsNullOrEmpty(controlItem.useStateFrom);

            if (!usesStateFromOtherControl)
            {
                control.m_StateBlock.byteOffset = controlItem.offset;
                if (controlItem.bit != InputStateBlock.kInvalidOffset)
                {
                    control.m_StateBlock.bitOffset = controlItem.bit;
                }
                if (controlItem.sizeInBits != 0)
                {
                    control.m_StateBlock.sizeInBits = controlItem.sizeInBits;
                }
                if (controlItem.format != 0)
                {
                    SetFormat(control, controlItem);
                }
            }
            else
            {
                // Mark controls that don't have state blocks of their own but rather get their
                // blocks from other controls by setting their state size to kInvalidOffset.
                control.m_StateBlock.sizeInBits         = kSizeForControlUsingStateFromOtherControl;
                haveChildrenUsingStateFromOtherControls = true;
            }

            ////REVIEW: the constant appending to m_UsagesForEachControl and m_AliasesForEachControl may lead to a lot
            ////        of successive re-allocations

            // Add usages.
            var usages = controlOverride != null ? controlOverride.Value.usages : controlItem.usages;

            if (usages.Count > 0)
            {
                var usageCount = usages.Count;
                var usageIndex =
                    ArrayHelpers.AppendToImmutable(ref m_Device.m_UsagesForEachControl, usages.m_Array);
                control.m_UsagesReadOnly =
                    new ReadOnlyArray <InternedString>(m_Device.m_UsagesForEachControl, usageIndex, usageCount);

                ArrayHelpers.GrowBy(ref m_Device.m_UsageToControl, usageCount);
                for (var n = 0; n < usageCount; ++n)
                {
                    m_Device.m_UsageToControl[usageIndex + n] = control;
                }
            }

            // Add aliases.
            if (controlItem.aliases.Count > 0)
            {
                var aliasCount = controlItem.aliases.Count;
                var aliasIndex =
                    ArrayHelpers.AppendToImmutable(ref m_Device.m_AliasesForEachControl, controlItem.aliases.m_Array);
                control.m_AliasesReadOnly =
                    new ReadOnlyArray <InternedString>(m_Device.m_AliasesForEachControl, aliasIndex, aliasCount);
            }

            // Set parameters.
            if (controlItem.parameters.Count > 0)
            {
                SetParameters(control, controlItem.parameters);
            }

            // Add processors.
            if (controlItem.processors.Count > 0)
            {
                AddProcessors(control, ref controlItem, layout.name);
            }

            return(control);
        }
Exemple #19
0
            private void AddControlItem(InputControlLayout.ControlItem control, TreeViewItem parent, ref int id)
            {
                var item = AddChild(parent, control.variants.IsEmpty() ? control.name : string.Format("{0} ({1})",
                                                                                                      control.name, control.variants), ref id);

                if (!control.layout.IsEmpty())
                {
                    item.icon = EditorInputControlLayoutCache.GetIconForLayout(control.layout);
                }

                ////TODO: fully merge TreeViewItems from isModifyingExistingControl control layouts into the control they modify

                ////TODO: allow clicking this field to jump to the layout
                if (!control.layout.IsEmpty())
                {
                    AddChild(item, $"Layout: {control.layout}", ref id);
                }
                if (!control.variants.IsEmpty())
                {
                    AddChild(item, $"Variant: {control.variants}", ref id);
                }
                if (!string.IsNullOrEmpty(control.displayName))
                {
                    AddChild(item, $"Display Name: {control.displayName}", ref id);
                }
                if (!string.IsNullOrEmpty(control.shortDisplayName))
                {
                    AddChild(item, $"Short Display Name: {control.shortDisplayName}", ref id);
                }
                if (control.format != 0)
                {
                    AddChild(item, $"Format: {control.format}", ref id);
                }
                if (control.offset != InputStateBlock.InvalidOffset)
                {
                    AddChild(item, $"Offset: {control.offset}", ref id);
                }
                if (control.bit != InputStateBlock.InvalidOffset)
                {
                    AddChild(item, $"Bit: {control.bit}", ref id);
                }
                if (control.sizeInBits != 0)
                {
                    AddChild(item, $"Size In Bits: {control.sizeInBits}", ref id);
                }
                if (control.isArray)
                {
                    AddChild(item, $"Array Size: {control.arraySize}", ref id);
                }
                if (!string.IsNullOrEmpty(control.useStateFrom))
                {
                    AddChild(item, $"Use State From: {control.useStateFrom}", ref id);
                }
                if (!control.defaultState.isEmpty)
                {
                    AddChild(item, $"Default State: {control.defaultState.ToString()}", ref id);
                }
                if (!control.minValue.isEmpty)
                {
                    AddChild(item, $"Min Value: {control.minValue.ToString()}", ref id);
                }
                if (!control.maxValue.isEmpty)
                {
                    AddChild(item, $"Max Value: {control.maxValue.ToString()}", ref id);
                }

                if (control.usages.Count > 0)
                {
                    AddChild(item, "Usages: " + string.Join(", ", control.usages.Select(x => x.ToString()).ToArray()), ref id);
                }
                if (control.aliases.Count > 0)
                {
                    AddChild(item, "Aliases: " + string.Join(", ", control.aliases.Select(x => x.ToString()).ToArray()), ref id);
                }

                if (control.isNoisy || control.isSynthetic)
                {
                    var flags = "Flags: ";
                    if (control.isNoisy)
                    {
                        flags += "Noisy";
                    }
                    if (control.isSynthetic)
                    {
                        if (control.isNoisy)
                        {
                            flags += ", Synthetic";
                        }
                        else
                        {
                            flags += "Synthetic";
                        }
                    }
                    AddChild(item, flags, ref id);
                }

                if (control.parameters.Count > 0)
                {
                    var parameters = AddChild(item, "Parameters", ref id);
                    foreach (var parameter in control.parameters)
                    {
                        AddChild(parameters, parameter.ToString(), ref id);
                    }
                }

                if (control.processors.Count > 0)
                {
                    var processors = AddChild(item, "Processors", ref id);
                    foreach (var processor in control.processors)
                    {
                        var processorItem = AddChild(processors, processor.name, ref id);
                        foreach (var parameter in processor.parameters)
                        {
                            AddChild(processorItem, parameter.ToString(), ref id);
                        }
                    }
                }
            }
Exemple #20
0
        private InputControl AddChildControl(InputControlLayout layout, InternedString variants, InputControl parent,
                                             ref bool haveChildrenUsingStateFromOtherControls,
                                             InputControlLayout.ControlItem controlItem,
                                             int childIndex, string nameOverride = null)
        {
            var name         = nameOverride ?? controlItem.name;
            var nameInterned = new InternedString(name);

            ////REVIEW: can we check this in InputControlLayout instead?
            if (string.IsNullOrEmpty(controlItem.layout))
            {
                throw new InvalidOperationException($"Layout has not been set on control '{controlItem.name}' in '{layout.name}'");
            }

            // See if there is an override for the control.
            if (m_ChildControlOverrides != null)
            {
                var path          = $"{parent.path}/{name}";
                var pathLowerCase = path.ToLower();

                if (m_ChildControlOverrides.TryGetValue(pathLowerCase, out var controlOverride))
                {
                    controlItem = controlOverride.Merge(controlItem);
                }
            }

            // Get name of layout to use for control.
            var layoutName = controlItem.layout;

            // Create control.
            InputControl control;

            try
            {
                control = InstantiateLayout(layoutName, variants, nameInterned, parent);
            }
            catch (InputControlLayout.LayoutNotFoundException exception)
            {
                // Throw better exception that gives more info.
                throw new InputControlLayout.LayoutNotFoundException(
                          $"Cannot find layout '{exception.layout}' used in control '{name}' of layout '{layout.name}'",
                          exception);
            }

            // Add to array.
            // NOTE: AddChildControls and InstantiateLayout take care of growing the array and making
            //       room for the immediate children of each control.
            m_Device.m_ChildrenForEachControl[childIndex] = control;

            // Set flags and misc things.
            control.noisy     = controlItem.isNoisy;
            control.synthetic = controlItem.isSynthetic;
            if (control.noisy)
            {
                m_Device.noisy = true;
            }

            // Remember the display names from the layout. We later do a proper pass once we have
            // the full hierarchy to set final names.
            control.m_DisplayNameFromLayout      = controlItem.displayName;
            control.m_ShortDisplayNameFromLayout = controlItem.shortDisplayName;

            // Set default value.
            control.m_DefaultState = controlItem.defaultState;
            if (!control.m_DefaultState.isEmpty)
            {
                m_Device.hasControlsWithDefaultState = true;
            }

            // Set min and max value. Don't just overwrite here as the control's constructor may
            // have set a default value.
            if (!controlItem.minValue.isEmpty)
            {
                control.m_MinValue = controlItem.minValue;
            }
            if (!controlItem.maxValue.isEmpty)
            {
                control.m_MaxValue = controlItem.maxValue;
            }

            // Pass state block config on to control.
            var usesStateFromOtherControl = !string.IsNullOrEmpty(controlItem.useStateFrom);

            if (!usesStateFromOtherControl)
            {
                control.m_StateBlock.byteOffset = controlItem.offset;
                control.m_StateBlock.bitOffset  = controlItem.bit;
                if (controlItem.sizeInBits != 0)
                {
                    control.m_StateBlock.sizeInBits = controlItem.sizeInBits;
                }
                if (controlItem.format != 0)
                {
                    SetFormat(control, controlItem);
                }
            }
            else
            {
                // Mark controls that don't have state blocks of their own but rather get their
                // blocks from other controls by setting their state size to InvalidOffset.
                control.m_StateBlock.sizeInBits         = kSizeForControlUsingStateFromOtherControl;
                haveChildrenUsingStateFromOtherControls = true;
            }

            ////REVIEW: the constant appending to m_UsagesForEachControl and m_AliasesForEachControl may lead to a lot
            ////        of successive re-allocations

            // Add usages.
            var usages = controlItem.usages;

            if (usages.Count > 0)
            {
                var usageCount = usages.Count;
                var usageIndex =
                    ArrayHelpers.AppendToImmutable(ref m_Device.m_UsagesForEachControl, usages.m_Array);
                control.m_UsageStartIndex = usageIndex;
                control.m_UsageCount      = usageCount;

                ArrayHelpers.GrowBy(ref m_Device.m_UsageToControl, usageCount);
                for (var n = 0; n < usageCount; ++n)
                {
                    m_Device.m_UsageToControl[usageIndex + n] = control;
                }
            }

            // Add aliases.
            if (controlItem.aliases.Count > 0)
            {
                var aliasCount = controlItem.aliases.Count;
                var aliasIndex =
                    ArrayHelpers.AppendToImmutable(ref m_Device.m_AliasesForEachControl, controlItem.aliases.m_Array);
                control.m_AliasStartIndex = aliasIndex;
                control.m_AliasCount      = aliasCount;
            }

            // Set parameters.
            if (controlItem.parameters.Count > 0)
            {
                NamedValue.ApplyAllToObject(control, controlItem.parameters);
            }

            // Add processors.
            if (controlItem.processors.Count > 0)
            {
                AddProcessors(control, ref controlItem, layout.name);
            }

            return(control);
        }
        private void ModifyChildControl(InputControlLayout layout, InternedString variants, InputControl parent,
                                        ref bool haveChildrenUsingStateFromOtherControls,
                                        ref InputControlLayout.ControlItem controlItem)
        {
            ////TODO: support arrays (we may modify an entire array in bulk)

            // Controls layout themselves as we come back up the hierarchy. However, when we
            // apply layout modifications reaching *into* the hierarchy, we need to retrigger
            // layouting on their parents.
            var haveChangedLayoutOfParent = false;

            // Find the child control.
            var child = InputControlPath.TryFindChild(parent, controlItem.name);

            if (child == null)
            {
                // We're adding a child somewhere in the existing hierarchy. This is a tricky
                // case as we have to potentially shift indices around in the hierarchy to make
                // room for the new control.

                ////TODO: this path does not support recovering existing controls? does it matter?

                child = InsertChildControl(layout, variants, parent,
                                           ref haveChildrenUsingStateFromOtherControls, ref controlItem);
                haveChangedLayoutOfParent = true;
            }
            else
            {
                // Apply modifications.
                if (controlItem.sizeInBits != 0 &&
                    child.m_StateBlock.sizeInBits != controlItem.sizeInBits)
                {
                    child.m_StateBlock.sizeInBits = controlItem.sizeInBits;
                }
                if (controlItem.format != 0 && child.m_StateBlock.format != controlItem.format)
                {
                    SetFormat(child, controlItem);
                    haveChangedLayoutOfParent = true;
                }
                ////REVIEW: ATM, when you move a child with a fixed offset, we only move the child
                ////        and don't move the parent or siblings. What this means is that if you move
                ////        leftStick/x, for example, leftStick stays put. ATM you have to move *all*
                ////        controls that are part of a chain manually. Not sure what the best behavior
                ////        is. If we opt to move parents along with children, we have to make sure we
                ////        are not colliding with any other relocations of children (e.g. if you move
                ////        both leftStick/x and leftStick/y, leftStick itself should move only once and
                ////        not at all if there indeed is a leftStick control layout with an offset;
                ////        so, it'd get quite complicated)
                if (controlItem.offset != InputStateBlock.InvalidOffset)
                {
                    child.m_StateBlock.byteOffset = controlItem.offset;
                }
                if (controlItem.bit != InputStateBlock.InvalidOffset)
                {
                    child.m_StateBlock.bitOffset = controlItem.bit;
                }
                if (controlItem.processors.Count > 0)
                {
                    AddProcessors(child, ref controlItem, layout.name);
                }
                ////REVIEW: ATM parameters applied using this path add on top instead of just overriding existing parameters
                if (controlItem.parameters.Count > 0)
                {
                    NamedValue.ApplyAllToObject(child, controlItem.parameters);
                }
                if (!string.IsNullOrEmpty(controlItem.displayName))
                {
                    child.m_DisplayNameFromLayout = controlItem.displayName;
                }
                if (!controlItem.defaultState.isEmpty)
                {
                    child.m_DefaultValue = controlItem.defaultState;
                    m_Device.hasControlsWithDefaultState = true;
                }
                if (!controlItem.minValue.isEmpty)
                {
                    child.m_MinValue = controlItem.minValue;
                }
                if (!controlItem.maxValue.isEmpty)
                {
                    child.m_MaxValue = controlItem.maxValue;
                }

                ////TODO: other modifications
            }

            // Apply layout change.
            ////REVIEW: not sure what's better here; trigger this immediately means we may trigger
            ////        it a number of times on the same parent but doing it as a final pass would
            ////        require either collecting the necessary parents or doing another pass through
            ////        the list of control layouts
            if (haveChangedLayoutOfParent && !ReferenceEquals(child.parent, parent))
            {
                ComputeStateLayout(child.parent);
            }
        }
 public void AddControlItem(InputControlPickerDropdown dropdown, DeviceDropdownItem parent,
                            ControlDropdownItem parentControl,
                            InputControlLayout.ControlItem control, string device, string usage, bool searchable)
 {
     dropdown.AddControlItem(this, parent, parentControl, control, device, usage, searchable);
 }