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

Base layer.

Draws quads with a color gradient, variable rounded corners and outline, optionally with texturing and background blur.

Setting up a base layer instance

If you create a UserInterfaceGL instance with a style and don't exclude StyleFeature::BaseLayer, an implicit instance of the BaseLayerGL subclass, configured for use with builtin widgets, is already provided and available through UserInterface::baseLayer().

For a custom instance, you first need to instantiate BaseLayer::Shared, which contains GPU shaders and style definitions. It takes a BaseLayer::Shared::Configuration instance, where at the very least you have to specify how many distinct visual styles you intend to use — which is for example the number 3 in the following snippet:

Ui::BaseLayerGL::Shared baseLayerShared{
    Ui::BaseLayer::Shared::Configuration{3}
};

The shared instance, in this case a concrete BaseLayerGL::Shared subclass for the OpenGL implementation of this layer, is then passed to the layer constructor alongside a fresh AbstractUserInterface::createLayer() handle, and is expected to stay alive for the whole layer lifetime. The shared instance can be used by multiple layers, for example if the application wants to have a dedicated layer for very dynamic UI content, or when it needs draw ordering that would be impossible with just one layer instance. If you want to supply an instance for the implicit UserInterface::baseLayer(), pass it to UserInterfaceGL::setBaseLayerInstance():

ui.setBaseLayerInstance(
    Containers::pointer<Ui::BaseLayerGL>(ui.createLayer(), baseLayerShared));

Otherwise, if you want to set up a custom base layer that's independent of the one exposed through UserInterface::baseLayer(), possibly for completely custom widgets, pass the newly created instance to AbstractUserInterface::setLayerInstance() instead:

Ui::BaseLayer& baseLayer = ui.setLayerInstance(
    Containers::pointer<Ui::BaseLayerGL>(ui.createLayer(), baseLayerShared));

Afterwards, in order to be able to draw the layer, a style has to be set with BaseLayer::Shared::setStyle(). At the very least you're expected to pass a BaseLayerCommonStyleUniform containing properties common to all styles, and an array of BaseLayerStyleUniform matching the style count set in the BaseLayer::Shared::Configuration. Default-constructed instances will result in white quads with no outline or rounded corners, you can then use method chaining to update only the properties you're interested in. So, for example, with style 0 being the default, style 1 being a blue quad and style 2 transparent with a white outline:

baseLayerShared.setStyle(Ui::BaseLayerCommonStyleUniform{}, {
    Ui::BaseLayerStyleUniform{}, /* Style 0, default */
    Ui::BaseLayerStyleUniform{}  /* Style 1 */
        .setColor(0x2f83cc_rgbf),
    Ui::BaseLayerStyleUniform{}  /* Style 2 */
        .setColor(0x00000000_rgbaf)
        .setOutlineColor(0xdcdcdc_rgbf)
        .setOutlineWidth(2.0f)
}, {});

With this, assuming AbstractUserInterface::draw() is called in an appropriate place, the layer is ready to use. All style options are described in detail further below.

Creating quads

A quad is created by calling create() with desired style index and a NodeHandle the data should be attached to. In this case it picks the style 1, which colors the quad blue:

Ui::NodeHandle blueBox = ui.createNode();

baseLayer.create(1, blueBox);

As with all other data, they're implicitly tied to lifetime of the node they're attached to. You can remember the Ui::DataHandle returned by create() to modify the data later, attach() to a different node or remove() it.

Using an enum for style indexing

While the style indices are internally just a contiguous sequence of integers, an enum is a more convenient way to index them. The create() as well as setStyle() can also accept an enum for the style index, and style() has a templated overload that you can use to retrieve the style index in the enum type again. The above style setup and use could then look for example like this:

enum class BaseLayerStyle {
    Default,
    Blue,
    Outline,
    Count
};

Ui::BaseLayerStyleUniform uniforms[Int(BaseLayerStyle::Count)];
uniforms[Int(BaseLayerStyle::Blue)]
    .setColor(0x2f83cc_rgbf);
uniforms[Int(BaseLayerStyle::Outline)]
    .setColor(0x00000000_rgbaf)
    .setOutlineColor(0xdcdcdc_rgbf)
    .setOutlineWidth(2.0f);
baseLayerShared.setStyle(Ui::BaseLayerCommonStyleUniform{}, uniforms, {});



baseLayer.create(BaseLayerStyle::Blue, blueBox);

Style options

Base color and gradient

Image

Apart from the single color shown above, there's a BaseLayerStyleUniform::setColor(const Color4&, const Color4&) overload that allows you to supply two colors for a gradient. Right now, the gradient can only be vertical.

baseLayerShared.setStyle(, {
    Ui::BaseLayerStyleUniform{} /* 0 */
        .setColor(0x2f83cc_rgbf),
    Ui::BaseLayerStyleUniform{} /* 1 */
        .setColor(0xdcdcdc_rgbf, 0xa5c9ea_rgbf)
}, {});



Ui::NodeHandle blueBox = ;
baseLayer.create(0, blueBox);

Ui::NodeHandle gradient = ;
baseLayer.create(1, gradient);

The style-supplied color is additionally multiplied by a data-specific color set with setColor(), which is shown in the third square above. Main use case is various color swatches where it would be impractical to have to add each custom color to the style data. Finally, the color is also multiplied by a per-node opacity coming from AbstractUserInterface::setNodeOpacity(), as shown in the fourth square above, which can be used for various fade-in / fade-out effects.

Ui::NodeHandle coloredGradient = ;
Ui::DataHandle coloredGradientData = baseLayer.create(1, coloredGradient);
baseLayer.setColor(coloredGradientData, 0x3bd267_rgbf);

Ui::NodeHandle fadedGradient = ;
ui.setNodeOpacity(fadedGradient, 0.25f);
baseLayer.create(1, fadedGradient);

Rounded corners

Image

With BaseLayerStyleUniform::setCornerRadius() the quads can have rounded corners, and the radius can be also different for each corner. The edges are aliased by default, use BaseLayerCommonStyleUniform::setSmoothness() to smoothen them out. The smoothness radius is in actual framebuffer pixels and not UI units in order to ensure crisp look everywhere without having to specifically deal with HiDPI displays, and it's a common setting for all as it's assumed that the styles will all want to use the same value.

baseLayerShared.setStyle(
    Ui::BaseLayerCommonStyleUniform{}
        .setSmoothness(1.0f),
    {
        Ui::BaseLayerStyleUniform{} /* 0 */
            .setCornerRadius(8.0f)
            .setColor(0xcd3431_rgbf),
        Ui::BaseLayerStyleUniform{} /* 1 */
            /* Top left, bottom left, top right, bottom right */
            .setCornerRadius({8.0f, 1.0f, 8.0f, 1.0f})
            .setColor(0xdcdcdc_rgbf)
    }, {});



Ui::NodeHandle heading = ui.createNode();
Ui::NodeHandle close = ui.createNode(heading, );
baseLayer.create(1, heading);
baseLayer.create(0, close);

Finally, BaseLayerCommonStyleUniform::setSmoothness() supports different inner and outer smoothness to achieve certain kind of effects, but again it's a common setting for all styles.

Outline width and color

Image

BaseLayerStyleUniform::setOutlineWidth() sets width of the outline. Similarly to corner radius, the width can be different for each edge, such as for the switch-like shape above. When outline is combined with rounded corners, BaseLayerStyleUniform::setInnerOutlineCornerRadius() specifies the inner corner radius. In many cases it'll be set to outer radius with outline width subtracted, but different values can be used to achieve various effects.

baseLayerShared.setStyle(, {
    Ui::BaseLayerStyleUniform{} /* 0 */
        .setColor(0x00000000_rgbaf)
        .setOutlineColor(0xdcdcdc_rgbf)
        .setOutlineWidth(1.0f),
    Ui::BaseLayerStyleUniform{} /* 1 */
        .setColor(0xa5c9ea_rgbf)
        .setOutlineColor(0x405363_rgbf)
        /* Left, top, right, bottom */
        .setOutlineWidth({1.0f, 1.0f, 16.0f, 1.0f})
        .setCornerRadius(12.0f)
        .setInnerOutlineCornerRadius(11.0f),
    Ui::BaseLayerStyleUniform{} /* 2 */
        .setColor(0x2a703f_rgbf)
        .setOutlineColor(0x3bd267_rgbf)
        .setOutlineWidth(2.0f)
        .setCornerRadius(2.0f)
        .setInnerOutlineCornerRadius(10.0f)
}, {});



Ui::NodeHandle frame = ;
Ui::NodeHandle toggle = ;
Ui::NodeHandle radio = ;
baseLayer.create(0, frame);
baseLayer.create(1, toggle);
baseLayer.create(2, radio);
Image

The style-supplied outline width is added together with per-data outline width coming from setOutlineWidth(). The intended usage scenario is implementing simple progress bars and scrollbars. For the image above, the style has the outline fully defined except for its width, which is then supplied dynamically based on the actual percentage it should visualize.

baseLayerShared.setStyle(, {
    Ui::BaseLayerStyleUniform{} /* 0 */
        .setColor(0x3bd267_rgbf)
        .setOutlineColor(0x405363_rgbf)
        .setCornerRadius(6.0f)
        .setInnerOutlineCornerRadius(6.0f)
}, {});



Ui::NodeHandle progress = ;
Ui::DataHandle progressData = baseLayer.create(0, progress);
baseLayer.setOutlineWidth(progressData, /* Left, top, right, bottom */
    {0.0f, 0.0f, ui.nodeSize(progress).x()*(100.0f - percentage)/100.0f, 0.0f});

Padding inside the node

The last argument to BaseLayer::Shared::setStyle() is an optional list of per-style padding values, which specify a padding between the rendered quad and edges of the node containing it. Visually, the same can be achieved by attaching the data to an appropriately shrunk child node instead, but doing so may interfere with event handling behavior if both nodes are meant to behave as a single active element.

Image

On the left above is a button-like shape with a detached outline, achieved by placing two quads inside the same node, with one being just an outline and the other having a padding. The draw order isn't guaranteed in case of multiple data attached to the same node, but as the shapes don't overlap, it doesn't matter.

baseLayerShared.setStyle(, {
    Ui::BaseLayerStyleUniform{} /* 0 */
        .setColor(0x00000000_rgbaf)
        .setOutlineColor(0xa5c9ea_rgbf)
        .setOutlineWidth(1.0f)
        .setCornerRadius(5.0f)
        .setInnerOutlineCornerRadius(4.0f),
    Ui::BaseLayerStyleUniform{} /* 1 */
        .setColor(0xa5c9ea_rgbf)
        .setCornerRadius(2.0f)
}, {
    {},                         /* 0 */
    Vector4{3.0f}               /* 1 */
});



Ui::NodeHandle button = ;
baseLayer.create(0, button);
baseLayer.create(1, button);

There's also setPadding() for additional per-data padding, which is useful for example when aligning icons next to variable-width text. Or, as shown with the slider above on the right, for additional styling flexibility. In order to ensure correct draw order, the green bar is put into a child node, but both have the same size to have the whole area react the same way to taps or clicks.

baseLayerShared.setStyle(, {
    Ui::BaseLayerStyleUniform{} /* 0 */
        .setColor(0x405363_rgbf*0.9f, 0x405363_rgbf*1.1f)
        .setCornerRadius(3.0f),
    Ui::BaseLayerStyleUniform{} /* 1 */
        .setColor(0x3bd267_rgbf*1.1f, 0x3bd267_rgbf*0.9f)
        .setCornerRadius(6.0f)
}, {
    Vector4{3.0f},              /* 0 */
    {},                         /* 1 */
});



Ui::NodeHandle slider = ui.createNode();
Ui::NodeHandle bar = ui.createNode(slider, {}, ui.nodeSize(slider));
baseLayer.create(0, slider);
baseLayer.create(1, bar);

Textured drawing

Image

If BaseLayerSharedFlag::Textured is enabled in BaseLayer::Shared::Configuration, each quad additionally multiplies its color with a texture. In case of a BaseLayerGL it's a GL::Texture2DArray supplied via setTexture(). By default the whole first slice of the texture array is used, however it's assumed that a texture atlas is used from which particular data use sub-rectangles defined with setTextureCoordinates(). Texturing can be combined with rounded corners and all other features. The texture coordinates include the outline, if it's present, but the outline itself isn't textured.

Ui::BaseLayerGL::Shared texturedLayerShared{
    Ui::BaseLayerGL::Shared::Configuration{}
        .addFlags(Ui::BaseLayerSharedFlag::Textured)
};
texturedLayerShared.setStyle(, {
    Ui::BaseLayerStyleUniform{}, /* 0 */
    Ui::BaseLayerStyleUniform{}  /* 1 */
        .setOutlineWidth(2.0f)
        .setOutlineColor(0xdcdcdcff_rgbaf*0.25f),
    Ui::BaseLayerStyleUniform{}  /* 2 */
        .setCornerRadius(12.0f)
}, {});

Ui::BaseLayerGL& texturedLayer = ui.setLayerInstance(
    Containers::pointer<Ui::BaseLayerGL>(ui.createLayer(), texturedLayerShared));
GL::Texture2DArray texture;

texturedLayer.setTexture(texture);



Ui::NodeHandle image = ;
Ui::NodeHandle outlined = ;
Ui::NodeHandle avatar = ;
texturedLayer.create(0, image);
texturedLayer.create(1, outlined);
Ui::DataHandle avatarData = texturedLayer.create(2, avatar);
texturedLayer.setTextureCoordinates(avatarData, {0.4f, 0.0f, 0.0f}, {0.25f, 0.5f});

You can use TextureTools::AtlasLandfill or TextureTools::atlasArrayPowerOfTwo() to pack multiple images into a single atlas, the former is usable for incremental packing as well. The supplied texture is expected to be alive for the whole layer lifetime, alternatively you can use BaseLayerGL::setTexture(GL::Texture2DArray&&) to move its ownership to the layer instance. Currently only a single texture can be used with the layer, if you need to draw from multiple textures, create additional layer instances.

Background blur

Image Default look of semi-transparent quads
Image With background blur enabled
Image Background blur with alpha of 0.75f

With BaseLayerSharedFlag::BackgroundBlur, and RendererGL::Flag::CompositingFramebuffer enabled for the renderer, semi-transparent quads will be drawn with their background blurred. Blur strength can be controlled with BaseLayer::Shared::Configuration::setBackgroundBlurRadius() and setBackgroundBlurPassCount(), BaseLayerCommonStyleUniform::setBackgroundBlurAlpha() can be used to achieve a frosted-glass-like effect. This effect relies on the UI being able to read back the framebuffer it draws to, see the RendererGL documentation for a detailed example of how to set up the compositing framebuffer in the application.

ui.setRendererInstance(Containers::pointer<Ui::RendererGL>(
    Ui::RendererGL::Flag::CompositingFramebuffer));

Ui::BaseLayerGL::Shared blurLayerShared{
    Ui::BaseLayerGL::Shared::Configuration{}
        .addFlags(Ui::BaseLayerSharedFlag::BackgroundBlur)
        .setBackgroundBlurRadius()
};
blurLayerShared.setStyle(, {
    Ui::BaseLayerStyleUniform{}  /* 0 */
        .setCornerRadius(12.0f)
        .setColor(0xffffffff_rgbaf*0.667f)
}, {});
Ui::BaseLayer& blurLayer = ui.setLayerInstance(
    Containers::pointer<Ui::BaseLayerGL>(ui.createLayer(), blurLayerShared));



Ui::NodeHandle background = ;
blurLayer.create(0, background);

As the effect is potentially expensive, only the framebuffer areas that actually are covered by quads get blurred. The effect is stackable, meaning that blurred quads in each top-level node hierarchy will blur contents of all top-level hierarchies underneath. To avoid performance issues, it's thus recommended to switch to non-blurred layers when stacking reaches a certain level.

Image Default blurred texturing behavior

Finally, when texturing is enabled together with background blur, the BaseLayerSharedFlag::TextureMask flag may come handy. By default, without blur, transparent texture areas are see-through so this flag doesn't make any visual difference, but with blur enabled, the flag excludes the transparent areas from being blurred, allowing you to apply any custom shape to the blurred area. In this case, the masking — but not the texture color — is applied to the outline as well.

Dynamic styles

The BaseLayer::Shared::setStyle() API is meant to be used to supply style data for all known styles at once, with the assumption that the styles are likely supplied from some constant memory and will be updated only in rare scenarios afterwards, such as when switching application themes.

If a particular style needs to be modified often and it's not achievable with setColor(), setOutlineWidth() or setPadding() on the data itself, a dynamic style can be used instead. Dynamic styles have to be explicitly requested with BaseLayer::Shared::Configuration::setDynamicStyleCount(), after which the style uniform buffer isn't shared among all layers using given BaseLayer::Shared instance, but is local to each layer and each layer has its own set of dynamic styles. Dynamic styles occupy indices from BaseLayer::Shared::styleCount() upwards and their properties are specified via setDynamicStyle().

Ui::BaseLayerGL::Shared baseLayerShared{
    Ui::BaseLayerGL::Shared::Configuration{}
        .setDynamicStyleCount(10)
};
Ui::BaseLayerGL& baseLayer = ui.setLayerInstance(
    Containers::pointer<Ui::BaseLayerGL>(ui.createLayer(), baseLayerShared));



UnsignedInt dynamicStyleId = ; /* anything less than the dynamic style count */
baseLayer.setDynamicStyle(dynamicStyleId, );

Ui::NodeHandle node = ;
baseLayer.create(baseLayer.shared().styleCount() + dynamicStyleId, node);

The main use case for dynamic styles is animations, for example various fade-in and fade-out transitions based on input events and application state changes. See the BaseLayerStyleAnimator class for a high-level style animator working with the base layer. Note that if you intend to directly use dynamic styles along with the animator, you should use allocateDynamicStyle() and recycleDynamicStyle() to prevent the animator from stealing dynamic styles you use elsewhere:

/* Attempt to allocate a dynamic style ID, if available */
Containers::Optional<UnsignedInt> dynamicStyleId = baseLayer.allocateDynamicStyle();
if(!dynamicStyleId) {
    
}

/* Populate it, use */
baseLayer.setDynamicStyle(*dynamicStyleId, );


/* Once not used anymore, recycle the ID again */
baseLayer.recycleDynamicStyle(*dynamicStyleId);

Style transition based on input events

With interactive UI elements such as buttons or inputs you'll likely have different styles for an inactive and active state, possibly handling hover and focus as well. While the style can be switched using setStyle() for example in an EventLayer::onPress() handler, this quickly becomes very tedious and repetitive. Instead, style transition can be implemented using BaseLayer::Shared::setStyleTransition().

It accepts a set of functions that get called with a style index when a node is hovered, pressed, released etc., and should return a style index that matches the new state. Assuming there's a button with various states, a label that doesn't visually react to events, and the style doesn't deal with focused or disabled state, implementing automatic transitions could look like this:

enum BaseLayerStyle {
    Button,
    ButtonHovered,
    ButtonPressed,
    ButtonPressedHovered,
    Label
};
BaseLayerStyle toInactiveOut(BaseLayerStyle style) {
    switch(style) {
        case BaseLayerStyle::ButtonHovered:
        case BaseLayerStyle::ButtonPressed:
        case BaseLayerStyle::ButtonPressedHovered:
            return BaseLayerStyle::Button;
        default:
            return style;
    }
}
BaseLayerStyle toInactiveOver(BaseLayerStyle style) {
    switch(style) {
        case BaseLayerStyle::Button:
        case BaseLayerStyle::ButtonPressed:
        case BaseLayerStyle::ButtonPressedHovered:
            return BaseLayerStyle::ButtonHovered;
        default:
            return style;
    }
}
BaseLayerStyle toPressedOut(BaseLayerStyle style) {
    switch(style) {
        case BaseLayerStyle::Button:
        case BaseLayerStyle::ButtonHovered:
        case BaseLayerStyle::ButtonPressedHovered:
            return BaseLayerStyle::ButtonPressed;
        default:
            return style;
    }
}
BaseLayerStyle toPressedOver(BaseLayerStyle style) {
    switch(style) {
        case BaseLayerStyle::Button:
        case BaseLayerStyle::ButtonHovered:
        case BaseLayerStyle::ButtonPressed:
            return BaseLayerStyle::ButtonPressedHovered;
        default:
            return style;
    }
}



baseLayerShared.setStyleTransition<BaseLayerStyle,
    toInactiveOut,
    toInactiveOver,
    nullptr,
    nullptr,
    toPressedOut,
    toPressedOver,
    nullptr>();

As in other APIs that deal with styles, the functions can operate either on an enum, or on a plain UnsignedInt. If nullptr is passed for any function, given transition is assumed to an identity, i.e. as if the input was returned unchanged. The focused transition can happen only on nodes that are NodeFlag::Focusable, disabled transition then on node hierarchies that have NodeFlag::Disabled set.

Looking at the above snippet, you'll likely notice that there's a lot of repetition. The BaseLayer::Shared::setStyleTransition() API is deliberately minimalistic and you're encouraged to implement the transitions in any way that makes sense for given use case. Instead of a switch they can be for example baked into a compile-time lookup table. Or there can be just a single transition function for which only a part of the result gets used each time:

struct Transition {
    BaseLayerStyle inactiveOut;
    BaseLayerStyle inactiveOver;
    BaseLayerStyle pressedOut;
    BaseLayerStyle pressedOver;
};
Transition transition(BaseLayerStyle style) {
    /* In C++20 you can further simplify with `using enum BaseLayerStyle` */
    switch(style) {
        case BaseLayerStyle::Button:
        case BaseLayerStyle::ButtonHovered:
        case BaseLayerStyle::ButtonPressed:
        case BaseLayerStyle::ButtonPressedHovered:
            return {BaseLayerStyle::Button,
                    BaseLayerStyle::ButtonHovered,
                    BaseLayerStyle::ButtonPressed,
                    BaseLayerStyle::ButtonPressedHovered};
        default:
            return {style, style, style, style};
    }
}
template<BaseLayerStyle Transition::*member> BaseLayerStyle to(BaseLayerStyle style) {
    return transition(style).*member;
}



baseLayerShared.setStyleTransition<BaseLayerStyle,
    to<&Transition::inactiveOut>,
    to<&Transition::inactiveOver>,
    nullptr,
    nullptr,
    to<&Transition::pressedOut>,
    to<&Transition::pressedOver>,
    nullptr>();

Options affecting performance

Configuring shader complexity

The BaseLayerSharedFlag::NoOutline and NoRoundedCorners options disable rendering of outline and rounded corners, respectively, making the layer behave as if those features were not specified in any style nor supplied via setOutlineWidth(). The likely use case for these flags is for example a texture / icon layer which don't make use of any outlines or rounded corners, and depending on the platform this can significantly reduce shader complexity.

By default, each quad is literally two triangles, and positioning of the outline and rounded corners is done purely in shader code. While that's fine on common hardware, certain low-power GPUs may struggle with fragment shader complexity. By enabling BaseLayerSharedFlag::SubdividedQuads the drawing is split up into 9 quads as shown on the right, offloading a large part of the work to the vertex shader instead. Apart from that, the visual output is exactly the same with and without this flag enabled. A downside is however that a lot more data needs to be uploaded to the GPU, so this flag is only useful when the gains in fragment processing time outweigh the additional vertex data overhead.

In case of background blur, smaller blur radii need less texture samples and thus are faster. Besides that, the second argument passed to BaseLayer::Shared::Configuration::setBackgroundBlurRadius() is a cutoff threshold, which excludes samples that would contribute to the final pixel value less than given threshold. By default it's 0.5f/255.0f, i.e. what would be at best a rounding error when operating on a 8-bit-per-channel framebuffer. With a higher threshold the processing will get faster in exchange for decreased blur quality. Finally, setBackgroundBlurPassCount() can be used to perform a blur of smaller radius in multiple passes, in case a bigger radius is hitting hardware or implementation limits.

Base classes

class AbstractVisualLayer new in Git master
Base for visual data layers.

Derived classes

class BaseLayerGL new in Git master
OpenGL implementation of the base layer.

Public types

class Shared
Shared state for the base layer.

Public functions

auto shared() -> Shared&
Shared state used by this layer.
auto shared() const -> const Shared&
auto backgroundBlurPassCount() const -> UnsignedInt
Background blur pass count.
auto setBackgroundBlurPassCount(UnsignedInt count) -> BaseLayer&
Set background blur pass count.
auto assignAnimator(BaseLayerStyleAnimator& animator) -> BaseLayer&
Assign a style animator to this layer.
auto defaultStyleAnimator() const -> BaseLayerStyleAnimator*
Default style animator for this layer.
auto setDefaultStyleAnimator(BaseLayerStyleAnimator* animator) -> BaseLayer&
Set a default style animator for this layer.
auto dynamicStyleUniforms() const -> Containers::ArrayView<const BaseLayerStyleUniform>
Dynamic style uniforms.
auto dynamicStylePaddings() const -> Containers::StridedArrayView1D<const Vector4>
Dynamic style paddings.
void setDynamicStyle(UnsignedInt id, const BaseLayerStyleUniform& uniform, const Vector4& padding)
Set a dynamic style.
auto create(UnsignedInt style, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a quad.
template<class StyleIndex>
auto create(StyleIndex style, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a quad with a style index in a concrete enum type.
void remove(DataHandle handle)
Remove a quad.
void remove(LayerDataHandle handle)
Remove a quad assuming it belongs to this layer.
auto color(DataHandle handle) const -> Color4
Quad custom base color.
auto color(LayerDataHandle handle) const -> Color4
Quad custom base color assuming it belongs to this layer.
void setColor(DataHandle handle, const Color4& color)
Set quad custom base color.
void setColor(LayerDataHandle handle, const Color4& color)
Set quad custom base color assuming it belongs to this layer.
auto outlineWidth(DataHandle handle) const -> Vector4
Quad custom outline width.
auto outlineWidth(LayerDataHandle handle) const -> Vector4
Quad custom outline width assuming it belongs to this layer.
void setOutlineWidth(DataHandle handle, const Vector4& width)
Set quad custom outline width.
void setOutlineWidth(DataHandle handle, Float width)
Set quad custom outline width with all edges having the same value.
void setOutlineWidth(LayerDataHandle handle, const Vector4& width)
Set quad custom outline width assuming it belongs to this layer.
void setOutlineWidth(LayerDataHandle handle, Float width)
Set quad custom outline width with all edges having the same value assuming it belongs to this layer.
auto padding(DataHandle handle) const -> Vector4
Quad custom padding.
auto padding(LayerDataHandle handle) const -> Vector4
Quad custom padding assuming it belongs to this layer.
void setPadding(DataHandle handle, const Vector4& padding)
Set quad custom padding.
void setPadding(LayerDataHandle handle, const Vector4& padding)
Set quad custom padding assuming it belongs to this layer.
void setPadding(DataHandle handle, Float padding)
Set quad custom padding with all edges having the same value.
void setPadding(LayerDataHandle handle, Float padding)
Set quad custom padding with all edges having the same value assuming it belongs to this layer.
auto textureCoordinateOffset(DataHandle handle) const -> Vector3
Quad texture coordinate offset.
auto textureCoordinateOffset(LayerDataHandle handle) const -> Vector3
Quad texture coordinate offset assuming it belongs to this layer.
auto textureCoordinateSize(DataHandle handle) const -> Vector2
Quad texture coordinate size.
auto textureCoordinateSize(LayerDataHandle handle) const -> Vector2
Quad texture coordinate size assuming it belongs to this layer.
void setTextureCoordinates(DataHandle handle, const Vector3& offset, const Vector2& size)
Set quad texture coordinates.
void setTextureCoordinates(LayerDataHandle handle, const Vector3& offset, const Vector2& size)
Set quad texture coordinates assuming it belongs to this layer.

Function documentation

Shared& Magnum::Ui::BaseLayer::shared()

Shared state used by this layer.

Reference to the instance passed to BaseLayerGL::BaseLayerGL(LayerHandle, Shared&).

const Shared& Magnum::Ui::BaseLayer::shared() const

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

UnsignedInt Magnum::Ui::BaseLayer::backgroundBlurPassCount() const

Background blur pass count.

Expects that BaseLayerSharedFlag::BackgroundBlur was enabled for the shared state the layer was created with.

BaseLayer& Magnum::Ui::BaseLayer::setBackgroundBlurPassCount(UnsignedInt count)

Set background blur pass count.

Returns Reference to self (for method chaining)

Expects that BaseLayerSharedFlag::BackgroundBlur was enabled for the shared state the layer was created with and that count is at least 1. Higher values will perform the blurring process several times, which has the same effect as applying a single, larger, Gaussian blur. With $ r $ being the radius configured by Shared::Configuration::setBackgroundBlurRadius() and $ n $ being the count, the relation to the larger radius $ l $ is as follows:

\[ l = \sqrt{nr^2} \]

Thus by combining the radius and pass count it's possible to achieve blurring in radii larger than the limit of 31 in Shared::Configuration::setBackgroundBlurRadius(), or alternatively tune the operation based on whether the GPU is faster with few passes and many texture samples each or many passes with few (localized) texture samples each. For example, a radius of 36 can be achieved with 16 passes of radius 9, or with 4 passes of radius 18, or other combinations.

Default pass count is 1.

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

BaseLayer& Magnum::Ui::BaseLayer::assignAnimator(BaseLayerStyleAnimator& animator)

Assign a style animator to this layer.

Returns Reference to self (for method chaining)

Expects that Shared::dynamicStyleCount() is non-zero and that given animator wasn't passed to assignAnimator() on any layer yet. On the other hand, it's possible to associate multiple different animators with the same layer.

BaseLayerStyleAnimator* Magnum::Ui::BaseLayer::defaultStyleAnimator() const

Default style animator for this layer.

If a style animator hasn't been set, returns nullptr. If not nullptr, the returned animator is guaranteed to be assigned to this layer, i.e. that BaseLayerStyleAnimator::layer() is equal to handle().

BaseLayer& Magnum::Ui::BaseLayer::setDefaultStyleAnimator(BaseLayerStyleAnimator* animator)

Set a default style animator for this layer.

Returns Reference to self (for method chaining)

Makes animator used in style transitions in response to events. Expects that animator is either nullptr or is already assigned to this layer, i.e. that assignAnimator() was called on this layer with animator before. Calling this function again with a different animator or with nullptr replaces the previous one.

Containers::ArrayView<const BaseLayerStyleUniform> Magnum::Ui::BaseLayer::dynamicStyleUniforms() const

Dynamic style uniforms.

Size of the returned view is Shared::dynamicStyleCount(). These uniforms are used by style indices greater than or equal to Shared::styleCount().

Containers::StridedArrayView1D<const Vector4> Magnum::Ui::BaseLayer::dynamicStylePaddings() const

Dynamic style paddings.

Size of the returned view is Shared::dynamicStyleCount(). These paddings are used by style indices greater than or equal to Shared::styleCount().

void Magnum::Ui::BaseLayer::setDynamicStyle(UnsignedInt id, const BaseLayerStyleUniform& uniform, const Vector4& padding)

Set a dynamic style.

Parameters
id Dynamic style ID
uniform Style uniform
padding Padding inside the node in order left, top, right, bottom

Expects that the id is less than Shared::dynamicStyleCount(). Shared::styleCount() plus id is then a style index that can be passed to create() or setStyle() in order to use this style. Compared to Shared::setStyle() the mapping between dynamic styles and uniforms is implicit. All dynamic styles are initially default-constructed BaseLayerStyleUniform instances and zero padding vectors.

Calling this function causes LayerState::NeedsCommonDataUpdate to be set to trigger an upload of changed dynamic style uniform data. If padding changed, LayerState::NeedsDataUpdate gets set as well.

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

Create a quad.

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

Expects that style is less than Shared::totalStyleCount(). All styling is driven from the BaseLayerStyleUniform at index style.

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

Create a quad with a style index in a concrete enum type.

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

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

Remove a quad.

Delegates to AbstractLayer::remove(DataHandle).

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

Remove a quad assuming it belongs to this layer.

Delegates to AbstractLayer::remove(LayerDataHandle).

Color4 Magnum::Ui::BaseLayer::color(DataHandle handle) const

Quad custom base color.

Expects that handle is valid.

Color4 Magnum::Ui::BaseLayer::color(LayerDataHandle handle) const

Quad custom base color assuming it belongs to this layer.

Expects that handle is valid.

void Magnum::Ui::BaseLayer::setColor(DataHandle handle, const Color4& color)

Set quad custom base color.

Expects that handle is valid. BaseLayerStyleUniform::topColor and bottomColor is multiplied with color. By default, the custom color is 0xffffffff_srgbaf, i.e. not affecting the style in any way.

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

void Magnum::Ui::BaseLayer::setColor(LayerDataHandle handle, const Color4& color)

Set quad custom base color assuming it belongs to this layer.

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

Vector4 Magnum::Ui::BaseLayer::outlineWidth(DataHandle handle) const

Quad custom outline width.

In order left, top. right, bottom. Expects that handle is valid.

Vector4 Magnum::Ui::BaseLayer::outlineWidth(LayerDataHandle handle) const

Quad custom outline width assuming it belongs to this layer.

In order left, top. right, bottom. Expects that handle is valid.

void Magnum::Ui::BaseLayer::setOutlineWidth(DataHandle handle, const Vector4& width)

Set quad custom outline width.

Expects that handle is valid. The width is in order left, top, right, bottom and is added to BaseLayerStyleUniform::outlineWidth. By default, unless specified in create() already, the custom outline width is a zero vector, i.e. not affecting the style in any way. Has no visual effect if BaseLayerSharedFlag::NoOutline is enabled.

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

void Magnum::Ui::BaseLayer::setOutlineWidth(DataHandle handle, Float width)

Set quad custom outline width with all edges having the same value.

Expects that handle is valid. The width is added to BaseLayerStyleUniform::outlineWidth. By default, the custom outline width is zero, i.e. not affecting the style in any way. Has no visual effect if BaseLayerSharedFlag::NoOutline is enabled.

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

void Magnum::Ui::BaseLayer::setOutlineWidth(LayerDataHandle handle, const Vector4& width)

Set quad custom outline width assuming it belongs to this layer.

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

void Magnum::Ui::BaseLayer::setOutlineWidth(LayerDataHandle handle, Float width)

Set quad custom outline width with all edges having the same value assuming it belongs to this layer.

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

Vector4 Magnum::Ui::BaseLayer::padding(DataHandle handle) const

Quad custom padding.

In order left, top. right, bottom. Expects that handle is valid.

Vector4 Magnum::Ui::BaseLayer::padding(LayerDataHandle handle) const

Quad custom padding assuming it belongs to this layer.

In order left, top. right, bottom. Expects that handle is valid.

void Magnum::Ui::BaseLayer::setPadding(DataHandle handle, const Vector4& padding)

Set quad custom padding.

Expects that handle is valid. The padding is in order left, top, right, bottom and is added to the per-style padding values specified in Shared::setStyle(). By default the padding is a zero vector, i.e. the quad fills the node area completely.

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

void Magnum::Ui::BaseLayer::setPadding(LayerDataHandle handle, const Vector4& padding)

Set quad custom padding assuming it belongs to this layer.

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

void Magnum::Ui::BaseLayer::setPadding(DataHandle handle, Float padding)

Set quad custom padding with all edges having the same value.

Expects that handle is valid. The padding is added to the per-style padding values specified in Shared::setStyle(). By default there's zero padding, i.e. the quad fills the node area completely.

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

void Magnum::Ui::BaseLayer::setPadding(LayerDataHandle handle, Float padding)

Set quad custom padding with all edges having the same value assuming it belongs to this layer.

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

Vector3 Magnum::Ui::BaseLayer::textureCoordinateOffset(DataHandle handle) const

Quad texture coordinate offset.

The third coordinate is array layer. Expects that handle is valid and that BaseLayerSharedFlag::Textured was enabled for the shared state the layer was created with.

Vector3 Magnum::Ui::BaseLayer::textureCoordinateOffset(LayerDataHandle handle) const

Quad texture coordinate offset assuming it belongs to this layer.

The third coordinate is array layer. Expects that handle is valid and that BaseLayerSharedFlag::Textured was enabled for the shared state the layer was created with.

Vector2 Magnum::Ui::BaseLayer::textureCoordinateSize(DataHandle handle) const

Quad texture coordinate size.

Expects that handle is valid and that BaseLayerSharedFlag::Textured was enabled for the shared state the layer was created with.

Vector2 Magnum::Ui::BaseLayer::textureCoordinateSize(LayerDataHandle handle) const

Quad texture coordinate size assuming it belongs to this layer.

Expects that handle is valid and that BaseLayerSharedFlag::Textured was enabled for the shared state the layer was created with.

void Magnum::Ui::BaseLayer::setTextureCoordinates(DataHandle handle, const Vector3& offset, const Vector2& size)

Set quad texture coordinates.

The third coordinate of offset is array layer. Expects that handle is valid and that BaseLayerSharedFlag::Textured was enabled for the shared state the layer was created with. By default the offset is {0.0f, 0.0f, 0.0f} and size is {1.0f, 1.0f}, i.e. covering the whole first slice of the texture.

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

void Magnum::Ui::BaseLayer::setTextureCoordinates(LayerDataHandle handle, const Vector3& offset, const Vector2& size)

Set quad texture coordinates assuming it belongs to this layer.

Like setTextureCoordinates(DataHandle, const Vector3&, const Vector2&) but without checking that handle indeed belongs to this layer. See its documentation for more information.