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

Text layer.

Draws text laid out using the Text library, including editing capabilities.

Setting up a text layer instance

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

For a custom layer, you first need to instantiate TextLayer::Shared, which contains a glyph cache, font instances, GPU shaders and style definitions. It takes a TextLayer::Shared::Configuration, 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::TextLayerGL::Shared textLayerShared{
    Ui::TextLayer::Shared::Configuration{3}
};

The shared instance, in this case a concrete TextLayerGL::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 if it combines visual options that have to be hardcoded in particular TextLayer::Shared instances. To make the layer available as the implicit UserInterface::textLayer(), pass it to UserInterfaceGL::setTextLayerInstance():

ui.setTextLayerInstance(
    Containers::pointer<Ui::TextLayerGL>(ui.createLayer(), textLayerShared));

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

Ui::TextLayer& textLayer = ui.setLayerInstance(
    Containers::pointer<Ui::TextLayerGL>(ui.createLayer(), textLayerShared));

Afterwards, in order to be able to draw the layer, a glyph cache with at least one font has to be added, and a style referencing them has to be set. For the TextLayerGL implementation it's Text::GlyphCacheGL. Assuming monochrome fonts, construct it with a single-channel pixel format and a size large enough to fit pre-rendered glyphs of all fonts you'll need, and pass it to TextLayerGL::Shared::setGlyphCache(). The glyph cache is expected to be alive for the whole shared instance lifetime, alternatively you can use TextLayerGL::Shared::setGlyphCache(Text::GlyphCacheGL&&) to move its ownership to the shared instance.

Text::GlyphCacheGL glyphCache{PixelFormat::R8Unorm, {1024, 1024}};

textLayerShared.setGlyphCache(glyphCache);

Text::AbstractFont instances, commonly loaded using a plugin manager, are then added with TextLayerGL::Shared::addFont(), together with specifying size in UI coordinates at which a text using them should be rendered. The function then returns a FontHandle, which is subsequently used to reference the font from styles. As with the glyph cache, the font instance is expected to be alive for the whole shared instance lifetime, or you can use TextLayerGL::Shared::addFont(Containers::Pointer<Text::AbstractFont>&&, Float) to move its ownership to the shared instance. Note that you're still responsible for keeping the plugin manager instance around even in that case.

PluginManager::Manager<Text::AbstractFont> fontManager;

Containers::Pointer<Text::AbstractFont> font16 =
    fontManager.loadAndInstantiate("TrueTypeFont");
font16->openFile("font.ttf", 16.0f);
font16->fillGlyphCache(glyphCache, "abcdefghijklmnopqrstuvwxyz"
                                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                   "0123456789?!:;,. ");

Containers::Pointer<Text::AbstractFont> font12 =
    fontManager.loadAndInstantiate("TrueTypeFont");
font12->openFile("font.ttf", 12.0f);
font12->fillGlyphCache(glyphCache, "abcdefghijklmnopqrstuvwxyz"
                                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                   "0123456789?!:;,. ");

Ui::FontHandle font16Handle = textLayerShared.addFont(*font16, font16->size());
Ui::FontHandle font12Handle = textLayerShared.addFont(*font12, font12->size());

Assuming the UI size matches the framebuffer size, a good default is to use the same size in Text::AbstractFont::openFile() / openData() and TextLayerGL::Shared::addFont(), like in the snippet above. See the Text crispness and DPI awareness section below for additional considerations.

Finally, a style referencing the fonts has to be set with TextLayer::Shared::setStyle(). At the very least you're expected to pass a TextLayerCommonStyleUniform containing properties common to all styles, an array of TextLayerStyleUniform matching the style count in the TextLayer::Shared::Configuration, and a FontHandle and Text::Alignment alignment that given style should use. Default-constructed instances will result in white text, you can then use method chaining to update only the properties you're interested in; zero-initialized Text::Alignment value is equivalent to Text::Alignment::LineLeft. In the following snippet, style 0 uses the first, larger font and style defaults, style 1 is the first font again but with a blue color, and style 2 is the smaller font centered:

textLayerShared.setStyle(Ui::TextLayerCommonStyleUniform{}, {
    Ui::TextLayerStyleUniform{}, /* Style 0, default */
    Ui::TextLayerStyleUniform{}  /* Style 1 */
        .setColor(0x2f83cc_rgbf),
    Ui::TextLayerStyleUniform{}  /* Style 2 */
}, {
    font16Handle,
    font16Handle,
    font12Handle
}, {
    Text::Alignment{},
    Text::Alignment{},
    Text::Alignment::MiddleCenter
}, {}, {}, {}, {}, {}, {});

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 texts

A text is created by calling create() with desired style index, the actual UTF-8 string to render, a TextProperties instance with optional data-specific shaping and layout settings, and a NodeHandle the data should be attached to. In this case it picks the style 1 from above, which makes the text blue:

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

textLayer.create(1, "hello!", {}, node);

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.

Style, shaping and layout options

Text color

Image

The color supplied with TextLayerStyleUniform::setColor() as shown above is additionally multiplied by a data-specific color set with setColor(). The main use case, like with BaseLayer, is to allow for example custom label coloring where it would be impractical to dynamically update style data based on what's being shown. The color is further multiplied by a per-node opacity coming from AbstractUserInterface::setNodeOpacity(), which can be used for various fade-in / fade-out effects.

textLayerShared.setStyle(, {
    Ui::TextLayerStyleUniform{}, /* 0 */
    Ui::TextLayerStyleUniform{}  /* 1 */
        .setColor(0x2f83cc_rgbf)
}, {}, {}, {}, {}, {}, {}, {}, {});



Ui::NodeHandle blue = ;
textLayer.create(1, "hello!", {}, blue);

Ui::NodeHandle colored = ;
Ui::DataHandle coloredData = textLayer.create(0, "HEY", {}, colored);
textLayer.setColor(coloredData, 0x3bd267_rgbf);

Ui::NodeHandle fadedBlue = ;
textLayer.create(1, "shh", {}, fadedBlue);
ui.setNodeOpacity(fadedBlue, 0.25f);

At the moment, there's no possibility to assign different color to individual glyphs or text ranges.

Alignment and padding inside the node

Text::Alignment::MiddleCenter passed to TextLayer::Shared::setStyle() aligns vertical and horizontal glyph bounds, defined by font ascent, descent, line advance and glyph advance, to vertical and horizontal center of given node. Text::Alignment::TopLeft and other alignment values then align a particular edge or baseline of the text to node edges and corners. With those, it's often desirable to specify also a padding inside the node with the last argument to TextLayer::Shared::setStyle(), so e.g. a text inside a text box has some spacing around without having to put it inside a slightly smaller child node. Similarly as with padding in BaseLayer, it's one value for each of four node edges, but often a single value for all is enough:

textLayerShared.setStyle(, {}, {}, {
    Text::Alignment::TopLeft
}, {}, {}, {}, {}, {}, {
    Vector4{8.0f}
});

If needed, style-provided alignment can be overriden for particular data with TextProperties::setAlignment() — or by passing a Text::Alignment directly, as it's implicitly convertible to TextProperties — when creating or updating the text. Note that, unlike e.g. LineLayer::setAlignment(), the alignment cannot be changed dynamically for an already shaped text. The reason is that the alignment may offset each line differently and is dependent on shaping direction of the input text, but neither the input text nor line break positions or other properties are remembered by default.

Image

Finally, the padding can be overriden on a per-data basis with setPadding(). This is especially useful when aligning text of a variable width next to other UI elements such as icons, or combining two differently styled pieces of text together. The following snippet shows aligning a text next to a colored hash character using size() to query the actual rendered size, the other element could also be anything from a BaseLayer or a LineLayer which provide the same padding interfaces. Additionally, the space between the two is defined by the style itself and thus doesn't have to be hardcoded in the offset calculation:

textLayerShared.setStyle(, {
    Ui::TextLayerStyleUniform{} /* 0 */
        .setColor(0xa5c9ea_rgbf),
    Ui::TextLayerStyleUniform{} /* 1 */
        .setColor(0x2f83cc_rgbf)
}, {}, {
    /* With centered alignment half of the padding value is used for each */
    Text::Alignment::MiddleCenter,
    Text::Alignment::MiddleCenter
}, {}, {}, {}, {}, {}, {
    /* Left, top, right, bottom */
    {0.0f, 0.0f, 2.0f, 0.0f},
    {2.0f, 0.0f, 0.0f, 0.0f}
});



Ui::NodeHandle node = ;
Ui::DataHandle hash = textLayer.create(0, "#", {}, node);
Ui::DataHandle text = textLayer.create(1, "whee", {}, node);

/* Left, top, right, bottom. Again, centered alignment makes use of half of the
   padding value for each, keeping the two centered as a group. */
textLayer.setPadding(hash, {0.0f, 0.0f, textLayer.size(text).x(), 0.0f});
textLayer.setPadding(text, {textLayer.size(hash).x(), 0.0f, 0.0f, 0.0f});

Font, shaping language, script and direction

Besides alignment, TextProperties::setFont() — or, again, passing a FontHandle directly, as it's implicitly convertible to TextProperties — allows you to override the font for a particular data. This is useful for example when a certain text is using a script not supported by the font coming from the style:

Ui::FontHandle greekFontHandle = textLayerShared.addFont();



Ui::NodeHandle hello = ;
textLayer.create(, "Γεια!", greekFontHandle, hello);

It's also possible to explicitly specify the script, language and direction of given text, however note that support for these options is highly dependent on the font plugin used. HarfBuzzFont, for example, attempts to detect these properties by default, so not setting them to any fixed value will likely provide better results when working with text coming from unknown international sources. On the other hand, specifying these properties if you're sure about the text origin avoids the autodetection, which can improve performance with highly dynamic text. In contrast, StbTrueTypeFont for example doesn't support any of these properties and setting them will have no effect.

textLayer.create(, "Γεια!",
    Ui::TextProperties{}
        .setFont(greekFontHandle)
        .setScript(Text::Script::Greek)
        .setLanguage("el-GR")
        .setShapeDirection(Text::ShapeDirection::LeftToRight),
    hello);

At the moment, only Text::LayoutDirection::HorizontalTopToBottom is supported, i.e. vertical text isn't possible yet. See also Text::Alignment::LineBegin, Text::Alignment::LineEnd and other alignment values that resolve to either left or right alignment based on either the detected or supplied Text::ShapeDirection.

Font feature selection

Image

TextProperties::setFeatures() allows you to configure OpenType typographic features applied when shaping given text. Again, support for these depends on the font plugin used as well as features exposed by a particular font file. At the moment, HarfBuzzFont is the only plugin implementing these, and most of the features are limited to OTF fonts, TTF supports only a small subset such as toggling Text::Feature::Kerning. In the following snippet, the whole text gets Text::Feature::OldstyleFigures enabled, causing certain numbers to reach below the baseline, then an alternative glyph of the a character in Status is picked, and Text::Feature::SmallCapitals is used for the part of the string containing the actual status description:

textLayer.create(, "Status: 418 I'm a Teapot",
    Ui::TextProperties{}.setFeatures({
         Text::Feature::OldstyleFigures,
        {Text::Feature::CharacterVariants2, 2, 3},
        {Text::Feature::SmallCapitals, 8, ~UnsignedInt{}},
    }), );

Note that the features will likely need additional glyphs to be pre-filled in the cache. The Text::AbstractFont::fillGlyphCache(AbstractGlyphCache&, const Containers::StridedArrayView1D<const UnsignedInt>&) overload allows you to specify individual glyph IDs, and Text::AbstractFont::glyphForName() allows you to query the IDs by names that you can look up in some font inspection utility. In case of the Source Sans font used here, it could look like this:

font->fillGlyphCache(glyphCache, {
    /* Small capitals */
    font->glyphForName("A.s"),
    font->glyphForName("B.s"),
    font->glyphForName("C.s"),
    font->glyphForName("D.s"),
    
    /* Oldstyle figures */
    font->glyphForName("one.t"),
    font->glyphForName("two.t"),
    font->glyphForName("three.t"),
    
    /* Character variant for lowercase a */
    font->glyphForName("a.a")
});

Font features can be also specified as part of the style. For example you may want to have all title labels in small caps, and all numeric fields with Text::Feature::TabularFigures instead of proportional and with Text::Feature::SlashedZero to distinguish a zero from an uppercase O. All used features are supplied as a list in the fifth argument to TextLayer::Shared::setStyle(), and then each style specifies an offset and count into this list in the next two arguments. The same features can be used by multiple ranges, count being 0 means no features are used for given style:

textLayerShared.setStyle(, {
    Ui::TextLayerStyleUniform{} /* title */
        .setColor(0xdcdcdc_rgbf),
    Ui::TextLayerStyleUniform{} /* main title */
        .setColor(0xa5c9ea_rgbf),
    Ui::TextLayerStyleUniform{} /* numeric fields */
        .setColor(0xdcdcdc_rgbf),
    Ui::TextLayerStyleUniform{} /* general text */
        .setColor(0xdcdcdc_rgbf)
}, {}, {}, {
    Text::Feature::SmallCapitals,  /* 0 */
    Text::Feature::TabularFigures, /* 1 */
    Text::Feature::SlashedZero,    /* 2 */
}, {
    0, /* title */
    0, /* main title */
    1, /* numeric fields */
    0, /* general text */
}, {
    1, /* title uses feature 0 */
    1, /* main title uses feature 0 as well */
    2, /* numeric fields use feature 1 and 2 */
    0  /* general text uses no features */
}, {}, {}, {});

Rendering single glyphs

Image

With createGlyph() you can render a single glyph specified with its ID. This is useful mainly with icon fonts, where you can for example use Text::AbstractFont::glyphForName() at runtime if the font has named glyphs:

textLayer.createGlyph(, font->glyphForName("coffee"), {}, node);

Or you can maintain an enum with name-to-glyph-ID mapping. Similarly to style IDs, createGlyph() accepts both plain integers and enums for a glyph ID:

enum class IconFont: UnsignedInt {
    
    Coffee = 2249, /* queried from the font in an offline step */
};

textLayer.createGlyph(, IconFont::Coffee, {}, node);

It's however also possible to insert your own image data into the glyph cache and reference them with this function. Assuming a list of images imported for example from a set of PNGs, call Text::AbstractGlyphCache::addFont() to add a new font ID, copy the image contents to places in the cache reserved using TextureTools::AtlasLandfill::add(), make the glyph cache aware of the new icons with Text::AbstractGlyphCache::addGlyph(), and at the end upload the updated part of the glyph cache to the GPU texture using Text::AbstractGlyphCache::flushImage().

Containers::Array<Trade::ImageData2D> icons = ; /* or Image, ImageView, etc. */
UnsignedInt iconFontId = textLayerShared.glyphCache().addFont(icons.size());

Containers::Array<Vector3i> offsets{NoInit, icons.size()};
Containers::Optional<Range3Di> flushRange = textLayerShared.glyphCache().atlas()
    .add(stridedArrayView(icons).slice(&Trade::ImageData2D::size), offsets);
CORRADE_INTERNAL_ASSERT(flushRange);
Containers::StridedArrayView3D<Color4ub> dst =
    textLayerShared.glyphCache().image().pixels<Color4ub>();
for(std::size_t i = 0; i != icons.size(); ++i) {
    Containers::StridedArrayView3D<const Color4ub> src = icons[i].pixels<Color4ub>();
    Utility::copy(src, dst.sliceSize(
        {std::size_t(offsets[i].z()),
         std::size_t(offsets[i].y()),
         std::size_t(offsets[i].x())}, src.size()));
    textLayerShared.glyphCache().addGlyph(iconFontId, i, {},
        offsets[i].z(), Range2Di::fromSize(offsets[i].xy(), icons[i].size()));
}

textLayerShared.glyphCache().flushImage(*flushRange);

Finally add the newly added icons as a new FontHandle using TextLayer::Shared::addInstancelessFont(). Compared to TextLayer::Shared::addFont() it takes a scale instead of size. Assuming the UI size matches the framebuffer size, use 1.0f if you want to draw the icons in their original pixel size. the Text crispness and DPI awareness section below for additional considerations. The font handle can then be referenced from the style or passed directly to createGlyph().

Ui::FontHandle iconFontHandle = textLayerShared.addInstancelessFont(iconFontId, 1.0f);



/* Create a glyph from containing icon at offset 3 in the `icons` array above */
textLayer.createGlyph(, 3, iconFontHandle, );

See Text::AbstractGlyphCache and TextureTools::AtlasLandfill for detailed information about how the cache and atlas packing works internally.

Updating text data

Any created text or single glyph can be subsequently updated with setText() and setGlyph(). These functions take the same arguments and behave the same as create() and createGlyph(). Internally there's no distinction between a text and a single glyph, so a text can be safely changed to just a glyph and vice versa.

Dynamic styles

Like with BaseLayer dynamic styles, the TextLayer::Shared::setStyle() API is meant to be used to supply data for static styles, and dynamic styles, requested with TextLayer::Shared::Configuration::setDynamicStyleCount() and specified via setDynamicStyle(), used for style data that change very often:

Ui::TextLayerGL::Shared textLayerShared{
    Ui::TextLayerGL::Shared::Configuration{}
        .setDynamicStyleCount(10)
};
Ui::TextLayerGL& textLayer = ui.setLayerInstance(
    Containers::pointer<Ui::TextLayerGL>(ui.createLayer(), textLayerShared));



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

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

The main use case for dynamic styles is animations, which is for the text layer implemented in the TextLayerStyleAnimator class. Again 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 = textLayer.allocateDynamicStyle();
if(!dynamicStyleId) {
    
}

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


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

Style transition based on input events

Like with BaseLayer, it's possible to configure TextLayer to perform automatic style transitions based on input events, such as highlighting on hover or press. See the BaseLayer documentation for style transition for a detailed example, the interfaces are the same between the two.

Editable text

By default, the text layer shapes the input text into glyphs, throwing away the input text and all its properties like language or direction, and remembering just which glyphs to render at which positions. As majority of text in user interfaces is non-editable and often only set, never queried back, there's no need to implicitly store such info.

To make editable text, first a style describing how cursor and selection looks like needs to be configured. As both the cursor and the selection are rectangular shapes, there's a single editing style definition, and then it's specified which of them are used for cursors and which for selection. By default there are no editing styles and their desired count has to be specified via TextLayer::Shared::Configuration::setEditingStyleCount(), in this example we'll have just two, used by three regular styles:

Ui::TextLayerGL::Shared textLayerShared{
    Ui::TextLayer::Shared::Configuration{3}
        .setEditingStyleCount(2)
};

Style data is then supplied with TextLayer::Shared::setEditingStyle(). Similarly to regular styles, there can be multiple editing styles, and they consist of a TextLayerCommonEditingStyleUniform, containing properties common to all editing styles, an array of TextLayerEditingStyleUniform, matching the style count specified in the configuration. Default-constructed instances will result in white rectangles, you can then use method chaining to update only the properties you're interested in. Finally, there is a list of padding values, which define how far from the actual glyph bounds the selection or cursor expands. In case of cursors in particular, with no padding specified they'd have a zero width and thus be invisible. The following snippet defines an azure style 0 for a cursor, expanding one UI unit on each side from the cursor position, and a blue selection style 1, tightly wrapping the glyph rectangle:

textLayerShared.setEditingStyle(Ui::TextLayerCommonEditingStyleUniform{}, {
    Ui::TextLayerEditingStyleUniform{}  /* 0 */
        .setBackgroundColor(0xa5c9ea_rgbf),
    Ui::TextLayerEditingStyleUniform{}  /* 1 */
        .setBackgroundColor(0x2f83cc_rgbf),
}, {}, {
    /* Begin, top, end, bottom */
    {1.0f, 0.0f, 1.0f, 0.0f},           /* 0 */
    {}                                  /* 1 */
});

After that, the editing styles need to be referenced from the regular styles. This is done with the eighth and ninth argument to TextLayer::Shared::setStyle(), which are are cursor and selection style indices, respectively, or -1 if given style shouldn't have a cursor / selection style assigned. If either of those arguments is left empty, it's the same as if it'd have -1 for all styles. Here style 0 is a regular non-editing style, 1 is a style with both a cursor and selection style, and 2 has just a selection, for example if a certain label is meant to be just selectable but not directly editable:

textLayerShared.setStyle(, {
    Ui::TextLayerStyleUniform{} /* 0 */
        ,
    Ui::TextLayerStyleUniform{} /* 1 */
        ,
    Ui::TextLayerStyleUniform{} /* 2 */
        ,
}, {}, {}, {}, {}, {}, {
    -1, /* Style 0 uses no cursor style */
    0,  /* Style 1 uses editing style 0 for cursor */
    -1  /* Style 2 uses no cursor style */
}, {
    -1, /* Style 0 uses no selection style */
    1,  /* Style 1 uses editing style 1 for selection */
    1   /* Style 2 uses editing style 1 for selection */
}, {});

With the editing style set up, an editable text can be made by passing TextDataFlag::Editable to either create() or setText() Initially it will have no selection and the cursor at the end, you can then use setCursor() to specify the byte position where the cursor and selection should be:

Ui::NodeHandle node = ;
Ui::DataHandle text = textLayer.create(, "Hello world!", {},
    Ui::TextDataFlag::Editable, node);
textLayer.setCursor(text, 7, 4); /* Selecting "o w" with cursor at byte 7 */

Contents of an editable text are then subsequently queryable with text(). The updateText() function can perform incremental updates in addition to cursor movement and the editText() API is for higher-level operations with UTF-8 and text directionality awareness. This function is also what is called on actual key and text input events, described in Reacting to focus, pointer and keyboard input events below.

Cursor and selection color, selection text style

Image

Apart from the selection / cursor rectangle color, set with TextLayerEditingStyleUniform::setBackgroundColor(), it's possible to override style of the text under selection using the third argument to TextLayer::Shared::setEditingStyle(). Compared to above, the following snippet adds a style number 3, which is then referenced from the selection editing style, and which makes the selected text darker:

textLayerShared.setStyle(, {
    Ui::TextLayerStyleUniform{} /* 0 */
        ,
    Ui::TextLayerStyleUniform{} /* 1 */
        ,
    Ui::TextLayerStyleUniform{} /* 2 */
        ,
    Ui::TextLayerStyleUniform{} /* 3 */
        .setColor(0x2f363f_rgbf),
}, );

textLayerShared.setEditingStyle(, {}, {
    -1, /* (Cursor) style 0 doesn't override any style for text selection */
    3   /* (Selection) style 1 uses style 3 for text selection */
}, {});

Note that only the TextLayerCommonStyleUniform is used from the style override, not font, alignment or other properties.

Cursor and selection rectangle padding

Image

Padding used in the snippets above was only horizontal, but vertical padding can be used for example to achieve better optical alignment of both the text itself and the selection rectangle inside an edit box, or to make a cursor stand out more. Horizontal padding also doesn't necessarily have to be symmetrical. For the cursor it's often desirable to have it shifted towards the typing direction. Here it's still two units wide, but is shifted to the right for Text::ShapeDirection::LeftToRight text, and to the left for RightToLeft text:

textLayerShared.setEditingStyle(, {}, {}, {
    /* Begin, top, end, bottom */
    {0.0f, 0.0f, 2.0f, 0.0f},          /* (Cursor) style 0 */
    {1.0f, -1.0f, 1.0f, -1.0f}         /* (Selection) style 1 */
});

Rounded corners and edge smoothness

Image

TextLayerEditingStyleUniform::setCornerRadius() allows you to make the cursor and selection rectangle corners rounded. The edges are aliased by default, use TextLayerCommonEditingStyleUniform::setSmoothness() to smoothen them out. The radius is in framebuffer pixels, same as with BaseLayer edge smoothness.

textLayerShared.setEditingStyle(, {
    Ui::TextLayerEditingStyleUniform{}  /* (Cursor) style 0 */
        .setBackgroundColor(0xa5c9ea_rgbf)
        .setCornerRadius(1.0f),
    Ui::TextLayerEditingStyleUniform{}  /* (Selection) style 1 */
        .setBackgroundColor(0x2f83cc_rgbf)
        .setCornerRadius(2.0f),
}, {}, {});

Reacting to focus, pointer and keyboard input events

Assuming key and text input events are propagated to the UI from the application as described in the AbstractUserInterface documentation, if a text with TextDataFlag::Editable is attached to a NodeFlag::Focusable node, focusing it either programmatically or with a pointer tap or click will make it receive all keyboard and text input events, allowing the user to edit the text. Arrow and other editing keys map to concrete actions described in the TextEdit enum.

Ui::NodeHandle node = ui.createNode(, Ui::NodeFlag::Focusable);
Ui::DataHandle text = textLayer.create(, Ui::TextDataFlag::Editable, node);

For proper visual feedback on focus and blur of a particular widget containing the editable text it's recommended to implement also the toFocusedOut and toFocusedOver state transitions in Shared::setStyleTransition() for all visual layers used in given widget, in addition to the basic inactive, pressed and disabled transitions shown in the BaseLayer documentation for style transitions.

Dynamic editing styles

Dynamic styles, described above, have also variants that include cursor and selection styles. Depending on the subset of editing styles you want to have for a particular dynamic style, use setDynamicStyleWithCursor(), setDynamicStyleWithSelection() or setDynamicStyleWithCursorSelection() instead of setDynamicStyle().

Text crispness and DPI awareness

If the UI size differs from the actual framebuffer size, which is often the case on HiDPI systems and other scenarios described in AbstractUserInterface DPI awareness docs, using the same font size in both Text::AbstractFont::openFile() / openData() and TextLayerGL::Shared::addFont() will result in blurry text. Additionally, because glyphs are often placed at subpixel locations, it's often better to supersample the font at twice the size it's used in for crisper edges even if no HiDPI interface scaling is involved. Thus, the size at which the font is opened and at which the glyph cache is filled should be multiplied by a factor compared to the size the font is used in the UI:

/* Supersample 2x in addition to DPI scaling */
Float scale = 2.0f*(Vector2{ui.framebufferSize()}/ui.size()).max();

font->openFile("font.ttf", 16.0f);

Ui::FontHandle fontHandle = textLayerShared.addFont(*font, font->size()*scale);

Fonts added with TextLayerGL::Shared::addInstancelessFont() should then use the scale directly.

Base classes

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

Derived classes

class TextLayerGL new in Git master
OpenGL implementation of the text layer.

Public types

class Shared
Shared state for the text layer.

Public functions

auto shared() -> Shared&
Shared state used by this layer.
auto shared() const -> const Shared&
auto assignAnimator(TextLayerStyleAnimator& animator) -> TextLayer&
Assign a style animator to this layer.
auto defaultStyleAnimator() const -> TextLayerStyleAnimator*
Default style animator for this layer.
auto setDefaultStyleAnimator(TextLayerStyleAnimator* animator) -> TextLayer&
Set a default style animator for this layer.
auto dynamicStyleUniforms() const -> Containers::ArrayView<const TextLayerStyleUniform>
Dynamic style uniforms.
auto dynamicStyleFonts() const -> Containers::StridedArrayView1D<const FontHandle>
Dynamic style fonts.
auto dynamicStyleAlignments() const -> Containers::StridedArrayView1D<const Text::Alignment>
Dynamic style alignments.
auto dynamicStyleFeatures(UnsignedInt id) const -> Containers::ArrayView<const TextFeatureValue>
Dynamic style font features.
auto dynamicStyleCursorStyles() const -> Containers::BitArrayView
Which dynamic style have associated cursor styles.
auto dynamicStyleCursorStyle(UnsignedInt id) const -> Int
Dynamic style cursor style ID.
auto dynamicStyleSelectionStyles() const -> Containers::BitArrayView
Which dynamic style have associated selection styles.
auto dynamicStyleSelectionStyle(UnsignedInt id) const -> Int
Dynamic style selection style ID.
auto dynamicStyleSelectionStyleTextUniform(UnsignedInt) const -> Int
Dynamic style selection style text uniform IDs.
auto dynamicStylePaddings() const -> Containers::StridedArrayView1D<const Vector4>
Dynamic style paddings.
auto dynamicEditingStyleUniforms() const -> Containers::ArrayView<const TextLayerEditingStyleUniform>
Dynamic editing style uniforms.
auto dynamicEditingStylePaddings() const -> Containers::StridedArrayView1D<const Vector4>
Dynamic editing style paddings.
void setDynamicStyle(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, Containers::ArrayView<const TextFeatureValue> features, const Vector4& padding)
Set a dynamic style for text only.
void setDynamicStyle(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, std::initializer_list<TextFeatureValue> features, const Vector4& padding)
void setDynamicStyleWithCursorSelection(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, Containers::ArrayView<const TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& cursorUniform, const Vector4& cursorPadding, const TextLayerEditingStyleUniform& selectionUniform, const Containers::Optional<TextLayerStyleUniform>& selectionTextUniform, const Vector4& selectionPadding)
Set a dynamic style for text, cursor and selection.
void setDynamicStyleWithCursorSelection(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, std::initializer_list<TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& cursorUniform, const Vector4& cursorPadding, const TextLayerEditingStyleUniform& selectionUniform, const Containers::Optional<TextLayerStyleUniform>& selectionTextUniform, const Vector4& selectionPadding)
void setDynamicStyleWithCursor(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, Containers::ArrayView<const TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& cursorUniform, const Vector4& cursorPadding)
Set a dynamic style for text and cursor only.
void setDynamicStyleWithCursor(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, std::initializer_list<TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& cursorUniform, const Vector4& cursorPadding)
void setDynamicStyleWithSelection(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, Containers::ArrayView<const TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& selectionUniform, const Containers::Optional<TextLayerStyleUniform>& selectionTextUniform, const Vector4& selectionPadding)
Set a dynamic style for text and selection only.
void setDynamicStyleWithSelection(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, std::initializer_list<TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& selectionUniform, const Containers::Optional<TextLayerStyleUniform>& selectionTextUniform, const Vector4& selectionPadding)
auto create(UnsignedInt style, Containers::StringView text, const TextProperties& properties, TextDataFlags flags = {}, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a text.
auto create(UnsignedInt style, Containers::StringView text, const TextProperties& properties, NodeHandle node) -> DataHandle
template<class StyleIndex>
auto create(StyleIndex style, Containers::StringView text, const TextProperties& properties, TextDataFlags flags = {}, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a text with a style index in a concrete enum type.
template<class StyleIndex>
auto create(StyleIndex style, Containers::StringView text, const TextProperties& properties, NodeHandle node) -> DataHandle
auto createGlyph(UnsignedInt style, UnsignedInt glyph, const TextProperties& properties, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a single glyph.
template<class StyleIndex>
auto createGlyph(StyleIndex style, UnsignedInt glyph, const TextProperties& properties, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a single glyph with a style index in a concrete enum type.
template<class GlyphIndex>
auto createGlyph(UnsignedInt style, GlyphIndex glyph, const TextProperties& properties, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a single glyph with a glyph ID in a concrete enum type.
template<class StyleIndex, class GlyphIndex>
auto createGlyph(StyleIndex style, GlyphIndex glyph, const TextProperties& properties, NodeHandle node = NodeHandle::Null) -> DataHandle
Create a single glyph with a style index and glyph ID in a concrete enum type.
void remove(DataHandle handle)
Remove a text.
void remove(LayerDataHandle handle)
Remove a text assuming it belongs to this layer.
auto flags(DataHandle handle) const -> TextDataFlags
Text flags.
auto flags(LayerDataHandle handle) const -> TextDataFlags
Text flags assuming it belongs to this layer.
auto glyphCount(DataHandle handle) const -> UnsignedInt
Text glyph count.
auto glyphCount(LayerDataHandle handle) const -> UnsignedInt
Text glyph count assuming it belongs to this layer.
auto size(DataHandle handle) const -> Vector2
Size of the laid out text.
auto size(LayerDataHandle handle) const -> Vector2
Size of the laid out text assuming it belongs to this layer.
auto cursor(DataHandle handle) const -> Containers::Pair<UnsignedInt, UnsignedInt>
Cursor and selection position in an editable text.
auto cursor(LayerDataHandle handle) const -> Containers::Pair<UnsignedInt, UnsignedInt>
Cursor and selection position in an editable text assuming it belongs to this layer.
void setCursor(DataHandle handle, UnsignedInt position, UnsignedInt selection)
Set cursor position and selection in an editable text.
void setCursor(DataHandle handle, UnsignedInt position)
Set cursor position in an editable text.
void setCursor(LayerDataHandle handle, UnsignedInt position, UnsignedInt selection)
Set cursor position and selection in an editable text assuming it belongs to this layer.
void setCursor(LayerDataHandle handle, UnsignedInt position)
Set cursor position in an editable text assuming it belongs to this layer.
auto textProperties(DataHandle handle) const -> TextProperties
Properties used for shaping an editable text.
auto textProperties(LayerDataHandle handle) const -> TextProperties
Properties used for shaping an editable text assuming it belongs to this layer.
auto text(DataHandle handle) const -> Containers::StringView
Contents of an editable text.
auto text(LayerDataHandle handle) const -> Containers::StringView
Contents of an editable text assuming it belongs to this layer.
void setText(DataHandle handle, Containers::StringView text, const TextProperties& properties)
Set text.
void setText(DataHandle handle, Containers::StringView text, const TextProperties& properties, TextDataFlags flags)
Set text with different flags.
void setText(LayerDataHandle handle, Containers::StringView text, const TextProperties& properties)
Set text assuming it belongs to this layer.
void setText(LayerDataHandle handle, Containers::StringView text, const TextProperties& properties, TextDataFlags flags)
Set text with different flags assuming it belongs to this layer.
void updateText(DataHandle handle, UnsignedInt removeOffset, UnsignedInt removeSize, UnsignedInt insertOffset, Containers::StringView insertText, UnsignedInt cursor, UnsignedInt selection)
Update text, cursor position and selection in an editable text.
void updateText(DataHandle handle, UnsignedInt removeOffset, UnsignedInt removeSize, UnsignedInt insertOffset, Containers::StringView insertText, UnsignedInt cursor)
Update text and cursor position in an editable text.
void updateText(LayerDataHandle handle, UnsignedInt removeOffset, UnsignedInt removeSize, UnsignedInt insertOffset, Containers::StringView insertText, UnsignedInt cursor, UnsignedInt selection)
Update text, cursor position and selection in an editable text assuming it belongs to this layer.
void updateText(LayerDataHandle handle, UnsignedInt removeOffset, UnsignedInt removeSize, UnsignedInt insertOffset, Containers::StringView insertText, UnsignedInt cursor)
Update text and cursor position in an editable text.
void editText(DataHandle handle, TextEdit edit, Containers::StringView insert)
Edit text at current cursor position.
void editText(LayerDataHandle handle, TextEdit edit, Containers::StringView insert)
Edit text at current cursor position assuming it belongs to this layer.
void setGlyph(DataHandle handle, UnsignedInt glyph, const TextProperties& properties)
Set a single glyph.
template<class GlyphIndex>
void setGlyph(DataHandle handle, GlyphIndex glyph, const TextProperties& properties)
Set a single glyph with a glyph ID in a concrete enum type.
void setGlyph(LayerDataHandle handle, UnsignedInt glyph, const TextProperties& properties)
Set a single glyph assuming it belongs to this layer.
template<class GlyphIndex>
void setGlyph(LayerDataHandle handle, GlyphIndex glyph, const TextProperties& properties)
Set a single glyph with a glyph ID in a concrete enum type assuming it belongs to this layer.
auto color(DataHandle handle) const -> Color4
Text custom base color.
auto color(LayerDataHandle handle) const -> Color4
Text custom base color assuming it belongs to this layer.
void setColor(DataHandle handle, const Color4& color)
Set text custom base color.
void setColor(LayerDataHandle handle, const Color4& color)
Set text custom base color assuming it belongs to this layer.
auto padding(DataHandle handle) const -> Vector4
Text custom padding.
auto padding(LayerDataHandle handle) const -> Vector4
Text custom padding assuming it belongs to this layer.
void setPadding(DataHandle handle, const Vector4& padding)
Set text custom padding.
void setPadding(LayerDataHandle handle, const Vector4& padding)
Set text custom padding assuming it belongs to this layer.
void setPadding(DataHandle handle, Float padding)
Set text custom padding with all edges having the same value.
void setPadding(LayerDataHandle handle, Float padding)
Set text custom padding with all edges having the same value assuming it belongs to this layer.

Function documentation

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

Shared state used by this layer.

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

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

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

TextLayer& Magnum::Ui::TextLayer::assignAnimator(TextLayerStyleAnimator& 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.

TextLayerStyleAnimator* Magnum::Ui::TextLayer::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().

TextLayer& Magnum::Ui::TextLayer::setDefaultStyleAnimator(TextLayerStyleAnimator* 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 TextLayerStyleUniform> Magnum::Ui::TextLayer::dynamicStyleUniforms() const

Dynamic style uniforms.

Size of the returned view is at least Shared::dynamicStyleCount(). These uniforms are used by style indices greater than or equal to Shared::styleCount(). If Shared::hasEditingStyles() is true, size of the returned view is three times Shared::dynamicStyleCount() and the extra elements are referenced by dynamicStyleSelectionStyleTextUniform(UnsignedInt) const.

Int Magnum::Ui::TextLayer::dynamicStyleSelectionStyle(UnsignedInt id) const

Dynamic style selection style ID.

Expects that the id is less than Shared::dynamicStyleCount(). If given dynamic style has no associated selection style, returns -1, otherwise the returned index is a constant calculated from id and points to the dynamicEditingStyleUniforms() and dynamicEditingStylePaddings() arrays. Use dynamicStyleSelectionStyleTextUniform(UnsignedInt) const to retrieve ID of the uniform override applied to selected text.

Int Magnum::Ui::TextLayer::dynamicStyleSelectionStyleTextUniform(UnsignedInt) const

Dynamic style selection style text uniform IDs.

Expects that the id is less than Shared::dynamicStyleCount(). If given dynamic style has no associated selection style, returns -1, otherwise the returned index is a constant calculated based on id and points to the dynamicStyleUniforms() array.

In particular, contrary to the styles specified with Shared::setEditingStyle(), even if a text uniform wasn't passed in setDynamicStyleWithCursorSelection() or setDynamicStyleWithSelection(), the ID still points to a valid uniform index – the uniform data are instead copied over from the base style.

void Magnum::Ui::TextLayer::setDynamicStyle(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, Containers::ArrayView<const TextFeatureValue> features, const Vector4& padding)

Set a dynamic style for text only.

Parameters
id Dynamic style ID
uniform Style uniform
font Font handle
alignment Alignment
features Font features to use
padding Padding inside the node in order left, top, right, bottom

Expects that the id is less than Shared::dynamicStyleCount(), font is either FontHandle::Null or valid and alignment is not *GlyphBounds as the implementation can only align based on font metrics and cursor position, not actual glyph bounds. Shared::styleCount() plus id is then a style index that can be passed to create(), createGlyph() 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 TextLayerStyleUniform instances, FontHandle::Null, Text::Alignment::MiddleCenter with empty feature lists, zero padding vectors and no associated cursor or selection styles.

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. On the other hand, changing the font, alignment or features doesn't cause LayerState::NeedsDataUpdate to be set — the font, alignment and features get only used in the following call to create(), createGlyph(), setText() or setGlyph().

This function doesn't set any cursor or selection style, meaning they won't be shown at all if using this style for a TextDataFlag::Editable text. Use setDynamicStyleWithCursorSelection(), setDynamicStyleWithCursor() or setDynamicStyleWithSelection() to specify those as well.

void Magnum::Ui::TextLayer::setDynamicStyle(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, std::initializer_list<TextFeatureValue> features, const Vector4& padding)

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

void Magnum::Ui::TextLayer::setDynamicStyleWithCursorSelection(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, Containers::ArrayView<const TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& cursorUniform, const Vector4& cursorPadding, const TextLayerEditingStyleUniform& selectionUniform, const Containers::Optional<TextLayerStyleUniform>& selectionTextUniform, const Vector4& selectionPadding)

Set a dynamic style for text, cursor and selection.

Parameters
id Dynamic style ID
uniform Style uniform
font Font handle
alignment Alignment
features Font features to use
padding Padding inside the node in order left, top, right, bottom
cursorUniform Style uniform for the editing cursor
cursorPadding Padding around the editing cursor in order left, top, right, bottom
selectionUniform Style uniform for the editing selection
selectionTextUniform Style uniform applied to selected portions of the text or Containers::NullOpt if uniform should be used for the selection as well
selectionPadding Padding around the editing selection in order left, top, right, bottom

Expects that the id is less than Shared::dynamicStyleCount(), Shared::hasEditingStyles() is true, font is either FontHandle::Null or valid and alignment is not *GlyphBounds as the implementation can only align based on font metrics and cursor position, not actual glyph bounds. Shared::styleCount() plus id is then a style index that can be passed to create(), createGlyph() 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 TextLayerStyleUniform instances, FontHandle::Null, Text::Alignment::MiddleCenter with empty feature lists, zero padding vectors and no associated cursor or selection styles.

Calling this function causes LayerState::NeedsCommonDataUpdate to be set to trigger an upload of changed dynamic style uniform data. If padding, cursorPadding or selectionPadding changed, LayerState::NeedsDataUpdate gets set as well. On the other hand, changing the font, alignment or features doesn't cause any state flag to be set — the font, alignment and features get only used in the following call to create(), createGlyph(), setText() or setGlyph().

Note that while selectionPadding is merely a way to visually fine-tune the appearance, cursorPadding has to be non-zero horizontally to actually be visible at all.

Use setDynamicStyleWithCursor() if showing selection is not desirable for given style, setDynamicStyleWithSelection() if showing cursor is not desirable and setDynamicStyle() for non-editable styles or editable styles where showing neither cursor nor selection is desirable, for example in widgets that aren't focused.

void Magnum::Ui::TextLayer::setDynamicStyleWithCursorSelection(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, std::initializer_list<TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& cursorUniform, const Vector4& cursorPadding, const TextLayerEditingStyleUniform& selectionUniform, const Containers::Optional<TextLayerStyleUniform>& selectionTextUniform, const Vector4& selectionPadding)

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

void Magnum::Ui::TextLayer::setDynamicStyleWithCursor(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, Containers::ArrayView<const TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& cursorUniform, const Vector4& cursorPadding)

Set a dynamic style for text and cursor only.

Parameters
id Dynamic style ID
uniform Style uniform
font Font handle
alignment Alignment
features Font features to use
padding Padding inside the node in order left, top, right, bottom
cursorUniform Style uniform for the editing cursor
cursorPadding Padding around the editing cursor in order left, top, right, bottom

Compared to setDynamicStyleWithCursorSelection(UnsignedInt, const TextLayerStyleUniform&, FontHandle, Text::Alignment, Containers::ArrayView<const TextFeatureValue>, const Vector4&, const TextLayerEditingStyleUniform&, const Vector4&, const TextLayerEditingStyleUniform&, const Containers::Optional<TextLayerStyleUniform>&, const Vector4&) this doesn't set any selection style, meaning a TextDataFlag::Editable text will be rendered without the selection visible. Useful for example for dynamic styles of input widgets which show the cursor always but selection only when focused.

void Magnum::Ui::TextLayer::setDynamicStyleWithCursor(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, std::initializer_list<TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& cursorUniform, const Vector4& cursorPadding)

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

void Magnum::Ui::TextLayer::setDynamicStyleWithSelection(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, Containers::ArrayView<const TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& selectionUniform, const Containers::Optional<TextLayerStyleUniform>& selectionTextUniform, const Vector4& selectionPadding)

Set a dynamic style for text and selection only.

Parameters
id Dynamic style ID
uniform Style uniform
font Font handle
alignment Alignment
features Font features to use
padding Padding inside the node in order left, top, right, bottom
selectionUniform Style uniform for the editing selection
selectionTextUniform Style uniform applied to selected portions of the text or Containers::NullOpt if uniform should be used for the selection as well
selectionPadding Padding around the editing selection in order left, top, right, bottom

Compared to setDynamicStyleWithCursorSelection(UnsignedInt, const TextLayerStyleUniform&, FontHandle, Text::Alignment, Containers::ArrayView<const TextFeatureValue>, const Vector4&, const TextLayerEditingStyleUniform&, const Vector4&, const TextLayerEditingStyleUniform&, const Containers::Optional<TextLayerStyleUniform>&, const Vector4&) this doesn't set any cursor style, meaning a TextDataFlag::Editable text will be rendered without the cursor visible. Useful for example for dynamic styles of input widgets which show the selection always but cursor only when focused.

void Magnum::Ui::TextLayer::setDynamicStyleWithSelection(UnsignedInt id, const TextLayerStyleUniform& uniform, FontHandle font, Text::Alignment alignment, std::initializer_list<TextFeatureValue> features, const Vector4& padding, const TextLayerEditingStyleUniform& selectionUniform, const Containers::Optional<TextLayerStyleUniform>& selectionTextUniform, const Vector4& selectionPadding)

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::TextLayer::create(UnsignedInt style, Containers::StringView text, const TextProperties& properties, TextDataFlags flags = {}, NodeHandle node = NodeHandle::Null)

Create a text.

Parameters
style Style index
text Text to render
properties Text properties
flags Flags
node Node to attach to
Returns New data handle

Expects that Shared::setStyle() has been called, style is less than Shared::totalStyleCount() and TextProperties::font() is either FontHandle::Null or valid. Styling is driven from the TextLayerStyleUniform at index style. If TextProperties::font() is not null it's used, otherwise the default FontHandle assigned to given style is used and is expected to not be null. The FontHandle, whether coming from the style or from properties, is expected to have a font instance. Instance-less fonts can be only used to create single glyphs (such as various icons or images) with createGlyph().

If flags contain TextDataFlag::Editable, the text and properties are remembered and subsequently accessible through text() and textProperties(), cursor() position and selection are both set to text size. Currently, for editable text, the properties are expected to have empty TextProperties::features() — only the features supplied by the style are used for editable text.

DataHandle Magnum::Ui::TextLayer::create(UnsignedInt style, Containers::StringView text, const TextProperties& properties, NodeHandle node)

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

template<class StyleIndex>
DataHandle Magnum::Ui::TextLayer::create(StyleIndex style, Containers::StringView text, const TextProperties& properties, TextDataFlags flags = {}, NodeHandle node = NodeHandle::Null)

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

Casts style to UnsignedInt and delegates to create(UnsignedInt, Containers::StringView, const TextProperties&, TextDataFlags, NodeHandle).

template<class StyleIndex>
DataHandle Magnum::Ui::TextLayer::create(StyleIndex style, Containers::StringView text, const TextProperties& properties, NodeHandle node)

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::TextLayer::createGlyph(UnsignedInt style, UnsignedInt glyph, const TextProperties& properties, NodeHandle node = NodeHandle::Null)

Create a single glyph.

Parameters
style Style index
glyph Glyph ID to render
properties Text properties
node Node to attach to
Returns New data handle

Expects that Shared::setStyle() has been called, style is less than Shared::totalStyleCount() and TextProperties::font() is either FontHandle::Null or valid. Styling is driven from the TextLayerStyleUniform at index style. If TextProperties::font() is not null it's used, otherwise the default FontHandle assigned to given style is used and is expected to not be null. The glyph is expected to be less than Text::AbstractGlyphCache::fontGlyphCount() for given font.

Compared to create(), the glyph is aligned according to TextProperties::alignment() based on its bounding rectangle coming from the glyph cache, not based on font metrics. If the Text::Alignment is *Start or *End, it's converted to an appropriate *Left or *Right value based on TextProperties::layoutDirection() and shapeDirection(). The TextProperties::script(), language() and features() properties aren't used in any way.

template<class StyleIndex>
DataHandle Magnum::Ui::TextLayer::createGlyph(StyleIndex style, UnsignedInt glyph, const TextProperties& properties, NodeHandle node = NodeHandle::Null)

Create a single glyph with a style index in a concrete enum type.

Casts style to UnsignedInt and delegates to createGlyph(UnsignedInt, UnsignedInt, const TextProperties&, NodeHandle).

template<class GlyphIndex>
DataHandle Magnum::Ui::TextLayer::createGlyph(UnsignedInt style, GlyphIndex glyph, const TextProperties& properties, NodeHandle node = NodeHandle::Null)

Create a single glyph with a glyph ID in a concrete enum type.

Casts glyph to UnsignedInt and delegates to createGlyph(UnsignedInt, UnsignedInt, const TextProperties&, NodeHandle).

template<class StyleIndex, class GlyphIndex>
DataHandle Magnum::Ui::TextLayer::createGlyph(StyleIndex style, GlyphIndex glyph, const TextProperties& properties, NodeHandle node = NodeHandle::Null)

Create a single glyph with a style index and glyph ID in a concrete enum type.

Casts style and glyph to UnsignedInt and delegates to createGlyph(UnsignedInt, UnsignedInt, const TextProperties&, NodeHandle).

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

Remove a text.

Delegates to AbstractLayer::remove(DataHandle) and additionally marks the now-unused glyph run for removal in the next update().

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

Remove a text assuming it belongs to this layer.

Delegates to AbstractLayer::remove(LayerDataHandle) and additionally marks the now-unused glyph run for removal in the next update().

TextDataFlags Magnum::Ui::TextLayer::flags(DataHandle handle) const

Text flags.

Expects that handle is valid. Note that, to avoid implementation complexity, the flags can be only specified in create() or setText(DataHandle, Containers::StringView, const TextProperties&, TextDataFlags) and cannot be modified independently. Data created with createGlyph() or updated with setGlyph() have the flags always empty.

TextDataFlags Magnum::Ui::TextLayer::flags(LayerDataHandle handle) const

Text flags assuming it belongs to this layer.

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

UnsignedInt Magnum::Ui::TextLayer::glyphCount(DataHandle handle) const

Text glyph count.

Expects that handle is valid.

UnsignedInt Magnum::Ui::TextLayer::glyphCount(LayerDataHandle handle) const

Text glyph count assuming it belongs to this layer.

Expects that handle is valid.

Vector2 Magnum::Ui::TextLayer::size(DataHandle handle) const

Size of the laid out text.

Expects that handle is valid. For text laid out with create() or setText() the size is derived from ascent, descent and advances of individual glyphs, not from actual area of the glyphs being drawn, as that may not be known at that time. For createGlyph() or setGlyph() the size is based on the actual glyph size coming out of the glyph cache.

Vector2 Magnum::Ui::TextLayer::size(LayerDataHandle handle) const

Size of the laid out text assuming it belongs to this layer.

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

Containers::Pair<UnsignedInt, UnsignedInt> Magnum::Ui::TextLayer::cursor(DataHandle handle) const

Cursor and selection position in an editable text.

Expects that handle is valid and the text was created or set with TextDataFlag::Editable present. Both values are guaranteed to be always within bounds of a corresponding text(DataHandle) const. The first value is cursor position, the second value denotes the other end of the selection. If both are the same, there's no selection.

Containers::Pair<UnsignedInt, UnsignedInt> Magnum::Ui::TextLayer::cursor(LayerDataHandle handle) const

Cursor and selection position in an editable text assuming it belongs to this layer.

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

void Magnum::Ui::TextLayer::setCursor(DataHandle handle, UnsignedInt position, UnsignedInt selection)

Set cursor position and selection in an editable text.

Low-level interface for cursor positioning. See updateText() for a low-level interface to perform text modifications together with cursor positioning, use editText() to perform higher-level operations with UTF-8 and text directionality awareness.

Expects that handle is valid and the text was created or set with TextDataFlag::Editable enabled. Both the position and selection is expected to be less or equal to text() size. The distance between the two is what's selected — if selection is less than position, the selection is before the cursor, if position is less than selection, the selection is after the cursor. If they're the same, there's no selection. No UTF-8 sequence boundary adjustment is done for either of the two, i.e. it's possible to move the cursor or selection inside a multi-byte UTF-8 sequence.

Calling this function causes LayerState::NeedsDataUpdate to be set, unless the operation performed is a no-op, which is when both removeSize and insertText size are both 0 and cursor is equal to cursor().

void Magnum::Ui::TextLayer::setCursor(DataHandle handle, UnsignedInt position)

Set cursor position in an editable text.

Same as calling setCursor(DataHandle, UnsignedInt, UnsignedInt) with position passed to both position and selection, i.e. with nothing selected.

void Magnum::Ui::TextLayer::setCursor(LayerDataHandle handle, UnsignedInt position, UnsignedInt selection)

Set cursor position and selection in an editable text assuming it belongs to this layer.

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

void Magnum::Ui::TextLayer::setCursor(LayerDataHandle handle, UnsignedInt position)

Set cursor position in an editable text assuming it belongs to this layer.

Same as calling setCursor(LayerDataHandle, UnsignedInt, UnsignedInt) with position passed to both position and selection, i.e. with nothing selected.

TextProperties Magnum::Ui::TextLayer::textProperties(DataHandle handle) const

Properties used for shaping an editable text.

Expects that handle is valid and the text was created with TextDataFlag::Editable set. Returns content of TextProperties passed in previous create() or setText() call for handle, except for TextProperties::font() that is picked from the style if it was passed as FontHandle::Null.

TextProperties Magnum::Ui::TextLayer::textProperties(LayerDataHandle handle) const

Properties used for shaping an editable text assuming it belongs to this layer.

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

Containers::StringView Magnum::Ui::TextLayer::text(DataHandle handle) const

Contents of an editable text.

Expects that handle is valid and the text was created or set with TextDataFlag::Editable present. The returned view is only valid until the next create(), setText() or update() call and is never Containers::StringViewFlag::NullTerminated.

Containers::StringView Magnum::Ui::TextLayer::text(LayerDataHandle handle) const

Contents of an editable text assuming it belongs to this layer.

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

void Magnum::Ui::TextLayer::setText(DataHandle handle, Containers::StringView text, const TextProperties& properties)

Set text.

Expects that handle is valid and TextProperties::font() is either FontHandle::Null or valid. If not null, the TextProperties::font() is used, otherwise the default FontHandle assigned to style() is used and expected to not be null. Note that it's not possible to change the font alone with setStyle(), it only can be done when setting the text. The FontHandle, whether coming from the style or from properties, is expected to have a font instance. Instance-less fonts can be only used to set single glyphs (such as various icons or images) with setGlyph().

This function preserves existing flags() for given handle, use setText(DataHandle, Containers::StringView, const TextProperties&, TextDataFlags) to supply different TextDataFlags for the new text.

If flags() contain TextDataFlag::Editable, the text and properties are remembered and subsequently accessible through text() and textProperties(), cursor() position and selection are both set to text size. Currently, for editable text, the properties are expected to have empty TextProperties::features() — only the features supplied by the style are used for editable text.

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

void Magnum::Ui::TextLayer::setText(DataHandle handle, Containers::StringView text, const TextProperties& properties, TextDataFlags flags)

Set text with different flags.

Like setText(DataHandle, Containers::StringView, const TextProperties&) but supplying different TextDataFlags for the new text instead of preserving existing flags().

void Magnum::Ui::TextLayer::setText(LayerDataHandle handle, Containers::StringView text, const TextProperties& properties)

Set text assuming it belongs to this layer.

Like setText(DataHandle, Containers::StringView, const TextProperties&) but without checking that handle indeed belongs to this layer. See its documentation for more information.

void Magnum::Ui::TextLayer::setText(LayerDataHandle handle, Containers::StringView text, const TextProperties& properties, TextDataFlags flags)

Set text with different flags assuming it belongs to this layer.

Like setText(DataHandle, Containers::StringView, const TextProperties&, TextDataFlags) but without checking that handle indeed belongs to this layer. See its documentation for more information.

void Magnum::Ui::TextLayer::updateText(DataHandle handle, UnsignedInt removeOffset, UnsignedInt removeSize, UnsignedInt insertOffset, Containers::StringView insertText, UnsignedInt cursor, UnsignedInt selection)

Update text, cursor position and selection in an editable text.

Parameters
handle Handle which to update
removeOffset Offset at which to remove
removeSize Count of bytes to remove
insertOffset Offset at which to insert after the removal
insertText Text to insert after the removal
cursor Cursor position to set after the removal and subsequent insert
selection Selection to set after the removal and subsequent insert

Low-level interface for text removal and insertion together with cursor positioning. See setCursor() for just cursor positioning alone, use editText() to perform higher-level operations with UTF-8 and text directionality awareness.

Expects that handle is valid and the text was created or set with TextDataFlag::Editable enabled. The removeOffset together with removeSize is expected to be less or equal to text() size; insertOffset then equal to the text size without removeSize; cursor and selection then to text size without removeSize but with insertText size. The distance between cursor and selection is what's selected — if selection is less than cursor, the selection is before the cursor, if cursor is less than selection, the selection is after the cursor. If they're the same, there's no selection. No UTF-8 sequence boundary adjustment is done for any of these, i.e. it's possible to remove or insert partial multi-byte UTF-8 sequences and position the cursor inside them as well.

Calling this function causes LayerState::NeedsDataUpdate to be set, unless the operation performed is a no-op, which is when both removeSize and insertText size are both 0 and cursor is equal to cursor().

void Magnum::Ui::TextLayer::updateText(DataHandle handle, UnsignedInt removeOffset, UnsignedInt removeSize, UnsignedInt insertOffset, Containers::StringView insertText, UnsignedInt cursor)

Update text and cursor position in an editable text.

Same as calling updateText(DataHandle, UnsignedInt, UnsignedInt, UnsignedInt, Containers::StringView, UnsignedInt, UnsignedInt) with position passed to both position and selection, i.e. with nothing selected.

void Magnum::Ui::TextLayer::updateText(LayerDataHandle handle, UnsignedInt removeOffset, UnsignedInt removeSize, UnsignedInt insertOffset, Containers::StringView insertText, UnsignedInt cursor, UnsignedInt selection)

Update text, cursor position and selection in an editable text assuming it belongs to this layer.

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

void Magnum::Ui::TextLayer::updateText(LayerDataHandle handle, UnsignedInt removeOffset, UnsignedInt removeSize, UnsignedInt insertOffset, Containers::StringView insertText, UnsignedInt cursor)

Update text and cursor position in an editable text.

Same as calling updateText(DataHandle, UnsignedInt, UnsignedInt, UnsignedInt, Containers::StringView, UnsignedInt, UnsignedInt) with position passed to both position and selection, i.e. with nothing selected.

void Magnum::Ui::TextLayer::editText(DataHandle handle, TextEdit edit, Containers::StringView insert)

Edit text at current cursor position.

High-level interface for text editing, mapping to usual user interface operations. Delegates to setCursor() or updateText() internally.

Expects that handle is valid, the text was created or set with TextDataFlag::Editable enabled and insert is non-empty only if appropriate edit operation is used. See documentation of the TextEdit enum for detailed behavior of each operation.

Calling this function causes LayerState::NeedsDataUpdate to be set, unless the operation performed is a no-op such as inserting empty text or moving a cursor / deleting a character to the left with the cursor already being on the leftmost side of the text.

void Magnum::Ui::TextLayer::editText(LayerDataHandle handle, TextEdit edit, Containers::StringView insert)

Edit text at current cursor position assuming it belongs to this layer.

Like editText(DataHandle, TextEdit, Containers::StringView) but without checking that handle indeed belongs to this layer. See its documentation for more information.

void Magnum::Ui::TextLayer::setGlyph(DataHandle handle, UnsignedInt glyph, const TextProperties& properties)

Set a single glyph.

Expects that handle is valid and TextProperties::font() is either FontHandle::Null or valid. If not null, the TextProperties::font() is used, otherwise the default FontHandle assigned to style() is used and expected to not be null. Note that it's not possible to change the font alone with setStyle(), it only can be done when setting the glyph. The glyph is expected to be less than Text::AbstractGlyphCache::fontGlyphCount() for given font.

Compared to setText(), the glyph is aligned according to TextProperties::alignment() based on its bounding rectangle coming from the glyph cache, not based on font metrics. The TextProperties::script(), language(), shapeDirection(), layoutDirection() and features() properties aren't used in any way; flags() get reset to empty. Note that it's also possible to change a handle that previously contained a text to a single glyph and vice versa — the internal representation of both is the same.

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

template<class GlyphIndex>
void Magnum::Ui::TextLayer::setGlyph(DataHandle handle, GlyphIndex glyph, const TextProperties& properties)

Set a single glyph with a glyph ID in a concrete enum type.

Casts glyph to UnsignedInt and delegates to setGlyph(DataHandle, UnsignedInt, const TextProperties&).

void Magnum::Ui::TextLayer::setGlyph(LayerDataHandle handle, UnsignedInt glyph, const TextProperties& properties)

Set a single glyph assuming it belongs to this layer.

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

template<class GlyphIndex>
void Magnum::Ui::TextLayer::setGlyph(LayerDataHandle handle, GlyphIndex glyph, const TextProperties& properties)

Set a single glyph with a glyph ID in a concrete enum type assuming it belongs to this layer.

Casts glyph to UnsignedInt and delegates to setGlyph(LayerDataHandle, UnsignedInt, const TextProperties&).

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

Text custom base color.

Expects that handle is valid.

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

Text custom base color assuming it belongs to this layer.

Expects that handle is valid.

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

Set text custom base color.

Expects that handle is valid. TextLayerStyleUniform::color is multiplied with color. Applies to style override for selected text as well, but not to TextLayerEditingStyleUniform::backgroundColor for cursor and selection rectangles. 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::TextLayer::setColor(LayerDataHandle handle, const Color4& color)

Set text 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::TextLayer::padding(DataHandle handle) const

Text custom padding.

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

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

Text custom padding assuming it belongs to this layer.

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

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

Set text 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().

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

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

Set text 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::TextLayer::setPadding(DataHandle handle, Float padding)

Set text 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().

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

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

Set text 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.