class new in Git master
DebugLayerDebug layer.
Provides a non-intrusive and extensible way to inspect node hierarchy and layer data attachments in any existing UI for debugging purposes. You can use either the DebugLayer base for inspection alone, or the DebugLayerGL subclass that provides also a way to visualize highlighted nodes directly in the inspected UI.
Setting up a debug layer instance
A debug layer, either DebugLayer or DebugLayerGL, is constructed using a fresh AbstractUserInterface::
ui.setLayerInstance(Containers::pointer<Ui::DebugLayerGL>( ui.createLayer(), Ui::DebugLayerSource::NodeHierarchy|Ui::DebugLayerSource::NodeDataAttachments, Ui::DebugLayerFlag::NodeHighlight));
With this, assuming AbstractUserInterface::
Node highlight
The setup shown above, in particular with DebugLayerFlag::
Ui::NodeHandle button = Ui::button(…, Ui::Icon::Yes, "Accept"); ui.eventLayer().onTapOrClick(button, []{ … });

With the DebugLayer set up, clicking on this button with Ctrl right mouse button (or Ctrl pen eraser in case of a pen input) highlights the node, showing a magenta rectangle over, and prints details about it to the console like shown below. Clicking on any other node will highlight that one instead, clicking again on the highlighted node will remove the highlight.

Node {0x9, 0x2}
Nested at level 1 with 0 direct children
4 data from 3 layers
Naming nodes and layers
In the details we can see that the node is placed somewhere and it has four data attachments. Because the widget is simple we can assume it's the background, the icon, the text and the event handler. It would be better if the layer could tell us that, but because naming various resources isn't essential to UI functionality, and because the DebugLayer is designed to work with any custom user interface containing any custom layers, not just the builtin ones, it can't have any knowledge about layer names on its own.
We have to supply those with setLayerName(). In this case we'll name all layers exposed on the UserInterface instance, you can do the same for any custom layer as well. Similarly, setNodeName() allows to assign names to particular nodes.
debugLayer.setLayerName(ui.eventLayer(), "Event"); debugLayer.setLayerName(ui.baseLayer(), "Base"); debugLayer.setLayerName(ui.textLayer(), "Text"); debugLayer.setNodeName(button, "Accept button");
Highlighting the same node then groups the data by layer, showing them in the order they're drawn. Besides the names now being listed in the printed details, you can query them back with layerName() and nodeName().
Node {0x9, 0x2} Accept button Nested at level 1 with 0 direct children 1 data from layer {0x0, 0x1} Base 2 data from layer {0x1, 0x1} Text 1 data from layer {0x2, 0x1} Event
Showing details about data attachments
Enabling DebugLayerSource::
Node {0x9, 0x2} Accept button Nested at level 1 with 0 direct children Data {0x3, 0x2} from layer {0x0, 0x1} Base Inactive out style: 1 Inactive over style: 2 Pressed out style: 3 Pressed over style: 4 Disabled style: 5 Data {0x6, 0x1} from layer {0x1, 0x1} Text Inactive style: 3 Pressed style: 7 Disabled style: 11 Data {0x7, 0x1} from layer {0x1, 0x1} Text Inactive style: 4 Pressed style: 8 Disabled style: 12 Data {0x0, 0x1} from layer {0x2, 0x1} Event reacting to tap or click
The way this works is that by passing a concrete layer type, the setLayerName(const T&, const Containers::
Node highlight options
Node highlight has defaults chosen in a way that makes the highlight clearly visible on most backgrounds, and with the pointer gesture unique enough to not conflict with usual event handlers. You can change both with setNodeHighlightColor() and setNodeHighlightGesture().
The default set of accepted gestures does not include touch input however, as on a touch device it usually isn't possible to press any modifier keys to distinguish a "debug tap" from a regular tap. It's however possible to use addFlags() and clearFlags() to enable DebugLayerFlag::
/* Enable node highlighting with just a touch */ debugLayer .addFlags(Ui::DebugLayerFlag::NodeHighlight) .setNodeHighlightGesture(Ui::Pointer::Finger, {}); … /* Disable it again and revert to a safe gesture when not used anymore */ debugLayer .clearFlags(Ui::DebugLayerFlag::NodeHighlight) .setNodeHighlightGesture(Ui::Pointer::MouseRight, Ui::Modifier::Ctrl);
Besides visual highlighting using a pointer, highlightNode() allows to highlight a node programmatically.
Finally, while the highlighted node details are by default printed to Debug, a console might not be always accessible. In that case you can direct the message to a callback using setNodeHighlightCallback(), and populate for example a Label directly in the UI with it instead. The callback also gets called with an empty string when the highlight is removed, which can be used to hide the label again. For example:
Ui::Label details{…}; … debugLayer.setNodeHighlightCallback([&details](Containers::StringView message) { details .setText(message) .setHidden(!message); });
Limitations
Currently, it's only possible to visually highlight nodes that are visible and are neither NodeFlag::
Additionally, if a top-level node covers other nodes but is otherwise invisible and doesn't react to events in any way, with DebugLayerFlag::
Making the DebugLayer opt-in
As mentioned above, having the layer always present may have unintended performance implications, and a node highlight can be confusing if triggered accidentally by an unsuspecting user. One possibility is to create it only with a certain startup option, such as is the case with --debug
in magnum-ui-gallery.
Another way is to create it with no DebugLayerFlag present, and enable them temporarily only if some debug mode is activated, similarly to what was shown for touch input above. Such setup will however still make it track all enabled DebugLayerSource bits all the time. To avoid this, it's also possible to defer the layer creation and setup to the point when it's actually needed, and then destroy it again after. Note that, as certain options involve iterating the whole node tree, with very complex UIs such on-the-fly setup might cause stalls.
DebugLayer integration for custom layers
To make a custom layer provide detailed info for DebugLayerSource::
class ColorLayer: public Ui::AbstractLayer { public: struct DebugIntegration; Color3 color(Ui::LayerDataHandle handle) const; … }; struct ColorLayer::DebugIntegration { void print(Debug& debug, const ColorLayer& layer, Containers::StringView layerName, Ui::LayerDataHandle data) { /* Convert to an 8-bit color for brevity */ Color3ub color8 = Math::pack<Color3ub>(layer.color(data)); debug << " Data" << Debug::packed << data << "from layer" << Debug::packed << layer.handle() << Debug::color(Debug::Color::Yellow) << layerName << Debug::resetColor << "with color" << Debug::color << color8 << color8 << Debug::newline; } };
Assuming the concrete layer type is passed to setLayerName(), nothing else needs to be done and the integration gets used automatically. If there's multiple data attached to the same node, the print() gets called for each of them.
ColorLayer& colorLayer = ui.setLayerInstance(…); … debugLayer.setLayerName(colorLayer, "Shiny");
Node {0xc, 0x1} Nested at level 2 with 0 direct children Data {0x4, 0x3} from layer {0x4, 0x1} Shiny with color ▓▓ #2f83cc Data {0x7, 0x1} from layer {0x4, 0x1} Shiny with color ██ #3bd267
If the integration needs additional state, a constructor can be implemented, and the instance then passed to DebugLayer::
class ColorLayer::DebugIntegration { public: /*implicit*/ DebugIntegration(DebugFlags flags = {}): _flags{flags} {} void print(Debug& debug, const ColorLayer& layer, Containers::StringView layerName, Ui::LayerDataHandle data); private: DebugFlags _flags; };
If additional details can be provided even without supplying additional state, it's recommended to have the class default-constructible as well so at least some functionality can be used even if users aren't aware of the extra options. Additionally, in the above code the constructor isn't made explicit
, which allows the debug integration arguments to be passed directly to setLayerName():
/* Default integration setup */ debugLayer.setLayerName(colorLayer, "Shiny"); /* Passing extra arguments */ debugLayer.setLayerName(colorLayer, "Shiny", ColorLayer::DebugFlag::PrintColor|ColorLayer::DebugFlag::ColorSwatch);
Overriding the integration in subclasses
Any layer derived from layers that already have an integration, such as from AbstractVisualLayer or EventLayer, will implicitly inherit the base implementation. If the subclass wants to provide its own output, it can either provide a custom type and hide the original, or derive from it by calling the base print() in its own implementation. Here the ColorLayer
from above is made to derive from AbstractVisualLayer now instead, and its DebugIntegration
derives from AbstractVisualLayer::
class ColorLayer: public Ui::AbstractVisualLayer { public: struct DebugIntegration; … }; struct ColorLayer::DebugIntegration: Ui::AbstractVisualLayer::DebugIntegration { void print(Debug& debug, const ColorLayer& layer, Containers::StringView layerName, Ui::LayerDataHandle data) { Ui::AbstractVisualLayer::DebugIntegration::print(debug, layer, layerName, data); … } };
Base classes
- class AbstractLayer new in Git master
- Base for data layers.
Derived classes
- class DebugLayerGL new in Git master
- OpenGL implementation of the debug layer.
Public types
- class DebugIntegration
- Debug layer integration.
Constructors, destructors, conversion operators
- DebugLayer(LayerHandle handle, DebugLayerSources sources, DebugLayerFlags flags) explicit
- Constructor.
- DebugLayer(const DebugLayer&) deleted
- Copying is not allowed.
- DebugLayer(DebugLayer&&) noexcept
- Move constructor.
Public functions
- auto operator=(const DebugLayer&) -> DebugLayer& deleted
- Copying is not allowed.
- auto operator=(DebugLayer&&) -> DebugLayer& noexcept
- Move assignment.
- auto sources() const -> DebugLayerSources
- Tracked data sources.
- auto flags() const -> DebugLayerFlags
- Behavior flags.
- auto setFlags(DebugLayerFlags flags) -> DebugLayer&
- Set behavior flags.
- auto addFlags(DebugLayerFlags flags) -> DebugLayer&
- Add behavior flags.
- auto clearFlags(DebugLayerFlags flags) -> DebugLayer&
- Clear flags.
-
auto nodeName(NodeHandle handle) const -> Containers::
StringView - Node name.
-
auto setNodeName(NodeHandle handle,
Containers::
StringView name) -> DebugLayer& - Set node name.
-
auto layerName(LayerHandle handle) const -> Containers::
StringView - Layer name.
-
auto setLayerName(const AbstractLayer& layer,
Containers::
StringView name) -> DebugLayer& - Set layer name.
-
template<class T>auto setLayerName(const T& layer, const Containers::
StringView& name) -> DebugLayer& - Set name for a layer with DebugIntegration implemented.
-
template<class T>auto setLayerName(const T& layer, const Containers::
StringView& name, const typename T::DebugIntegration& integration) -> DebugLayer& - Set name for a layer with DebugIntegration implemented.
-
template<class T>auto setLayerName(const T& layer, const Containers::
StringView& name, typename T::DebugIntegration&& integration) -> DebugLayer& - auto nodeHighlightColor() const -> Color4
- Node highlight color.
- auto setNodeHighlightColor(const Color4& color) -> DebugLayer&
- Set node highlight color.
-
auto nodeHighlightGesture() const -> Containers::
Pair<Pointers, Modifiers> - Node highlight gesture.
- auto setNodeHighlightGesture(Pointers pointers, Modifiers modifiers) -> DebugLayer&
- Set node highlight gesture.
- auto hasNodeHighlightCallback() const -> bool
- Whether a node highlight callback is set.
-
auto setNodeHighlightCallback(Containers::
Function<void(Containers:: StringView message)>&& callback) -> DebugLayer& - Set node highlight callback.
- auto currentHighlightedNode() const -> NodeHandle
- Node highlighted by last pointer press.
- auto highlightNode(NodeHandle node) -> bool
- Highlight a node.
Function documentation
Magnum:: Ui:: DebugLayer:: DebugLayer(LayerHandle handle,
DebugLayerSources sources,
DebugLayerFlags flags) explicit
Constructor.
Parameters | |
---|---|
handle | Layer handle returned from AbstractUserInterface:: |
sources | Data sources to track |
flags | Behavior flags |
See particular DebugLayerFlag values for information about which DebugLayerSource is expected to be enabled for a particular feature. While sources
have to specified upfront, the flags
can be subsequently modified using setFlags(), addFlags() and clearFlags().
Note that you can also construct the DebugLayerGL subclass instead to have the layer with visual feedback.
Magnum:: Ui:: DebugLayer:: DebugLayer(DebugLayer&&) noexcept
Move constructor.
Performs a destructive move, i.e. the original object isn't usable afterwards anymore.
DebugLayer& Magnum:: Ui:: DebugLayer:: setFlags(DebugLayerFlags flags)
Set behavior flags.
Returns | Reference to self (for method chaining) |
---|
See particular DebugLayerFlag values for information about which DebugLayerSource is expected to be enabled for a particular feature.
If a node was highlighted and DebugLayerFlag::
DebugLayer& Magnum:: Ui:: DebugLayer:: addFlags(DebugLayerFlags flags)
Add behavior flags.
Returns | Reference to self (for method chaining) |
---|
Calls setFlags() with the existing flags ORed with flags
. Useful for preserving previously set flags.
DebugLayer& Magnum:: Ui:: DebugLayer:: clearFlags(DebugLayerFlags flags)
Clear flags.
Returns | Reference to self (for method chaining) |
---|
Calls setFlags() with the existing flags ANDed with the inverse of flags
. Useful for removing a subset of previously set flags.
Containers:: StringView Magnum:: Ui:: DebugLayer:: nodeName(NodeHandle handle) const
Node name.
Expects that the debug layer has been already passed to AbstractUserInterface::handle
isn't NodeHandle::handle
doesn't have to be valid however. If DebugLayerSource::handle
isn't known, returns an empty string.
If not empty, the returned string is always Containers::
DebugLayer& Magnum:: Ui:: DebugLayer:: setNodeName(NodeHandle handle,
Containers:: StringView name)
Set node name.
Returns | Reference to self (for method chaining) |
---|
Expects that the debug layer has been already passed to AbstractUserInterface::handle
isn't NodeHandle::handle
doesn't have to be valid however. If DebugLayerSource::name
will be used to annotate given handle
, otherwise the function does nothing.
If name
is Containers::
Containers:: StringView Magnum:: Ui:: DebugLayer:: layerName(LayerHandle handle) const
Layer name.
Expects that the debug layer has been already passed to AbstractUserInterface::handle
isn't LayerHandle::handle
doesn't have to be valid however. If DebugLayerSource::handle
isn't known, returns an empty string. For handle() returns "DebugLayer"
if a different name wasn't set.
If not empty, the returned string is always Containers::
DebugLayer& Magnum:: Ui:: DebugLayer:: setLayerName(const AbstractLayer& layer,
Containers:: StringView name)
Set layer name.
Returns | Reference to self (for method chaining) |
---|
Expects that the debug layer has been already passed to AbstractUserInterface::layer
is part of the same user interface. If DebugLayerSource::name
will be used to annotate attachments from given layer
, otherwise the function does nothing.
If name
is Containers::
If a concrete layer type gets passed instead of just AbstractLayer, the setLayerName(const T&, const Containers::
template<class T>
DebugLayer& Magnum:: Ui:: DebugLayer:: setLayerName(const T& layer,
const Containers:: StringView& name)
Set name for a layer with DebugIntegration implemented.
Returns | Reference to self (for method chaining) |
---|
In addition to setLayerName(const AbstractLayer&, Containers::
template<class T>
DebugLayer& Magnum:: Ui:: DebugLayer:: setLayerName(const T& layer,
const Containers:: StringView& name,
const typename T::DebugIntegration& integration)
Set name for a layer with DebugIntegration implemented.
Returns | Reference to self (for method chaining) |
---|
In addition to setLayerName(const AbstractLayer&, Containers::
template<class T>
DebugLayer& Magnum:: Ui:: DebugLayer:: setLayerName(const T& layer,
const Containers:: StringView& name,
typename T::DebugIntegration&& integration)
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
DebugLayer& Magnum:: Ui:: DebugLayer:: setNodeHighlightColor(const Color4& color)
Set node highlight color.
Returns | Reference to self (for method chaining) |
---|
Used only if DebugLayerFlag::0xff00ffff_rgbaf*0.5f
.
If the layer is instantiated as DebugLayerGL, calling this function causes LayerState::
DebugLayer& Magnum:: Ui:: DebugLayer:: setNodeHighlightGesture(Pointers pointers,
Modifiers modifiers)
Set node highlight gesture.
Returns | Reference to self (for method chaining) |
---|
Used only if DebugLayerFlag::pointers
with modifiers being exactly modifiers
. Pressing on a different node moves the highlight to the other node, pressing on a node that's currently highlighted removes the highlight. Expects that pointers
are non-empty. Default is a combination of Pointer::
DebugLayer& Magnum:: Ui:: DebugLayer:: setNodeHighlightCallback(Containers:: Function<void(Containers:: StringView message)>&& callback)
Set node highlight callback.
Returns | Reference to self (for method chaining) |
---|
Used only if DebugLayerFlag::callback
receives a UTF-8 message
with details when a highlight happens on a pointer press, and an empty string if a highlight is removed again. If not empty, the message
is guaranteed to be Containers::
If the callback is not set or if set to nullptr
, details about the highlighted node are printed to Debug instead.
NodeHandle Magnum:: Ui:: DebugLayer:: currentHighlightedNode() const
Node highlighted by last pointer press.
Expects that DebugLayerFlag::
The returned handle may be invalid if the node or any of its parents were removed and AbstractUserInterface::
bool Magnum:: Ui:: DebugLayer:: highlightNode(NodeHandle node)
Highlight a node.
Expects that DebugLayerFlag::
If node
is a known handle, the function performs similarly to the node highlight gesture using a pointer press — currentHighlightedNode() is set to node
, details about the node are printed to Debug or passed to a callback if set, the node is visually higlighted if this is a DebugLayerGL instance, and the function returns true
.
If node
is NodeHandle::true
if node
is NodeHandle::false
if the handle is unknown.
Note that, compared to the node highlight gesture, where the node details are always extracted from an up-to-date UI state, this function only operates with the state known at the last call to AbstractUserInterface::
If the layer is instantiated as DebugLayerGL, calling this function causes LayerState::