Rolling your own CSS

Having an easily data driven method for customizing style information can be invaluable for building nice user interfaces. For special purpose user interfaces like games it is even more important. Designers need to be able to easily skin and customize different UI elements with out having to alter source code.

UI Elements as Properties

To control the look of user interface elements a simple flat set of properties can be used such as "background-color" or "font". A style sheet is simply a class that you can get and set properties from: class Style { public object GetValue(IStyleable target, string propertyName); public void SetValue(StyleSelecter selecter, string propertyName, object value); } Just like in CSS a common way to refer to elements is needed. I used StyleID, a set of Classes and a Type to replicate the familiar id,class,elementType trio. This is formulated in a simple interface: interface IStyleable { string StyleID { get; } ICollection<string> { get; } string StyleType { get; } } A predicate for if a certain property definition applies is needed: abstract class StyleSelector { public abstract bool Matches(IStyleable target); }

Finding a Property

Every selector must at the very least support the ability to find a property by an O(n) search--this is what the Matches method is all about. Finding a property consists of a search over a list property definitions (StyleSelector,name,value). If a definition's name matches and StyleSelector.Matches is true then the search is terminated and value is returned as the value for that property.

In order for the cascading part to make any sense an ordering of property definitions needs to be defined. CSS solves this problem with the notion of specificity. Properties with more stringent criteria for being applied are allowed to float to the top and are used over the more generic definitions.

For my purposes I organize things simply by id > classes > type > *. So selectors that specify the id of its target are examined first. This conveniently allows for property searches to be culled to smaller sets that must be visited. Hashtables for each of these categories can be constructed to speed up search times so that for example (id,name) is indexed. This reduces linear searching only to custom and other miscellaneous selectors.

Combining Selectors

Aggregated selectors can be formulated based the AND logical operator. Since AND is commutative the selector can be rummaged through for any sub-selectors that are indexable. This can then be used as criteria for which index the aggregation should be inserted into. For example #myButton:hover can be inserted into the id indexing table since it contains an id selector.

I will probably post the code to this and the rest of my GUI toolkit when I am done / bored with it.

0 comments: