Magnum::Ui::LayoutLayer class new in Git master

Layout properties layer.

Allows attaching layout properties such as min/max sizes, aspect ratios, paddings and margins to particular nodes.

Setting up a layout layer instance

If you create a UserInterfaceGL instance with a theme and don't exclude ThemeFeature::LayoutLayer, an implicit instance, configured for use with builtin widgets, is already provided and available through UserInterface::layoutLayer().

Otherwise, the layout layer is constructed from a fresh AbstractUserInterface::createLayer() handle along with a count of how many distinct layout styles you intend to use — which is for example the number 3u in the following snippet. Afterwards, pass the newly created layer to UserInterface::setLayoutLayerInstance():

ui.setLayoutLayerInstance(
    Containers::pointer<Ui::LayoutLayer>(ui.createLayer(), 3u));

In comparison, if you want to set up a custom layout layer that's independent of the one exposed through UserInterface::layoutLayer(), pass the newly created instance to AbstractUserInterface::setLayerInstance() instead:

Ui::LayoutLayer& layer = ui.setLayerInstance(
    Containers::pointer<Ui::LayoutLayer>(ui.createLayer(), 3u));

Afterwards, in order to be able to actually use the layer, a style has to be set with setStyle(). As an example, let's create a style with layout properties for a menu bar — the menu bar itself, icons and (textual) menu entries inside, i.e. the 3 styles we specified when constructing the layer above. For the purpose of this example let's specify just min sizes, paddings and margins for the style, and leave max sizes and aspect ratios at their defaults. See the setStyle() function documentation for details about each property. Similarly to how styles are commonly used with e.g. BaseLayer, we'll have an enum with them:

enum class LayoutStyle {
    Menu,
    MenuIcon,
    MenuEntry,
    Count
};

struct Style {
    Vector2 minSize;
    Vector4 padding;
    Vector4 margin;
} styles[Int(LayoutStyle::Count)];
/* What isn't set stays at the default (zero) value */
styles[Int(LayoutStyle::Menu)].padding = {25.0f, 10.0f, 25.0f, 10.0f};
styles[Int(LayoutStyle::MenuEntry)].minSize = {100.0f, 40.0f};
styles[Int(LayoutStyle::MenuIcon)].minSize = {60.0f, 60.0f};
styles[Int(LayoutStyle::MenuEntry)].margin = Vector4{5.0f};
styles[Int(LayoutStyle::MenuIcon)].margin = Vector4{5.0f};

layer.setStyle(
    Containers::stridedArrayView(styles).slice(&Style::minSize),
    {}, /* max sizes not specified */
    {}, /* aspect ratios not specified */
    Containers::stridedArrayView(styles).slice(&Style::padding),
    Containers::stridedArrayView(styles).slice(&Style::margin));

With this, assuming AbstractUserInterface::draw() is called in an appropriate place, the layer is ready to be used to feed the layout properties to a layouter instance.

Attaching layout properties to nodes

A node is equipped with layout properties by calling create() with desired style index and the corresponding NodeHandle. Continuing with the menu bar example from above, the following creates a menu with two icons and three entries inside:

Ui::NodeHandle menuNode = ui.createNode({}, {});
layer.create(LayoutStyle::Menu, menuNode);

Ui::NodeHandle iconNode1 = ui.createNode(menuNode, {}, {});
layer.create(LayoutStyle::MenuIcon, iconNode1);

Ui::NodeHandle entryNode1 = ui.createNode(menuNode, {}, {});
layer.create(LayoutStyle::MenuEntry, entryNode1);

/* An entry that's deliberately wider */
Ui::NodeHandle entryNode2 = ui.createNode(menuNode, {}, {150, 0});
layer.create(LayoutStyle::MenuEntry, entryNode2);

Ui::NodeHandle iconNode2 = ui.createNode(menuNode, {}, {});
layer.create(LayoutStyle::MenuIcon, iconNode2);

Ui::NodeHandle entryNode3 = ui.createNode(menuNode, {}, {});
layer.create(LayoutStyle::MenuEntry, entryNode3);

The layout properties however don't do anything on their own, they only get used when actual layouts are assigned to these nodes. With a SnapLayouter set up, we can turn the menu bar into a simple row layout:

Ui::SnapLayouter& layouter = ;

Ui::LayoutHandle menu = layouter.add(menuNode);
layouter.setChildSnap(menu, Ui::Snap::Right);

for(Ui::NodeHandle i: {iconNode1, entryNode1, entryNode2, iconNode2, entryNode3})
    layouter.add(i);

Afterwards, when visualizing node placement, for example with Ui::DebugLayer node highlight, the layout looks like this — the nodes didn't have explicitly specified sizes except for one, yet the icons have appropriate larger square sizes, menu entries are wider, mutually separated from each other with specified paddings, and the menu bar itself has a bigger padding on the sides and smaller on the top and bottom:

Image

Unlike layouts, multiple layout properties can be assigned to a particular node, and they'll be combined together — in particular, picking the max of specified min sizes, paddings and margins. Note also that not all properties may be respected by every layouter and each layouter may implement different logic for conflict resolution. Consult particular layouter documentation for more information. For example, in case of the GenericLayouter, the layout properties are exposed by the API, but it's up to the concrete callback implementation to actually use them.

Debug layer integration

When using DebugLayer node inspect and DebugLayerSource::NodeDataDetails is enabled, passing this layer to DebugLayer::setLayerName() will make it list style assignments of all data. For example:

Node {0x18, 0x1}
  Data {0x0, 0x1} from layer {0x9, 0x1} Layout with style 9

By default only style IDs are shown, but the DebugIntegration constructor accepts an optional function to map them from an UnsignedInt to a Containers::StringView. Assuming a LayoutStyle enum is used by given layer, the function could look like this, resulting in the output below:

debugLayer.setLayerName(layoutLayer, "Layout", [](UnsignedInt style) {
    using namespace Containers::Literals;

    switch(LayoutStyle(style)) {
        case LayoutStyle::Button: return "Button"_s;
        case LayoutStyle::Label: return "Label"_s;
        case LayoutStyle::Panel: return "Panel"_s;
        
    }

    return ""_s;
});
Node {0x18, 0x1}
  Data {0x0, 0x1} from layer {0x9, 0x1} Layout with style Panel (9)

Base classes

class AbstractLayer new in Git master
Base for data layers.

Public types

class DebugIntegration
Debug layer integration.

Constructors, destructors, conversion operators

LayoutLayer(LayerHandle handle, UnsignedInt styleCount) explicit
Constructor.
LayoutLayer(const LayoutLayer&) deleted
Copying is not allowed.
LayoutLayer(LayoutLayer&&) noexcept
Move constructor.

Public functions

auto operator=(const LayoutLayer&) -> LayoutLayer& deleted
Copying is not allowed.
auto operator=(LayoutLayer&&) -> LayoutLayer& noexcept
Move assignment.
auto styleCount() const -> UnsignedInt
Style count.
auto styleMinSizes() const -> Containers::StridedArrayView1D<const Vector2>
Minimal node size for each style.
auto styleMaxSizes() const -> Containers::StridedArrayView1D<const Vector2>
Maximal node size for each style.
auto styleAspectRatios() const -> Containers::StridedArrayView1D<const Float>
Node aspect ratio for each style.
auto stylePaddings() const -> Containers::StridedArrayView1D<const Vector4>
Node padding for each style.
auto styleMargins() const -> Containers::StridedArrayView1D<const Vector4>
Node margin for each style.
void setStyle(const Containers::StridedArrayView1D<const Vector2>& minSizes, const Containers::StridedArrayView1D<const Vector2>& maxSizes, const Containers::StridedArrayView1D<const Float>& aspectRatios, const Containers::StridedArrayView1D<const Vector4>& paddings, const Containers::StridedArrayView1D<const Vector4>& margins)
Set style data.
void setStyle(std::initializer_list<Vector2> minSizes, std::initializer_list<Vector2> maxSizes, std::initializer_list<Float> aspectRatios, std::initializer_list<Vector4> paddings, std::initializer_list<Vector4> margins)
auto create(UnsignedInt style, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a data with given style index.
template<class StyleIndex>
auto create(StyleIndex style, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a data with given style index in a concrete enum type.
void remove(DataHandle handle)
Remove a data.
void remove(LayerDataHandle handle)
Remove a data assuming it belongs to this layer.
auto style(DataHandle handle) const -> UnsignedInt
Type-erased data style index.
template<class StyleIndex>
auto style(DataHandle handle) const -> StyleIndex
Data style index in a concrete enum type.
auto style(LayerDataHandle handle) const -> UnsignedInt
Type-erased data style index assuming it belongs to this layer.
template<class StyleIndex>
auto style(LayerDataHandle handle) const -> StyleIndex
Data style index in a concrete enum type assuming it belongs to this layer.
void setStyle(DataHandle handle, UnsignedInt style)
Set data style index.
template<class StyleIndex>
void setStyle(DataHandle handle, StyleIndex style)
Set data style index in a concrete enum type.
void setStyle(LayerDataHandle handle, UnsignedInt style)
Set data style index assuming it belongs to this layer.
template<class StyleIndex>
void setStyle(LayerDataHandle handle, StyleIndex style)
Set data style index in a concrete enum type assuming it belongs to this layer.

Function documentation

Magnum::Ui::LayoutLayer::LayoutLayer(LayerHandle handle, UnsignedInt styleCount) explicit

Constructor.

Parameters
handle Layer handle returned from AbstractUserInterface::createLayer()
styleCount Number of distinct styles to use

Expects that styleCount is not 0. In order to update the UI containing this layer it's expected that setStyle() was called.

Magnum::Ui::LayoutLayer::LayoutLayer(LayoutLayer&&) noexcept

Move constructor.

Performs a destructive move, i.e. the original object isn't usable afterwards anymore.

Containers::StridedArrayView1D<const Vector2> Magnum::Ui::LayoutLayer::styleMinSizes() const

Minimal node size for each style.

Expects that setStyle() was called.

Containers::StridedArrayView1D<const Vector2> Magnum::Ui::LayoutLayer::styleMaxSizes() const

Maximal node size for each style.

Expects that setStyle() was called.

Containers::StridedArrayView1D<const Float> Magnum::Ui::LayoutLayer::styleAspectRatios() const

Node aspect ratio for each style.

Expects that setStyle() was called.

Containers::StridedArrayView1D<const Vector4> Magnum::Ui::LayoutLayer::stylePaddings() const

Node padding for each style.

Expects that setStyle() was called.

Containers::StridedArrayView1D<const Vector4> Magnum::Ui::LayoutLayer::styleMargins() const

Node margin for each style.

Expects that setStyle() was called.

void Magnum::Ui::LayoutLayer::setStyle(const Containers::StridedArrayView1D<const Vector2>& minSizes, const Containers::StridedArrayView1D<const Vector2>& maxSizes, const Containers::StridedArrayView1D<const Float>& aspectRatios, const Containers::StridedArrayView1D<const Vector4>& paddings, const Containers::StridedArrayView1D<const Vector4>& margins)

Set style data.

Parameters
minSizes Minimal node size for each style, or an empty view for no style-specific minimal node sizes
maxSizes Maximal node size for each style, or an empty view for no style-specific maximal node sizes
aspectRatios Node aspect ratio for each style, or an empty view for no style-specific node aspect ratios
paddings Padding inside a node for child node placement for each style, in order left, top, right, bottom, or an empty view for no style-specific node padding
margins Margin outside nodes for placement within parents and next to neighbor nodes for each style, in order left, top, right, bottom, or an empty view for no style-specific node margin

All views are expected to either have the same size as styleCount(), or be empty, in which case a default value will be used. If all views are empty, default values will be set for all properties. For a data with a particular style attached to a particular node, the properties are interpreted as follows:

  • A max of the value coming from minSizes for given style and min sizes reported by any other data attached to the same node is used as a minimal node size. Specifying a min size of 0.0f in either axis doesn't affect the final node min size in given axis in any way. The defaults if no minSizes are specified are all 0.0f.
  • A min of the value coming from maxSizes for given style and min sizes reported by any other data attached to the same node is used as a maximal node size. Specifying a max size of Constants::inf() in either axis doesn't affect the final node max size in given axis in any way. The defaults if no maxSizes are specified are all Constants::inf().
  • A first non-zero value coming from aspectRatios for given style and aspect ratios reported by any other data attached to the same node is used as a node aspect ratio. Specifying an aspect ratio of 0.0f doesn't affect the final node aspect ratio in any way. The defaults if no aspectRatios are specified are all 0.0f.
  • A max of the value coming from paddings for given style and paddings reported by any other data attached to the same node is used as a node padding. Specifying a padding of 0.0f at given edge doesn't affect the final node padding at given edge in any way. The defaults if no paddings are specified are all 0.0f.
  • A max of the value coming from margins for given style and margins reported by any other data attached to the same node is used as a node margin. Specifying a margin of 0.0f at given edge doesn't affect the final node margin at given edge in any way. The defaults if no margins are specified are all 0.0f.

Calling this function causes LayerState::NeedsLayoutUpdate to be set.

void Magnum::Ui::LayoutLayer::setStyle(std::initializer_list<Vector2> minSizes, std::initializer_list<Vector2> maxSizes, std::initializer_list<Float> aspectRatios, std::initializer_list<Vector4> paddings, std::initializer_list<Vector4> margins)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

DataHandle Magnum::Ui::LayoutLayer::create(UnsignedInt style, NodeHandle node = NodeHandle::Null)

Create a data with given style index.

Parameters
style Style index
node Node to attach to
Returns New data handle

Expects that style is less than styleCount(). It is allowed to call this function before any styles are set with setStyle(). Once a style is set, layout properties are driven from given style. There is currently no way to customize the layout properties on a per-data basis.

Delegates to AbstractLayer::create(), see its documentation for detailed description of all constraints.

template<class StyleIndex>
DataHandle Magnum::Ui::LayoutLayer::create(StyleIndex style, NodeHandle node = NodeHandle::Null)

Create a data with given style index in a concrete enum type.

Casts style to UnsignedInt and delegates to create(UnsignedInt, NodeHandle).

void Magnum::Ui::LayoutLayer::remove(DataHandle handle)

Remove a data.

Delegates to AbstractLayer::remove(DataHandle), see its documentation for detailed description of all constraints.

void Magnum::Ui::LayoutLayer::remove(LayerDataHandle handle)

Remove a data assuming it belongs to this layer.

Compared to remove(DataHandle) delegates to AbstractLayer::remove(LayerDataHandle) instead.

UnsignedInt Magnum::Ui::LayoutLayer::style(DataHandle handle) const

Type-erased data style index.

Expects that handle is valid. The index is guaranteed to be less than styleCount().

template<class StyleIndex>
StyleIndex Magnum::Ui::LayoutLayer::style(DataHandle handle) const

Data style index in a concrete enum type.

Expects that handle is valid. The index is guaranteed to be less than styleCount().

UnsignedInt Magnum::Ui::LayoutLayer::style(LayerDataHandle handle) const

Type-erased data style index assuming it belongs to this layer.

Like style(DataHandle) const but without checking that handle indeed belongs to this layer. See its documentation for more information.

template<class StyleIndex>
StyleIndex Magnum::Ui::LayoutLayer::style(LayerDataHandle handle) const

Data style index in a concrete enum type assuming it belongs to this layer.

Like style(DataHandle) const but without checking that handle indeed belongs to this layer. See its documentation for more information.

void Magnum::Ui::LayoutLayer::setStyle(DataHandle handle, UnsignedInt style)

Set data style index.

Expects that handle is valid and style is less than styleCount().

If handle is attached to a non-null node, calling this function causes LayerState::NeedsLayoutUpdate to be set.

template<class StyleIndex>
void Magnum::Ui::LayoutLayer::setStyle(DataHandle handle, StyleIndex style)

Set data style index in a concrete enum type.

Casts style to UnsignedInt and delegates to setStyle(DataHandle, UnsignedInt).

void Magnum::Ui::LayoutLayer::setStyle(LayerDataHandle handle, UnsignedInt style)

Set data style index assuming it belongs to this layer.

Like setStyle(DataHandle, UnsignedInt) but without checking that handle indeed belongs to this layer. See its documentation for more information.

template<class StyleIndex>
void Magnum::Ui::LayoutLayer::setStyle(LayerDataHandle handle, StyleIndex style)

Set data style index in a concrete enum type assuming it belongs to this layer.

Casts style to UnsignedInt and delegates to setStyle(LayerDataHandle, UnsignedInt).