Objective Toolkit : Chapter 23 Layout Manager Framework : Layout Manager Architecture
Layout Manager Architecture
The Layout Manager has a strong architecture that is suited for reuse and extensibility. Based in part on the Composite design pattern, the Layout Manager is a collection of nodes arranged in a tree-like hierarchy of responsibility.
Layout Nodes
The basis of this tree is the base class SECLayoutNode, which defines the interface for membership in this tree. A layout node is defined as any object that derives its interface from the SECLayoutNode base class. Conceptually, layout nodes are either proactive or reactive in nature.
Proactive nodes, also known as composites, hold the layout algorithms. Each proactive node encapsulates one layout algorithm. Examples are SECLNRelative, SECLNScale, and SECLNGridBag. Proactive nodes are designed to contain and administrate child nodes.
Reactive nodes, also known as primitives, are home to the leaf objects controlled by the proactive nodes. When you override the appropriate functions in the SECLayoutNode base class, a reactive node can respond to events driven by its parent node and position, resize, and render itself as appropriate. SECLayoutNodeWnd is an example of a reactive node. Although SECLayoutNodeWnd is designed to link to a CWnd, you could also design your own reactive node to link to a non-CWnd, like a rendered graphic in a view.
The distinction between proactive and reactive nodes is only conceptual in nature. Syntactically, both are derived from SECLayoutNode and possess the same type-interface. Only the intended usage of the object defines its designation. Some objects can be both proactive and reactive. For example, the SECLayoutNodeSplitter class is a reactive end-node with a simple proactive layout algorithm.
In general, proactive nodes are not visible entities. For example, an algorithmic layout node is a black box rectangle that is responsible for administrating all its children within that rectangle. Nonetheless, one of its children could be a proactive node that administers its child nodes. This is the strength of a polymorphic layout node in a composite, tree-like hierarchy.
For example, suppose you want to create a dialog with three push buttons. Suppose you want to lay out those push buttons in two rows, with one button spanning the entire top row, and the other two buttons sharing the bottom row.
Figure 156 – Sample Button Layout
Use nested layouts to solve this problem. At the top of your layout tree, create an SECLNGrid grid layout node. You now have one black-box grid layout node.
Figure 157 – Sample Grid Layout Node
If you configured this grid layout node to have two rows and one column, it would have two layout node children.
Figure 158 – Sample Grid Layout Node with Two Children
The SECLNGrid parent does consider what kind of children it creates; however, it does know that it has two child objects derived from SECLayoutNode.
To hook Child Node 1 to a button, use SECLayoutNodeWnd. SECLayoutNodeWnd attaches to a CWnd through aggregation, which guarantees type compatibility with the layout-node hierarchy.
For Child Node 2, you need to administrate both button two and button three. The easiest way to do this is to nest another proactive node. In this case, you would embed another grid node to administrate the two buttons.
Figure 159 – Sample Grid Layout with One Button Child Node and One Grid Child Node
Finally, you would configure this nested grid node to be a 1x2 grid, with two SECLayoutNodeWnd children (one for button two, one for button three). Here’s the result.
Figure 160 – Final Three Button Layout
The following figure shows this layout represented in a tree structure.
Figure 161 – Tree Structure for the Three Button Layout
The top-level grid node only possesses knowledge of its two children: an SECLayoutNodeWnd object and an SECLNGrid object. Because all children are derived from SECLayoutNode, it can polymorphically manipulate the two children with the same interface. After the top-level grid node positions the nested grid, the nested grid positions its two children — the two SECLayoutNodeWnd objects attached to buttons 2 and 3. Each parent treats its child nodes as black boxes. There is no parent-grandchild manipulation.
This divide-and-conquer tactic provides a powerful and extensible mechanism. You can easily add your own custom algorithms or window types into this framework with little or no change to an existing layout.
Window Listeners
The SECWndListener class acts as a bridge between the WIN32 windowing world and the abstracted layout node tree. The window listener attaches laterally through aggregation to an existing dialog, view, control bar, or any other CWnd-derived window. It listens for the appropriate windows messages to start the layout management recalculations.
The listen mechanism uses a streamlined window subclass that has a negligible performance hit and is in no way disruptive to the normal message flow. This enables you to create a seamless hook into your application’s layout framework without making any changes to the base class. The window listener is not required for use of the layout framework. The window listener’s sole purpose is to simplify the layout insertion process.
Layout Factory
The SECLayoutFactory class simplifies the process of creating and merging layout nodes into the Layout Manager framework. A component is a window control, graphical entity, layout algorithm, or other object. Because every component of a layout must have a corresponding layout node, you need to allocate a large number of layout node objects. The layout factory simplifies this process by:
Dynamically creating the node type of interest.
Automatically setting some default parent-child relationships and other miscellaneous settings.
Keeping track of the allocated storage for self-contained memory management.
Typically, a user of the layout framework only needs to instantiate one layout factory object as a member of his or her parent window class. Then, the layout factory methods are used to allocate all additional storage required. The layout factory can also allocate a window listener object, a feature that simplifies layout integration even further. Like the window listener, use of the layout factory is not required for utilization of the Layout Manager, although it is highly recommended.
Splitter Node
The SECLayoutNodeSplitter class easily merges splitter functionality into your Layout Manager. This powerful splitter class is optimized so that you can plug it into the layout framework just as you would any other layout node. This approach is much easier than trying to use CSplitterWnd with a window that is not a CView. The SECLayoutNodeSplitter works with any layout node. You can even embed layouts inside a splitter pane. It also works inside a dialog, or a controlbar. Additionally, it supports full drag mode for instant dragging without a tracker, two-dimensional dragging for both three and four pane splitters, and virtual callbacks for further customization.