{"id":5115,"date":"2026-04-07T00:13:27","date_gmt":"2026-04-07T00:13:27","guid":{"rendered":"http:\/\/renaudguezennec.eu\/?p=5115"},"modified":"2026-04-07T00:13:27","modified_gmt":"2026-04-07T00:13:27","slug":"ui-walker-ui-walk-through-in-qml","status":"publish","type":"post","link":"http:\/\/renaudguezennec.eu\/index.php\/2026\/04\/07\/ui-walker-ui-walk-through-in-qml\/","title":{"rendered":"UI\u00a0walker &#8211; UI\u00a0Walk through in QML"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" id=\"the-problem\">The problem<\/h2>\n\n\n\n<p>At the first start of an application, user can be a bit confused in front of all of these features, buttons and data.\nIn response to that, we often have a short presentation of each element on the screen. \nThis also presents a typical workflow with the application. <\/p>\n\n\n\n<p>First, You need to create a project or document.\nThen, define the name, the type\u2026\nThen add content using this or that.<\/p>\n\n\n\n<p>This feature is often called <strong>UI walk through<\/strong>, or <strong>UI\u00a0tour<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"ui-walker\">UI Walker<\/h2>\n\n\n\n<p>I made this library to provide an easy way to do a walkthrough in any QML&nbsp;application.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"important-links\">Important Links<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Code source of <a href=\"https:\/\/github.com\/obiwankennedy\/UiWalker\">UiWalker<\/a>.<\/li>\n\n\n\n<li><a href=\"https:\/\/invent.kde.org\/rolisteam\/rolisteam\/-\/blob\/master\/src\/binaries\/3DDicer\/DiceRollPage.qml?ref_type=heads\">QML Item ready for the Walker<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/invent.kde.org\/rolisteam\/rolisteam\/-\/blob\/master\/src\/binaries\/3DDicer\/Main.qml?ref_type=heads\">QML&nbsp;Window including UiWalker<\/a><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"1132\" style=\"aspect-ratio: 535 \/ 1132;\" width=\"535\" controls src=\"http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/peek_1.webm\"><\/video><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"include-ui-walker-to-your-project\">Include Ui Walker to your project<\/h2>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntarget_link_libraries(MyProject\nPRIVATE\n    Qt6::Core\n    Qt6::Quick\n    WalkerComponent # add UIWalker\n)\n<\/pre><\/div>\n\n\n<p>Using <strong>cmake<\/strong> it is really easy. You can define the library to be a git submodule and then <\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prepare-you-qml-code\">Prepare you qml code<\/h2>\n\n\n\n<p>The whole concept is based on attached property.\nTo highlight a item, you must define two properties:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>WalkerItem.description: the text that will be displayed when this element is highlighted<\/li>\n\n\n\n<li>WalkerItem.weight: Numeric value to define the order (ascending order).<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: css; title: ; notranslate\" title=\"\">\nToolButton {\n    WalkerItem.description: qsTr(&quot;Description of the element&quot;)\n    WalkerItem.weight: 104\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"add-the-ui-walker\">Add the UI walker<\/h2>\n\n\n\n<p>Currently, you have to add one item. It should have the size of the whole window.\nThis item provides several properties in order to help you manage the output. <\/p>\n\n\n\n<p>Properties:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>name<\/th><th>description<\/th><th>type<\/th><\/tr><\/thead><tbody><tr><td>count<\/td><td>Total number of highlighted Item<\/td><td>int<\/td><\/tr><tr><td>current<\/td><td>Index of the current highlighted item<\/td><td>int<\/td><\/tr><tr><td>currentDesc<\/td><td>Description of the current item<\/td><td>QString<\/td><\/tr><tr><td>dimColor<\/td><td>Color which hides the rest of the application<\/td><td>QColor<\/td><\/tr><tr><td>dimOpacity<\/td><td>Opacity of the dim<\/td><td>qreal<\/td><\/tr><tr><td>availableRect<\/td><td>Biggest Rectangle (where text can be displayed).<\/td><td>QRectF<\/td><\/tr><tr><td>borderRect<\/td><td>Rect of the current highlighted item<\/td><td>QRectF<\/td><\/tr><tr><td>interval<\/td><td>Define the time interval on each highlighted item<\/td><td>int<\/td><\/tr><tr><td>active<\/td><td>True when the Walker is displayed<\/td><td>bool<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Here is an example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: css; title: ; notranslate\" title=\"\">\n WalkerItem {\n    id: walker\n    anchors.fill: parent\n    visible: false\n    dimOpacity: 0.8\n\n    onActiveChanged: {\n        \/\/walker.active\n    }\n        \n    Label {\n        id: label\n\n        \n        text: walker.currentDesc \/\/ text from walker\n        x: walker.availableRect.x \/\/ calculated position\n        y: walker.availableRect.y \/\/ calculated position\n        width: walker.availableRect.width \/\/ calculated position\n        height: walker.availableRect.height \/\/ calculated position\n    }\n    \n    Rectangle {\n        x: walker.borderRect.x-2\n        y: walker.borderRect.y-2\n        width: walker.borderRect.width+4\n        height: walker.borderRect.height+4\n        border.color: &quot;red&quot;\n        color: &quot;transparent&quot;\n        radius: 10\n        border.width: 4\n    }\n\n    ToolButton {\n        icon.source: walker.current + 1 === walker.count ? &quot;qrc:\/finish.svg&quot; : &quot;qrc:\/next.svg&quot;\n        \n        onClicked:{\n            if(walker.current +1 === walker.count)\n                walker.skip()\n            else\n                walker.next()\n        }   \n    }\n}\n\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"react-on-highlight-event\">React on Highlight event<\/h3>\n\n\n\n<p>Highlighted items get notified through two signals: <code>enter<\/code> and <code>exit<\/code>. \nDefining signal handlers allow you to react. So you can show the full workflow to add new data.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: css; title: ; notranslate\" title=\"\">\nTextField {\n    id: nameField\n    \/\/ \u2026\n    WalkerItem.description: qsTr(&quot;Set macro name.&quot;)\n    WalkerItem.weight: 30\n    WalkerItem.onEnter: {\n        nameField.text = qsTr(&quot;Skill Roll&quot;)\n    }\n}\n\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"start-it-\">Start it !<\/h3>\n\n\n\n<p>In order, to start the UI&nbsp;tour, you simply have to call the function: <code>start()<\/code> of the Item.\nOf course, it is up to you to trigger it automatically when it&#8217;s the first start of the application or if the user asked for the tour.<\/p>\n\n\n\n<p>Here you have an example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: css; title: ; notranslate\" title=\"\">\nComponent.onCompleted: {\n    if(DiceMainController.uiTour === DiceMainController.UnDone) {\n        walker.start()\n    }\n}\n\n<\/pre><\/div>\n\n\n<p>I have a CPP controller with property <code>UiTour<\/code> which gives the current status of the tour.\nHere, I call directly the walker function. But it may be safer to call a function to reset the state of the window.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"navigation\">Navigation<\/h3>\n\n\n\n<p>The walker provides two important function <code>next()<\/code> and <code>previous()<\/code> to navigate. \nBasically on the walker, you can add buttons in the <strong>available Rect<\/strong> to manage the navigation. <\/p>\n\n\n\n<p>Other option, you can define an interval in milliseconds which will call the <code>next()<\/code> function.<\/p>\n\n\n\n<p>You have to make sure the item is visible while the walker highlight it. It could be tricky to make the path from the end to be beginning. In some case, it is easier to never use the <code>previous<\/code> function. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"finish-it\">Finish it!<\/h3>\n\n\n\n<p>Calling the function <code>skip()<\/code>, close the walker. Then the application is displayed normally.\nIt can be called at any time. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"cheat-code\">Cheat code<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Function<\/th><th>description<\/th><\/tr><\/thead><tbody><tr><td>start()<\/td><td>The walker becomes visible, and the first item is highligthed<\/td><\/tr><tr><td>next()<\/td><td>Highlight the next item, trigger appropriated signals<\/td><\/tr><tr><td>previous()<\/td><td>Highlight the previous item, trigger appropriated signals<\/td><\/tr><tr><td>skip()<\/td><td>Hide the walker<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-it-works-\">How it works ?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-attached-properties\">The attached properties<\/h3>\n\n\n\n<p>In order to harvest all data from the QML, I had to define attached property. <\/p>\n\n\n\n<p>This is the definition of QObject which will be attached, each time a QML&nbsp;item has defined any&nbsp;Walker property.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass WalkerAttachedType : public QObject\n{\n    Q_OBJECT\n    Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged FINAL)\n    Q_PROPERTY(int weight READ weight WRITE setWeight NOTIFY weightChanged FINAL)\n    QML_ANONYMOUS\npublic:\n    explicit WalkerAttachedType(QObject* parent= nullptr);\n    \/\/\u2026\nsignals:\n    void enter();\n    void exit();\n    \/\/\u2026\n};\n\n<\/pre><\/div>\n\n\n<p>In the WalkerItem.h, I have to create this static function.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n    \/\/ \u2026\n    Q_OBJECT\n    QML_ATTACHED(WalkerAttachedType)\n    \/\/ \u2026\n    static WalkerAttachedType* qmlAttachedProperties(QObject* object)\n    {\n        QQuickItem* item= qobject_cast&lt;QQuickItem*&gt;(object);\n        if(!item)\n            qDebug() &lt;&lt; &quot;Walker must be attached to an Item&quot;;\n        s_items.append(item);\n        return new WalkerAttachedType(object);\n    }\n\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"qscenegraph-and-nodes\">QSceneGraph and Nodes<\/h3>\n\n\n\n<p><code>WalkreItem<\/code> defines a QML&nbsp;item, written in <strong>cpp<\/strong> to be light-weighted. \nI used <code>QSGNode<\/code> to draw it on screen. The item code manages the logic of the walkthrough and the update of the geometry.<\/p>\n\n\n\n<p>To make it short, the SceneGraph is the rendering engine of QML. <strong>QSGNode<\/strong> defines an API to communicate with it directly.<\/p>\n\n\n\n<p>First, I create the QML item in cpp, using QSGNode to be rendered.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/walkeritem.h\nclass WalkerItem : public QQuickItem\n{\n    Q_OBJECT\n    QML_ATTACHED(WalkerAttachedType)\n    QML_ELEMENT\n    Q_PROPERTY(QString currentDesc READ currentDesc NOTIFY currentChanged FINAL)\n    Q_PROPERTY(QColor dimColor READ dimColor WRITE setDimColor NOTIFY dimColorChanged FINAL)\n    Q_PROPERTY(qreal dimOpacity READ dimOpacity WRITE setDimOpacity NOTIFY dimOpacityChanged FINAL)\n    Q_PROPERTY(QRectF availableRect READ availableRect NOTIFY availableRectChanged FINAL)\n    Q_PROPERTY(QRectF borderRect READ borderRect NOTIFY borderRectChanged FINAL)\n    Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged FINAL)\n    Q_PROPERTY(bool active READ active NOTIFY activeChanged FINAL)\npublic:\n    WalkerItem();\n    \n    \/\/ accessors, signals, slots\u2026\n\nprotected:\n    QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override;\/\/ update scenegraph\n};\n\n\n\n\/\/walkeritem.cpp\nWalkerItem::WalkerItem()\/\/ in the constructor\n{\n    setFlag(QQuickItem::ItemHasContents);\/\/ must be called\n    connect(child, &amp;QQuickItem::widthChanged, this, &amp;WalkerItem::updateComputation);\n    connect(child, &amp;QQuickItem::heightChanged, this, &amp;WalkerItem::updateComputation);\n}\n\nvoid WalkerItem::updateComputation()\n{\n    \/\/ compute geometry and list any changes that must be sync with the SceneGraph.\n    m_change|= WalkerItem::ChangeType::GeometryChanged;\n    update();\/\/ call to paint the item\n}\n\nQSGNode* WalkerItem::updatePaintNode(QSGNode* node, UpdatePaintNodeData*)\n{\n    auto wNode= static_cast&lt;WalkerNode*&gt;(node);\n    if(!wNode)\n    {\n        wNode= new WalkerNode();\/\/first time\n    }\n\n    if(m_change &amp; WalkerItem::ChangeType::ColorChanged)\n        wNode-&gt;updateColor(m_dimColor);\n    if(m_change &amp; WalkerItem::ChangeType::GeometryChanged)\n        wNode-&gt;update(boundingRect(), m_targetRect);\n    if(m_change &amp; WalkerItem::ChangeType::OpacityChanged)\n        wNode-&gt;updateOpacity(m_dimOpacity);\n\n    m_change= WalkerItem::ChangeType::NoChanges;\n    return wNode;\n}\n\n<\/pre><\/div>\n\n\n<p>We have here an item with a geometry like any other item (x,y,width, height), we also have a <em>dimColor<\/em> and <em>dimOpacity<\/em>.\nAny time one of these properties change. I have to sync with the QSceneGraph to update either the geometry, the <em>dimColor<\/em> or the <em>dimOpacity<\/em>.\nEach time, one property changes, I stored the type of change in the <code>m_change<\/code> member and I call <code>update()<\/code>.<\/p>\n\n\n\n<p>The render engine will call my item with the QSGNode reprenting it on the SceneGraph side. \nThen I can call function on my SGNode. When sync is finished I reset the change to NoChange and return the node.<\/p>\n\n\n\n<p>The <code>updatePaintNode<\/code> can be called with a null node. In this case, you have to create it. It will be the case, the first time. And it could happen later in some cases for optimalization reason.<\/p>\n\n\n\n<p>Now, let see the code of the QSGNode.&nbsp;You have to see the QSGNode as the root item of a tree. Where each node is in charge of representing one aspect of the item: its geometry, its color and its opacity.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/ header\nclass WalkerNode : public QSGNode\n{\npublic:\n    WalkerNode();\n    virtual ~WalkerNode();\n    void update(const QRectF&amp; outRect, const QRectF&amp; inRect);\n    void updateColor(const QColor&amp; dim);\n    void updateOpacity(qreal opacity);\n\nprivate:\n    QSGOpacityNode m_opacity;\n    QSGFlatColorMaterial m_dimMat;\n    QSGGeometryNode m_dim;\n};\n\n<\/pre><\/div>\n\n\n<p>In the constructor, I create each node, and then I define the hierarchy.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nWalkerNode::WalkerNode()\n{\n    auto dimGeo= new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 0);\n    dimGeo-&gt;setDrawingMode(QSGGeometry::DrawTriangles);\n    dimGeo-&gt;allocate(triangleCount * 3);\n\n    m_dim.setGeometry(dimGeo);\n    m_dim.setMaterial(&amp;m_dimMat);\n    m_dimMat.setColor(Qt::black);\n\n    m_opacity.setOpacity(0.6);\n\n    m_opacity.appendChildNode(&amp;m_dim);\n    appendChildNode(&amp;m_opacity);\n\n    markDirty(QSGNode::DirtyMaterial | QSGNode::DirtyGeometry | QSGNode::DirtyOpacity);\n}\n\n<\/pre><\/div>\n\n\n<p>Here the final tree:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"821\" height=\"1024\" src=\"http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/graphviz-821x1024.png\" alt=\"\" class=\"wp-image-5117\" srcset=\"http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/graphviz-821x1024.png 821w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/graphviz-240x300.png 240w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/graphviz-768x958.png 768w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/graphviz-1231x1536.png 1231w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/graphviz-1642x2048.png 1642w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/graphviz-620x773.png 620w\" sizes=\"auto, (max-width: 821px) 100vw, 821px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"define-or-update-the-geometry\">Define or Update the geometry<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid WalkerNode::update(const QRectF&amp; out, const QRectF&amp; in)\n{\n    \/\/ out is the geometry of the window\n    \/\/ in is the geometry of the highlighted item\n    const auto a= out.topLeft();\n    const auto b= in.topLeft();\n    const auto c= in.topRight();\n    const auto d= out.topRight();\n    const auto e= in.bottomRight();\n    const auto f= out.bottomRight();\n    const auto g= in.bottomLeft();\n    const auto h= out.bottomLeft();\n\n    {\n        auto gem= m_dim.geometry();\n        auto vertices= gem-&gt;vertexDataAsPoint2D();\n        QList&lt;std::array&lt;QPointF, 3&gt;&gt; triangles{{a, b, d}, {b, d, c}, {d, c, f}, {c, f, e},\n                                                {f, e, h}, {e, g, h}, {h, g, a}, {g, a, b}};\n        int i= 0;\n        for(auto t : triangles)\n        {\n            vertices&#x5B;i + 0].set(t&#x5B;0].x(), t&#x5B;0].y());\n            vertices&#x5B;i + 1].set(t&#x5B;1].x(), t&#x5B;1].y());\n            vertices&#x5B;i + 2].set(t&#x5B;2].x(), t&#x5B;2].y());\n            i+= 3;\n        }\n\n        m_dim.markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);\n    }\n\n    markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);\n}\n\n<\/pre><\/div>\n\n\n<p>We split the surface we have to cover in triangles.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/drawing-1024x576.png\" alt=\"\" class=\"wp-image-5116\" srcset=\"http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/drawing-1024x576.png 1024w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/drawing-300x169.png 300w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/drawing-768x432.png 768w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/drawing-620x349.png 620w, http:\/\/renaudguezennec.eu\/wp-content\/uploads\/2026\/04\/drawing.png 1080w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"todo\">Todo<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Animations: Smooth animation while transiting from one item to another.<\/li>\n\n\n\n<li>Test on bigger apps<\/li>\n\n\n\n<li>Find a logic to allow previous<\/li>\n\n\n\n<li>Use shader effect to make it better.<\/li>\n\n\n\n<li>Other\u2026<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion:<\/h2>\n\n\n\n<p>UiWalker is already in production. It works like a charm. I hope to use it elsewhere. Then, I will add some new features. Contributions and comments are welcomed.<\/p>\n\n\n\n<p>Hope you find this article interesting.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The problem At the first start of an application, user can be a bit confused in front of all of these features, buttons and data. In response to that, we often have a short presentation of each element on the screen. This also presents a typical workflow with the application. First, You need to create [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":5072,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[81],"tags":[88,34,87,89,90,91],"class_list":["post-5115","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-en","tag-cpp","tag-qml","tag-qt6","tag-ui","tag-walker","tag-walkthrough"],"_links":{"self":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/5115","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/comments?post=5115"}],"version-history":[{"count":1,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/5115\/revisions"}],"predecessor-version":[{"id":5120,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/5115\/revisions\/5120"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/media\/5072"}],"wp:attachment":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/media?parent=5115"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/categories?post=5115"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/tags?post=5115"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}