The current state of user interfaces is disappointing in the sense they fail to scale. Put thousands of entries in a Listbox and expect a degradation of performance, put thousand nodes on the screen, and zooming stutters. Just one thousand and we need hundreds of thousands.
Virtualization is the pathetical attempt to improve the situation, spot-wise. Globally, when your favorite user interface once failed to build scalability in, the application developer must optimize the details, which is hard and often impossible.
What is wrong here?
- User-interface frameworks just ignore what is placed off-screen. Conceptually, they handle every component the same, if it is visible or not, all the controls are “alive”. There may be some attempts like lazy bitmap rendering and “virtualized” Listboxes, but essentially, the computational weight of all components stays the same.
Layout is bound to the components. One scalability issue is Layouting. Layouting puts related components into context and constraints them. But layouting itself is a process that just handles sizing and placing rectangles. The actual elements and bitmaps are not required for the process at all. - Viewing and the Model is not separated. Separating the model from the view is common. The means provided by the framework are, for example, Data Binding. In my opinion a short sighted concept. To make Data Binding work, it must be supported by the framework and so sets harsh limitations on the application developer. More complex updating scenarios require a huge number of coordination classes. It fails to feel right and unforeseen bindings result in a lot of maintenance effort. Why is it required to have duplicated data sitting around in memory at all times, obviously violating our beloved DRY principle? The view should be a cache and not more.
- Rendering is combined with Input Processing. As discussed in a previous post, I strongly recommend to separate sensors from rendering elements. If they two are separated, the underlying rendering engine could be isolated from the framework. It is very important to use the fastest means to render you UI and don’t let rendering to be slowed down by all the organizational overhead.
One approach to tackle these problems is by separating the UI framework into clearly defined layers.
Though incomplete, here is my first attempt:
- Model The model should represented by simple classes, probably encoded in a relational model, so that they can be stored in databases. If a relational model is chosen, an additional relationship-mapper must be active to support fast lookups of inverse relations.
- StyleModel Another model that describes details for the view, such as font sizes or padding. It should be applicable to the Model so that it does not need to contain any style information. This is similar to CSS.
- Model Invalidation Distribution Model objects may refer to others. If they are changed, related objects may get invalid. For example, if a node-connector refers two nodes, its position and size depend on them. As soon one of the nodes is changed, the connector needs to be invalidated, too. If the model is relational, the relationship-mapper may be used for that.
- Size Computation supports the computation of size constraints by accessing the Model and the StyleModel.
- Layout Computation This puts the model objects into context and places them. The layout model is special in the sense that it separates the model from the viewing and rendering part: There are two passes: 1. is the computation of the size constraints by using Size Computation and 2. is the actual placement of the objects. The first pass is processed eagerly, immediately after an invalidated model has been spotted. The second pass is requested by Visibility Culling.
Visibility Culling The layer that finally decides what is visible and what not. It uses the current view’s size and Layout Computation to select the objects that need to be rendered.
Taking a look at most user interface frameworks, these layers are all existing, but interleaved into one single class, the Control, and I think this needs to be changed.