/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2019-2022 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 #include #include #include #include #include #include #include #include #include #include #include "Code/Interface/QRDInterface.h" #if !defined(RELEASE) #define ENABLE_UNIT_TESTS 1 #else #define ENABLE_UNIT_TESTS 0 #endif class QHeaderView; template inline T AlignUp(T x, T a) { return (x + (a - 1)) & (~(a - 1)); } #ifndef ARRAY_COUNT #define ARRAY_COUNT(arr) (sizeof(arr) / sizeof(arr[0])) #endif // this will be here to lighten the burden of converting from rdcstr to // QString everywhere. template inline QString ToQStr(const T &el) { return QString(ToStr(el)); } // overloads for a couple of things that need to know the pipeline type when converting QString ToQStr(const ResourceUsage usage, const GraphicsAPI apitype); QString ToQStr(const ShaderStage stage, const GraphicsAPI apitype); QString ToQStr(const AddressMode addr, const GraphicsAPI apitype); QString ToQStr(const ShadingRateCombiner addr, const GraphicsAPI apitype); inline QMetaType::Type GetVariantMetatype(const QVariant &v) { // this is explicitly called out by the documentation as the recommended process: // "Although this function is declared as returning QVariant::Type, the return value should be // interpreted as QMetaType::Type." // Suppress static analysis complaints about the enums mismatching: // coverity[mixed_enums] return (QMetaType::Type)v.type(); } namespace Packing { // see note in Rules below enum APIConfig { // property | vector_align_component | vector_straddle_16b | tight_arrays | trailing_overlap std140, // | false | false | false | false std430, // | false | false | true | false D3DCB, // | true | false | false | true C, // | true | true | true | false Scalar, // | true | true | true | true // D3D UAVs are assumed to be the same as C packing. With only 4 and 8 byte types this can't be // fully verified and it's not documented at all. D3DUAV = C, }; // individual rules for packing. In general, true is more lenient on packing than false for each // property, though struct_aligned is an exception (in that case true is more 'sensible') // NOTE: If any of these rules or the above APIConfigs change, make sure to update // BufferFormatter::EstimatePackingRules struct Rules { Rules() = default; Rules(APIConfig config) { // default to the most conservative packing ruleset switch(config) { case std140: { break; } case std430: { tight_arrays = true; break; } case D3DCB: { vector_align_component = true; trailing_overlap = true; break; } case C: { vector_align_component = true; vector_straddle_16b = true; tight_arrays = true; break; } case Scalar: { vector_align_component = true; vector_straddle_16b = true; tight_arrays = true; trailing_overlap = true; break; } } } bool operator==(Packing::Rules o) const { return vector_align_component == o.vector_align_component && vector_straddle_16b == o.vector_straddle_16b && tight_arrays == o.tight_arrays && trailing_overlap == o.trailing_overlap; } bool operator!=(Packing::Rules o) const { return !(*this == o); } // is a vector's alignment equal to its component alignment? If not, vectors must have an // a larger alignment e.g. for floats a float2 has 8 byte alignment, float3 and float4 have // 16-byte alignment bool vector_align_component = false; // can vectors straddle a 16-byte boundary? // if not, offsets of vectors are padded as necessary so they do not cross the boundary // // note that vectors can only straddle the 16-byte boundary if they are not component aligned, so // this can only be true if vector_align_component is also true. bool vector_straddle_16b = false; // are arrays packed tightly with all elements contiguous? if not, each element starts on a // 16-byte aligned offset bool tight_arrays = false; // do non-tightly packed arrays and structs have reserved padding up to a multiple of their // alignment? // if so, subsequent elements must be placed after that padding region, if not subsequent elements // can be inside that padding region. // // For D3D this is allowed for cbuffers, but *not* for UAVs/structured types. This is only // applicable for structs since structured types have tight arrays, but in that case trailing // padding is not usable - matching C // // note this is compatible with C packing for structs (C structs have a size that includes their // trailing padding and members after a struct are not packed in that padding). For arrays it does // not apply since C arrays are packed. bool trailing_overlap = false; }; }; // namespace Packing struct ParsedFormat { ShaderConstant fixed, repeating; Packing::Rules packing; QMap errors; }; struct StructFormatData; struct BufferFormatter { private: Q_DECLARE_TR_FUNCTIONS(BufferFormatter); static GraphicsAPI m_API; static bool CheckInvalidUnbounded(const StructFormatData &structDef, const QMap &structelems, QMap &errors); static bool ContainsUnbounded(const ShaderConstant &structType, rdcpair *found = NULL); static QString DeclareStruct(Packing::Rules pack, ResourceId shader, QList &declaredStructs, QMap &anonStructs, const QString &name, const rdcarray &members, uint32_t requiredByteStride, QString innerSkippedPrefixString); static uint32_t GetAlignment(Packing::Rules pack, const ShaderConstant &constant); static uint32_t GetUnpaddedStructAdvance(Packing::Rules pack, const rdcarray &members); static uint32_t GetVarStraddleSize(const ShaderConstant &var); static uint32_t GetVarSizeAndTrail(const ShaderConstant &var); static void EstimatePackingRules(Packing::Rules &pack, ResourceId shader, const ShaderConstant &constant); static void EstimatePackingRules(Packing::Rules &pack, ResourceId shader, const rdcarray &members); static QString DeclarePacking(Packing::Rules pack); public: BufferFormatter() = default; static void Init(GraphicsAPI api) { m_API = api; } static ParsedFormat ParseFormatString(const QString &formatString, uint64_t maxLen, bool cbuffer); static uint32_t GetVarAdvance(Packing::Rules pack, const ShaderConstant &var); static Packing::Rules EstimatePackingRules(ResourceId shader, const rdcarray &members); static QString GetTextureFormatString(const TextureDescription &tex); static QString GetBufferFormatString(Packing::Rules pack, ResourceId shader, const ShaderResource &res, const ResourceFormat &viewFormat); static QString DeclareStruct(Packing::Rules pack, ResourceId shader, const QString &name, const rdcarray &members, uint32_t requiredByteStride); }; QVariantList GetVariants(ResourceFormat format, const ShaderConstant &var, const byte *&data, const byte *end); ResourceFormat GetInterpretedResourceFormat(const ShaderConstant &elem); void SetInterpretedResourceFormat(ShaderConstant &elem, ResourceFormatType interpretType, CompType interpretCompType); ShaderVariable InterpretShaderVar(const ShaderConstant &elem, const byte *data, const byte *end); QString TypeString(const ShaderVariable &v, const ShaderConstant &c = ShaderConstant()); QString RowString(const ShaderVariable &v, uint32_t row, VarType type = VarType::Unknown); QString VarString(const ShaderVariable &v, const ShaderConstant &c); QString RowTypeString(const ShaderVariable &v); QString TypeString(const SigParameter &sig); QString D3DSemanticString(const SigParameter &sig); QString GetComponentString(byte mask); void CombineUsageEvents( ICaptureContext &ctx, const rdcarray &usage, std::function callback); class RDTreeWidgetItem; QVariant SDObject2Variant(const SDObject *obj, bool inlineImportant); void addStructuredChildren(RDTreeWidgetItem *parent, const SDObject &parentObj); struct PointerTypeRegistry { public: static void Init(); static void CacheShader(const ShaderReflection *reflection); static uint32_t GetTypeID(ResourceId shader, uint32_t pointerTypeId); static uint32_t GetTypeID(PointerVal val) { return GetTypeID(val.shader, val.pointerTypeID); } static uint32_t GetTypeID(const ShaderConstantType &structDef); static const ShaderConstantType &GetTypeDescriptor(uint32_t typeId); static const ShaderConstantType &GetTypeDescriptor(ResourceId shader, uint32_t pointerTypeId) { return GetTypeDescriptor(GetTypeID(shader, pointerTypeId)); } static const ShaderConstantType &GetTypeDescriptor(PointerVal val) { return GetTypeDescriptor(GetTypeID(val)); } private: static void CacheSubTypes(const ShaderReflection *reflection, ShaderConstantType &structDef); static QMap, uint32_t> typeMapping; static rdcarray typeDescriptions; }; struct GPUAddress { GPUAddress() = default; GPUAddress(const PointerVal &v) : val(v) {} PointerVal val; // cached data ResourceId base; uint64_t offset = 0; // cache the context once we've obtained it. const ICaptureContext *ctxptr = NULL; void cacheAddress(const QWidget *widget); void cacheAddress(const ICaptureContext &ctx); }; struct EnumInterpValue { QString str; uint64_t val; }; Q_DECLARE_METATYPE(EnumInterpValue); ICaptureContext *getCaptureContext(const QWidget *widget); // this will check the variant, and if it contains a ResourceId directly or text with ResourceId // identifiers then it will be converted into a RichResourceTextPtr or ResourceId in-place. The new // QVariant will still convert to QString so it doesn't have to be special-cased. However it must be // processed through one of the functions below (generally painting) to cache the rendered text. If // the variant doesn't match the above conditions, it's unchanged. So it's safe to apply this // reasonably liberally. // // In this case the variant may not actually be a complex RichResourceText object, that's only used // when there is text with ResourceId(s) inside it. If the text is just a ResourceId on its own // (which is quite common) it will be stored as just a ResourceId but will still be painted & mouse // handled the same way, and all of the below functions can be used on the variant either way. // // NOTE: It is not possible to move a RichResourceText instance from one ICaptureContext to another // as the pointer is cached internally. Instead you should delete the old and re-initialise from // scratch. void RichResourceTextInitialise(QVariant &var, ICaptureContext *ctx = NULL, bool parseURLs = false); // Checks if a variant is rich resource text and should be treated specially // Particularly meaning we need mouse tracking on the widget to handle the on-hover highlighting // and mouse clicks bool RichResourceTextCheck(const QVariant &var); // Paint the given variant containing rich text with the given parameters. void RichResourceTextPaint(const QWidget *owner, QPainter *painter, QRect rect, QFont font, QPalette palette, QStyle::State state, QPoint mousePos, const QVariant &var); // Gives the width/height for a size hint for the rich text (since it might be larger than the // original text) int RichResourceTextWidthHint(const QWidget *owner, const QFont &font, const QVariant &var); int RichResourceTextHeightHint(const QWidget *owner, const QFont &font, const QVariant &var); // Handle a mouse event on some rich resource text. // Returns true if the event is processed - for mouse move events, this means that the mouse is over // a resource link (which can be used to change the cursor to a pointing hand, for example). bool RichResourceTextMouseEvent(const QWidget *owner, const QVariant &var, QRect rect, const QFont &font, QMouseEvent *event); // immediately format a variant that may contain rich resource text. For use in places where we // can't paint rich resource text but we still want to display the string nicely QString RichResourceTextFormat(ICaptureContext &ctx, QVariant var); // Register runtime conversions for custom Qt metatypes void RegisterMetatypeConversions(); struct Formatter { static void setParams(const PersistantConfig &config); static void setPalette(QPalette palette); static void shutdown(); static QString Format(double f, bool hex = false); static QString Format(rdhalf f, bool hex = false) { return Format((float)f, hex); } static QString HumanFormat(uint64_t u); static QString Format(uint64_t u, bool hex = false) { return QFormatStr("%1").arg(u, hex ? 16 : 0, hex ? 16 : 10, QLatin1Char('0')).toUpper(); } static QString Format(uint32_t u, bool hex = false) { return QFormatStr("%1").arg(u, hex ? 8 : 0, hex ? 16 : 10, QLatin1Char('0')).toUpper(); } static QString Format(uint16_t u, bool hex = false) { return QFormatStr("%1").arg(u, hex ? 4 : 0, hex ? 16 : 10, QLatin1Char('0')).toUpper(); } static QString Format(uint8_t u, bool hex = false) { return QFormatStr("%1").arg(u, hex ? 2 : 0, hex ? 16 : 10, QLatin1Char('0')).toUpper(); } static QString Format(bool b, bool hex = false) { return b ? lit("true") : lit("false"); } static QString HexFormat(uint32_t u, uint32_t byteSize) { if(byteSize == 1) return Format(uint8_t(u & 0xff), true); else if(byteSize == 2) return Format(uint16_t(u & 0xffff), true); else return Format(u, true); } static QString BinFormat(uint64_t u, uint32_t byteSize) { return QFormatStr("%1").arg(u, byteSize * 8, 2, QLatin1Char('0')).toUpper(); } static QString BinFormat(uint64_t u) { return BinFormat(u, 8); } static QString BinFormat(uint32_t u) { return BinFormat(u, 4); } static QString BinFormat(uint16_t u) { return BinFormat(u, 2); } static QString BinFormat(uint8_t u) { return BinFormat(u, 1); } static QString BinFormat(int64_t i) { return Format(i); } static QString BinFormat(int32_t i) { return Format(i); } static QString BinFormat(int16_t i) { return Format(i); } static QString BinFormat(int8_t i) { return Format(i); } static QString BinFormat(float f) { return Format(f); } static QString BinFormat(rdhalf f) { return Format(f); } static QString BinFormat(double d) { return Format(d); } static QString BinFormat(bool b) { return b ? lit("true") : lit("false"); } static QString Format(int32_t i, bool hex = false) { return QString::number(i); } static QString Format(int64_t i, bool hex = false) { return QString::number(i); } static const QFont &PreferredFont() { return *m_Font; } static const QFont &FixedFont() { return *m_FixedFont; } static const QColor DarkCheckerColor() { return m_DarkChecker; } static const QColor LightCheckerColor() { return m_LightChecker; } static QString DefaultFontFamily() { return m_DefaultFontFamily; } static QString DefaultMonoFontFamily() { return m_DefaultMonoFontFamily; } private: static int m_minFigures, m_maxFigures, m_expNegCutoff, m_expPosCutoff; static double m_expNegValue, m_expPosValue; static QFont *m_Font, *m_FixedFont; static float m_FontBaseSize, m_FixedFontBaseSize; static QString m_DefaultFontFamily, m_DefaultMonoFontFamily; static QColor m_DarkChecker, m_LightChecker; }; bool SaveToJSON(QVariantMap &data, QIODevice &f, const char *magicIdentifier, uint32_t magicVersion); bool LoadFromJSON(QVariantMap &data, QIODevice &f, const char *magicIdentifier, uint32_t magicVersion); QString VariantToJSON(const QVariantMap &data); QVariantMap JSONToVariant(const QString &json); // implementation of QOverload, to avoid depending on 5.7. // From: http://stackoverflow.com/a/16795664/4070143 template struct OverloadedSlot { template static constexpr auto of(R (C::*pmf)(Args...)) -> decltype(pmf) { return pmf; } }; // Utility class for invoking a lambda on the GUI thread. // This is supported by QTimer::singleShot on Qt 5.4 but it's probably // wise not to require a higher version that necessary. #include #include class GUIInvoke : public QObject { private: Q_OBJECT GUIInvoke(QObject *obj, const std::function &f) : ptr(obj), func(f) {} QPointer ptr; std::function func; static int methodIndex; public: static void init(); static void call(QObject *obj, const std::function &f); static void blockcall(QObject *obj, const std::function &f); static bool onUIThread(); // same as call() above, but it doesn't check for an instant call on the UI thread static void defer(QObject *obj, const std::function &f); protected slots: void doInvoke() { if(ptr) func(); deleteLater(); } }; // Utility class for calling a lambda on a new thread. #include class LambdaThread : public QObject { private: Q_OBJECT std::function m_func; QThread *m_Thread; QSemaphore completed; bool m_SelfDelete = false; QString m_Name; void windowsSetName(); public slots: void process() { if(!m_Name.isEmpty()) windowsSetName(); m_func(); m_Thread->quit(); if(m_SelfDelete) deleteLater(); completed.acquire(); } void selfDelete(bool d) { m_SelfDelete = d; } public: explicit LambdaThread(std::function f) { completed.release(); m_Thread = new QThread(); m_func = f; moveToThread(m_Thread); QObject::connect(m_Thread, &QThread::started, this, &LambdaThread::process); } ~LambdaThread() { m_Thread->deleteLater(); } void setName(QString name) { m_Name = name; m_Thread->setObjectName(name); } void start(QThread::Priority prio = QThread::InheritPriority) { m_Thread->start(prio); } bool isRunning() { return completed.available(); } bool wait(unsigned long time = ULONG_MAX) { if(m_Thread) return m_Thread->wait(time); return true; } void moveObjectToThread(QObject *o) { o->moveToThread(m_Thread); } bool isCurrentThread() { return QThread::currentThread() == m_Thread; } }; class RDProcess : public QProcess { public: RDProcess(QObject *parent = NULL) : QProcess(parent) {} void detach() { setProcessState(QProcess::NotRunning); } }; class RegisteredMenuItem : public QObject { private: Q_OBJECT public: ContextMenu context; PanelMenu panel; rdcarray submenus; IExtensionManager::ExtensionCallback callback; }; class QFileFilterModel : public QSortFilterProxyModel { Q_OBJECT public: explicit QFileFilterModel(QObject *parent = Q_NULLPTR) : QSortFilterProxyModel(parent) {} void setRequirePermissions(QDir::Filters mask) { m_requireMask = mask; } void setExcludePermissions(QDir::Filters mask) { m_excludeMask = mask; } protected: virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; private: QDir::Filters m_requireMask, m_excludeMask; }; class QCollator; class QCollatorSortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit QCollatorSortFilterProxyModel(QObject *parent = Q_NULLPTR); ~QCollatorSortFilterProxyModel(); QCollator *collator() { return m_collator; } protected: virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; private: QCollator *m_collator; }; // Simple QStyledItemDelegate child that will either forward to an external delegate (allowing // chaining) or to the base implementation. Delegates can derive from this and specialise a couple // of functions to still be able to chain class ForwardingDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit ForwardingDelegate(QObject *parent = NULL) : QStyledItemDelegate(parent) {} ~ForwardingDelegate() {} void setForwardDelegate(QAbstractItemDelegate *real) { if(m_delegate) { QObject::disconnect(m_delegate, &QAbstractItemDelegate::commitData, this, &QAbstractItemDelegate::commitData); QObject::disconnect(m_delegate, &QAbstractItemDelegate::closeEditor, this, &QAbstractItemDelegate::closeEditor); QObject::disconnect(m_delegate, &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemDelegate::sizeHintChanged); } m_delegate = real; if(m_delegate) { QObject::connect(m_delegate, &QAbstractItemDelegate::commitData, this, &QAbstractItemDelegate::commitData); QObject::connect(m_delegate, &QAbstractItemDelegate::closeEditor, this, &QAbstractItemDelegate::closeEditor); QObject::connect(m_delegate, &QAbstractItemDelegate::sizeHintChanged, this, &QAbstractItemDelegate::sizeHintChanged); } } void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if(m_delegate) return m_delegate->paint(painter, option, index); return QStyledItemDelegate::paint(painter, option, index); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { if(m_delegate) return m_delegate->sizeHint(option, index); return QStyledItemDelegate::sizeHint(option, index); } QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if(m_delegate) return m_delegate->createEditor(parent, option, index); return QStyledItemDelegate::createEditor(parent, option, index); } void destroyEditor(QWidget *editor, const QModelIndex &index) const override { if(m_delegate) return m_delegate->destroyEditor(editor, index); return QStyledItemDelegate::destroyEditor(editor, index); } void setEditorData(QWidget *editor, const QModelIndex &index) const override { if(m_delegate) return m_delegate->setEditorData(editor, index); return QStyledItemDelegate::setEditorData(editor, index); } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { if(m_delegate) return m_delegate->setModelData(editor, model, index); return QStyledItemDelegate::setModelData(editor, model, index); } void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if(m_delegate) return m_delegate->updateEditorGeometry(editor, option, index); return QStyledItemDelegate::updateEditorGeometry(editor, option, index); } bool eventFilter(QObject *watched, QEvent *event) override { if(m_delegate) return m_delegate->eventFilter(watched, event); return QStyledItemDelegate::eventFilter(watched, event); } bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override { if(m_delegate) return m_delegate->editorEvent(event, model, option, index); return QStyledItemDelegate::editorEvent(event, model, option, index); } bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) override { if(m_delegate) return m_delegate->helpEvent(event, view, option, index); return QStyledItemDelegate::helpEvent(event, view, option, index); } QVector paintingRoles() const override { if(m_delegate) return m_delegate->paintingRoles(); return QStyledItemDelegate::paintingRoles(); } private: QAbstractItemDelegate *m_delegate = NULL; }; class FullEditorDelegate : public QStyledItemDelegate { private: Q_OBJECT public: FullEditorDelegate(QWidget *parent); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; // delegate that will handle painting, hovering and clicking on rich text items. // owning view needs to call linkHover, and adjust its cursor and repaint as necessary. class RichTextViewDelegate : public ForwardingDelegate { Q_OBJECT public: explicit RichTextViewDelegate(QAbstractItemView *parent); ~RichTextViewDelegate(); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; bool linkHover(QMouseEvent *e, const QFont &font, const QModelIndex &index); private: QAbstractItemView *m_widget; }; class StructuredDataItemModel : public QAbstractItemModel { public: enum SDColumn { Name, Value, Type, }; void setColumns(QStringList names, rdcarray values) { m_ColumnNames = names; m_ColumnValues = values; } const rdcarray &objects() const; void setObjects(const rdcarray &objs); StructuredDataItemModel(QWidget *parent) : QAbstractItemModel(parent) {} QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: enum IndexTag { Direct, PageNode, ArrayMember, }; struct Index { IndexTag tag; SDObject *obj; int indexInArray; }; Index decodeIndex(QModelIndex idx) const; quintptr encodeIndex(Index idx) const; bool isLargeArray(SDObject *obj) const; rdcarray m_Objects; QStringList m_ColumnNames; rdcarray m_ColumnValues; // these below members have to be mutable since we update these caches in const functions // set of large arrays, so we can refer to them by compressed index instead of needing a full // pointer. mutable rdcarray m_Arrays; // objects that are in large arrays. This map gives us the index they are in their parent. // This is only used for parent() when the parent is an array member, so we only add into this // list when creating a child index of such an array member - which only happens when the array // member is expanded. That keeps to a minimum the number of entries. mutable QMap m_ArrayMembers; }; // helper functions for using a double spinbox for 64-bit integers. We do this because it's // infeasible in Qt to actually derive and create a real 64-bit integer spinbox because critical // functionality depends on deriving QAbstractSpinBoxPrivate which is unavailable. So instead we use // a QDoubleSpinBox and restrict ourselves to the 53 bits of mantissa. This struct only has inline // helpers so we can cast a QDoubleSpinBox to one of these and use it as-is. class RDSpinBox64 : public QDoubleSpinBox { private: static const qlonglong mask = (1ULL << 53U) - 1; public: void configure() { QDoubleSpinBox::setDecimals(0); } void setSingleStep(qlonglong val) { QDoubleSpinBox::setSingleStep(makeValue(val)); } void setMinimum(qlonglong min) { QDoubleSpinBox::setMinimum(makeValue(min)); } void setMaximum(qlonglong max) { QDoubleSpinBox::setMaximum(makeValue(max)); } void setRange(qlonglong min, qlonglong max) { RDSpinBox64::setMinimum(min); RDSpinBox64::setMaximum(max); } void setSingleStep(qulonglong val) { QDoubleSpinBox::setSingleStep(makeValue(val)); } void setMinimum(qulonglong min) { QDoubleSpinBox::setMinimum(makeValue(min)); } void setMaximum(qulonglong max) { QDoubleSpinBox::setMaximum(makeValue(max)); } void setRange(qulonglong min, qulonglong max) { RDSpinBox64::setMinimum(min); RDSpinBox64::setMaximum(max); } static qlonglong getValue(double d) { return qlonglong(d); } static qulonglong getUValue(double d) { return qulonglong(d); } static double makeValue(qlonglong l) { return l < 0 ? -double((-l) & mask) : double(l & mask); } static double makeValue(qulonglong l) { return double(l & mask); } }; class QMenu; // helper for doing a manual blocking invoke of a dialog struct RDDialog { static const QMessageBox::StandardButtons YesNoCancel; static QString DefaultBrowsePath; static void show(QMenu *menu, QPoint pos); static int show(QDialog *dialog); static QMessageBox::StandardButton messageBox( QMessageBox::Icon, QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); static QMessageBox::StandardButton messageBoxChecked( QMessageBox::Icon, QWidget *parent, const QString &title, const QString &text, QCheckBox *checkBox, bool &checked, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); static QMessageBox::StandardButton information( QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return messageBox(QMessageBox::Information, parent, title, text, buttons, defaultButton); } static QMessageBox::StandardButton question( QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return messageBox(QMessageBox::Question, parent, title, text, buttons, defaultButton); } static QMessageBox::StandardButton questionChecked( QWidget *parent, const QString &title, const QString &text, QCheckBox *checkBox, bool &checked, QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return messageBoxChecked(QMessageBox::Question, parent, title, text, checkBox, checked, buttons, defaultButton); } static QMessageBox::StandardButton warning( QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return messageBox(QMessageBox::Warning, parent, title, text, buttons, defaultButton); } static QMessageBox::StandardButton critical( QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return messageBox(QMessageBox::Critical, parent, title, text, buttons, defaultButton); } static QString getExistingDirectory(QWidget *parent = NULL, const QString &caption = QString(), const QString &dir = QString(), QFileDialog::Options options = QFileDialog::ShowDirsOnly); static QString getOpenFileName(QWidget *parent = NULL, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = NULL, QFileDialog::Options options = QFileDialog::Options()); static QString getExecutableFileName(QWidget *parent = NULL, const QString &caption = QString(), const QString &dir = QString(), const QString &defaultExe = QString(), QFileDialog::Options options = QFileDialog::Options()); static QString getSaveFileName(QWidget *parent = NULL, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = NULL, QFileDialog::Options options = QFileDialog::Options()); }; class QGridLayout; void addGridLines(QGridLayout *grid, QColor gridColor); class QProgressDialog; class QProgressBar; class QElapsedTimer; typedef std::function ProgressUpdateMethod; typedef std::function ProgressFinishedMethod; typedef std::function ProgressCancelMethod; QStringList ParseArgsList(const QString &args); bool IsRunningAsAdmin(); bool RunProcessAsAdmin(const QString &fullExecutablePath, const QStringList ¶ms, QWidget *parent = NULL, bool hidden = false, std::function finishedCallback = std::function()); void RevealFilenameInExternalFileBrowser(const QString &filePath); void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinishedMethod finished, ProgressUpdateMethod update = ProgressUpdateMethod(), ProgressCancelMethod cancel = ProgressCancelMethod()); void UpdateTransferProgress(qint64 xfer, qint64 total, QElapsedTimer *timer, QProgressBar *progressBar, QLabel *progressLabel, QString progressText); void setEnabledMultiple(const QList &widgets, bool enabled); QString GetSystemUsername(); void BringToForeground(QWidget *window); bool IsDarkTheme(); float getLuminance(const QColor &col); QColor contrastingColor(const QColor &col, const QColor &defaultCol); void *AccessWaylandPlatformInterface(const QByteArray &resource, QWindow *window); void UpdateVisibleColumns(rdcstr windowTitle, int columnCount, QHeaderView *header, const QStringList &headers);