Add marker breadcrumbs to event browser. Closes #1031

This commit is contained in:
baldurk
2021-06-10 15:38:50 +01:00
parent 5a1f6fdba8
commit 3d2101476e
9 changed files with 524 additions and 5 deletions
+11
View File
@@ -297,6 +297,17 @@ If no capture is loaded or the EID doesn't correspond to a known event, ``None``
)");
virtual const DrawcallDescription *GetDrawcallForEID(uint32_t eventId) = 0;
DOCUMENT(R"(Determines if a given EID is visible with the current filters applied to the event
browser.
If no capture is loaded or the EID doesn't correspond to a known event, ``False`` will be returned.
:param int eventId: The EID to look up.
:return: Whether or not the event is currently visible (passing the filters).
:rtype: bool
)");
virtual bool IsAPIEventVisible(uint32_t eventId) = 0;
DOCUMENT(R"(Registers a new event browser filter function.
Filter functions are available as $name() so long as they don't shadow an existing function. The
+354
View File
@@ -0,0 +1,354 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "MarkerBreadcrumbs.h"
#include <QAction>
#include <QMenu>
#include "Code/QRDUtils.h"
#include "Widgets/Extended/RDLabel.h"
#include "Widgets/Extended/RDToolButton.h"
BreadcrumbsLayout::BreadcrumbsLayout(QWidget *parent, RDToolButton *elidedItems) : QLayout(parent)
{
m_ElidedItems = elidedItems;
}
BreadcrumbsLayout::~BreadcrumbsLayout()
{
}
void BreadcrumbsLayout::clear()
{
while(!m_Items.isEmpty())
{
QLayoutItem *item = m_Items.takeAt(0);
delete item->widget();
delete item;
}
}
void BreadcrumbsLayout::addItem(QLayoutItem *item)
{
m_Items.push_back(item);
}
int BreadcrumbsLayout::count() const
{
return m_Items.count();
}
Qt::Orientations BreadcrumbsLayout::expandingDirections() const
{
return 0;
}
QSize BreadcrumbsLayout::minimumSize() const
{
QSize ret(0, 16);
if(!m_Items.isEmpty())
ret = m_Items[0]->minimumSize();
ret.setWidth(100);
return ret + QSize(contentsMargins().left() + contentsMargins().right(),
contentsMargins().top() + contentsMargins().bottom());
}
void BreadcrumbsLayout::setGeometry(const QRect &rect)
{
bool needUpdate = (rect != m_PrevRect);
QLayout::setGeometry(rect);
{
QRect avail = QRect(rect.topLeft(), rect.marginsRemoved(contentsMargins()).size());
QList<QLayoutItem *> items = m_Items;
avail.setWidth(qMin(avail.width(), sizeHint().width()));
// alternate between taking the last and first, starting with the last
bool takeLast = true;
bool elided = false;
QLayoutItem *prevItem = NULL;
while(!items.isEmpty() && avail.width() > 0)
{
QLayoutItem *item = items.takeAt(takeLast ? items.count() - 1 : 0);
QSize sz = item->sizeHint();
QSize s = sz;
s.setWidth(qMin(s.width(), avail.width()));
s.setHeight(avail.height());
// if we're eliding, this is the last item we'll add. Leave space for the elide label
if(s.width() < sz.width())
s.setWidth(s.width() - s.height());
QPoint p;
if(takeLast)
{
p = avail.topRight();
p.setX(p.x() - s.width());
}
else
{
p = avail.topLeft();
}
QRect itemRect(p, s);
if(itemRect.width() < 40 ||
(itemRect.width() < sz.width() / 2 && itemRect.width() < itemRect.height() * 3))
{
item->setGeometry(QRect(0, 0, 0, 0));
elided = true;
break;
}
item->setGeometry(itemRect);
if(takeLast)
avail.setRight(itemRect.left());
else
avail.setLeft(itemRect.right());
prevItem = item;
takeLast = !takeLast;
}
if(elided || !items.isEmpty())
{
// if there's not enough room for the elide label, steal from the last item
if(avail.width() < avail.height() && prevItem)
{
int neededSpace = avail.height() - avail.width();
QRect itemRect = prevItem->geometry();
// if we're now taking last, the previous item was on the first side, so shrink it from
// the right
if(takeLast)
{
itemRect.adjust(0, 0, -neededSpace, 0);
avail.setLeft(avail.left() - neededSpace);
}
else
{
itemRect.adjust(neededSpace, 0, 0, 0);
avail.setWidth(avail.width() + neededSpace);
}
prevItem->setGeometry(itemRect);
}
}
else
{
avail.setWidth(0);
avail.setHeight(0);
}
m_ElidedItems->setGeometry(avail);
for(QLayoutItem *item : items)
item->setGeometry(QRect(0, 0, 0, 0));
}
if(needUpdate)
update();
m_PrevRect = rect;
}
QSize BreadcrumbsLayout::sizeHint() const
{
QSize ret(0, 16);
for(QLayoutItem *item : m_Items)
{
QSize s = item->sizeHint();
ret.setWidth(ret.width() + s.width());
ret.setHeight(qMax(ret.height(), s.height()));
}
return ret;
}
QLayoutItem *BreadcrumbsLayout::itemAt(int index) const
{
return index >= 0 && index < m_Items.count() ? m_Items[index] : NULL;
}
QLayoutItem *BreadcrumbsLayout::takeAt(int index)
{
if(index >= 0 && index < m_Items.count())
return m_Items.takeAt(index);
return NULL;
}
MarkerBreadcrumbs::MarkerBreadcrumbs(ICaptureContext &ctx, QWidget *parent)
: QFrame(parent), m_Ctx(ctx)
{
setFont(Formatter::PreferredFont());
m_ElidedItems = new RDToolButton(this);
m_ElidedItems->setAutoRaise(true);
m_ElidedItems->setText(lit("..."));
m_Layout = new BreadcrumbsLayout(this, m_ElidedItems);
m_Layout->setContentsMargins(QMargins(0, 2, 0, 2));
m_Layout->setMargin(0);
setLayout(m_Layout);
m_ElidedMenu = new QMenu(this);
QObject::connect(m_ElidedItems, &RDToolButton::clicked, this,
&MarkerBreadcrumbs::elidedItemsClicked);
}
MarkerBreadcrumbs::~MarkerBreadcrumbs()
{
}
void MarkerBreadcrumbs::OnEventChanged(uint32_t eventId)
{
const DrawcallDescription *parent = m_Ctx.GetEventBrowser()->GetDrawcallForEID(m_Ctx.CurEvent());
if(parent != NULL && !(parent->flags & DrawFlags::PushMarker))
parent = parent->parent;
if(m_CurParent == parent && m_Layout->count() != 0)
return;
m_CurParent = parent;
m_Layout->clear();
QVector<const DrawcallDescription *> path;
while(parent)
{
path.push_back(parent);
parent = parent->parent;
}
AddPathButton(NULL);
m_Path.clear();
for(int i = path.count() - 1; i >= 0; i--)
{
m_Path.push_back(path[i]);
AddPathButton(path[i]);
}
}
void MarkerBreadcrumbs::ForceRefresh()
{
m_CurParent = NULL;
OnEventChanged(m_Ctx.CurEvent());
}
void MarkerBreadcrumbs::ConfigurePathMenu(QMenu *menu, const DrawcallDescription *draw)
{
const rdcarray<DrawcallDescription> &draws = draw ? draw->children : m_Ctx.CurDrawcalls();
menu->clear();
for(const DrawcallDescription &child : draws)
{
if((child.flags & DrawFlags::PushMarker) &&
m_Ctx.GetEventBrowser()->IsAPIEventVisible(child.eventId))
{
QAction *action = new QAction(child.name, menu);
uint32_t eid = child.eventId;
if(child.IsFakeMarker())
eid = child.children[0].eventId;
QObject::connect(action, &QAction::triggered,
[this, eid]() { m_Ctx.SetEventID({}, eid, eid); });
menu->addAction(action);
}
}
}
void MarkerBreadcrumbs::elidedItemsClicked()
{
m_ElidedMenu->clear();
for(int i = 0; i < m_Layout->count(); i++)
{
RDToolButton *button = qobject_cast<RDToolButton *>(m_Layout->itemAt(i)->widget());
if(button && button->size().width() == 0)
{
QAction *action = new QAction(button->text(), m_ElidedMenu);
QObject::connect(action, &QAction::triggered, [button]() { button->click(); });
m_ElidedMenu->addAction(action);
}
}
m_ElidedMenu->move(mapToGlobal(m_ElidedItems->geometry().bottomLeft()));
m_ElidedMenu->show();
}
void MarkerBreadcrumbs::AddPathButton(const DrawcallDescription *draw)
{
RDToolButton *b = new RDToolButton();
b->setText(draw ? QString(draw->name) : lit("Capture"));
b->setToolTip(b->text());
bool hasChildMarkers = false;
for(const DrawcallDescription &child : draw ? draw->children : m_Ctx.CurDrawcalls())
{
if((child.flags & DrawFlags::PushMarker) &&
m_Ctx.GetEventBrowser()->IsAPIEventVisible(child.eventId))
{
hasChildMarkers = true;
break;
}
}
if(hasChildMarkers)
{
QMenu *menu = new QMenu(b);
b->setPopupMode(QToolButton::MenuButtonPopup);
b->setMenu(menu);
QObject::connect(menu, &QMenu::aboutToShow,
[this, menu, draw]() { ConfigurePathMenu(menu, draw); });
}
uint32_t eid = draw ? draw->eventId : 0;
if(draw && draw->IsFakeMarker())
eid = draw->children[0].eventId;
b->setAutoRaise(true);
QObject::connect(b, &RDToolButton::clicked, [this, eid]() { m_Ctx.SetEventID({}, eid, eid); });
m_Layout->addWidget(b);
}
+91
View File
@@ -0,0 +1,91 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include <QFrame>
#include <QLayout>
struct ICaptureContext;
class RDToolButton;
class RDLabel;
struct DrawcallDescription;
class QMenu;
class BreadcrumbsLayout : public QLayout
{
public:
explicit BreadcrumbsLayout(QWidget *parent, RDToolButton *elidedItems);
~BreadcrumbsLayout();
void clear();
void addItem(QLayoutItem *item) override;
int count() const override;
QLayoutItem *itemAt(int index) const override;
QLayoutItem *takeAt(int index) override;
Qt::Orientations expandingDirections() const override;
QSize minimumSize() const override;
void setGeometry(const QRect &rect) override;
QSize sizeHint() const override;
private:
QList<QLayoutItem *> m_Items;
QRect m_PrevRect;
RDToolButton *m_ElidedItems;
};
class MarkerBreadcrumbs : public QFrame
{
Q_OBJECT
public:
explicit MarkerBreadcrumbs(ICaptureContext &ctx, QWidget *parent = 0);
~MarkerBreadcrumbs();
// when a new event is selected
void OnEventChanged(uint32_t eventId);
// forcibly refresh even if the event hasn't changed
void ForceRefresh();
private slots:
// manual slots
void elidedItemsClicked();
private:
void AddPathButton(const DrawcallDescription *);
void ConfigurePathMenu(QMenu *, const DrawcallDescription *);
ICaptureContext &m_Ctx;
QVector<const DrawcallDescription *> m_Path;
const DrawcallDescription *m_CurParent = NULL;
BreadcrumbsLayout *m_Layout;
RDToolButton *m_ElidedItems;
QMenu *m_ElidedMenu;
};
+40 -5
View File
@@ -48,6 +48,7 @@
#include "Widgets/Extended/RDLabel.h"
#include "Widgets/Extended/RDListWidget.h"
#include "Widgets/Extended/RDTreeWidget.h"
#include "Widgets/MarkerBreadcrumbs.h"
#include "flowlayout/FlowLayout.h"
#include "scintilla/include/qt/ScintillaEdit.h"
#include "ui_EventBrowser.h"
@@ -446,10 +447,16 @@ struct EventItemModel : public QAbstractItemModel
{
// except if the draw is a fake marker. In this case its own event ID is invalid, so we
// check the range of its children (knowing it only has one layer of children)
if(d.IsFakeMarker() && d.children[0].eventId < eid)
if(d.IsFakeMarker())
{
rowInParent++;
continue;
if(d.eventId == eid)
break;
if(d.children[0].eventId < eid)
{
rowInParent++;
continue;
}
}
// keep counting until we get to the row within this draw
@@ -3096,6 +3103,18 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent)
m_BookmarkStripLayout->addWidget(ui->bookmarkStripHeader);
m_BookmarkStripLayout->addItem(m_BookmarkSpacer);
{
QHBoxLayout *box = new QHBoxLayout(ui->breadcrumbStrip);
box->setContentsMargins(QMargins(0, 0, 0, 0));
box->setMargin(0);
box->setSpacing(0);
m_Breadcrumbs = new MarkerBreadcrumbs(m_Ctx, this);
box->addWidget(m_Breadcrumbs);
ui->breadcrumbStrip->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
m_Breadcrumbs->hide();
}
Qt::Key keys[] = {
Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4, Qt::Key_5,
Qt::Key_6, Qt::Key_7, Qt::Key_8, Qt::Key_9, Qt::Key_0,
@@ -3203,8 +3222,6 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent)
CreateFilterDialog();
filter_apply();
m_redPalette = palette();
m_redPalette.setColor(QPalette::Base, Qt::red);
@@ -3254,9 +3271,16 @@ void EventBrowser::OnCaptureLoaded()
// expand the root frame node
ui->events->expand(ui->events->model()->index(0, 0));
filter_apply();
ui->events->scrollTo(ui->events->model()->index(0, 0));
clearBookmarks();
repopulateBookmarks();
m_Breadcrumbs->show();
m_Breadcrumbs->ForceRefresh();
ui->find->setEnabled(true);
ui->timeDraws->setEnabled(true);
ui->bookmark->setEnabled(true);
@@ -3271,6 +3295,8 @@ void EventBrowser::OnCaptureClosed()
on_HideFind();
m_Breadcrumbs->hide();
m_FilterModel->ResetCache();
// older Qt versions lose all the sections when a model resets even if the sections don't change.
// Manually save/restore them
@@ -3294,6 +3320,7 @@ void EventBrowser::OnEventChanged(uint32_t eventId)
highlightBookmarks();
m_Model->RefreshCache();
m_Breadcrumbs->OnEventChanged(eventId);
}
void EventBrowser::on_find_toggled(bool checked)
@@ -3352,6 +3379,7 @@ void EventBrowser::events_currentChanged(const QModelIndex &current, const QMode
m_Ctx.SetEventID({this}, selectedEID, effectiveEID);
m_Model->RefreshCache();
m_Breadcrumbs->OnEventChanged(effectiveEID);
const DrawcallDescription *draw = m_Ctx.GetDrawcall(selectedEID);
@@ -3976,6 +4004,8 @@ void EventBrowser::filter_apply()
if(m_FilterTimeout->isActive())
m_FilterTimeout->stop();
m_Breadcrumbs->ForceRefresh();
// unselect everything while applying the filter, to avoid updating the source model while the
// filter is processing if the current event is no longer selected
uint32_t curSelEvent = m_Ctx.CurSelectedEvent();
@@ -4855,6 +4885,11 @@ const DrawcallDescription *EventBrowser::GetDrawcallForEID(uint32_t eid)
return m_Model->GetDrawcallForEID(eid);
}
bool EventBrowser::IsAPIEventVisible(uint32_t eid)
{
return m_FilterModel->mapFromSource(m_Model->GetIndexForEID(eid)).isValid();
}
bool EventBrowser::RegisterEventFilterFunction(const rdcstr &name, const rdcstr &description,
EventFilterCallback filter, FilterParseCallback parser,
AutoCompleteCallback completer)
+5
View File
@@ -58,6 +58,8 @@ struct EventFilterModel;
struct ParseTrace;
class MarkerBreadcrumbs;
enum class MatchType
{
MustMatch,
@@ -120,6 +122,7 @@ public:
void UpdateDurationColumn() override;
APIEvent GetAPIEventForEID(uint32_t eid) override;
const DrawcallDescription *GetDrawcallForEID(uint32_t eid) override;
bool IsAPIEventVisible(uint32_t eid) override;
bool RegisterEventFilterFunction(const rdcstr &name, const rdcstr &description,
EventFilterCallback filter, FilterParseCallback parser,
AutoCompleteCallback completer) override;
@@ -225,6 +228,8 @@ private:
QSpacerItem *m_BookmarkSpacer;
QMap<uint32_t, QToolButton *> m_BookmarkButtons;
MarkerBreadcrumbs *m_Breadcrumbs;
struct
{
QDialog *Dialog;
+4
View File
@@ -410,6 +410,10 @@
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="breadcrumbStrip" native="true">
</widget>
</item>
<item>
<widget class="QWidget" name="bookmarkStrip" native="true">
<widget class="QWidget" name="bookmarkStripHeader" native="true">
+2
View File
@@ -228,6 +228,7 @@ SOURCES += Code/qrenderdoc.cpp \
Windows/TimelineBar.cpp \
Windows/Dialogs/SettingsDialog.cpp \
Widgets/OrderedListEditor.cpp \
Widgets/MarkerBreadcrumbs.cpp \
Widgets/Extended/RDTableWidget.cpp \
Windows/Dialogs/SuggestRemoteDialog.cpp \
Windows/Dialogs/VirtualFileDialog.cpp \
@@ -311,6 +312,7 @@ HEADERS += Code/CaptureContext.h \
Windows/TimelineBar.h \
Windows/Dialogs/SettingsDialog.h \
Widgets/OrderedListEditor.h \
Widgets/MarkerBreadcrumbs.h \
Widgets/Extended/RDTableWidget.h \
Windows/Dialogs/SuggestRemoteDialog.h \
Windows/Dialogs/VirtualFileDialog.h \
+8
View File
@@ -633,6 +633,7 @@
<ClCompile Include="$(IntDir)generated\moc_LiveCapture.cpp" />
<ClCompile Include="$(IntDir)generated\moc_EnvironmentEditor.cpp" />
<ClCompile Include="$(IntDir)generated\moc_OrderedListEditor.cpp" />
<ClCompile Include="$(IntDir)generated\moc_MarkerBreadcrumbs.cpp" />
<ClCompile Include="$(IntDir)generated\moc_RemoteManager.cpp" />
<ClCompile Include="$(IntDir)generated\moc_ExtensionManager.cpp" />
<ClCompile Include="$(IntDir)generated\moc_PerformanceCounterSelection.cpp" />
@@ -733,6 +734,7 @@
<ClCompile Include="Widgets\ThumbnailStrip.cpp" />
<ClCompile Include="Widgets\ReplayOptionsSelector.cpp" />
<ClCompile Include="Widgets\OrderedListEditor.cpp" />
<ClCompile Include="Widgets\MarkerBreadcrumbs.cpp" />
<ClCompile Include="Code\CaptureContext.cpp" />
<ClCompile Include="Styles\RDStyle\RDStyle.cpp" />
<ClCompile Include="Styles\StyleData.cpp" />
@@ -1169,6 +1171,12 @@
<Message>MOC %(Filename).h</Message>
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
</CustomBuild>
<CustomBuild Include="Widgets\MarkerBreadcrumbs.h">
<AdditionalInputs>%(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(QtBinDir)\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(QtIncludeDir)" -I"$(QtIncludeDir)\QtWidgets" -I"$(QtIncludeDir)\QtGui" -I"$(QtIncludeDir)\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp"</Command>
<Message>MOC %(Filename).h</Message>
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
</CustomBuild>
<CustomBuild Include="Windows\APIInspector.h">
<AdditionalInputs>%(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(QtBinDir)\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(QtIncludeDir)" -I"$(QtIncludeDir)\QtWidgets" -I"$(QtIncludeDir)\QtGui" -I"$(QtIncludeDir)\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp"</Command>
@@ -750,6 +750,12 @@
<ClCompile Include="Windows\ShaderMessageViewer.cpp">
<Filter>Windows</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)generated\moc_MarkerBreadcrumbs.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
<ClCompile Include="Widgets\MarkerBreadcrumbs.cpp">
<Filter>Widgets</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="3rdparty\flowlayout\FlowLayout.h">
@@ -1526,6 +1532,9 @@
<CustomBuild Include="Windows\ShaderMessageViewer.ui">
<Filter>Windows</Filter>
</CustomBuild>
<CustomBuild Include="Widgets\MarkerBreadcrumbs.h">
<Filter>Widgets</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="Resources\action.png">