Move RDToolTip into separate file independent of tree view

This commit is contained in:
baldurk
2026-08-13 17:46:43 +01:00
parent f9035e1c42
commit 94c4e656d6
9 changed files with 234 additions and 150 deletions
+139
View File
@@ -0,0 +1,139 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2026 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 "RDToolTip.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QMouseEvent>
#include <QProxyStyle>
#include <QStyleOptionFrame>
#include <QStylePainter>
void ICustomToolTipDisplay::showTip(QWidget *widget, QString text, QModelIndex idx)
{
QPoint p = QCursor::pos();
// estimate, as this is not easily queryable
const QPoint cursorSize(16, 16);
const QRect screenAvailGeom = QApplication::desktop()->availableGeometry(p);
// start with the tooltip placed bottom-right of the cursor, as the default
QRect tooltipRect;
tooltipRect.setTopLeft(p + cursorSize);
tooltipRect.setSize(configureTip(widget, text, idx));
// clip by the available geometry in x
if(tooltipRect.right() > screenAvailGeom.right())
tooltipRect.moveRight(screenAvailGeom.right());
// if we'd go out of bounds in y, place the tooltip above the cursor. Don't just clip like
// in x, because that could place the tooltip over the cursor.
if(tooltipRect.bottom() > screenAvailGeom.bottom())
tooltipRect.moveBottom(p.y() - cursorSize.y());
showTipAtPos(tooltipRect.topLeft());
}
RDToolTip::RDToolTip(QWidget *listener) : QLabel(NULL), mouseListener(listener)
{
int margin = style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, NULL, this);
int opacity = style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, NULL, this);
setWindowFlags(Qt::ToolTip);
setAttribute(Qt::WA_TransparentForMouseEvents);
setForegroundRole(QPalette::ToolTipText);
setBackgroundRole(QPalette::ToolTipBase);
setMargin(margin + 1);
setFrameStyle(QFrame::NoFrame);
setAlignment(Qt::AlignLeft);
setIndent(1);
setWindowOpacity(opacity / 255.0);
}
QSize RDToolTip::configureTip(QWidget *, QString text, QModelIndex)
{
setText(text);
QSize ret = minimumSizeHint();
resize(ret);
return ret;
}
void RDToolTip::showTipAtPos(QPoint pos)
{
move(pos);
show();
}
bool RDToolTip::forceTip(QWidget *widget, QModelIndex idx)
{
return false;
}
void RDToolTip::paintEvent(QPaintEvent *ev)
{
QStylePainter p(this);
QStyleOptionFrame opt;
opt.init(this);
p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
p.end();
QLabel::paintEvent(ev);
}
void RDToolTip::mousePressEvent(QMouseEvent *e)
{
if(mouseListener)
sendListenerEvent(e);
}
void RDToolTip::sendListenerEvent(QMouseEvent *e)
{
QMouseEvent *duplicate =
new QMouseEvent(e->type(), mouseListener->mapFromGlobal(e->globalPos()), e->windowPos(),
e->globalPos(), e->button(), e->buttons(), e->modifiers(), e->source());
QCoreApplication::postEvent(mouseListener, duplicate);
}
void RDToolTip::mouseReleaseEvent(QMouseEvent *e)
{
if(mouseListener)
sendListenerEvent(e);
}
void RDToolTip::mouseDoubleClickEvent(QMouseEvent *e)
{
if(mouseListener)
sendListenerEvent(e);
}
void RDToolTip::resizeEvent(QResizeEvent *e)
{
QStyleHintReturnMask frameMask;
QStyleOption option;
option.init(this);
if(style()->styleHint(QStyle::SH_ToolTip_Mask, &option, this, &frameMask))
setMask(frameMask.region);
QLabel::resizeEvent(e);
}
+63
View File
@@ -0,0 +1,63 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2026 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 <QLabel>
#include <QModelIndex>
struct ICustomToolTipDisplay
{
public:
virtual void hideTip() = 0;
virtual QSize configureTip(QWidget *widget, QString text, QModelIndex idx = QModelIndex()) = 0;
virtual void showTipAtPos(QPoint pos) = 0;
virtual bool forceTip(QWidget *widget, QModelIndex idx) = 0;
void showTip(QWidget *widget, QString text, QModelIndex idx = QModelIndex());
};
class RDToolTip : public QLabel, public ICustomToolTipDisplay
{
private:
Q_OBJECT
QWidget *mouseListener;
public:
explicit RDToolTip(QWidget *listener = NULL);
void hideTip() { hide(); }
QSize configureTip(QWidget *widget, QString text, QModelIndex idx = QModelIndex());
void showTipAtPos(QPoint pos);
bool forceTip(QWidget *widget, QModelIndex idx);
protected:
void paintEvent(QPaintEvent *);
void mousePressEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
void mouseDoubleClickEvent(QMouseEvent *);
void resizeEvent(QResizeEvent *);
void sendListenerEvent(QMouseEvent *e);
};
+4 -108
View File
@@ -26,16 +26,13 @@
#include <QApplication>
#include <QClipboard>
#include <QContextMenuEvent>
#include <QDesktopWidget>
#include <QHeaderView>
#include <QLabel>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QProxyStyle>
#include <QScrollBar>
#include <QStack>
#include <QStylePainter>
#include <QWheelEvent>
#include "Code/QRDUtils.h"
#include "Code/Resources.h"
@@ -117,87 +114,6 @@ QSize RDTreeViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo
return ret;
}
RDTipLabel::RDTipLabel(QWidget *listener) : QLabel(NULL), mouseListener(listener)
{
int margin = style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, NULL, this);
int opacity = style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, NULL, this);
setWindowFlags(Qt::ToolTip);
setAttribute(Qt::WA_TransparentForMouseEvents);
setForegroundRole(QPalette::ToolTipText);
setBackgroundRole(QPalette::ToolTipBase);
setMargin(margin + 1);
setFrameStyle(QFrame::NoFrame);
setAlignment(Qt::AlignLeft);
setIndent(1);
setWindowOpacity(opacity / 255.0);
}
QSize RDTipLabel::configureTip(QWidget *, QModelIndex, QString text)
{
setText(text);
return minimumSizeHint();
}
void RDTipLabel::showTip(QPoint pos)
{
move(pos);
show();
}
bool RDTipLabel::forceTip(QWidget *widget, QModelIndex idx)
{
return false;
}
void RDTipLabel::paintEvent(QPaintEvent *ev)
{
QStylePainter p(this);
QStyleOptionFrame opt;
opt.init(this);
p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
p.end();
QLabel::paintEvent(ev);
}
void RDTipLabel::mousePressEvent(QMouseEvent *e)
{
if(mouseListener)
sendListenerEvent(e);
}
void RDTipLabel::sendListenerEvent(QMouseEvent *e)
{
QMouseEvent *duplicate =
new QMouseEvent(e->type(), mouseListener->mapFromGlobal(e->globalPos()), e->windowPos(),
e->globalPos(), e->button(), e->buttons(), e->modifiers(), e->source());
QCoreApplication::postEvent(mouseListener, duplicate);
}
void RDTipLabel::mouseReleaseEvent(QMouseEvent *e)
{
if(mouseListener)
sendListenerEvent(e);
}
void RDTipLabel::mouseDoubleClickEvent(QMouseEvent *e)
{
if(mouseListener)
sendListenerEvent(e);
}
void RDTipLabel::resizeEvent(QResizeEvent *e)
{
QStyleHintReturnMask frameMask;
QStyleOption option;
option.init(this);
if(style()->styleHint(QStyle::SH_ToolTip_Mask, &option, this, &frameMask))
setMask(frameMask.region);
QLabel::resizeEvent(e);
}
RDTreeView::RDTreeView(QWidget *parent) : QTreeView(parent)
{
setMouseTracking(true);
@@ -205,7 +121,7 @@ RDTreeView::RDTreeView(QWidget *parent) : QTreeView(parent)
m_delegate = new RDTreeViewDelegate(this);
QTreeView::setItemDelegate(m_delegate);
m_TooltipLabel = new RDTipLabel(viewport());
m_TooltipLabel = new RDToolTip(viewport());
m_TooltipLabel->hide();
m_CurrentTooltipElided = false;
@@ -258,27 +174,7 @@ void RDTreeView::mouseMoveEvent(QMouseEvent *e)
// We don't use QToolTip since we have a custom tooltip for showing elided results, and we
// use that for consistency. This also makes it easier to slot in a custom tooltip widget
// externally.
QPoint p = QCursor::pos();
// estimate, as this is not easily queryable
const QPoint cursorSize(16, 16);
const QRect screenAvailGeom = QApplication::desktop()->availableGeometry(p);
// start with the tooltip placed bottom-right of the cursor, as the default
QRect tooltipRect;
tooltipRect.setTopLeft(p + cursorSize);
tooltipRect.setSize(m_Tooltip->configureTip(this, m_currentHoverIndex, tooltip));
// clip by the available geometry in x
if(tooltipRect.right() > screenAvailGeom.right())
tooltipRect.moveRight(screenAvailGeom.right());
// if we'd go out of bounds in y, place the tooltip above the cursor. Don't just clip like
// in x, because that could place the tooltip over the cursor.
if(tooltipRect.bottom() > screenAvailGeom.bottom())
tooltipRect.moveBottom(p.y() - cursorSize.y());
m_Tooltip->showTip(tooltipRect.topLeft());
m_Tooltip->showTip(this, tooltip, m_currentHoverIndex);
m_CurrentTooltipElided = false;
}
}
@@ -459,8 +355,8 @@ bool RDTreeView::viewportEvent(QEvent *event)
// need to use a custom label tooltip since the QToolTip freaks out as we're placing it
// underneath the cursor instead of next to it (so that the tooltip lines up over the
// row)
m_Tooltip->configureTip(this, index, fullText);
m_Tooltip->showTip(viewport()->mapToGlobal(option.rect.topLeft()));
m_Tooltip->configureTip(this, fullText, index);
m_Tooltip->showTipAtPos(viewport()->mapToGlobal(option.rect.topLeft()));
m_CurrentTooltipElided = true;
}
}
+4 -37
View File
@@ -28,6 +28,7 @@
#include <QStyledItemDelegate>
#include <QTreeView>
#include "Code/QRDUtils.h"
#include "RDToolTip.h"
class RDTreeView;
@@ -47,40 +48,6 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
struct ITreeViewTipDisplay
{
public:
virtual void hideTip() = 0;
virtual QSize configureTip(QWidget *widget, QModelIndex idx, QString text) = 0;
virtual void showTip(QPoint pos) = 0;
virtual bool forceTip(QWidget *widget, QModelIndex idx) = 0;
};
class RDTipLabel : public QLabel, public ITreeViewTipDisplay
{
private:
Q_OBJECT
QWidget *mouseListener;
public:
explicit RDTipLabel(QWidget *listener = NULL);
void hideTip() { hide(); }
QSize configureTip(QWidget *widget, QModelIndex idx, QString text);
void showTip(QPoint pos);
bool forceTip(QWidget *widget, QModelIndex idx);
protected:
void paintEvent(QPaintEvent *);
void mousePressEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
void mouseDoubleClickEvent(QMouseEvent *);
void resizeEvent(QResizeEvent *);
void sendListenerEvent(QMouseEvent *e);
};
typedef std::function<uint(QModelIndex, uint)> ExpansionKeyGen;
class RDTreeView : public QTreeView
@@ -116,7 +83,7 @@ public:
void setItemDelegate(QAbstractItemDelegate *delegate);
QAbstractItemDelegate *itemDelegate() const;
void setCustomTooltip(ITreeViewTipDisplay *tip)
void setCustomTooltip(ICustomToolTipDisplay *tip)
{
m_Tooltip = tip;
m_TooltipElidedItems = false;
@@ -230,8 +197,8 @@ private:
QAbstractItemDelegate *m_userDelegate = NULL;
RDTreeViewDelegate *m_delegate;
RDTipLabel *m_TooltipLabel;
ITreeViewTipDisplay *m_Tooltip;
RDToolTip *m_TooltipLabel;
ICustomToolTipDisplay *m_Tooltip;
bool m_CurrentTooltipElided = false;
int m_VertMargin = 6;
@@ -127,7 +127,7 @@ void RDPreviewTooltip::hideTip()
hide();
}
QSize RDPreviewTooltip::configureTip(QWidget *widget, QModelIndex idx, QString text)
QSize RDPreviewTooltip::configureTip(QWidget *widget, QString text, QModelIndex idx)
{
ResourceId id = pipe->updateThumbnail(widget, idx);
if(id != ResourceId())
@@ -146,7 +146,7 @@ QSize RDPreviewTooltip::configureTip(QWidget *widget, QModelIndex idx, QString t
return minimumSizeHint();
}
void RDPreviewTooltip::showTip(QPoint pos)
void RDPreviewTooltip::showTipAtPos(QPoint pos)
{
move(pos);
resize(minimumSize());
@@ -59,7 +59,7 @@ struct ScopedTreeUpdater
int vs;
};
class RDPreviewTooltip : public QFrame, public ITreeViewTipDisplay
class RDPreviewTooltip : public QFrame, public ICustomToolTipDisplay
{
private:
Q_OBJECT
@@ -74,8 +74,8 @@ public:
ICaptureContext &ctx);
void hideTip();
QSize configureTip(QWidget *widget, QModelIndex idx, QString text);
void showTip(QPoint pos);
QSize configureTip(QWidget *widget, QString text, QModelIndex idx);
void showTipAtPos(QPoint pos);
bool forceTip(QWidget *widget, QModelIndex idx);
protected:
+2
View File
@@ -222,6 +222,7 @@ SOURCES += Code/qrenderdoc.cpp \
Windows/PipelineState/D3D12PipelineStateViewer.cpp \
Windows/PipelineState/GLPipelineStateViewer.cpp \
Widgets/Extended/RDTreeView.cpp \
Widgets/Extended/RDToolTip.cpp \
Widgets/Extended/RDTreeWidget.cpp \
Widgets/BufferFormatSpecifier.cpp \
Windows/BufferViewer.cpp \
@@ -311,6 +312,7 @@ HEADERS += Code/CaptureContext.h \
Windows/PipelineState/D3D12PipelineStateViewer.h \
Windows/PipelineState/GLPipelineStateViewer.h \
Widgets/Extended/RDTreeView.h \
Widgets/Extended/RDToolTip.h \
Widgets/Extended/RDTreeWidget.h \
Widgets/BufferFormatSpecifier.h \
Windows/BufferViewer.h \
+8
View File
@@ -662,6 +662,7 @@
<ClCompile Include="$(IntDir)generated\moc_RDTableView.cpp" />
<ClCompile Include="$(IntDir)generated\moc_RDTableWidget.cpp" />
<ClCompile Include="$(IntDir)generated\moc_RDTreeView.cpp" />
<ClCompile Include="$(IntDir)generated\moc_RDToolTip.cpp" />
<ClCompile Include="$(IntDir)generated\moc_RDTreeWidget.cpp" />
<ClCompile Include="$(IntDir)generated\moc_ResourcePreview.cpp" />
<ClCompile Include="$(IntDir)generated\moc_ScintillaDocument.cpp">
@@ -724,6 +725,7 @@
<ClCompile Include="Widgets\Extended\RDTableView.cpp" />
<ClCompile Include="Widgets\Extended\RDTableWidget.cpp" />
<ClCompile Include="Widgets\Extended\RDTreeView.cpp" />
<ClCompile Include="Widgets\Extended\RDToolTip.cpp" />
<ClCompile Include="Widgets\Extended\RDTreeWidget.cpp" />
<ClCompile Include="Widgets\PipelineFlowChart.cpp" />
<ClCompile Include="Widgets\RangeHistogram.cpp" />
@@ -1138,6 +1140,12 @@
<Message>MOC %(Filename).h</Message>
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
</CustomBuild>
<CustomBuild Include="Widgets\Extended\RDToolTip.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="Widgets\Extended\RDTreeWidget.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>
@@ -789,6 +789,12 @@
<ClCompile Include="Windows\Dialogs\ProjectionGuessDialog.cpp">
<Filter>Windows\Dialogs</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)generated\moc_RDToolTip.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
<ClCompile Include="Widgets\Extended\RDToolTip.cpp">
<Filter>Widgets\Extended</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="3rdparty\flowlayout\FlowLayout.h">
@@ -1616,6 +1622,9 @@
<CustomBuild Include="Windows\Dialogs\ProjectionGuessDialog.ui">
<Filter>Windows\Dialogs</Filter>
</CustomBuild>
<CustomBuild Include="Widgets\Extended\RDToolTip.h">
<Filter>Widgets\Extended</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="Resources\action.png">