Technical details on the layout engine

In the DocktorUI library particular care has been given to the implementation of the Layout Engine and all the basic building blocks for the construction of layouts. The reasoning behind this is that docking layouts can get very cluttered and complicated, so a robust Layout Engine must be able to handle these situations with ease and most importantly without forcing the same level of complexity over simpler layouts.

Nested Subdivisions

Layouts in the DocktorUI framework are assembled from a basic structure of nested subdivisions. Each subdivision is contained within the boundaries of a so-called "compact" container which can be subdivided again recursively into secondary subdivisions and tertiary and so on up the the 32nd level of recursion. The latter is a limit imposed arbitrarily by the implementation (mostly for performance reasons) simply because it's practically impossible to surpass this limit in any realistic circumstance. The limit itself however can be increased if needed.

All the mentioned containers subdivide on-demand when constructing the desired layout by placing new panels. The process of subdivision is completely managed by the Layout Engine itself and generally transparent to the developer using the library. Knowing how the underlying process works though, can be helpful in understanding how certain components are supposed to behave.

Compact Container

As said above subdivided containers are called internally "compact" containers. The reason for this is that the Layout Engine always tries to optimize and reuse available space so that there are no blanks or empty areas in-between the subdivisions. Thus, it could be said that all the containers are "pushed" together thus are "compacted" within one another.

The key component of the Layout Engine is the CompactContainer class, which is where all the layout logic is calculated and where CompactPositions are translated into pixel values. The CompactPosition class stores the generic or abstract placement of elements in the layout in relation to the root container, that is the CompactContainer to which all the other containers belong and whose abstract area corresponds to or is contained by the containing control's physical bounds.

Compact Content

The Layout Engine is an abstract space distribution system in which compact containers split, resize and move on demand. Because the system itself is abstracted it doesn't need to presume the existence of Controls and instead represent the basic structural element as "content".

In this terms, content is an atomic piece of space that cannot be further subdivided, thus any attempt in doing so will actually split the content's parent container and move the content on one of the subdivisions thus created. Content in the Layout Engine is also called "compact" because it follows the same conditions of space tightness as containers and is represented code-wise by the ICompactContent interface.

To have Controls or any other type of visual object handled by the Layout Engine it is simply a matter of implementing the ICompactContent interface, which is exactly what the DockControl class does internally.

Because most of the times the elements within a layout will be single Controls, it is very easy to implement the ICompactContent interface for controls, as most of the interface members actually corresponds to members the Control class already has. For instance here below is the simplified definition of the DockTabView class used internally by the DockMainContainerControl control.

Code Example:

protected class DockTabView
    : TabDocumentView, ICompactContent
{
    private Size _OptimalSize;
    
    
    ICompactContainer ICompactContent.Container
    {
        get
        {
            return CompactLayoutUtils.GetContainerForControl(this);
        }
    }

    CompactPosition ICompactContent.Position
    {
        get
        {
            return CompactLayoutUtils.GetPositionForContent(this);
        }
    }

    Size ICompactContent.OptimalSize
    {
        get
        {
            return _OptimalSize;
        }
        set
        {
            _OptimalSize = value;
        }
    }
    

    public DockTabView()
    { }
}