UI Autolayout

From cxwiki

The cxsource framework provides automatic layout facilities, allowing user interface layouts to be built with runtime-evaluated rules rather than hardcoded values. This offers a number of immediate benefits:

  • The underlying fonts and control appearances can be swapped out, leading to changes in the user interface layout requirements, without having to manually update every single UI.
    • This is especially useful when developing cross-platform interface layouts where the native appearance and even font rendering may require subtle changes to the layout.
  • Localised versions of the UI can reflow correctly without running out of space and without leaving ugly gaps.
  • A lot of manual coding, or manual calculations, can be skipped in favor of automated placement.
  • Layouts can adjust as windows are resized, including controlled collapse of elements when the window is too small to fit the full user interface.
  • Layouts can be hot-reloaded during development.
  • Window sizes and size limitations are calculated automatically, and can automatically adapt as the UI changes.
  • Layouts can be animated easily.

There are also some downsides:

  • Calculating the layout at runtime is slower than simply loading some predefined values.
  • Debugging problems in the user interface layout is sometimes more time-consuming.

 

Alignment Containers

All UIElement-derived classes may participate in autolayout, however only specific classes offer the opportunity to configure custom layout rules.

UIAlignmentContainer is the primary class involved in configuring an autolayout hierarchy; it offers the following capabilities:

  • Loading its children from a definition stored in a UX File.
  • Loading layout rules from a UX File.
  • Maintaining a single UIAlignmentMethod (per UIAlignmentContainer) and using that to perform autolayout of its user element hierarchy.

It is not required that a single UIAlignmentContainer be used to maintain an entire user interface hierarchy. Instead, each such container may have additional UIAlignmentContainer elements within their hierarchy.

UX Files

While it is possible to create autolayout hierarchies completely from code, this tends to be a lot of work and generally isn't necessary. Instead, UX Files are typically loaded (using TextConfig) and parsed to build the user interface. Each UX file can contain a complete hierarchy, but it is also possible to separate sub-components out into their own files to keep things simple or to enourage re-use.

In the rare cases where program code needs to modify the interface layout at runtime, modifications can be performed at any time after the initial loading from the UX File is complete.

Alignment Methods

Each UIAlignmentContainer specifies a single UIAlignmentMethod. The alignment method determines what kind of rules are used to layout the user interface. Tradeoffs exist between flexibility, simplicity, and performance.

The following methods are provided at the current time:

  • UIAlignmentMethodConstraints - Uses an externally-specified set of rules to relate the various user interface components to each other. Each rule is a simple relationship, such as "button.top = text.bottom + padding". This is quite powerful, but requires each components position in the layout to be described in mathematical terms. More complex layouts with many freedoms can take longer to process.
  • UIAlignmentMethodGrid - Creates a grid layout for its direct children based on a few simple options. This is simple and relatively high-performance.

 

Autolayout Lock

UIElement itself offers a locking mechanism which postpones any autolayout. The intended usage is to lock the autolayout, perform a number of user interface edits, and then unlock the autolayout. This helps prevent expensive layout updates after each edit.

The locking mechanism is quite generic. Other user interface updates may also take advantage of its facilities.

// Lock all autolayout on the current thread. This maintains a lock 
// count and may be called multiple times, requiring an equivalent 
// number of calls to UnlockAutolayout() to undo.
static void LockAutolayout(void);

// Undoes one call to LockAutolayout(). If the lock count reaches
// zero, any queued layout operations are performed immediately.
// It is an error to call this function when the lock count is
// already zero.
static void UnlockAutolayout(void);

// If autolayout is not currently locked, this simply calls the
// specified function before returning to the caller. Otherwise,
// the function is placed into a queue and each operation is
// performed in order once autolayout is unlocked. During the
// unlock operation, the queue remains in effect and additional
// ProcessAutolayout() calls are not performed out-of-order.
void ProcessAutolayout(const std::function<void(void)>& autoLayoutFunction);