KindFlags refactor, indicator helpers, auto-padding, sizeof format fix

- Add KindFlags bitmask enum (KF_HexPreview, KF_Container, KF_String, KF_Vector)
- Add helper functions: isHexPreview(), flagsFor(), allTypeNamesForUI()
- Add editor helpers: clearIndicatorLine(), fillIndicatorCols(), resolvedSpanFor()
- Use SCI_FINDCOLUMN for UTF-8 safe column-to-position conversion
- Auto-emit hex padding nodes when changing type to smaller size
- Fix struct footer format: sizeof(Name)=0x... (no spaces)
- Add tests for sizeof recalculation after node deletion
- Add debug output to investigate sizeof refresh issue
- Replace icons.qrc with resources.qrc, add embedded fonts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
sysadmin
2026-02-03 05:55:29 -07:00
parent abe5e3ebd9
commit af183f27f0
14 changed files with 787 additions and 325 deletions

View File

@@ -1,5 +1,6 @@
#pragma once
#include <QString>
#include <QStringList>
#include <QVector>
#include <QJsonObject>
#include <QJsonArray>
@@ -27,6 +28,16 @@ enum class NodeKind : uint8_t {
Struct, Array
};
// ── Kind flags (replaces repeated Hex/Padding switches) ──
enum KindFlags : uint32_t {
KF_None = 0,
KF_HexPreview = 1 << 0, // Hex8..Hex64 + Padding (ASCII+hex layout)
KF_Container = 1 << 1, // Struct/Array
KF_String = 1 << 2, // UTF8/UTF16
KF_Vector = 1 << 3, // Vec2/3/4
};
// ── Unified kind metadata table (single source of truth) ──
struct KindMeta {
@@ -36,36 +47,37 @@ struct KindMeta {
int size; // byte size (0 = dynamic: Struct/Array)
int lines; // display line count
int align; // natural alignment
uint32_t flags; // KindFlags bitmask
};
inline constexpr KindMeta kKindMeta[] = {
// kind name typeName sz ln al
{NodeKind::Hex8, "Hex8", "Hex8", 1, 1, 1},
{NodeKind::Hex16, "Hex16", "Hex16", 2, 1, 2},
{NodeKind::Hex32, "Hex32", "Hex32", 4, 1, 4},
{NodeKind::Hex64, "Hex64", "Hex64", 8, 1, 8},
{NodeKind::Int8, "Int8", "int8_t", 1, 1, 1},
{NodeKind::Int16, "Int16", "int16_t", 2, 1, 2},
{NodeKind::Int32, "Int32", "int32_t", 4, 1, 4},
{NodeKind::Int64, "Int64", "int64_t", 8, 1, 8},
{NodeKind::UInt8, "UInt8", "uint8_t", 1, 1, 1},
{NodeKind::UInt16, "UInt16", "uint16_t", 2, 1, 2},
{NodeKind::UInt32, "UInt32", "uint32_t", 4, 1, 4},
{NodeKind::UInt64, "UInt64", "uint64_t", 8, 1, 8},
{NodeKind::Float, "Float", "float", 4, 1, 4},
{NodeKind::Double, "Double", "double", 8, 1, 8},
{NodeKind::Bool, "Bool", "bool", 1, 1, 1},
{NodeKind::Pointer32, "Pointer32", "ptr32", 4, 1, 4},
{NodeKind::Pointer64, "Pointer64", "ptr64", 8, 1, 8},
{NodeKind::Vec2, "Vec2", "Vec2", 8, 2, 4},
{NodeKind::Vec3, "Vec3", "Vec3", 12, 3, 4},
{NodeKind::Vec4, "Vec4", "Vec4", 16, 4, 4},
{NodeKind::Mat4x4, "Mat4x4", "Mat4x4", 64, 4, 4},
{NodeKind::UTF8, "UTF8", "char[]", 1, 1, 1},
{NodeKind::UTF16, "UTF16", "wchar_t[]", 2, 1, 2},
{NodeKind::Padding, "Padding", "pad", 1, 1, 1},
{NodeKind::Struct, "Struct", "struct", 0, 1, 1},
{NodeKind::Array, "Array", "array", 0, 1, 1},
// kind name typeName sz ln al flags
{NodeKind::Hex8, "Hex8", "Hex8", 1, 1, 1, KF_HexPreview},
{NodeKind::Hex16, "Hex16", "Hex16", 2, 1, 2, KF_HexPreview},
{NodeKind::Hex32, "Hex32", "Hex32", 4, 1, 4, KF_HexPreview},
{NodeKind::Hex64, "Hex64", "Hex64", 8, 1, 8, KF_HexPreview},
{NodeKind::Int8, "Int8", "int8_t", 1, 1, 1, KF_None},
{NodeKind::Int16, "Int16", "int16_t", 2, 1, 2, KF_None},
{NodeKind::Int32, "Int32", "int32_t", 4, 1, 4, KF_None},
{NodeKind::Int64, "Int64", "int64_t", 8, 1, 8, KF_None},
{NodeKind::UInt8, "UInt8", "uint8_t", 1, 1, 1, KF_None},
{NodeKind::UInt16, "UInt16", "uint16_t", 2, 1, 2, KF_None},
{NodeKind::UInt32, "UInt32", "uint32_t", 4, 1, 4, KF_None},
{NodeKind::UInt64, "UInt64", "uint64_t", 8, 1, 8, KF_None},
{NodeKind::Float, "Float", "float", 4, 1, 4, KF_None},
{NodeKind::Double, "Double", "double", 8, 1, 8, KF_None},
{NodeKind::Bool, "Bool", "bool", 1, 1, 1, KF_None},
{NodeKind::Pointer32, "Pointer32", "ptr32", 4, 1, 4, KF_None},
{NodeKind::Pointer64, "Pointer64", "ptr64", 8, 1, 8, KF_None},
{NodeKind::Vec2, "Vec2", "Vec2", 8, 2, 4, KF_Vector},
{NodeKind::Vec3, "Vec3", "Vec3", 12, 3, 4, KF_Vector},
{NodeKind::Vec4, "Vec4", "Vec4", 16, 4, 4, KF_Vector},
{NodeKind::Mat4x4, "Mat4x4", "Mat4x4", 64, 4, 4, KF_None},
{NodeKind::UTF8, "UTF8", "char[]", 1, 1, 1, KF_String},
{NodeKind::UTF16, "UTF16", "wchar_t[]", 2, 1, 2, KF_String},
{NodeKind::Padding, "Padding", "pad", 1, 1, 1, KF_HexPreview},
{NodeKind::Struct, "Struct", "struct", 0, 1, 1, KF_Container},
{NodeKind::Array, "Array", "array", 0, 1, 1, KF_Container},
};
inline constexpr const KindMeta* kindMeta(NodeKind k) {
@@ -100,6 +112,27 @@ inline NodeKind kindFromTypeName(const QString& s, bool* ok = nullptr) {
return NodeKind::Hex8;
}
inline constexpr uint32_t flagsFor(NodeKind k) {
const auto* m = kindMeta(k);
return m ? m->flags : 0;
}
inline constexpr bool isHexPreview(NodeKind k) {
return flagsFor(k) & KF_HexPreview;
}
inline QStringList allTypeNamesForUI(bool stripBrackets = false) {
QStringList out;
out.reserve(std::size(kKindMeta));
for (const auto& m : kKindMeta) {
QString t = QString::fromLatin1(m.typeName);
if (stripBrackets) t.remove(QStringLiteral("[]"));
out << t;
}
out.sort(Qt::CaseInsensitive);
out.removeDuplicates();
return out;
}
// ── Marker vocabulary ──
enum Marker : int {
@@ -450,29 +483,36 @@ inline ColumnSpan typeSpanFor(const LineMeta& lm) {
inline ColumnSpan nameSpanFor(const LineMeta& lm) {
if (lm.isContinuation || lm.lineKind != LineKind::Field) return {};
// Hex/Padding nodes show ASCII data preview instead of name
switch (lm.nodeKind) {
case NodeKind::Hex8: case NodeKind::Hex16:
case NodeKind::Hex32: case NodeKind::Hex64:
case NodeKind::Padding:
return {};
default: break;
}
int ind = kFoldCol + lm.depth * 3;
int start = ind + kColType + kSepWidth;
// Hex/Padding: ASCII preview takes the name column position (8 chars)
if (isHexPreview(lm.nodeKind))
return {start, start + 8, true};
return {start, start + kColName, true};
}
inline ColumnSpan valueSpanFor(const LineMeta& lm, int lineLength) {
if (lm.lineKind == LineKind::Header || lm.lineKind == LineKind::Footer) return {};
int ind = kFoldCol + lm.depth * 3;
// Hex/Padding layout: [Type][sep][ASCII(8)][sep][hex bytes...]
bool isHexPad = isHexPreview(lm.nodeKind);
if (lm.isContinuation) {
int prefixW = kColType + kColName + 4; // 2 seps × 2 chars
int prefixW = isHexPad
? (kColType + kSepWidth + 8 + kSepWidth)
: (kColType + kColName + 4);
int start = ind + prefixW;
return {start, lineLength, start < lineLength};
}
if (lm.lineKind != LineKind::Field) return {};
int start = ind + kColType + kSepWidth + kColName + kSepWidth;
int start = isHexPad
? (ind + kColType + kSepWidth + 8 + kSepWidth)
: (ind + kColType + kSepWidth + kColName + kSepWidth);
return {start, lineLength, start < lineLength};
}
@@ -514,6 +554,7 @@ namespace fmt {
QString editableValue(const Node& node, const Provider& prov,
uint64_t addr, int subLine);
QByteArray parseValue(NodeKind kind, const QString& text, bool* ok);
QByteArray parseAsciiValue(const QString& text, int expectedSize, bool* ok);
} // namespace fmt
// ── Compose function forward declaration ──