Automated Component
The new internal management system for GUIMultiComponents enables you to automatically create variables of the desired type, without casting. It also handles initialization, tab order, and render weight for each automated component. It involves two new specifiers: one for classes - instanced, and one for properties - automated.
var automated GUIButton FantasticButton; defaultproperties { Begin Object Class=GUIButton Name=FantaticButtonTemplate OnClick=InternalOnClick WinWidth=0.06 WinHeight=0.04 bBoundToParent=True bScaleToParent=True End Object FantasticButton = FantasticButtonTemplate }
Automated is a new variable modifier that tells the native code to automatically create instances of a subobject definition. By declaring a variable automated, all assignment and initialization will be taken care of natively. The auto-initialization is kicked off in GUIMultiComponent.InitComponent(). GUIMultiComponent.InitComponent() calls InitializeControls(), a native function which iterates through the class's properties. For any automated properties, it copies the default properties from a subobject definition into the associated variable. Similar to spawn, it always returns an object of the specified type, without need for casting.
GUIMultiComponent defines two arrays of GUIComponents - Controls and Components. The controls array contains all GUIComponents defined and initialized for that page. When using automated controls, the controls array will be sorted natively by RenderWeight, a property of GUIComponent. The native rendering code will draw Controls[0] first, proceeding through the array until it reaches the end. The components array contains all controls on the page that are tab stops. Components are generally controls on the page that can accept input, such as buttons, edit boxes, and tabs. The components array is sorted by TabOrder, a property of GUIComponent. Both arrays are managed automatically for automated components, but GUIMultiComponent provides methods for also manually managing the Components array.
GUIMultiComponent Methods and Properties:
var array<GUIComponent> Controls; // An Array of Components that make up this Control var array<GUIComponent> Components; // An Array of Controls that can be tabbed to var GUIComponent FocusedControl; // Which component inside this one has focus var bool PropagateVisibility; // Propgate changes to visibility to my Controls array var bool bOldStyleMenus; // Set natively if Controls array members are defined in default properties delegate OnCreateComponent(GUIComponent NewComponent, GUIComponent Sender); // Hook to access components before they're initialized function native InitializeControls(); // Initialize automated components event GUIComponent AddComponent(string ComponentClass, optional bool SkipRemap) // Create and add a new component, optionally skipping TabOrder remapping event GUIComponent InsertComponent(GUIComponent NewComp, int Index, optional bool SkipRemap) event GUIComponent AppendComponent(GUIComponent NewComp, optional bool SkipRemap) // Append an existing component event bool RemoveComponent(GUIComponent Comp, optional bool SkipRemap) // Remove a component from the Components array event int FindComponentIndex(GUIComponent Who) // Find the Components array index of a GUIComponent event RemapComponents() // Remap the Components - resort based on TabOrder event Free() // The control is no longer needed, perform any cleanup here.
The two systems (current system and new system) cannot be mixed. InitializeControls() checks if there are any existing members defined for the Controls array in the class's default properties, and if so, simply returns. If you want to use automated components on your menus, you'll need to update everything
UT2004 will come with a completely new GUI which uses the automated system, as well as the original, which will use the current system.
AutoPositioning
// Auto-positioning - accounts for bBoundToParent & bScaleToParent native final function AutoPosition( array<GUIComponent> Components, float LeftBound, float UpperBound, float RightBound, float LowerBound, float LeftPad, float UpperPad, float RightPad, float LowerPad );
GUIComponent.AutoPosition() is a native function that will automatically position an array of GUIComponents within the bounds passed to the function. It takes nine parameters. The first parameter is an array of GUIComponents that will be positioned. The next four parameters define the bounds of the area that you wish the components to be positions within, while the last four parameters specify the amount of padding to add to each side. These parameters must be actual screen values, not scaled values (For example, pass in the value of ActualTop(), not WinTop).
AutoPosition() first applies the padding values, then divides the length of the passed-in array by the difference between the lower bound and the upper bound to find the component offset. It adjusts each of the array member's WinWidth, WinLeft values based on the values passed into the function, and adjust each array member's WinTop value based on component offset. It does NOT modify the WinHeight value of the components. If the total number of components multiplied by the offset value would be greater than the bounded area, AutoPosition() writes an error to the log, and returns. AutoPosition() accounts for the bBoundToParent and bScaleToParent values, and internally adjusts the ratios of the components accordingly.
Multiple instancing
One of the largest benefits of using AutoPosition() to position your components is that you can very easily take advantage of multiple instancing. Multiple instancing is where you have one templated subobject definition, but assign multiple variables to it.
Begin Object class=GUIImage Name=HUDPreviewImage ImageStyle=ISTY_Scaled ImageColor=(R=255,G=255,B=255,A=255) WinHeight=0.5 RenderWeight=0.51 End Object i_HUDTop=HUDPreviewImage i_HUDTopRight=HUDPreviewImage i_HUDTopLeft=HUDPreviewImage i_HUDMiddleRight=HUDPreviewImage i_HUDMiddleLeft=HUDPreviewImage i_HUDMiddleRight=HUDPreviewImage i_HUDBottomLeft=HUDPreviewImage i_HUDBottomRight=HUDPreviewImage i_HUDBottom=HUDPreviewImage
You can then pass these components to AutoPosition() as an array, and they will be automatically positioned for you. If you need to make changes to the original template image, you'd only need to make this change in one place. Likewise, if you wanted to add additional components, or remove components, you'd need only to add/remove the assignments in default properties. No more resizing dozens of components in design mode every time you change one option!
Overriding automated subobject definitions
The finer art of 'default vs. instanced'
Mychaeel: That's a very neat set of new features you're describing here. It's just a bit of a pity that it's specific to the GUI classes and not made so generic that all classes could benefit from it – by allowing the "automated" keyword in any class, introducing an "InitObject" event in Object and defining a native "InitObjects" function in Object instead of InitComponents in GUIMultiComponent.
Evolution: Actually, 'automated' isn't specific to GUI subclasses. It can be used in any class, as long as the class is specified 'instanced'. The 'instanced' specifier is inherited, which is why all GUI classes are able to use 'automated' variables without the 'instanced' specifier (GUI itself is declared 'instanced'). However, the InitializeControls() doesn't have anything to do with automated components, per se. InitializeControls() does native management of the Controls array for automated variables in GUIMultiComponent subclasses, but auto-instancing doesn't require an InitializeControls() function at all. Any subobject definition can be automatically assigned to an 'automated' variable.
Mychaeel: Then I'm afraid I haven't understood yet what the "automated" specifier does at all, especially in contrast to how subobjects work in UT2003. Could you elaborate on that a bit, please?