private InputControl InstantiateLayout(InputControlLayout layout, InternedString variants, InternedString name, InputControl parent) { Debug.Assert(layout.type != null, "Layout has no type set on it"); // No, so create a new control. var controlObject = Activator.CreateInstance(layout.type); if (!(controlObject is InputControl control)) { throw new InvalidOperationException( $"Type '{layout.type.Name}' referenced by layout '{layout.name}' is not an InputControl"); } // If it's a device, perform some extra work specific to the control // hierarchy root. if (control is InputDevice controlAsDevice) { if (parent != null) { throw new InvalidOperationException( $"Cannot instantiate device layout '{layout.name}' as child of '{parent.path}'; devices must be added at root"); } m_Device = controlAsDevice; m_Device.m_StateBlock.byteOffset = 0; m_Device.m_StateBlock.bitOffset = 0; m_Device.m_StateBlock.format = layout.stateFormat; // If we have an existing device, we'll start the various control arrays // from scratch. Note that all the controls still refer to the existing // arrays and so we can iterate children, for example, just fine while // we are rebuilding the control hierarchy. m_Device.m_AliasesForEachControl = null; m_Device.m_ChildrenForEachControl = null; m_Device.m_UsagesForEachControl = null; m_Device.m_UsageToControl = null; if (layout.m_UpdateBeforeRender == true) { m_Device.m_DeviceFlags |= InputDevice.DeviceFlags.UpdateBeforeRender; } } else if (parent == null) { // Someone did "new InputDeviceBuilder(...)" with a control layout. // We don't support creating control hierarchies without a device at the root. throw new InvalidOperationException( $"Toplevel layout used with InputDeviceBuilder must be a device layout; '{layout.name}' is a control layout"); } // Name defaults to name of layout. if (name.IsEmpty()) { name = layout.name; // If there's a namespace in the layout name, snip it out. var indexOfLastColon = name.ToString().LastIndexOf(':'); if (indexOfLastColon != -1) { name = new InternedString(name.ToString().Substring(indexOfLastColon + 1)); } } // Variant defaults to variants of layout. if (variants.IsEmpty()) { variants = layout.variants; if (variants.IsEmpty()) { variants = InputControlLayout.DefaultVariant; } } control.m_Name = name; control.m_DisplayNameFromLayout = layout.m_DisplayName; // No short display names at layout roots. control.m_Layout = layout.name; control.m_Variants = variants; control.m_Parent = parent; control.m_Device = m_Device; // Create children and configure their settings from our // layout values. var haveChildrenUsingStateFromOtherControl = false; try { // Pass list of existing control on to function as we may have decided to not // actually reuse the existing control (and thus control.m_ChildrenReadOnly will // now be blank) but still want crawling down the hierarchy to preserve existing // controls where possible. AddChildControls(layout, variants, control, ref haveChildrenUsingStateFromOtherControl); } catch { ////TODO: remove control from collection and rethrow throw; } // Come up with a layout for our state. ComputeStateLayout(control); // Finally, if we have child controls that take their state blocks from other // controls, assign them their blocks now. if (haveChildrenUsingStateFromOtherControl) { var controls = layout.m_Controls; for (var i = 0; i < controls.Length; ++i) { ref var item = ref controls[i]; if (string.IsNullOrEmpty(item.useStateFrom)) { continue; } ApplyUseStateFrom(control, ref item, layout); } }
private InputControl InstantiateLayout(InputControlLayout layout, InternedString variant, InternedString name, InputControl parent, InputControl existingControl) { InputControl control; // If we have an existing control, see whether it's usable. if (existingControl != null && existingControl.layout == layout.name && existingControl.GetType() == layout.type) { control = existingControl; ////FIXME: the re-use path probably has some data that could stick around when it shouldn't control.m_UsagesReadOnly = new ReadOnlyArray <InternedString>(); control.ClearProcessors(); } else { Debug.Assert(layout.type != null); // No, so create a new control. var controlObject = Activator.CreateInstance(layout.type); control = controlObject as InputControl; if (control == null) { throw new Exception(string.Format("Type '{0}' referenced by layout '{1}' is not an InputControl", layout.type.Name, layout.name)); } } // If it's a device, perform some extra work specific to the control // hierarchy root. var controlAsDevice = control as InputDevice; if (controlAsDevice != null) { if (parent != null) { throw new Exception(string.Format( "Cannot instantiate device layout '{0}' as child of '{1}'; devices must be added at root", layout.name, parent.path)); } m_Device = controlAsDevice; m_Device.m_StateBlock.byteOffset = 0; m_Device.m_StateBlock.format = layout.stateFormat; // If we have an existing device, we'll start the various control arrays // from scratch. Note that all the controls still refer to the existing // arrays and so we can iterate children, for example, just fine while // we are rebuilding the control hierarchy. m_Device.m_AliasesForEachControl = null; m_Device.m_ChildrenForEachControl = null; m_Device.m_UsagesForEachControl = null; m_Device.m_UsageToControl = null; // But we preserve IDs and descriptions of existing devices. if (existingControl != null) { var existingDevice = (InputDevice)existingControl; m_Device.m_Id = existingDevice.m_Id; m_Device.m_Description = existingDevice.m_Description; } if (layout.m_UpdateBeforeRender == true) { m_Device.m_Flags |= InputDevice.Flags.UpdateBeforeRender; } } else if (parent == null) { // Someone did "new InputDeviceBuilder(...)" with a control layout. // We don't support creating control hierarchies without a device at the root. throw new InvalidOperationException( string.Format( "Toplevel layout used with InputDeviceBuilder must be a device layout; '{0}' is a control layout", layout.name)); } // Name defaults to name of layout. if (name.IsEmpty()) { name = layout.name; // If there's a namespace in the layout name, snip it out. var indexOfLastColon = name.ToString().LastIndexOf(':'); if (indexOfLastColon != -1) { name = new InternedString(name.ToString().Substring(indexOfLastColon + 1)); } } // Variant defaults to variant of layout. if (variant.IsEmpty()) { variant = layout.variant; if (variant.IsEmpty()) { variant = InputControlLayout.DefaultVariant; } } control.m_Name = name; control.m_DisplayNameFromLayout = layout.m_DisplayName; control.m_Layout = layout.name; control.m_Variant = variant; control.m_Parent = parent; control.m_Device = m_Device; // Create children and configure their settings from our // layout values. var haveChildrenUsingStateFromOtherControl = false; try { // Pass list of existing control on to function as we may have decided to not // actually reuse the existing control (and thus control.m_ChildrenReadOnly will // now be blank) but still want crawling down the hierarchy to preserve existing // controls where possible. AddChildControls(layout, variant, control, existingControl != null ? existingControl.m_ChildrenReadOnly : (ReadOnlyArray <InputControl>?)null, ref haveChildrenUsingStateFromOtherControl); } catch { ////TODO: remove control from collection and rethrow throw; } // Come up with a layout for our state. ComputeStateLayout(control); // Finally, if we have child controls that take their state blocks from other // controls, assign them their blocks now. if (haveChildrenUsingStateFromOtherControl) { foreach (var controlLayout in layout.controls) { if (string.IsNullOrEmpty(controlLayout.useStateFrom)) { continue; } var child = TryGetControl(control, controlLayout.name); Debug.Assert(child != null); // Find the referenced control. var referencedControl = TryGetControl(control, controlLayout.useStateFrom); if (referencedControl == null) { throw new Exception( string.Format( "Cannot find control '{0}' referenced in 'useStateFrom' of control '{1}' in layout '{2}'", controlLayout.useStateFrom, controlLayout.name, layout.name)); } // Copy its state settings. child.m_StateBlock = referencedControl.m_StateBlock; // At this point, all byteOffsets are relative to parents so we need to // walk up the referenced control's parent chain and add offsets until // we are at the same level that we are at. for (var parentInChain = referencedControl.parent; parentInChain != control; parentInChain = parentInChain.parent) { child.m_StateBlock.byteOffset += parentInChain.m_StateBlock.byteOffset; } } } return(control); }