private static void AddProcessors(InputControl control, ref InputTemplate.ControlTemplate controlTemplate, string templateName) { var processorCount = controlTemplate.processors.Count; for (var n = 0; n < processorCount; ++n) { var name = controlTemplate.processors[n].name; var type = InputProcessor.TryGet(name); if (type == null) { throw new Exception( string.Format("Cannot find processor '{0}' referenced by control '{1}' in template '{2}'", name, controlTemplate.name, templateName)); } var processor = Activator.CreateInstance(type); var parameters = controlTemplate.processors[n].parameters; if (parameters.Count > 0) { SetParameters(processor, parameters); } control.AddProcessor(processor); } }
private static void SetFormat(InputControl control, InputTemplate.ControlTemplate controlTemplate) { control.m_StateBlock.format = controlTemplate.format; if (controlTemplate.sizeInBits == 0) { var primitiveFormatSize = InputStateBlock.GetSizeOfPrimitiveFormatInBits(controlTemplate.format); if (primitiveFormatSize != -1) { control.m_StateBlock.sizeInBits = (uint)primitiveFormatSize; } } }
private InputControl InsertChildControl(InputTemplate template, InternedString variant, InputControl parent, ref bool haveChildrenUsingStateFromOtherControls, ref InputTemplate.ControlTemplate controlTemplate) { var path = controlTemplate.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 template '{2}'", immediateParentPath, controlTemplate.name, template.name)); } var controlName = path.Substring(indexOfSlash + 1); if (controlName.Length == 0) { throw new Exception( string.Format("Path cannot end in '/' (control '{0}' in template '{1}')", controlTemplate.name, template.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(template, variant, immediateParent, null, ref haveChildrenUsingStateFromOtherControls, ref controlTemplate, ref childIndex, controlName); // Adjust indices of control's that have been shifted around by our insertion. ShiftChildIndicesInHierarchyOneUp(parent, childIndex); return(control); }
private static bool ControlTemplateMatchesPathComponent(ref InputTemplate.ControlTemplate controlTemplate, ref PathParser parser) { // Match template. var template = parser.current.template; if (template.length > 0) { if (!StringMatches(template, controlTemplate.template)) { return(false); } } // Match usage. var usage = parser.current.usage; if (usage.length > 0) { var usageCount = controlTemplate.usages.Count; var anyUsageMatches = false; for (var i = 0; i < usageCount; ++i) { if (StringMatches(usage, controlTemplate.usages[i])) { anyUsageMatches = true; break; } } if (!anyUsageMatches) { return(false); } } // Match name. var name = parser.current.name; if (name.length > 0) { if (!StringMatches(name, controlTemplate.name)) { return(false); } } return(true); }
private void ModifyChildControl(InputTemplate template, InternedString variant, InputControl parent, ref bool haveChildrenUsingStateFromOtherControls, ref InputTemplate.ControlTemplate controlTemplate) { // 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 = TryGetControl(parent, controlTemplate.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(template, variant, parent, ref haveChildrenUsingStateFromOtherControls, ref controlTemplate); haveChangedLayoutOfParent = true; } else { // Apply modifications. if (controlTemplate.sizeInBits != 0 && child.m_StateBlock.sizeInBits != controlTemplate.sizeInBits) { child.m_StateBlock.sizeInBits = controlTemplate.sizeInBits; } if (controlTemplate.format != 0 && child.m_StateBlock.format != controlTemplate.format) { SetFormat(child, controlTemplate); 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 template with an offset; //// so, it'd get quite complicated) if (controlTemplate.offset != InputStateBlock.kInvalidOffset) { child.m_StateBlock.byteOffset = controlTemplate.offset; } if (controlTemplate.bit != InputStateBlock.kInvalidOffset) { child.m_StateBlock.bitOffset = controlTemplate.bit; } if (controlTemplate.processors.Count > 0) { AddProcessors(child, ref controlTemplate, template.name); } if (controlTemplate.parameters.Count > 0) { SetParameters(child, controlTemplate.parameters); } if (!string.IsNullOrEmpty(controlTemplate.displayName)) { child.m_DisplayNameFromTemplate = controlTemplate.displayName; } ////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 templates if (haveChangedLayoutOfParent && !ReferenceEquals(child.parent, parent)) { ComputeStateLayout(child.parent); } }
private InputControl AddChildControl(InputTemplate template, InternedString variant, InputControl parent, ReadOnlyArray <InputControl>?existingChildren, ref bool haveChildrenUsingStateFromOtherControls, ref InputTemplate.ControlTemplate controlTemplate, ref int childIndex, string nameOverride = null) { var name = nameOverride ?? controlTemplate.name; var nameLowerCase = nameOverride != null?nameOverride.ToLower() : controlTemplate.name.ToLower(); var nameInterned = nameOverride != null ? new InternedString(nameOverride) : controlTemplate.name; ////REVIEW: can we check this in InputTemplate instead? if (string.IsNullOrEmpty(controlTemplate.template)) { throw new Exception(string.Format("Template has not been set on control '{0}' in '{1}'", controlTemplate.name, template.name)); } // 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.template == controlTemplate.template && existingChild.name.ToLower() == nameLowerCase) { existingControl = existingChild; break; } } } // Create control. InputControl control; try { control = AddControl(controlTemplate.template, variant, nameInterned, parent, existingControl); } catch (InputTemplate.TemplateNotFoundException exception) { // Throw better exception that gives more info. throw new Exception( string.Format("Cannot find template '{0}' used in control '{1}' of template '{2}'", exception.template, controlTemplate.name, template.name), exception); } // Add to array. m_Device.m_ChildrenForEachControl[childIndex] = control; ++childIndex; // Set display name. control.m_DisplayNameFromTemplate = controlTemplate.displayName; // Pass state block config on to control. var usesStateFromOtherControl = !string.IsNullOrEmpty(controlTemplate.useStateFrom); if (!usesStateFromOtherControl) { control.m_StateBlock.byteOffset = controlTemplate.offset; if (controlTemplate.bit != InputStateBlock.kInvalidOffset) { control.m_StateBlock.bitOffset = controlTemplate.bit; } if (controlTemplate.sizeInBits != 0) { control.m_StateBlock.sizeInBits = controlTemplate.sizeInBits; } if (controlTemplate.format != 0) { SetFormat(control, controlTemplate); } } 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. if (controlTemplate.usages.Count > 0) { var usageCount = controlTemplate.usages.Count; var usageIndex = ArrayHelpers.AppendToImmutable(ref m_Device.m_UsagesForEachControl, controlTemplate.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 (controlTemplate.aliases.Count > 0) { var aliasCount = controlTemplate.aliases.Count; var aliasIndex = ArrayHelpers.AppendToImmutable(ref m_Device.m_AliasesForEachControl, controlTemplate.aliases.m_Array); control.m_AliasesReadOnly = new ReadOnlyArray <InternedString>(m_Device.m_AliasesForEachControl, aliasIndex, aliasCount); } // Set parameters. if (controlTemplate.parameters.Count > 0) { SetParameters(control, controlTemplate.parameters); } // Add processors. if (controlTemplate.processors.Count > 0) { AddProcessors(control, ref controlTemplate, template.name); } return(control); }
private void BuildItem(InputTemplate.ControlTemplate control, TreeViewItem parent, ref int id) { var item = AddChild(parent, control.variant.IsEmpty() ? control.name : string.Format("{0} ({1})", control.name, control.variant), ref id); ////TODO: fully merge TreeViewItems from isModifyingChildControlByPath control templates into the control they modify ////TODO: allow clicking this field to jump to the template if (!control.template.IsEmpty()) { AddChild(item, string.Format("Template: {0}", control.template), ref id); } if (!control.variant.IsEmpty()) { AddChild(item, string.Format("Variant: {0}", control.variant), 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 (!string.IsNullOrEmpty(control.useStateFrom)) { AddChild(item, string.Format("Use State From: {0}", control.useStateFrom), ref id); } if (control.isAutoResetControl) { AddChild(item, "Auto-Reset: true", 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); } } } }