class new in Git master
#include <Magnum/Ui/AbstractLayer.h>
AbstractLayer Base for data layers.
Attaches data to particular nodes in the UI hierarchy, providing rendering and event handling functionality. See the AbstractUserInterface class documentation for introduction and overview of builtin layers. The following sections describe behavior common to all layers and provide a guide for implementing custom layers from scratch.
Layer data creation and removal
Layer data get created using create() with an optional NodeHandle to attach the data to, returning a DataHandle. The create() function is protected
on the AbstractLayer, as concrete implementations require additional parameters. Such as TextLayer::
The DataHandle is a combination of a LayerHandle, identifying a particular layer the data is coming from, and a LayerDataHandle identifying data within given layer, extractible using dataHandleLayer() and dataHandleData(), respectively. All builtin layer APIs taking a DataHandle have overloads taking the smaller LayerDataHandle type as well, which is useful to save space in case you're storing the handles and know which layer they come from.
/* Query the node given data from an arbitrary layer is attached to */ Ui::DataHandle data = …; Ui::NodeHandle node = ui.layer(dataHandleLayer(data)).node(data); /* Remembering a data handle from a known layer in order to update it later */ Ui::LayerDataHandle bg = dataHandleData(ui.baseLayer().create(…)); … ui.baseLayer().setColor(bg, 0x3bd267_rgbf);
Data lifetime is implicitly tied to a NodeHandle they're attached to, if any, so if the node or any of its parents get removed, all data attached to it from all layers get removed. It's also possible to remove the data directly using remove(), after which the DataHandle (or LayerDataHandle) becomes invalid. The remove() function is again protected
as concrete layers may want to extend its behavior, such as in case of TextLayer::
Node data attachments
Besides attaching directly in create() or its derivatives in subclasses, node attachment can be modified using attach(). A data can be also detached from a node by passing NodeHandle::
Ui::DataHandle label = textLayer.create(…, node); /* Stop showing the label but keep it instead of removing */ textLayer.attach(label, Ui::NodeHandle::Null); … /* Show it again */ textLayer.attach(label, node);
Creating a custom drawing layer
If none of the builtin layers provide desired functionality, it's possible to implement a custom layer that then gets added among others. At the very least, the doFeatures() function needs to be implemented. Based on which LayerFeature values it returns, other interfaces need to be implemented as well.
As an example, let's assume we want to implement a simple layer that draws colored quads with OpenGL. In other words, a very small subset of what BaseLayer provides. The initial setup could look like this, with doFeatures() returning LayerFeature::color
at creation time:
class QuadLayer: public Ui::AbstractLayer { public: explicit QuadLayer(Ui::LayerHandle handle); Ui::DataHandle create(const Color3& color, Ui::NodeHandle node = Ui::NodeHandle::Null); void remove(Ui::DataHandle handle); void remove(Ui::LayerDataHandle handle); private: Ui::LayerFeatures doFeatures() const override { return Ui::LayerFeature::Draw; } … struct Vertex { Vector2 position; Color3 color; }; GL::Buffer _indices, _vertices; GL::Mesh _mesh; Shaders::FlatGL2D _shader{Shaders::FlatGL2D::Configuration{} .setFlags(Shaders::FlatGL2D::Flag::VertexColor)}; Containers::Array<Color3> _colors; };
Internally the layer contains a struct
definition describing vertex layout, GL::
QuadLayer::QuadLayer(Ui::LayerHandle handle): Ui::AbstractLayer{handle} { _mesh.addVertexBuffer(_vertices, 0, Shaders::FlatGL2D::Position{}, Shaders::FlatGL2D::Color3{}) .setIndexBuffer(_indices, 0, GL::MeshIndexType::UnsignedInt); }
A common pattern for storing data associated with UI handles, as shown in Handles and resource ownership, is to have a contiguous array indexed by the handle ID, which is the case with the _colors
member above. Creating a data delegates to the base create(), extracts the handle ID using dataHandleId(), enlarges the array to fit it, and saves the color there:
Ui::DataHandle QuadLayer::create(const Color3& color, Ui::NodeHandle node) { Ui::DataHandle handle = Ui::AbstractLayer::create(node); UnsignedInt dataId = dataHandleId(handle); if(dataId >= _colors.size()) arrayResize(_colors, dataId + 1); _colors[dataId] = color; return handle; }
As the layer stores just plain data, there's nothing to be done when data get removed — the color just stays unused in the array until it's overwritten by a different handle that reuses the same ID. Data removal thus simply delegates to the base remove(), providing both a DataHandle and a LayerDataHandle overload for convenience. Dealing with resources that need explicit destruction is described later.
void QuadLayer::remove(Ui::DataHandle handle) { Ui::AbstractLayer::remove(handle); } void QuadLayer::remove(Ui::LayerDataHandle handle) { Ui::AbstractLayer::remove(handle); }
Implementing an update function
Each time AbstractUserInterface::
The doUpdate() function receives a broad set of inputs, initially we'll be interested in just the essential parameters to generate the quad mesh with, shown in the snippet below. The dataIds
view is a list of data handle IDs attached to currently visible nodes, ordered back-to-front, so when they're drawn in this order, they overlap correctly. Each time doUpdate() is called, the dataIds
may have different size and contain different entries in different order, based on what's currently visible.
void QuadLayer::doUpdate(Ui::LayerStates, const Containers::StridedArrayView1D<const UnsignedInt>& dataIds, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const Vector2>& nodeOffsets, const Containers::StridedArrayView1D<const Vector2>& nodeSizes, const Containers::StridedArrayView1D<const Float>&, Containers::BitArrayView, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&)
Next there are nodeOffsets
and nodeSizes
, containing final node offsets and sizes with node hierarchy and all layouts applied. The views contain offsets and sizes for all nodes in the UI and are indexed by node ID. In other words, they're not matching the order in dataIds
— instead, to get a NodeHandle attachment for a particular data ID, the view returned from nodes() is used, with nodeHandleId() extracting the ID out of the handle. Nodes that are not visible have offsets and sizes left in an unspecified state, but as dataIds
only contain IDs attached to currently visible nodes, it's guaranteed that the nodes[dataId]
is never NodeHandle::nodeOffsets[nodeId]
and nodeSizes[nodeId]
have a meaningful value.
With the above inputs, the vertexData
(containing 4
vertices for each quad), and indexData
(with 6
indices corresponding to the two triangles) are filled, and then those are uploaded to the GPU mesh for drawing:
{ Containers::Array<Vertex> vertexData{NoInit, dataIds.size()*4}; Containers::Array<UnsignedInt> indexData{NoInit, dataIds.size()*6}; Containers::StridedArrayView1D<const Ui::NodeHandle> nodes = this->nodes(); for(UnsignedInt i = 0; i != dataIds.size(); ++i) { UnsignedInt dataId = dataIds[i]; UnsignedInt nodeId = nodeHandleId(nodes[dataId]); Range2D rect = Range2D::fromSize(nodeOffsets[nodeId], nodeSizes[nodeId]); /* 0--1 0-2 3 vertices: | | indices: |/ /| 2--3 1 4-5 */ for(UnsignedInt j = 0; j != 4; ++j) { vertexData[i*4 + j].position = Math::lerp(rect.min(), rect.max(), j); vertexData[i*4 + j].color = _colors[dataId]; } Utility::copy({i*4 + 0, i*4 + 2, i*4 + 1, i*4 + 1, i*4 + 2, i*4 + 3}, indexData.sliceSize(i*6, 6)); } _vertices.setData(vertexData); _indices.setData(indexData); _mesh.setCount(indexData.size()); }
Drawing the data
In the doUpdate() implementation above, we filled the mesh with vertex positions in UI units. To apply a correct projection in the shader, we need to know how large the UI is. The UI size is passed to the doSetSize() interface, which is called at least once before the first draw, and then each time the UI size changes. We'll use the size
to form a projection matrix passed to Shaders::
void QuadLayer::doSetSize(const Vector2& size, const Vector2i&) { _shader.setTransformationProjectionMatrix( Matrix3::scaling(Vector2::yScale(-1.0f))* Matrix3::translation({-1.0f, -1.0f})* Matrix3::projection(size)); }
Finally, the doDraw() interface is called with almost the same inputs as doUpdate(), but additionally an offset
and count
is supplied, describing the range of dataIds
that is meant to be drawn. Since all setup and upload was done in doUpdate() already, we don't need to use any other arguments and just use the appropriate index range, i.e. multiplying the offset
and count
by 6
to get the quad index range corresponding to the data:
void QuadLayer::doDraw( const Containers::StridedArrayView1D<const UnsignedInt>&, std::size_t offset, std::size_t count, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, std::size_t, std::size_t, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Float>&, Containers::BitArrayView, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&) { _mesh .setIndexOffset(offset*6) .setCount(count*6); _shader.draw(_mesh); }
Drawing with alpha blending
The above showed the simplest possible case of drawing a fully opaque mesh. In many cases you'll however want to have some sort of transparency, even if just for smooth edges. For that, the layer can advertise Ui::QuadLayer
would look like this instead:
class QuadLayer: public Ui::AbstractLayer { public: explicit QuadLayer(Ui::LayerHandle handle): Ui::AbstractLayer{handle} { _mesh.addVertexBuffer(_vertices, 0, Shaders::FlatGL2D::Position{}, Shaders::FlatGL2D::Color4{}); … } Ui::DataHandle create(const Color4& color, Ui::NodeHandle node = Ui::NodeHandle::Null); … private: Ui::LayerFeatures doFeatures() const override { return Ui::LayerFeature::Draw| Ui::LayerFeature::DrawUsesBlending; } … struct Vertex { Vector2 position; Color4 color; }; … Containers::Array<Color4> _colors; };
Note that the rest of the Ui library uses a premultiplied alpha workflow and the custom layer should match that. Thus for example making a color with 50% transparency is rgba*0.5f
rather than {rgb, 0.5f}
. You can also use the Color4::
Dealing with node opacity and disabled state
Among the other inputs passed to doUpdate() are nodeOpacities
and nodesEnabled
. They reflect presence of NodeFlag::
void QuadLayer::doUpdate(Ui::LayerStates, const Containers::StridedArrayView1D<const UnsignedInt>& dataIds, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const Vector2>& nodeOffsets, const Containers::StridedArrayView1D<const Vector2>& nodeSizes, const Containers::StridedArrayView1D<const Float>& nodeOpacities, Containers::BitArrayView nodesEnabled, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&) { … for(UnsignedInt i = 0; i != dataIds.size(); ++i) { … Color4 color = _colors[dataId]; if(!nodesEnabled[nodeId]) color.rgb() = Color3{color.value()*0.75f}; color *= nodeOpacities[nodeId]; for(UnsignedInt j = 0; j != 4; ++j) vertexData[i*4 + j].color = color; } … }
You're free to do anything else with these inputs — for example have an entirely different "disabled look", or even ignore them altogether if given layer is not expected to be used on such nodes.
Taking clip rectangles into account
If the layer may get used within nodes that have NodeFlag::clipRectOffsets
and clipRectSizes
views passed to doUpdate() describe clip rectangle placement, clipRectIds
and clipRectDataCounts
then specify which of the rectangles is used for which subrange of dataIds
. There's always at least one clip rectangle present and the sum of clipRectDataCounts
is equal to size of dataIds
.
As with node opacity and disabled state above, the actual implementation is entirely up to the layer itself. One option is to apply the clip rectangles directly to the vertex data, in this case performing an intersection of the quad with the clip rectangle using Math::
void QuadLayer::doUpdate(Ui::LayerStates, const Containers::StridedArrayView1D<const UnsignedInt>& dataIds, const Containers::StridedArrayView1D<const UnsignedInt>& clipRectIds, const Containers::StridedArrayView1D<const UnsignedInt>& clipRectDataCounts, const Containers::StridedArrayView1D<const Vector2>& nodeOffsets, const Containers::StridedArrayView1D<const Vector2>& nodeSizes, const Containers::StridedArrayView1D<const Float>&, Containers::BitArrayView, const Containers::StridedArrayView1D<const Vector2>& clipRectOffsets, const Containers::StridedArrayView1D<const Vector2>& clipRectSizes, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&) { … UnsignedInt clipRect = 0; UnsignedInt clipRectDataCount = 0; for(UnsignedInt i = 0; i != dataIds.size(); ++i) { … /* If the clip rectangle is empty, no clipping is active */ Range2D rect = Range2D::fromSize(nodeOffsets[nodeId], nodeSizes[nodeId]); Range2D clip = Range2D::fromSize(clipRectOffsets[clipRectIds[clipRect]], clipRectSizes[clipRectIds[clipRect]]); if(!clip.size().isZero()) rect = Math::intersect(rect, clip); for(UnsignedInt j = 0; j != 4; ++j) vertexData[i*4 + j].position = Math::lerp(rect.min(), rect.max(), j); /* The clip rect got applied to all data it affects, move to the next */ if(++clipRectDataCount == clipRectDataCounts[clipRect]) { ++clipRect; clipRectDataCount = 0; } } }
Clipping using GPU scissor rectangles
It's not always possible or efficient to clip the vertex data directly. In such cases it's possible to make use of scissor rectangles instead. Similarly as with blending, the layer advertises Ui::
Ui::LayerFeatures QuadLayer::doFeatures() const { return …|Ui::LayerFeature::DrawUsesScissor; } void QuadLayer::doSetSize(const Vector2& size, const Vector2i& framebufferSize) { … _size = size; _framebufferSize = framebufferSize; }
And then, instead of clipping inside doUpdate(), the doDraw() function performs not just a single draw, but one for each clip rectangle. To match OpenGL framebuffer coordinates that have origin bottom left and Y up, the clip rectangles get scaled, Y-flipped and converted to integers.
void QuadLayer::doDraw( const Containers::StridedArrayView1D<const UnsignedInt>&, std::size_t offset, std::size_t, const Containers::StridedArrayView1D<const UnsignedInt>& clipRectIds, const Containers::StridedArrayView1D<const UnsignedInt>& clipRectDataCounts, std::size_t clipRectOffset, std::size_t clipRectCount, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Vector2>&, const Containers::StridedArrayView1D<const Float>&, Containers::BitArrayView, const Containers::StridedArrayView1D<const Vector2>& clipRectOffsets, const Containers::StridedArrayView1D<const Vector2>& clipRectSizes) { std::size_t clipDataOffset = offset; for(std::size_t i = 0; i != clipRectCount; ++i) { UnsignedInt clipRectId = clipRectIds[clipRectOffset + i]; UnsignedInt clipRectDataCount = clipRectDataCounts[clipRectOffset + i]; Vector2i clipOffset = clipRectOffsets[clipRectId]*_framebufferSize/_size; Vector2i clipSize = clipRectSizes[clipRectId]*_framebufferSize/_size; /* If the clip rectangle is empty, not clipping anything, reset the scissor back to the whole framebuffer */ GL::Renderer::setScissor(clipSize.isZero() ? Range2Di::fromSize({}, _framebufferSize) : Range2Di::fromSize( {clipOffset.x(), _framebufferSize.y() - clipOffset.y() - clipSize.y()}, clipSize)); _mesh .setIndexOffset(clipDataOffset*6) .setCount(clipRectDataCount*6); _shader.draw(_mesh); clipDataOffset += clipRectDataCount; } }
You can assume that the list of clip rectangles is made in a way that minimizes the amount of extra draw calls this approach needs compared to culling CPU-side.
Setters and triggering data updates
So far, all updates and drawing happened only in a response to the node hierarchy changing in some way — nodes changing place, visibility, or data being attached / detached. Combined with the user interface only redrawing when needed it means that any updates to the layer data, such as changing the color of a particular quad, need to notify the UI that an update and redraw is needed. This is done with setNeedsUpdate(). A color setter would thus look like this:
void QuadLayer::setColor(Ui::DataHandle handle, const Color3& color) { CORRADE_ASSERT(isHandleValid(handle), "QuadLayer::setColor(): invalid handle" << handle, ); _colors[Ui::dataHandleId(handle)] = color; setNeedsUpdate(Ui::LayerState::NeedsDataUpdate); }
As the snippet shows, it's a good practice to check for handle validity, to ensure stale handles don't accidentally change unrelated data. All builtin APIs have those checks, but in this case we're directly accessing our own data array and thus nothing else can check the validity for us. Additionally it makes sense to provide also a LayerDataHandle overload so the setter can be called with the layer-specific handle type as well:
void QuadLayer::setColor(Ui::LayerDataHandle handle, const Color3& color) { CORRADE_ASSERT(isHandleValid(handle), "QuadLayer::setColor(): invalid handle" << handle, ); _colors[Ui::layerDataHandleId(handle)] = color; setNeedsUpdate(Ui::LayerState::NeedsDataUpdate); }
Populating the vertex data in data order instead of draw order
So far, the doUpdate() function populated the mesh with vertex data in order as drawn in that particular frame. While that makes the mesh always contain only exactly what's needed, it means reuploading also data that didn't change, such as the quad colors in our QuadLayer
, just in a slightly different order.
Putting aside node opacity and disabled state for now, an alternative approach could be to fill the vertex colors directly in create()
and setColor()
instead of managing a CPU-side _colors
array and then copying from it. The doUpdate() would then map the vertex buffer, update just the positions in it, and only the index buffer gets fully regenerated every time. We can use capacity() to size the buffer mapping, as it's an upper bound for all data IDs:
void QuadLayer::doUpdate(…) { /* vertices[i*4] to vertices[i*4 + 4] is a quad for data i */ Containers::ArrayView<Vertex> vertices = Containers::arrayCast<Vertex>( _vertices.map(0, capacity()*sizeof(Vertex)*4, GL::Buffer::MapFlag::Write)); Containers::StridedArrayView1D<const Ui::NodeHandle> nodes = this->nodes(); for(UnsignedInt i = 0; i != dataIds.size(); ++i) { UnsignedInt dataId = dataIds[i]; UnsignedInt nodeId = nodeHandleId(nodes[dataId]); Range2D rect = Range2D::fromSize(nodeOffsets[nodeId], nodeSizes[nodeId]); for(UnsignedInt j = 0; j != 4; ++j) vertices[dataId*4 + j].position = Math::lerp(rect.min(), rect.max(), j); } _vertices.unmap(); /* indexData[i*6] to indexData[i*6 + 6] draws a quad for dataIds[i] */ Containers::Array<UnsignedInt> indexData{NoInit, dataIds.size()*6}; for(UnsignedInt i = 0; i != dataIds.size(); ++i) { UnsignedInt dataId = dataIds[i]; Utility::copy({dataId*4 + 0, dataId*4 + 2, dataId*4 + 1, dataId*4 + 1, dataId*4 + 2, dataId*4 + 3}, indexData.sliceSize(i*6, 6)); } _indices.setData(indexData); _mesh.setCount(indexData.size()); }
Partial updates
The first argument to doUpdate() is a set of LayerState bits, which enumerates the reasons why an update needs to done. For example, in given frame only LayerState::
The doUpdate() implementation can make use of these states to perform just partial updates. This makes sense especially in the above case where the draw data are stored in a way that's independent from the actual draw order, because otherwise even changes in node visibility would require rebuilding all data in the new order. Note that there are various interactions between the states, see particular LayerState values for more information.
void QuadLayer::doUpdate(Ui::LayerStates state, …) { if(state & Ui::LayerState::NeedsNodeOffsetSizeUpdate) { /* Perform updates to vertex positions */ } if(state & (Ui::LayerState::NeedsNodeEnabledUpdate| Ui::LayerState::NeedsNodeOpacityUpdate| Ui::LayerState::NeedsDataUpdate)) { /* Perform updates to vertex colors */ } if(state & Ui::LayerState::NeedsNodeOrderUpdate) { /* Perform updates to the index buffer */ } }
Explicitly and implicitly triggered updates
In addition to LayerState::
For example, in the original case of data being stored in draw order the index buffer is always the same and only needs to be updated if it isn't large enough. For that, the create()
implementation would call setNeedsUpdate() with LayerState::
Ui::DataHandle QuadLayer::create(const Color3& color, Ui::NodeHandle node) { UnsignedInt capacityBefore = capacity(); Ui::DataHandle handle = Ui::AbstractLayer::create(node); UnsignedInt dataId = dataHandleId(handle); if(dataId >= capacityBefore) setNeedsUpdate(Ui::LayerState::NeedsCommonDataUpdate); … return handle; } void QuadLayer::doUpdate(Ui::LayerStates state, …) { if(state & Ui::LayerState::NeedsCommonDataUpdate) { Containers::Array<UnsignedInt> indexData{NoInit, capacity()*6}; for(UnsignedInt i = 0; i != capacity(); ++i) { Utility::copy({i*4 + 0, i*4 + 2, i*4 + 1, i*4 + 1, i*4 + 2, i*4 + 3}, indexData.sliceSize(i*6, 6)); } _indices.setData(indexData); } … }
It's of course possible to perform the index buffer upload directly in create()
as well, but when creating a lot of data the buffer could get reuploaded several times over. Deferring the update like this makes it updated at most once per frame.
The LayerState::
Finally, it might not always be possible to have setNeedsUpdate() called in order to trigger an update. One such case is when the layer polls data from an external source, and there's no way for the source to explicitly notify the layer about updates. To solve this, the layer can implement the doState() interface and return LayerState::
Ui::LayerStates QuadLayer::doState() const { if(_lastUpdate != _externalColors.lastUpdate()) return Ui::LayerState::NeedsDataUpdate; return {}; } void QuadLayer::doUpdate(Ui::LayerStates state, …) { if(state & Ui::LayerState::NeedsDataUpdate) { _lastUpdate = _externalColors.lastUpdate(); … } … }
Resource cleanup on data removal
Besides plain data, it's possible for layers to store heavier resources. As an example, let's assume the quads are textured, with each such quad using a dedicated GL::
class QuadLayer: public Ui::AbstractLayer { … private: Containers::Array<Containers::Optional<GL::Texture2D>> _textures; }; void QuadLayer::remove(Ui::DataHandle handle) { Ui::AbstractLayer::remove(handle); _textures[dataHandleId(handle)] = Containers::NullOpt; }
More commonly however, instead of users explicitly calling remove(), the data get removed as a consequence of node hierarchy removal. For that there's the doClean() interface, which gets called as part of the regular update if any cascaded removes happened since last time. It gets a bitmask marking which data IDs got removed, which we use to perform a cleanup:
void QuadLayer::doClean(Containers::BitArrayView dataIdsToRemove) { for(UnsignedInt i = 0; i != dataIdsToRemove.size(); ++i) { if(i) _textures[i] = Containers::NullOpt; } }
The doClean() interface is designed like this instead of something like calling remove() in a loop in order to allow implementations to batch the operations. For example, if the textures would be instead in some sort of an atlas that needs repacking afterwards, doing it just once for all removed textures would be more efficient than repacking after each removal.
Custom event handling layers
While the builtin EventLayer allows attaching callbacks to various high-level events like taps, clicks or pinch gestures, you may need specialized behavior that can only be implemented by directly accessing the low-level event interfaces in a custom layer. Another use case is implementing event handling in addition to drawing, for example to implicitly handle hover and pressed state. While it could be done externally with EventLayer::
A layer that wants to handle events advertises LayerFeature::do*Event()
interfaces, overview of which is below. As with drawing, an event handling layer doesn't need to implement doUpdate() or doClean() — if it has nothing to do on a per-data basis, it can contain only event handlers alone.
Reacting to hover
For a simple introductory example, let's make the QuadLayer
react to hover by making given quad brighter. The doPointerMoveEvent() is called in a response to a pointer moving over area of a node that given dataId
is attached to. With PointerMoveEvent::
Ui::LayerFeatures QuadLayer::doFeatures() const { return …|Ui::LayerFeature::Event; } void QuadLayer::doPointerMoveEvent(UnsignedInt, Ui::PointerMoveEvent& event) { if(!event.isPrimary()) return; event.setAccepted(); }
If the move event gets accepted and the node wasn't hovered already, doPointerEnterEvent() gets called next. Once the pointer moves outside of the node area, doPointerLeaveEvent() gets called. We'll simply brighten the color in one and darken again in the other, and call setNeedsUpdate() to let the UI know that we need an update and a redraw. Compared to doPointerMoveEvent(), calling setAccepted() isn't needed, as the enter and leave events don't propagate anywhere if not handled.
void QuadLayer::doPointerEnterEvent(UnsignedInt dataId, Ui::PointerMoveEvent&) { _colors[dataId] *= 1.25f; setNeedsUpdate(Ui::LayerState::NeedsDataUpdate); } void QuadLayer::doPointerLeaveEvent(UnsignedInt dataId, Ui::PointerMoveEvent&) { _colors[dataId] /= 1.25f; setNeedsUpdate(Ui::LayerState::NeedsDataUpdate); }
The pair of enter / leave events deals with the common case of a pointer moving across the user interface, but it can also happen that the currently hovered nodes stops being visible or for example gets disabled, and at that point it should no longer show a hover state. We'll get notified about that and other cases in doVisibilityLostEvent(), VisibilityLostEvent::
void QuadLayer::doVisibilityLostEvent(UnsignedInt dataId, Ui::VisibilityLostEvent& event) { if(event.isNodeHovered()) { _colors[dataId] /= 1.25f; setNeedsUpdate(Ui::LayerState::NeedsDataUpdate); } }
Pointer press and release, pointer capture
The doPointerPressEvent() and doPointerReleaseEvent() interfaces are called when a pointer is pressed and released on a node. Compared to the EventLayer tap or click handler, there isn't any high-level tap or click event on the AbstractLayer itself, that's up to the layer to implement if needed. The PointerEvent exposes various state for this, allowing you to decide what a click should actually be, such as taking into distance between a press and a release, time between the events, pointers being pressed etc.
Additionally, pointer capture is implicitly enabled, meaning that all events following a press get sent to the originating node even if the pointer leaves its area. Furthermore, the event handlers can toggle the capture at any time, which can be used to implement advanced functionality.
For example, we can use a press and a drag of a (primary mouse, pen or touch) pointer to change the color brightness, but dragging more than a certain distance will cancel the whole action, reverting back to the original color:
void QuadLayer::doPointerPressEvent(UnsignedInt dataId, Ui::PointerEvent& event) { /* If a primary pointer is pressed, remember the current color. No need for setting NeedsDataUpdate as nothing visually changed. */ if(event.isPrimary() && (event.pointer() & (Ui::Pointer::MouseLeft| Ui::Pointer::Pen| Ui::Pointer::Finger))) { _originalColor = _colors[dataId]; event.setAccepted(); } } void QuadLayer::doPointerMoveEvent(UnsignedInt dataId, Ui::PointerMoveEvent& event) { /* If a primary pointer is among the ones currently pressed and it's captured, i..e. it originated from a press on this node */ if(event.isPrimary() && (event.pointers() & (Ui::Pointer::MouseLeft| Ui::Pointer::Pen| Ui::Pointer::Finger)) && event.isCaptured()) { /* Calculate distance from node center */ Vector2 distance = event.position() - event.nodeSize()*0.5f; /* If further than 100 pixels, cancel pointer capture and reset the color back */ if(distance.dot() >= Math::pow<2>(100.0f)) { event.setCaptured(false); _colors[dataId] = _originalColor; /* Otherwise update the color based on the Y distance, brightening up and darkening down */ } else { _colors[dataId] = distance.y() > 0.0f ? _originalColor/(1.0f + distance.y()/100.0f) : _originalColor*(1.0f + distance.y()/100.0f); } event.setAccepted(); setNeedsUpdate(Ui::LayerState::NeedsDataUpdate); } }
Finally, doScrollEvent() handles scroll wheel and trackpad input. It's affected by pointer capture as well but there isn't anything specific to wheel events that would need a dedicated example.
Key events, node focus and text input
The doKeyPressEvent() and doKeyReleaseEvent() interfaces are called in response to keyboard input. By default, if no node is focused, they're delivered the same way as pointer events, i.e. to a node under pointer or to the currently captured node. As an example, we could react to R being pressed to reset a color to some default. Important to note is that we check for KeyEvent::
void QuadLayer::doKeyPressEvent(UnsignedInt dataId, Ui::KeyEvent& event) { if(event.key() == Ui::Key::R && event.modifiers() == Ui::Modifiers{}) { _colors[dataId] = …; event.setAccepted(); setNeedsUpdate(Ui::LayerState::NeedsDataUpdate); } }
Nodes that have NodeFlag::
For a practical example, let's say we want to be able to type out a hexadecimal color to change a color of a focused quad — assuming it was attached to a NodeFlag::
void QuadLayer::doPointerPressEvent(UnsignedInt, Ui::PointerEvent& event) { /* Accept press to get a focus */ event.setAccepted(); } void QuadLayer::doFocusEvent(UnsignedInt, Ui::FocusEvent& event) { /* Accept focus to get a text input */ event.setAccepted(); } void QuadLayer::doKeyPressEvent(UnsignedInt, Ui::KeyEvent& event) { /* If the node is focused (and thus we're editing the color), remove last char on backspace */ if(event.isNodeFocused() && event.key() == Ui::Key::Backspace && event.modifiers() == Ui::Modifiers{}) { _editedColor >>= 4; if(_charCount) --_charCount; event.setAccepted(); } } void QuadLayer::doTextInputEvent(UnsignedInt dataId, Ui::TextInputEvent& event) { for(char c: event.text()) { char value; if(c >= '0' && c <= '9') value = c - 0; else if((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) value = 10 + (c & ~0x20) - 'a'; /* Skip unknown chars for simplicity */ else continue; _editedColor = (_editedColor << 4)|value; ++_charCount; /* Got exactly six chars, update the color and reset */ if(_charCount == 6) { _colors[dataId] = Color3::fromLinearRgbInt(_editedColor); _charCount = _editedColor = {}; setNeedsUpdate(Ui::LayerState::NeedsDataUpdate); } } event.setAccepted(); }
Note that TextInputEvent::
Derived classes
- class AbstractVisualLayer new in Git master
- Base for visual data layers.
- class DebugLayer new in Git master
- Debug layer.
- class EventLayer new in Git master
- Event handling layer.
Constructors, destructors, conversion operators
- AbstractLayer(LayerHandle handle) explicit
- Constructor.
- AbstractLayer(const AbstractLayer&) deleted
- Copying is not allowed.
- AbstractLayer(AbstractLayer&&) noexcept
- Move constructor.
Public functions
- auto operator=(const AbstractLayer&) -> AbstractLayer& deleted
- Copying is not allowed.
- auto operator=(AbstractLayer&&) -> AbstractLayer& noexcept
- Move assignment.
- auto handle() const -> LayerHandle
- Layer handle.
- auto features() const -> LayerFeatures
- Features exposed by a layer.
- auto state() const -> LayerStates
- Layer state.
- void setNeedsUpdate(LayerStates state)
- Mark the layer as needing an update.
-
auto capacity() const -> std::
size_t - Current capacity of the data storage.
-
auto usedCount() const -> std::
size_t - Count of used items in the data storage.
- auto isHandleValid(LayerDataHandle handle) const -> bool
- Whether a data handle is valid.
- auto isHandleValid(DataHandle handle) const -> bool
- Whether a data handle is valid.
- void attach(DataHandle data, NodeHandle node)
- Attach data to a node.
- void attach(LayerDataHandle data, NodeHandle node)
- Attach data to a node assuming it belongs to this layer.
- auto node(DataHandle data) const -> NodeHandle
- Node attachment for given data.
- auto node(LayerDataHandle data) const -> NodeHandle
- Node attachment for given data assuming it belongs to this layer.
-
auto nodes() const -> Containers::
StridedArrayView1D<const NodeHandle> - Node attachments for all data.
-
auto generations() const -> Containers::
StridedArrayView1D<const UnsignedShort> - Generation counters for all data.
- void setSize(const Vector2& size, const Vector2i& framebufferSize)
- Set user interface size.
-
void cleanNodes(const Containers::
StridedArrayView1D<const UnsignedShort>& nodeHandleGenerations) - Clean data attached to no longer valid nodes.
-
void cleanData(const Containers::
Iterable<AbstractAnimator>& animators) - Clean animations attached to no longer valid data.
-
void advanceAnimations(Nanoseconds time,
Containers::
MutableBitArrayView activeStorage, Containers:: MutableBitArrayView startedStorage, Containers:: MutableBitArrayView stoppedStorage, const Containers:: StridedArrayView1D<Float>& factorStorage, Containers:: MutableBitArrayView removeStorage, const Containers:: Iterable<AbstractDataAnimator>& animators) - Advance data animations in animators assigned to this layer.
-
void advanceAnimations(Nanoseconds time,
Containers::
MutableBitArrayView activeStorage, Containers:: MutableBitArrayView startedStorage, Containers:: MutableBitArrayView stoppedStorage, const Containers:: StridedArrayView1D<Float>& factorStorage, Containers:: MutableBitArrayView removeStorage, const Containers:: Iterable<AbstractStyleAnimator>& animators) - Advance style animations in animators assigned to this layer.
- void preUpdate(LayerStates state)
- Pre-update common and shared layer data.
-
void update(LayerStates state,
const Containers::
StridedArrayView1D<const UnsignedInt>& dataIds, const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectIds, const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectDataCounts, const Containers:: StridedArrayView1D<const Vector2>& nodeOffsets, const Containers:: StridedArrayView1D<const Vector2>& nodeSizes, const Containers:: StridedArrayView1D<const Float>& nodeOpacities, Containers:: BitArrayView nodesEnabled, const Containers:: StridedArrayView1D<const Vector2>& clipRectOffsets, const Containers:: StridedArrayView1D<const Vector2>& clipRectSizes, const Containers:: StridedArrayView1D<const Vector2>& compositeRectOffsets, const Containers:: StridedArrayView1D<const Vector2>& compositeRectSizes) - Update visible layer data to given offsets and positions.
-
void composite(AbstractRenderer& renderer,
const Containers::
StridedArrayView1D<const Vector2>& rectOffsets, const Containers:: StridedArrayView1D<const Vector2>& rectSizes, std:: size_t offset, std:: size_t count) - Composite previously rendered contents.
-
void draw(const Containers::
StridedArrayView1D<const UnsignedInt>& dataIds, std:: size_t offset, std:: size_t count, const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectIds, const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectDataCounts, std:: size_t clipRectOffset, std:: size_t clipRectCount, const Containers:: StridedArrayView1D<const Vector2>& nodeOffsets, const Containers:: StridedArrayView1D<const Vector2>& nodeSizes, const Containers:: StridedArrayView1D<const Float>& nodeOpacities, Containers:: BitArrayView nodesEnabled, const Containers:: StridedArrayView1D<const Vector2>& clipRectOffsets, const Containers:: StridedArrayView1D<const Vector2>& clipRectSizes) - Draw a sub-range of visible layer data.
- void pointerPressEvent(UnsignedInt dataId, PointerEvent& event)
- Handle a pointer press event.
- void pointerReleaseEvent(UnsignedInt dataId, PointerEvent& event)
- Handle a pointer release event.
- void pointerMoveEvent(UnsignedInt dataId, PointerMoveEvent& event)
- Handle a pointer move event.
- void pointerEnterEvent(UnsignedInt dataId, PointerMoveEvent& event)
- Handle a pointer enter event.
- void pointerLeaveEvent(UnsignedInt dataId, PointerMoveEvent& event)
- Handle a pointer leave event.
- void pointerCancelEvent(UnsignedInt dataId, PointerCancelEvent& event)
- Handle a pointer cancel event.
- void scrollEvent(UnsignedInt dataId, ScrollEvent& event)
- Handle a scroll event.
- void focusEvent(UnsignedInt dataId, FocusEvent& event)
- Handle a focus event.
- void blurEvent(UnsignedInt dataId, FocusEvent& event)
- Handle a blur event.
- void keyPressEvent(UnsignedInt dataId, KeyEvent& event)
- Handle a key press event.
- void keyReleaseEvent(UnsignedInt dataId, KeyEvent& event)
- Handle a key release event.
- void textInputEvent(UnsignedInt dataId, TextInputEvent& event)
- Handle a text input event.
- void visibilityLostEvent(UnsignedInt dataId, VisibilityLostEvent& event)
- Handle a visibility lost event.
Protected functions
- auto hasUi() const -> bool
- Whether the layer is a part of an user interface instance.
- auto ui() -> AbstractUserInterface&
- User interface instance the layer is part of.
- auto ui() const -> const AbstractUserInterface&
-
auto create(NodeHandle node = NodeHandle::
Null) -> DataHandle - Create a data.
- void remove(DataHandle handle)
- Remove a data.
- void remove(LayerDataHandle handle)
- Remove a data assuming it belongs to this layer.
- void assignAnimator(AbstractDataAnimator& animator) const
- Assign a data animator to this layer.
- void assignAnimator(AbstractStyleAnimator& animator) const
- Assign a style animator to this layer.
Private functions
- auto doFeatures() const -> LayerFeatures pure virtual
- Implementation for features()
- auto doState() const -> LayerStates virtual
- Query layer state.
- void doSetSize(const Vector2& size, const Vector2i& framebufferSize) virtual
- Set user interface size.
-
void doClean(Containers::
BitArrayView dataIdsToRemove) virtual - Clean no longer valid layer data.
-
void doAdvanceAnimations(Nanoseconds time,
Containers::
MutableBitArrayView activeStorage, Containers:: MutableBitArrayView startedStorage, Containers:: MutableBitArrayView stoppedStorage, const Containers:: StridedArrayView1D<Float>& factorStorage, Containers:: MutableBitArrayView removeStorage, const Containers:: Iterable<AbstractDataAnimator>& animators) virtual - Advance data animations in animators assigned to this layer.
-
void doAdvanceAnimations(Nanoseconds time,
Containers::
MutableBitArrayView activeStorage, Containers:: MutableBitArrayView startedStorage, Containers:: MutableBitArrayView stoppedStorage, const Containers:: StridedArrayView1D<Float>& factorStorage, Containers:: MutableBitArrayView removeStorage, const Containers:: Iterable<AbstractStyleAnimator>& animators) virtual - Advance style animations in animators assigned to this layer.
- void doPreUpdate(LayerStates state) virtual
- Pre-update common and shared layer data.
-
void doUpdate(LayerStates state,
const Containers::
StridedArrayView1D<const UnsignedInt>& dataIds, const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectIds, const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectDataCounts, const Containers:: StridedArrayView1D<const Vector2>& nodeOffsets, const Containers:: StridedArrayView1D<const Vector2>& nodeSizes, const Containers:: StridedArrayView1D<const Float>& nodeOpacities, Containers:: BitArrayView nodesEnabled, const Containers:: StridedArrayView1D<const Vector2>& clipRectOffsets, const Containers:: StridedArrayView1D<const Vector2>& clipRectSizes, const Containers:: StridedArrayView1D<const Vector2>& compositeRectOffsets, const Containers:: StridedArrayView1D<const Vector2>& compositeRectSizes) virtual - Update visible layer data to given offsets and positions.
-
void doComposite(AbstractRenderer& renderer,
const Containers::
StridedArrayView1D<const Vector2>& compositeRectOffsets, const Containers:: StridedArrayView1D<const Vector2>& compositeRectSizes, std:: size_t offset, std:: size_t count) virtual - Composite previously rendered contents.
-
void doDraw(const Containers::
StridedArrayView1D<const UnsignedInt>& dataIds, std:: size_t offset, std:: size_t count, const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectIds, const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectDataCounts, std:: size_t clipRectOffset, std:: size_t clipRectCount, const Containers:: StridedArrayView1D<const Vector2>& nodeOffsets, const Containers:: StridedArrayView1D<const Vector2>& nodeSizes, const Containers:: StridedArrayView1D<const Float>& nodeOpacities, Containers:: BitArrayView nodesEnabled, const Containers:: StridedArrayView1D<const Vector2>& clipRectOffsets, const Containers:: StridedArrayView1D<const Vector2>& clipRectSizes) virtual - Draw a sub-range of visible layer data.
- void doPointerPressEvent(UnsignedInt dataId, PointerEvent& event) virtual
- Handle a pointer press event.
- void doPointerReleaseEvent(UnsignedInt dataId, PointerEvent& event) virtual
- Handle a pointer release event.
- void doPointerMoveEvent(UnsignedInt dataId, PointerMoveEvent& event) virtual
- Handle a pointer move event.
- void doPointerEnterEvent(UnsignedInt dataId, PointerMoveEvent& event) virtual
- Handle a pointer enter event.
- void doPointerLeaveEvent(UnsignedInt dataId, PointerMoveEvent& event) virtual
- Handle a pointer leave event.
- void doPointerCancelEvent(UnsignedInt dataId, PointerCancelEvent& event) virtual
- Handle a pointer cancel event.
- void doScrollEvent(UnsignedInt dataId, ScrollEvent& event) virtual
- Handle a scroll event.
- void doFocusEvent(UnsignedInt dataId, FocusEvent& event) virtual
- Handle a focus event.
- void doBlurEvent(UnsignedInt dataId, FocusEvent& event) virtual
- Handle a blur event.
- void doKeyPressEvent(UnsignedInt dataId, KeyEvent& event) virtual
- Handle a key press event.
- void doKeyReleaseEvent(UnsignedInt dataId, KeyEvent& event) virtual
- Handle a pointer release event.
- void doTextInputEvent(UnsignedInt dataId, TextInputEvent& event) virtual
- Handle a text input event.
- void doVisibilityLostEvent(UnsignedInt dataId, VisibilityLostEvent& event) virtual
- Handle a visibility lost event.
Function documentation
Magnum:: Ui:: AbstractLayer:: AbstractLayer(LayerHandle handle) explicit
Constructor.
Parameters | |
---|---|
handle | Handle returned by AbstractUserInterface:: |
Magnum:: Ui:: AbstractLayer:: AbstractLayer(AbstractLayer&&) noexcept
Move constructor.
Performs a destructive move, i.e. the original object isn't usable afterwards anymore.
LayerHandle Magnum:: Ui:: AbstractLayer:: handle() const
Layer handle.
Returns the handle passed to the constructor.
LayerStates Magnum:: Ui:: AbstractLayer:: state() const
Layer state.
See the LayerState enum for more information. By default no flags are set.
void Magnum:: Ui:: AbstractLayer:: setNeedsUpdate(LayerStates state)
Mark the layer as needing an update.
Meant to be called by layer implementations when the data get modified. Expects that state
is a non-empty subset of LayerState::
std:: size_t Magnum:: Ui:: AbstractLayer:: capacity() const
Current capacity of the data storage.
Can be at most 1048576. If create() is called and there's no free slots left, the internal storage gets grown.
std:: size_t Magnum:: Ui:: AbstractLayer:: usedCount() const
Count of used items in the data storage.
Always at most capacity(). Expired handles are counted among used as well. The operation is done with a complexity where is capacity().
bool Magnum:: Ui:: AbstractLayer:: isHandleValid(LayerDataHandle handle) const
Whether a data handle is valid.
A handle is valid if it has been returned from create() before and remove() wasn't called on it yet. For LayerDataHandle::false
.
bool Magnum:: Ui:: AbstractLayer:: isHandleValid(DataHandle handle) const
Whether a data handle is valid.
A shorthand for extracting a LayerHandle from handle
using dataHandleLayer(), comparing it to handle() and if it's the same, calling isHandleValid(LayerDataHandle) const with a LayerDataHandle extracted from handle
using dataHandleData(). See these functions for more information. For DataHandle::false
.
void Magnum:: Ui:: AbstractLayer:: attach(DataHandle data,
NodeHandle node)
Attach data to a node.
Makes the data
handle tied to a particular node
, meaning it gets included in draw or event processing depending on node position and visibility. Also, AbstractUserInterface::node
or any parent node will then mean that the data
gets scheduled for removal during the next cleanNodes() call.
Expects that data
is valid. The node
can be anything including NodeHandle::data
is already attached to some node, this will overwrite the previous attachment — i.e., it's not possible to have the same data attached to multiple nodes. The inverse, attaching multiple different data handles to a single node, is supported however.
If data
wasn't attached to node
before, calling this function causes LayerState::node
isn't NodeHandle::
void Magnum:: Ui:: AbstractLayer:: attach(LayerDataHandle data,
NodeHandle node)
Attach data to a node assuming it belongs to this layer.
Like attach(DataHandle, NodeHandle) but without checking that data
indeed belongs to this layer. See its documentation for more information.
NodeHandle Magnum:: Ui:: AbstractLayer:: node(DataHandle data) const
Node attachment for given data.
Expects that data
is valid. If given data isn't attached to any node, returns NodeHandle::
The returned handle may be invalid if either the data got attached to an invalid node in the first place or the node or any of its parents were removed and AbstractUserInterface::
NodeHandle Magnum:: Ui:: AbstractLayer:: node(LayerDataHandle data) const
Node attachment for given data assuming it belongs to this layer.
Like node(DataHandle) const but without checking that data
indeed belongs to this layer. See its documentation for more information.
Containers:: StridedArrayView1D<const NodeHandle> Magnum:: Ui:: AbstractLayer:: nodes() const
Node attachments for all data.
Used internally from AbstractUserInterface::
Containers:: StridedArrayView1D<const UnsignedShort> Magnum:: Ui:: AbstractLayer:: generations() const
Generation counters for all data.
Meant to be used by code that only gets data IDs or masks but needs the full handle, or for various diagnostic purposes such as tracking handle recycling. Size of the returned view is the same as capacity(), individual items correspond to generations of particular data IDs. All values fit into the DataHandle / LayerDataHandle generation bits, 0
denotes an expired generation counter.
Passing an ID along with the corresponding generation to layerDataHandle() produces a LayerDataHandle, passing that along with handle() to dataHandle() produces a DataHandle. Use isHandleValid(LayerDataHandle) const / isHandleValid(DataHandle) const to determine whether given slot is actually used.
void Magnum:: Ui:: AbstractLayer:: setSize(const Vector2& size,
const Vector2i& framebufferSize)
Set user interface size.
Used internally from AbstractUserInterface::
void Magnum:: Ui:: AbstractLayer:: cleanNodes(const Containers:: StridedArrayView1D<const UnsignedShort>& nodeHandleGenerations)
Clean data attached to no longer valid nodes.
Used internally from AbstractUserInterface::nodeHandleGenerations
contains handle generation counters for all nodes, where the index is implicitly the handle ID. They're used to decide about node attachment validity, data with invalid node attachments are then removed. Delegates to doClean(), see its documentation for more information about the arguments.
void Magnum:: Ui:: AbstractLayer:: cleanData(const Containers:: Iterable<AbstractAnimator>& animators)
Clean animations attached to no longer valid data.
Used internally from AbstractUserInterface::animators
expose AnimatorFeature::
Calling this function resets LayerState::
void Magnum:: Ui:: AbstractLayer:: advanceAnimations(Nanoseconds time,
Containers:: MutableBitArrayView activeStorage,
Containers:: MutableBitArrayView startedStorage,
Containers:: MutableBitArrayView stoppedStorage,
const Containers:: StridedArrayView1D<Float>& factorStorage,
Containers:: MutableBitArrayView removeStorage,
const Containers:: Iterable<AbstractDataAnimator>& animators)
Advance data animations in animators assigned to this layer.
Used internally from AbstractUserInterface::
Expects that activeStorage
, startedStorage
, stoppedStorage
, factorStorage
and removeStorage
have the same size, which is at least as large as the largest capacity of all animators
, and that all animators
expose AnimatorFeature::
void Magnum:: Ui:: AbstractLayer:: advanceAnimations(Nanoseconds time,
Containers:: MutableBitArrayView activeStorage,
Containers:: MutableBitArrayView startedStorage,
Containers:: MutableBitArrayView stoppedStorage,
const Containers:: StridedArrayView1D<Float>& factorStorage,
Containers:: MutableBitArrayView removeStorage,
const Containers:: Iterable<AbstractStyleAnimator>& animators)
Advance style animations in animators assigned to this layer.
Used internally from AbstractUserInterface::
Expects that activeStorage
, startedStorage
, stoppedStorage
, factorStorage
and removeStorage
have the same size, which is at least as large as the largest capacity of all animators
, and that all animators
expose AnimatorFeature::
void Magnum:: Ui:: AbstractLayer:: preUpdate(LayerStates state)
Pre-update common and shared layer data.
Used internally from AbstractUserInterface::
Expects that states
isn't empty and is a subset of LayerState::
Note that, unlike update(), calling this function does not reset LayerStates present in state
, that's only done once update() is subsequently called.
void Magnum:: Ui:: AbstractLayer:: update(LayerStates state,
const Containers:: StridedArrayView1D<const UnsignedInt>& dataIds,
const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectIds,
const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectDataCounts,
const Containers:: StridedArrayView1D<const Vector2>& nodeOffsets,
const Containers:: StridedArrayView1D<const Vector2>& nodeSizes,
const Containers:: StridedArrayView1D<const Float>& nodeOpacities,
Containers:: BitArrayView nodesEnabled,
const Containers:: StridedArrayView1D<const Vector2>& clipRectOffsets,
const Containers:: StridedArrayView1D<const Vector2>& clipRectSizes,
const Containers:: StridedArrayView1D<const Vector2>& compositeRectOffsets,
const Containers:: StridedArrayView1D<const Vector2>& compositeRectSizes)
Update visible layer data to given offsets and positions.
Used internally from AbstractUserInterface::
Expects that states
isn't empty and is a subset of LayerState::clipRectIds
and clipRectDataCounts
views have the same size, nodeOffsets
, nodeSizes
, nodeOpacities
and nodesEnabled
have the same size, clipRectOffsets
and clipRectOffset
have the same size and compositeRectOffsets
and compositeRectSizes
have the same size. If LayerFeature::compositeRectOffsets
and compositeRectSizes
are expected to be empty. The nodeOffsets
, nodeSizes
, nodeOpacities
and nodesEnabled
views should be large enough to contain any valid node ID. If the layer advertises LayerFeature::
Calling this function resets LayerStates present in state
, however note that behavior of this function is independent of state() — it performs the update only based on what's passed in state
.
void Magnum:: Ui:: AbstractLayer:: composite(AbstractRenderer& renderer,
const Containers:: StridedArrayView1D<const Vector2>& rectOffsets,
const Containers:: StridedArrayView1D<const Vector2>& rectSizes,
std:: size_t offset,
std:: size_t count)
Composite previously rendered contents.
Used internally from AbstractUserInterface::rectOffsets
and rectSizes
views have the same size and that offset
and count
fits into their size. Delegates to doComposite(), see its documentation for more information about the arguments.
void Magnum:: Ui:: AbstractLayer:: draw(const Containers:: StridedArrayView1D<const UnsignedInt>& dataIds,
std:: size_t offset,
std:: size_t count,
const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectIds,
const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectDataCounts,
std:: size_t clipRectOffset,
std:: size_t clipRectCount,
const Containers:: StridedArrayView1D<const Vector2>& nodeOffsets,
const Containers:: StridedArrayView1D<const Vector2>& nodeSizes,
const Containers:: StridedArrayView1D<const Float>& nodeOpacities,
Containers:: BitArrayView nodesEnabled,
const Containers:: StridedArrayView1D<const Vector2>& clipRectOffsets,
const Containers:: StridedArrayView1D<const Vector2>& clipRectSizes)
Draw a sub-range of visible layer data.
Used internally from AbstractUserInterface::offset
and count
fits into dataIds
size, that the clipRectIds
and clipRectDataCounts
views have the same size, nodeOffsets
, nodeSizes
, nodeOpacities
and nodesEnabled
have the same size and clipRectOffsets
and clipRectOffset
have the same size. The nodeOffsets
, nodeSizes
, nodeOpacities
and nodesEnabled
views should be large enough to contain any valid node ID. Delegates to doDraw(), see its documentation for more information about the arguments.
void Magnum:: Ui:: AbstractLayer:: pointerPressEvent(UnsignedInt dataId,
PointerEvent& event)
Handle a pointer press event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data and PointerEvent::
void Magnum:: Ui:: AbstractLayer:: pointerReleaseEvent(UnsignedInt dataId,
PointerEvent& event)
Handle a pointer release event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data and PointerEvent::
void Magnum:: Ui:: AbstractLayer:: pointerMoveEvent(UnsignedInt dataId,
PointerMoveEvent& event)
Handle a pointer move event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data and PointerMoveEvent::
void Magnum:: Ui:: AbstractLayer:: pointerEnterEvent(UnsignedInt dataId,
PointerMoveEvent& event)
Handle a pointer enter event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data and PointerMoveEvent::
void Magnum:: Ui:: AbstractLayer:: pointerLeaveEvent(UnsignedInt dataId,
PointerMoveEvent& event)
Handle a pointer leave event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data and PointerMoveEvent::
void Magnum:: Ui:: AbstractLayer:: pointerCancelEvent(UnsignedInt dataId,
PointerCancelEvent& event)
Handle a pointer cancel event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data. Delegates to doPointerCancelEvent(), see its documentation for more information.
void Magnum:: Ui:: AbstractLayer:: scrollEvent(UnsignedInt dataId,
ScrollEvent& event)
Handle a scroll event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data and ScrollEvent::
void Magnum:: Ui:: AbstractLayer:: focusEvent(UnsignedInt dataId,
FocusEvent& event)
Handle a focus event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data. The event is expected to not be accepted yet. Delegates to doFocusEvent(), see its documentation for more information.
void Magnum:: Ui:: AbstractLayer:: blurEvent(UnsignedInt dataId,
FocusEvent& event)
Handle a blur event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data. The event is expected to not be accepted yet. Delegates to doBlurEvent(), see its documentation for more information.
void Magnum:: Ui:: AbstractLayer:: keyPressEvent(UnsignedInt dataId,
KeyEvent& event)
Handle a key press event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data. The event is expected to not be accepted yet. Delegates to doKeyPressEvent(), see its documentation for more information.
void Magnum:: Ui:: AbstractLayer:: keyReleaseEvent(UnsignedInt dataId,
KeyEvent& event)
Handle a key release event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data. The event is expected to not be accepted yet. Delegates to doKeyReleaseEvent(), see its documentation for more information.
void Magnum:: Ui:: AbstractLayer:: textInputEvent(UnsignedInt dataId,
TextInputEvent& event)
Handle a text input event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data. The event is expected to not be accepted yet. Delegates to doTextInputEvent(), see its documentation for more information.
void Magnum:: Ui:: AbstractLayer:: visibilityLostEvent(UnsignedInt dataId,
VisibilityLostEvent& event)
Handle a visibility lost event.
Used internally from AbstractUserInterface::dataId
is less than capacity(), with the assumption that the ID points to a valid data. Delegates to doVisibilityLostEvent(), see its documentation for more information.
bool Magnum:: Ui:: AbstractLayer:: hasUi() const protected
Whether the layer is a part of an user interface instance.
Returns true
if the layer has been already passed to AbstractUserInterface::false
otherwise. The function isn't public as it's intended to be used only by the layer implementation itself, not user code.
AbstractUserInterface& Magnum:: Ui:: AbstractLayer:: ui() protected
User interface instance the layer is part of.
Expects that the layer has been already passed to AbstractUserInterface::
const AbstractUserInterface& Magnum:: Ui:: AbstractLayer:: ui() const protected
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:: AbstractLayer:: create(NodeHandle node = NodeHandle:: Null) protected
Create a data.
Parameters | |
---|---|
node | Node to attach to |
Returns | New data handle |
Allocates a new handle in a free slot in the internal storage or grows the storage if there's no free slots left. Expects that there's at most 1048576 data. The returned handle can be removed again with remove(). If node
is not NodeHandle::
Calling this function causes LayerState::node
is not NodeHandle::
void Magnum:: Ui:: AbstractLayer:: remove(DataHandle handle) protected
Remove a data.
Expects that handle
is valid. After this call, isHandleValid(DataHandle) const returns false
for handle
. See also remove(LayerDataHandle) which is a simpler operation if the data is already known to belong to this layer.
Calling this function causes LayerState::handle
is attached to a node, calling this function also causes LayerState::
void Magnum:: Ui:: AbstractLayer:: remove(LayerDataHandle handle) protected
Remove a data assuming it belongs to this layer.
Expects that handle
is valid. After this call, isHandleValid(LayerDataHandle) const returns false
for handle
. See also remove(DataHandle) which additionally checks that the data belongs to this layer.
Calling this function causes LayerState::handle
is attached to a node, calling this function also causes LayerState::
void Magnum:: Ui:: AbstractLayer:: assignAnimator(AbstractDataAnimator& animator) const protected
Assign a data animator to this layer.
Expects that the layer supports LayerFeature::animator
wasn't passed to assignAnimator(AbstractDataAnimator&) const on any layer yet. On the other hand, it's possible to assign multiple different animators to the same layer. Saves handle() into AbstractAnimator::animator
to advanceAnimations(Nanoseconds, Containers::
A concrete layer implementation is meant to wrap this function in a public API, restricting to a more concrete animator type, in order to be able to safely cast back to that type in doAdvanceAnimations(Nanoseconds, Containers::
See assignAnimator(AbstractStyleAnimator&) const for style animators, a corresponding API for an AbstractGenericAnimator is AbstractGenericAnimator::
void Magnum:: Ui:: AbstractLayer:: assignAnimator(AbstractStyleAnimator& animator) const protected
Assign a style animator to this layer.
Expects that the layer supports LayerFeature::animator
wasn't passed to assignAnimator(AbstractStyleAnimator&) const on any layer yet. On the other hand, it's possible to assign multiple different animators to the same layer. Saves handle() into AbstractAnimator::animator
to advanceAnimations(Nanoseconds, Containers::
A concrete layer implementation is meant to wrap this function in a public API, restricting to a more concrete animator type, in order to be able to safely cast back to that type in doAdvanceAnimations(Nanoseconds, Containers::
See assignAnimator(AbstractDataAnimator&) const for data animators, a corresponding API for an AbstractGenericAnimator is AbstractGenericAnimator::
LayerFeatures Magnum:: Ui:: AbstractLayer:: doFeatures() const pure virtual private
Implementation for features()
Note that the value returned by this function is assumed to stay constant during the whole layer lifetime.
LayerStates Magnum:: Ui:: AbstractLayer:: doState() const virtual private
Query layer state.
Called by state() to retrieve additional state bits that might have changed without layer's direct involvement, such as data shared between multiple layers getting modified by another layer. The implementation is expected to return a subset of LayerState::
Default implementation returns an empty set.
void Magnum:: Ui:: AbstractLayer:: doSetSize(const Vector2& size,
const Vector2i& framebufferSize) virtual private
Set user interface size.
Parameters | |
---|---|
size | Size of the user interface to which everything including events is positioned |
framebufferSize | Size of the window framebuffer |
Implementation for setSize(), which is called from AbstractUserInterface::
Default implementation does nothing.
void Magnum:: Ui:: AbstractLayer:: doClean(Containers:: BitArrayView dataIdsToRemove) virtual private
Clean no longer valid layer data.
Parameters | |
---|---|
dataIdsToRemove | Data IDs to remove |
Implementation for cleanNodes(), which is called from AbstractUserInterface::
The dataIdsToRemove
view has the same size as capacity() and is guaranteed to have bits set only for valid data IDs, i.e. data IDs that are already removed are not set.
This function may get also called with dataIdsToRemove
having all bits zero.
Default implementation does nothing.
void Magnum:: Ui:: AbstractLayer:: doAdvanceAnimations(Nanoseconds time,
Containers:: MutableBitArrayView activeStorage,
Containers:: MutableBitArrayView startedStorage,
Containers:: MutableBitArrayView stoppedStorage,
const Containers:: StridedArrayView1D<Float>& factorStorage,
Containers:: MutableBitArrayView removeStorage,
const Containers:: Iterable<AbstractDataAnimator>& animators) virtual private
Advance data animations in animators assigned to this layer.
Parameters | |
---|---|
time in | Time to which to advance |
activeStorage in/out | Storage for animators to put a mask of active animations into |
startedStorage in/out | Storage for animators to put a mask of started animations into |
stoppedStorage in/out | Storage for animators to put a mask of stopped animations into |
factorStorage in/out | Storage for animators to put animation interpolation factors into |
removeStorage in/out | Storage for animators to put a mask of animations to remove into |
animators in | Animators to advance |
Implementation for advanceAnimations(), which is called from AbstractUserInterface::
The activeStorage
, startedStorage
, stoppedStorage
, factorStorage
and removeStorage
views are guaranteed to be at least as large as the largest capacity of all animators
. The animators
are all guaranteed to support AnimatorFeature::
For each animator in animators
the implementation is expected to call AbstractAnimator::activeStorage
, factorStorage
and removeStorage
; then, if the returned value says advance is needed, pass the slices of activeStorage
, factorStorage
and removeStorage
to the animator-specific advance function; and then, if the returned value says clean is needed, pass the slice of removeStorage
to AbstractAnimator::
Assuming the layer implementation publicizes assignAnimator(AbstractDataAnimator&) const with a restricted type, the animators can then be safely cast back to that type in order to call a concrete layer-specific advance function.
void Magnum:: Ui:: AbstractLayer:: doAdvanceAnimations(Nanoseconds time,
Containers:: MutableBitArrayView activeStorage,
Containers:: MutableBitArrayView startedStorage,
Containers:: MutableBitArrayView stoppedStorage,
const Containers:: StridedArrayView1D<Float>& factorStorage,
Containers:: MutableBitArrayView removeStorage,
const Containers:: Iterable<AbstractStyleAnimator>& animators) virtual private
Advance style animations in animators assigned to this layer.
Parameters | |
---|---|
time in | Time to which to advance |
activeStorage in/out | Storage for animators to put a mask of active animations into |
startedStorage in/out | Storage for animators to put a mask of started animations into |
stoppedStorage in/out | Storage for animators to put a mask of stopped animations into |
factorStorage in/out | Storage for animators to put animation interpolation factors into |
removeStorage in/out | Storage for animators to put a mask of animations to remove into |
animators in | Animators to advance |
Implementation for advanceAnimations(), which is called from AbstractUserInterface::
The activeStorage
, startedStorage
, stoppedStorage
, factorStorage
and removeStorage
views are guaranteed to be at least as large as the largest capacity of all animators
. The animators
are all guaranteed to support AnimatorFeature::
For each animator in animators
the implementation is expected to call AbstractAnimator::activeStorage
, factorStorage
and removeStorage
; then, if the returned value says advance is needed, pass the slices of activeStorage
, factorStorage
and removeStorage
to the animator-specific advance function; and then, if the returned value says clean is needed, pass the slice of removeStorage
to AbstractAnimator::
Assuming the layer implementation publicizes assignAnimator(AbstractStyleAnimator&) const with a restricted type, the animators can then be safely cast back to that type in order to call a concrete layer-specific advance function.
void Magnum:: Ui:: AbstractLayer:: doPreUpdate(LayerStates state) virtual private
Pre-update common and shared layer data.
Parameters | |
---|---|
state | State that's needed to be updated |
Implementation for preUpdate(), which is called from AbstractUserInterface whenever UserInterfaceState::
The state
is guaranteed to be a subset of LayerState::state
, so the layer can choose to not implement this function at all if relevant updates can be done during the regular doUpdate() as well.
Default implementation does nothing.
void Magnum:: Ui:: AbstractLayer:: doUpdate(LayerStates state,
const Containers:: StridedArrayView1D<const UnsignedInt>& dataIds,
const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectIds,
const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectDataCounts,
const Containers:: StridedArrayView1D<const Vector2>& nodeOffsets,
const Containers:: StridedArrayView1D<const Vector2>& nodeSizes,
const Containers:: StridedArrayView1D<const Float>& nodeOpacities,
Containers:: BitArrayView nodesEnabled,
const Containers:: StridedArrayView1D<const Vector2>& clipRectOffsets,
const Containers:: StridedArrayView1D<const Vector2>& clipRectSizes,
const Containers:: StridedArrayView1D<const Vector2>& compositeRectOffsets,
const Containers:: StridedArrayView1D<const Vector2>& compositeRectSizes) virtual private
Update visible layer data to given offsets and positions.
Parameters | |
---|---|
state | State that's needed to be updated |
dataIds | Data IDs to update, in order that matches the draw order |
clipRectIds | IDs of clip rects to use for dataIds |
clipRectDataCounts | Counts of dataIds to use for each clip rect from clipRectIds |
nodeOffsets | Absolute node offsets indexed by node ID |
nodeSizes | Node sizes indexed by node ID |
nodeOpacities | Absolute node opacities (i.e., inheriting parent node opacities as well) indexed by node ID |
nodesEnabled | Which visible nodes are enabled, i.e. which don't have NodeFlag:: |
clipRectOffsets | Absolute clip rect offsets referenced by clipRectIds |
clipRectSizes | Clip rect sizes referenced by clipRectIds |
compositeRectOffsets | Offsets of framebuffer rectangles to composite |
compositeRectSizes | Sizes of framebuffer rectangles to composite |
Implementation for update(), which is called from AbstractUserInterface::
The state
is guaranteed to be a subset of LayerState::
Node handles corresponding to dataIds
are available in nodes(), node IDs can be then extracted from the handles using nodeHandleId(). The node IDs then index into the nodeOffsets
, nodeSizes
, nodeOpacities
and nodesEnabled
views. The nodeOffsets
, nodeSizes
, nodeOpacities
and nodesEnabled
have the same size and are guaranteed to be large enough to contain any valid node ID.
All nodes() at indices corresponding to dataIds
are guaranteed to not be NodeHandle::nodeOffsets
, nodeSizes
, nodeOpacities
and nodesDisabled
arrays may contain random or uninitialized values for nodes different than those referenced from dataIds
, such as for nodes that are not currently visible or freed node handles.
The node data are meant to be clipped by rects defined in clipRectOffsets
and clipRectSizes
. The clipRectIds
and clipRectDataCounts
have the same size and specify which of these rects is used for which data. For example, a sequence of {3, 2}, {0, 4}, {1, 7}
means clipping the first two data with clip rect 3, then the next four data with clip rect 0 and then the next seven data with clip rect 1. The sum of all clipRectDataCounts
is equal to the size of the dataIds
array. The clipRectOffsets
and clipRectSizes
have the same size and are guaranteed to be large enough to contain any ID from clipRectIds
. They're in the same coordinate system as nodeOffsets
and nodeSizes
, a zero offset and a zero size denotes that no clipping is needed. Tt's up to the implementation whether it clips the actual data directly or whether it performs clipping at draw time.
The compositeRectOffsets
and compositeRectOffsets
have the same size and define rectangles to be used by compositing operations, i.e. intersections of node rectangles with corresponding clip rectangles. If the layer doesn't advertise LayerFeature::
This function may get also called with dataIds
being empty, for example when setNeedsUpdate() was called but the layer doesn't have any data currently visible.
Default implementation does nothing. Data passed to this function are subsequently passed to doComposite() / doDraw() calls as well, the only difference is that doUpdate() gets called just once with all data to update, while doComposite() / doDraw() is called several times with different sub-ranges of the data based on desired draw order.
void Magnum:: Ui:: AbstractLayer:: doComposite(AbstractRenderer& renderer,
const Containers:: StridedArrayView1D<const Vector2>& compositeRectOffsets,
const Containers:: StridedArrayView1D<const Vector2>& compositeRectSizes,
std:: size_t offset,
std:: size_t count) virtual private
Composite previously rendered contents.
Parameters | |
---|---|
renderer | Renderer instance containing the previously rendered contents |
compositeRectOffsets | Offsets of framebuffer rectangles to composite. Same as the view passed to doUpdate() earlier. |
compositeRectSizes | Sizes of framebuffer rectangles to composite. Same as the view passed to doUpdate() earlier. |
offset | Offset into compositeRectOffsets and compositeRectSizes |
count | Count of compositeRectOffsets and compositeRectSizes to composite |
Implementation for composite(), which is called from AbstractUserInterface::compositeRectOffsets
and compositeRectSizes
, see its documentation for their relations and constraints. This function is called after drawing contents of all layers earlier in the top-level node and layer draw order. It's guaranteed that doDraw() will get called after this function.
This function usually gets called several times with the same views but different offset
and count
. The range of compositeRectOffsets
and compositeRectSizes
views defined by offset
and count
contains rectangles that correspond to the layer data and can be used to restrict the compositing operation to only the area that's actually subsequently drawn.)
void Magnum:: Ui:: AbstractLayer:: doDraw(const Containers:: StridedArrayView1D<const UnsignedInt>& dataIds,
std:: size_t offset,
std:: size_t count,
const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectIds,
const Containers:: StridedArrayView1D<const UnsignedInt>& clipRectDataCounts,
std:: size_t clipRectOffset,
std:: size_t clipRectCount,
const Containers:: StridedArrayView1D<const Vector2>& nodeOffsets,
const Containers:: StridedArrayView1D<const Vector2>& nodeSizes,
const Containers:: StridedArrayView1D<const Float>& nodeOpacities,
Containers:: BitArrayView nodesEnabled,
const Containers:: StridedArrayView1D<const Vector2>& clipRectOffsets,
const Containers:: StridedArrayView1D<const Vector2>& clipRectSizes) virtual private
Draw a sub-range of visible layer data.
Parameters | |
---|---|
dataIds | Data IDs to update, in order that matches the draw order. Same as the view passed to doUpdate() earlier. |
offset | Offset into dataIds |
count | Count of dataIds to draw |
clipRectIds | IDs of clip rects to use for dataIds . Same as the view passed to doUpdate() earlier. |
clipRectDataCounts | Counts of dataIds to use for each clip rect from clipRectIds . Same as the view passed to doUpdate() earlier. |
clipRectOffset | Offset into clipRectIds and clipRectDataCounts |
clipRectCount | Count of clipRectIds and clipRectDataCounts to use |
nodeOffsets | Absolute node offsets. Same as the view passed to doUpdate() earlier. |
nodeSizes | Node sizes. Same as the view passed to doUpdate() earlier. |
nodeOpacities | Absolute node opacities. Same as the view passed to doUpdate() earlier. |
nodesEnabled | Which visible nodes are enabled, i.e. which don't have NodeFlag:: |
clipRectOffsets | Absolute clip rect offsets. Same as the view passed to doUpdate() earlier. |
clipRectSizes | Clip rect sizes. Same as the view passed to doUpdate() earlier. |
Implementation for draw(), which is called from AbstractUserInterface::dataIds
, clipRectIds
, clipRectDataCounts
, nodeOffsets
, nodeSizes
, nodeOpacities
, nodesEnabled
, clipRectOffsets
and clipRectSizes
, see its documentation for their relations and constraints. If LayerFeature::
Like with doUpdate(), the clipRectOffsets
and clipRectSizes
are in the same coordinate system as nodeOffsets
and nodeSizes
. If performing the clipping at draw time (instead of clipping the actual data directly in doDraw()
), the implementation may need to scale these to match actual framebuffer pixels, i.e. by multiplying them with Vector2{framebufferSize}/size
inside doSetSize().
This function usually gets called several times with the same views but different offset
, count
, clipRectOffset
and clipRectCount
values in order to interleave the draws for a correct back-to-front order. In each call, the sum of all clipRectDataCounts
in the range given by clipRectOffset
and clipRectCount
is equal to count
. Unlike doUpdate() or doClean(), this function is never called with an empty count
.
void Magnum:: Ui:: AbstractLayer:: doPointerPressEvent(UnsignedInt dataId,
PointerEvent& event) virtual private
Handle a pointer press event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data, with PointerEvent:: |
Implementation for pointerPressEvent(), which is called from AbstractUserInterface::dataId
.
If the implementation handles the event, it's expected to call PointerEvent::
Default implementation does nothing, i.e. the event
gets implicitly propagated further.
void Magnum:: Ui:: AbstractLayer:: doPointerReleaseEvent(UnsignedInt dataId,
PointerEvent& event) virtual private
Handle a pointer release event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data, with PointerEvent:: |
Implementation for pointerReleaseEvent(), which is called from AbstractUserInterface::dataId
.
If the implementation handles the event, it's expected to call PointerEvent::
Default implementation does nothing, i.e. the event
gets implicitly propagated further.
void Magnum:: Ui:: AbstractLayer:: doPointerMoveEvent(UnsignedInt dataId,
PointerMoveEvent& event) virtual private
Handle a pointer move event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data, with PointerMoveEvent:: |
Implementation for pointerMoveEvent(), which is called from AbstractUserInterface::dataId
.
If the implementation handles the event, it's expected to call PointerEvent::
Default implementation does nothing, i.e. the event
gets implicitly propagated further. That also implies the node is never marked as hovered for primary events and enter / leave events are not emitted for it.
void Magnum:: Ui:: AbstractLayer:: doPointerEnterEvent(UnsignedInt dataId,
PointerMoveEvent& event) virtual private
Handle a pointer enter event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data, with PointerMoveEvent:: |
Implementation for pointerEnterEvent(), which is called from AbstractUserInterface::dataId
. See its documentation for more information about relation of pointer enter/leave events to doPointerMoveEvent(). It's guaranteed that doUpdate() was called before this function with up-to-date data for dataId
, the event
is guaranteed to be always primary.
Unlike doPointerMoveEvent(), the accept status is ignored for enter and leave events, as the event isn't propagated anywhere if it's not handled. Thus calling PointerEvent::
Default implementation does nothing.
void Magnum:: Ui:: AbstractLayer:: doPointerLeaveEvent(UnsignedInt dataId,
PointerMoveEvent& event) virtual private
Handle a pointer leave event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data, with PointerMoveEvent:: |
Implementation for pointerEnterEvent(), which is called from AbstractUserInterface::dataId
. See its documentation for more information about relation of pointer enter/leave events to doPointerMoveEvent(). It's guaranteed that doUpdate() was called before this function with up-to-date data for dataId
, the event
is guaranteed to be always primary.
Unlike doPointerMoveEvent(), the accept status is ignored for enter and leave events, as the event isn't propagated anywhere if it's not handled. Thus calling PointerEvent::
Default implementation does nothing.
void Magnum:: Ui:: AbstractLayer:: doPointerCancelEvent(UnsignedInt dataId,
PointerCancelEvent& event) virtual private
Handle a pointer cancel event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data |
Implementation for pointerCancelEvent(), which is called from AbstractUserInterface::dataId
.
Default implementation does nothing.
void Magnum:: Ui:: AbstractLayer:: doScrollEvent(UnsignedInt dataId,
ScrollEvent& event) virtual private
Handle a scroll event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data, with ScrollEvent:: |
Implementation for scrollEvent(), which is called from AbstractUserInterface::dataId
.
If the implementation handles the event, it's expected to call ScrollEvent::
Default implementation does nothing, i.e. the event
gets implicitly propagated further.
void Magnum:: Ui:: AbstractLayer:: doFocusEvent(UnsignedInt dataId,
FocusEvent& event) virtual private
Handle a focus event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data |
Implementation for focusEvent(), which is called from AbstractUserInterface::dataId
.
If the implementation handles the event, it's expected to call FocusEvent::
Default implementation does nothing.
void Magnum:: Ui:: AbstractLayer:: doBlurEvent(UnsignedInt dataId,
FocusEvent& event) virtual private
Handle a blur event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data |
Implementation for blurEvent(), which is called from AbstractUserInterface::dataId
.
Unlike doFocusEvent(), the accept status is ignored for blur events, as the node is still unmarked as focused if the event is not handled. Thus calling FocusEvent::
Default implementation does nothing.
void Magnum:: Ui:: AbstractLayer:: doKeyPressEvent(UnsignedInt dataId,
KeyEvent& event) virtual private
Handle a key press event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data |
Implementation for keyPressEvent(), which is called from AbstractUserInterface::dataId
.
If the implementation handles the event, it's expected to call KeyEvent::
Default implementation does nothing, i.e. the event
gets implicitly propagated further.
void Magnum:: Ui:: AbstractLayer:: doKeyReleaseEvent(UnsignedInt dataId,
KeyEvent& event) virtual private
Handle a pointer release event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data |
Implementation for keyReleaseEvent(), which is called from AbstractUserInterface::dataId
.
If the implementation handles the event, it's expected to call KeyEvent::
Default implementation does nothing, i.e. the event
gets implicitly propagated further.
void Magnum:: Ui:: AbstractLayer:: doTextInputEvent(UnsignedInt dataId,
TextInputEvent& event) virtual private
Handle a text input event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data |
Implementation for textInputEvent(), which is called from AbstractUserInterface::dataId
.
If the implementation handles the event, it's expected to call TextInputEvent::
Default implementation does nothing, i.e. the event
gets implicitly propagated further.
void Magnum:: Ui:: AbstractLayer:: doVisibilityLostEvent(UnsignedInt dataId,
VisibilityLostEvent& event) virtual private
Handle a visibility lost event.
Parameters | |
---|---|
dataId | Data ID the event happens on. Guaranteed to be less than capacity() and point to a valid data. |
event | Event data |
Implementation for visibilityLostEvent(), which is called from AbstractUserInterface::dataId
can no longer receive events due to NodeFlag::
Default implementation does nothing.