mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Dim command row address, fix source picker zoom/styling, lowercase menu items, margin spacing
- Apply IND_HEX_DIM to base address on command row so it matches source/struct grey - Source picker QMenu respects Scintilla zoom level for font size - Style source picker with dark theme selection color instead of system blue - Disable blue selection background for picker-based edit targets (Source, Type, etc.) - Rename menu items File/Process to file/process - Add trailing space to margin offset text for breathing room
This commit is contained in:
@@ -39,10 +39,9 @@ struct ComposeState {
|
|||||||
if (currentLine > 0) text += '\n';
|
if (currentLine > 0) text += '\n';
|
||||||
// 3-char fold indicator column: " - " expanded, " + " collapsed, " " other
|
// 3-char fold indicator column: " - " expanded, " + " collapsed, " " other
|
||||||
// CommandRow has no fold prefix (flush left)
|
// CommandRow has no fold prefix (flush left)
|
||||||
if (lm.lineKind == LineKind::CommandRow || lm.lineKind == LineKind::Blank
|
if (lm.lineKind == LineKind::CommandRow
|
||||||
|| lm.lineKind == LineKind::CommandRow2
|
|
||||||
|| (lm.lineKind == LineKind::Footer && lm.isRootHeader)) {
|
|| (lm.lineKind == LineKind::Footer && lm.isRootHeader)) {
|
||||||
// no prefix — flush left like CommandRow2
|
// no prefix — flush left
|
||||||
} else if (lm.foldHead)
|
} else if (lm.foldHead)
|
||||||
text += lm.foldCollapsed ? QStringLiteral(" \u25B8 ") : QStringLiteral(" \u25BE ");
|
text += lm.foldCollapsed ? QStringLiteral(" \u25B8 ") : QStringLiteral(" \u25BE ");
|
||||||
else
|
else
|
||||||
@@ -218,13 +217,13 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detect root header: first root-level struct — suppressed from display
|
// Detect root header: first root-level struct — suppressed from display
|
||||||
// (CommandRow2 already shows the root class type + name)
|
// (CommandRow already shows the root class type + name)
|
||||||
bool isRootHeader = (node.parentId == 0 && node.kind == NodeKind::Struct && !state.baseEmitted);
|
bool isRootHeader = (node.parentId == 0 && node.kind == NodeKind::Struct && !state.baseEmitted);
|
||||||
if (isRootHeader)
|
if (isRootHeader)
|
||||||
state.baseEmitted = true;
|
state.baseEmitted = true;
|
||||||
|
|
||||||
// Header line (skip for array element structs and root struct)
|
// Header line (skip for array element structs and root struct)
|
||||||
// Root struct header is on CommandRow2 (type + name + {)
|
// Root struct header is on CommandRow (type + name + {)
|
||||||
if (!isArrayChild && !isRootHeader) {
|
if (!isArrayChild && !isRootHeader) {
|
||||||
// Get per-scope widths for this header's parent scope
|
// Get per-scope widths for this header's parent scope
|
||||||
int typeW = state.effectiveTypeW(scopeId);
|
int typeW = state.effectiveTypeW(scopeId);
|
||||||
@@ -474,8 +473,8 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
|
|||||||
state.scopeNameW[0] = qBound(kMinNameW, rootMaxName, kMaxNameW);
|
state.scopeNameW[0] = qBound(kMinNameW, rootMaxName, kMaxNameW);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit CommandRow as line 0 (synthetic UI line)
|
// Emit CommandRow as line 0 (combined: source + address + root class type + name)
|
||||||
const QString cmdRowText = QStringLiteral("source\u25BE \u203A 0x0");
|
const QString cmdRowText = QStringLiteral("source\u25BE \u00B7 0x0 \u00B7 struct\u25BE <no class> {");
|
||||||
{
|
{
|
||||||
LineMeta lm;
|
LineMeta lm;
|
||||||
lm.nodeIdx = -1;
|
lm.nodeIdx = -1;
|
||||||
@@ -484,48 +483,13 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
|
|||||||
lm.lineKind = LineKind::CommandRow;
|
lm.lineKind = LineKind::CommandRow;
|
||||||
lm.foldLevel = SC_FOLDLEVELBASE;
|
lm.foldLevel = SC_FOLDLEVELBASE;
|
||||||
lm.foldHead = false;
|
lm.foldHead = false;
|
||||||
lm.offsetText.clear();
|
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress, false);
|
||||||
lm.markerMask = 0;
|
lm.markerMask = 0;
|
||||||
lm.effectiveTypeW = state.typeW;
|
lm.effectiveTypeW = state.typeW;
|
||||||
lm.effectiveNameW = state.nameW;
|
lm.effectiveNameW = state.nameW;
|
||||||
state.emitLine(cmdRowText, lm);
|
state.emitLine(cmdRowText, lm);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit dotted separator (line 1) — fixed-width spaced dots in text + margin
|
|
||||||
{
|
|
||||||
// ~20 dots in text area, ~10 dots in margin, using middle dot · (U+00B7)
|
|
||||||
QString dots(20, QChar(0x00B7));
|
|
||||||
QString marginDots(10, QChar(0x00B7));
|
|
||||||
LineMeta lm;
|
|
||||||
lm.nodeIdx = -1;
|
|
||||||
lm.nodeId = kCommandRowId;
|
|
||||||
lm.depth = 0;
|
|
||||||
lm.lineKind = LineKind::Blank;
|
|
||||||
lm.foldLevel = SC_FOLDLEVELBASE;
|
|
||||||
lm.foldHead = false;
|
|
||||||
lm.offsetText = marginDots;
|
|
||||||
lm.markerMask = 0;
|
|
||||||
lm.effectiveTypeW = state.typeW;
|
|
||||||
lm.effectiveNameW = state.nameW;
|
|
||||||
state.emitLine(dots, lm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit CommandRow2 as line 2 (root class type + name)
|
|
||||||
{
|
|
||||||
LineMeta lm;
|
|
||||||
lm.nodeIdx = -1;
|
|
||||||
lm.nodeId = kCommandRow2Id;
|
|
||||||
lm.depth = 0;
|
|
||||||
lm.lineKind = LineKind::CommandRow2;
|
|
||||||
lm.foldLevel = SC_FOLDLEVELBASE;
|
|
||||||
lm.foldHead = false;
|
|
||||||
lm.offsetText.clear();
|
|
||||||
lm.markerMask = 0;
|
|
||||||
lm.effectiveTypeW = state.typeW;
|
|
||||||
lm.effectiveNameW = state.nameW;
|
|
||||||
state.emitLine(QStringLiteral("struct\u25BE <no class> {"), lm);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<int> roots = state.childMap.value(0);
|
QVector<int> roots = state.childMap.value(0);
|
||||||
std::sort(roots.begin(), roots.end(), [&](int a, int b) {
|
std::sort(roots.begin(), roots.end(), [&](int a, int b) {
|
||||||
return tree.nodes[a].offset < tree.nodes[b].offset;
|
return tree.nodes[a].offset < tree.nodes[b].offset;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ static QString crumbFor(const rcx::NodeTree& t, uint64_t nodeId) {
|
|||||||
std::reverse(parts.begin(), parts.end());
|
std::reverse(parts.begin(), parts.end());
|
||||||
if (parts.size() > 4)
|
if (parts.size() > 4)
|
||||||
parts = {parts.front(), QStringLiteral("\u2026"), parts[parts.size() - 2], parts.back()};
|
parts = {parts.front(), QStringLiteral("\u2026"), parts[parts.size() - 2], parts.back()};
|
||||||
return parts.join(QStringLiteral(" \u203A "));
|
return parts.join(QStringLiteral(" \u00B7 "));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── RcxDocument ──
|
// ── RcxDocument ──
|
||||||
@@ -205,7 +205,7 @@ void RcxController::connectEditor(RcxEditor* editor) {
|
|||||||
// Inline editing signals
|
// Inline editing signals
|
||||||
connect(editor, &RcxEditor::inlineEditCommitted,
|
connect(editor, &RcxEditor::inlineEditCommitted,
|
||||||
this, [this](int nodeIdx, int subLine, EditTarget target, const QString& text) {
|
this, [this](int nodeIdx, int subLine, EditTarget target, const QString& text) {
|
||||||
// CommandRow BaseAddress/Source edit has nodeIdx=-1; CommandRow2 edits too
|
// CommandRow BaseAddress/Source/RootClass edit has nodeIdx=-1
|
||||||
if (nodeIdx < 0 && target != EditTarget::BaseAddress && target != EditTarget::Source
|
if (nodeIdx < 0 && target != EditTarget::BaseAddress && target != EditTarget::Source
|
||||||
&& target != EditTarget::RootClassType && target != EditTarget::RootClassName) { refresh(); return; }
|
&& target != EditTarget::RootClassType && target != EditTarget::RootClassName) { refresh(); return; }
|
||||||
switch (target) {
|
switch (target) {
|
||||||
@@ -332,7 +332,7 @@ void RcxController::connectEditor(RcxEditor* editor) {
|
|||||||
if (text.startsWith(QStringLiteral("#saved:"))) {
|
if (text.startsWith(QStringLiteral("#saved:"))) {
|
||||||
int idx = text.mid(7).toInt();
|
int idx = text.mid(7).toInt();
|
||||||
switchToSavedSource(idx);
|
switchToSavedSource(idx);
|
||||||
} else if (text == QStringLiteral("File")) {
|
} else if (text == QStringLiteral("file")) {
|
||||||
auto* w = qobject_cast<QWidget*>(parent());
|
auto* w = qobject_cast<QWidget*>(parent());
|
||||||
QString path = QFileDialog::getOpenFileName(w, "Load Binary Data", {}, "All Files (*)");
|
QString path = QFileDialog::getOpenFileName(w, "Load Binary Data", {}, "All Files (*)");
|
||||||
if (!path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
@@ -366,7 +366,7 @@ void RcxController::connectEditor(RcxEditor* editor) {
|
|||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (text == QStringLiteral("Process")) {
|
else if (text == QStringLiteral("process")) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
auto* w = qobject_cast<QWidget*>(parent());
|
auto* w = qobject_cast<QWidget*>(parent());
|
||||||
ProcessPicker picker(w);
|
ProcessPicker picker(w);
|
||||||
@@ -1234,7 +1234,7 @@ void RcxController::handleNodeClick(RcxEditor* source, int line,
|
|||||||
int to = qMax(m_anchorLine, line);
|
int to = qMax(m_anchorLine, line);
|
||||||
for (int i = from; i <= to && i < m_lastResult.meta.size(); i++) {
|
for (int i = from; i <= to && i < m_lastResult.meta.size(); i++) {
|
||||||
uint64_t nid = m_lastResult.meta[i].nodeId;
|
uint64_t nid = m_lastResult.meta[i].nodeId;
|
||||||
if (nid != 0 && nid != kCommandRowId && nid != kCommandRow2Id) m_selIds.insert(effectiveId(i, nid));
|
if (nid != 0 && nid != kCommandRowId) m_selIds.insert(effectiveId(i, nid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // Ctrl+Shift
|
} else { // Ctrl+Shift
|
||||||
@@ -1246,7 +1246,7 @@ void RcxController::handleNodeClick(RcxEditor* source, int line,
|
|||||||
int to = qMax(m_anchorLine, line);
|
int to = qMax(m_anchorLine, line);
|
||||||
for (int i = from; i <= to && i < m_lastResult.meta.size(); i++) {
|
for (int i = from; i <= to && i < m_lastResult.meta.size(); i++) {
|
||||||
uint64_t nid = m_lastResult.meta[i].nodeId;
|
uint64_t nid = m_lastResult.meta[i].nodeId;
|
||||||
if (nid != 0 && nid != kCommandRowId && nid != kCommandRow2Id) m_selIds.insert(effectiveId(i, nid));
|
if (nid != 0 && nid != kCommandRowId) m_selIds.insert(effectiveId(i, nid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1397,10 +1397,10 @@ void RcxController::updateCommandRow() {
|
|||||||
// Build the row. If we have a symbol, append it after the address.
|
// Build the row. If we have a symbol, append it after the address.
|
||||||
QString row;
|
QString row;
|
||||||
if (sym.isEmpty()) {
|
if (sym.isEmpty()) {
|
||||||
row = QStringLiteral("%1 \u203A %2")
|
row = QStringLiteral("%1 \u00B7 %2")
|
||||||
.arg(elide(src, 40), elide(addr, 24));
|
.arg(elide(src, 40), elide(addr, 24));
|
||||||
} else {
|
} else {
|
||||||
row = QStringLiteral("%1 \u203A %2 %3")
|
row = QStringLiteral("%1 \u00B7 %2 %3")
|
||||||
.arg(elide(src, 40), elide(addr, 24), elide(sym, 40));
|
.arg(elide(src, 40), elide(addr, 24), elide(sym, 40));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1419,9 +1419,10 @@ void RcxController::updateCommandRow() {
|
|||||||
if (row2.isEmpty())
|
if (row2.isEmpty())
|
||||||
row2 = QStringLiteral("struct\u25BE <no class> {");
|
row2 = QStringLiteral("struct\u25BE <no class> {");
|
||||||
|
|
||||||
|
QString combined = row + QStringLiteral(" \u00B7 ") + row2;
|
||||||
|
|
||||||
for (auto* ed : m_editors) {
|
for (auto* ed : m_editors) {
|
||||||
ed->setCommandRowText(row);
|
ed->setCommandRowText(combined);
|
||||||
ed->setCommandRow2Text(row2);
|
|
||||||
}
|
}
|
||||||
emit selectionChanged(m_selIds.size());
|
emit selectionChanged(m_selIds.size());
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/core.h
58
src/core.h
@@ -393,18 +393,14 @@ struct NodeTree {
|
|||||||
// ── LineMeta ──
|
// ── LineMeta ──
|
||||||
|
|
||||||
enum class LineKind : uint8_t {
|
enum class LineKind : uint8_t {
|
||||||
CommandRow, // line 0: source + address
|
CommandRow, // line 0: source + address + root class type + name
|
||||||
Blank, // line 1: dotted separator
|
Blank, // (unused — kept for enum stability)
|
||||||
CommandRow2, // line 2: root class type + name
|
|
||||||
Header, Field, Continuation, Footer, ArrayElementSeparator
|
Header, Field, Continuation, Footer, ArrayElementSeparator
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr uint64_t kCommandRowId = UINT64_MAX;
|
static constexpr uint64_t kCommandRowId = UINT64_MAX;
|
||||||
static constexpr uint64_t kCommandRow2Id = UINT64_MAX - 1;
|
|
||||||
static constexpr int kCommandRowLine = 0;
|
static constexpr int kCommandRowLine = 0;
|
||||||
static constexpr int kBlankLine = 1;
|
static constexpr int kFirstDataLine = 1;
|
||||||
static constexpr int kCommandRow2Line = 2;
|
|
||||||
static constexpr int kFirstDataLine = 3;
|
|
||||||
static constexpr uint64_t kFooterIdBit = 0x8000000000000000ULL;
|
static constexpr uint64_t kFooterIdBit = 0x8000000000000000ULL;
|
||||||
|
|
||||||
struct LineMeta {
|
struct LineMeta {
|
||||||
@@ -435,7 +431,7 @@ struct LineMeta {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline bool isSyntheticLine(const LineMeta& lm) {
|
inline bool isSyntheticLine(const LineMeta& lm) {
|
||||||
return lm.lineKind == LineKind::CommandRow || lm.lineKind == LineKind::Blank || lm.lineKind == LineKind::CommandRow2;
|
return lm.lineKind == LineKind::CommandRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Layout Info ──
|
// ── Layout Info ──
|
||||||
@@ -573,10 +569,10 @@ inline ColumnSpan commentSpanFor(const LineMeta& lm, int lineLength, int typeW =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── CommandRow spans ──
|
// ── CommandRow spans ──
|
||||||
// Line format: "source▾ › 0x140000000"
|
// Line format: "source▾ · 0x140000000"
|
||||||
|
|
||||||
inline ColumnSpan commandRowSrcSpan(const QString& lineText) {
|
inline ColumnSpan commandRowSrcSpan(const QString& lineText) {
|
||||||
int idx = lineText.indexOf(QStringLiteral(" \u203A"));
|
int idx = lineText.indexOf(QStringLiteral(" \u00B7"));
|
||||||
if (idx < 0) return {};
|
if (idx < 0) return {};
|
||||||
int start = 0;
|
int start = 0;
|
||||||
while (start < idx && !lineText[start].isLetterOrNumber()
|
while (start < idx && !lineText[start].isLetterOrNumber()
|
||||||
@@ -590,38 +586,48 @@ inline ColumnSpan commandRowSrcSpan(const QString& lineText) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline ColumnSpan commandRowAddrSpan(const QString& lineText) {
|
inline ColumnSpan commandRowAddrSpan(const QString& lineText) {
|
||||||
int tag = lineText.indexOf(QStringLiteral(" \u203A"));
|
int tag = lineText.indexOf(QStringLiteral(" \u00B7"));
|
||||||
if (tag < 0) return {};
|
if (tag < 0) return {};
|
||||||
int start = tag + 3; // after " › "
|
int start = tag + 3; // after " · "
|
||||||
int end = start;
|
int end = start;
|
||||||
while (end < lineText.size() && !lineText[end].isSpace()) end++;
|
while (end < lineText.size() && !lineText[end].isSpace()) end++;
|
||||||
if (end <= start) return {};
|
if (end <= start) return {};
|
||||||
return {start, end, true};
|
return {start, end, true};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── CommandRow2 spans ──
|
// ── CommandRow root-class spans ──
|
||||||
// Line format: "struct▾ ClassName {"
|
// Combined CommandRow format ends with: " struct▾ ClassName {"
|
||||||
|
|
||||||
inline ColumnSpan commandRow2TypeSpan(const QString& lineText) {
|
inline int commandRowRootStart(const QString& lineText) {
|
||||||
int start = 0;
|
int best = -1;
|
||||||
while (start < lineText.size() && lineText[start].isSpace()) start++;
|
int i;
|
||||||
if (start >= lineText.size()) return {};
|
i = lineText.lastIndexOf(QStringLiteral("struct\u25BE"));
|
||||||
|
if (i > best) best = i;
|
||||||
|
i = lineText.lastIndexOf(QStringLiteral("class\u25BE"));
|
||||||
|
if (i > best) best = i;
|
||||||
|
i = lineText.lastIndexOf(QStringLiteral("enum\u25BE"));
|
||||||
|
if (i > best) best = i;
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ColumnSpan commandRowRootTypeSpan(const QString& lineText) {
|
||||||
|
int start = commandRowRootStart(lineText);
|
||||||
|
if (start < 0) return {};
|
||||||
int end = start;
|
int end = start;
|
||||||
while (end < lineText.size() && lineText[end] != QChar(' ') && lineText[end] != QChar(0x25BE)) end++;
|
while (end < lineText.size() && lineText[end] != QChar(' ')
|
||||||
if (end <= start) return {start, (int)lineText.size(), true};
|
&& lineText[end] != QChar(0x25BE)) end++;
|
||||||
|
if (end <= start) return {};
|
||||||
return {start, end, true};
|
return {start, end, true};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ColumnSpan commandRow2NameSpan(const QString& lineText) {
|
inline ColumnSpan commandRowRootNameSpan(const QString& lineText) {
|
||||||
// Format: "keyword name {" — extract just the name part (before " {")
|
int base = commandRowRootStart(lineText);
|
||||||
int start = 0;
|
if (base < 0) return {};
|
||||||
while (start < lineText.size() && lineText[start].isSpace()) start++;
|
int space = lineText.indexOf(' ', base);
|
||||||
int space = lineText.indexOf(' ', start);
|
|
||||||
if (space < 0) return {};
|
if (space < 0) return {};
|
||||||
int nameStart = space + 1;
|
int nameStart = space + 1;
|
||||||
while (nameStart < lineText.size() && lineText[nameStart].isSpace()) nameStart++;
|
while (nameStart < lineText.size() && lineText[nameStart].isSpace()) nameStart++;
|
||||||
if (nameStart >= lineText.size()) return {};
|
if (nameStart >= lineText.size()) return {};
|
||||||
// Stop before trailing " {"
|
|
||||||
int nameEnd = lineText.indexOf(QStringLiteral(" {"), nameStart);
|
int nameEnd = lineText.indexOf(QStringLiteral(" {"), nameStart);
|
||||||
if (nameEnd < 0) nameEnd = lineText.size();
|
if (nameEnd < 0) nameEnd = lineText.size();
|
||||||
while (nameEnd > nameStart && lineText[nameEnd - 1].isSpace()) nameEnd--;
|
while (nameEnd > nameStart && lineText[nameEnd - 1].isSpace()) nameEnd--;
|
||||||
|
|||||||
210
src/editor.cpp
210
src/editor.cpp
@@ -24,12 +24,12 @@ static const QColor kFgMarginDim("#505050");
|
|||||||
|
|
||||||
static constexpr int IND_EDITABLE = 8;
|
static constexpr int IND_EDITABLE = 8;
|
||||||
static constexpr int IND_HEX_DIM = 9;
|
static constexpr int IND_HEX_DIM = 9;
|
||||||
static constexpr int IND_BASE_ADDR = 10; // Green color for base address
|
static constexpr int IND_BASE_ADDR = 10; // Default text color override for command row address
|
||||||
static constexpr int IND_HOVER_SPAN = 11; // Blue text on hover (link-like)
|
static constexpr int IND_HOVER_SPAN = 11; // Blue text on hover (link-like)
|
||||||
static constexpr int IND_CMD_PILL = 12; // Rounded chip behind command row spans
|
static constexpr int IND_CMD_PILL = 12; // Rounded chip behind command row spans
|
||||||
static constexpr int IND_DATA_CHANGED = 13; // Amber text for changed data values
|
static constexpr int IND_DATA_CHANGED = 13; // Amber text for changed data values
|
||||||
static constexpr int IND_CLASS_NAME = 14; // Teal text for root class name
|
static constexpr int IND_CLASS_NAME = 14; // Teal text for root class name
|
||||||
static constexpr int IND_CLASS_ULINE = 15; // Underline for root class name
|
static constexpr int IND_HINT_GREEN = 15; // Green text for hint/comment text
|
||||||
|
|
||||||
static QString g_fontName = "Consolas";
|
static QString g_fontName = "Consolas";
|
||||||
|
|
||||||
@@ -148,11 +148,11 @@ void RcxEditor::setupScintilla() {
|
|||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
||||||
IND_HEX_DIM, QColor("#505050"));
|
IND_HEX_DIM, QColor("#505050"));
|
||||||
|
|
||||||
// Base address indicator — green like comments
|
// Base address indicator — default text color to override lexer green on command row
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
||||||
IND_BASE_ADDR, 17 /*INDIC_TEXTFORE*/);
|
IND_BASE_ADDR, 17 /*INDIC_TEXTFORE*/);
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
||||||
IND_BASE_ADDR, QColor("#5a8248"));
|
IND_BASE_ADDR, QColor("#d4d4d4"));
|
||||||
|
|
||||||
// Hover span indicator — muted teal text (distinct from blue keywords)
|
// Hover span indicator — muted teal text (distinct from blue keywords)
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
||||||
@@ -182,11 +182,11 @@ void RcxEditor::setupScintilla() {
|
|||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
||||||
IND_CLASS_NAME, QColor("#4EC9B0"));
|
IND_CLASS_NAME, QColor("#4EC9B0"));
|
||||||
|
|
||||||
// Root class name underline
|
// Green text for hint/comment annotations
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
||||||
IND_CLASS_ULINE, (long)0 /*INDIC_PLAIN*/);
|
IND_HINT_GREEN, 17 /*INDIC_TEXTFORE*/);
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETFORE,
|
||||||
IND_CLASS_ULINE, QColor(124, 180, 226));
|
IND_HINT_GREEN, QColor("#5a8248"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,9 +395,6 @@ void RcxEditor::applyMarkers(const QVector<LineMeta>& meta) {
|
|||||||
m_sci->markerAdd(i, M_CMD_ROW);
|
m_sci->markerAdd(i, M_CMD_ROW);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (meta[i].lineKind == LineKind::CommandRow2) {
|
|
||||||
continue; // regular background (no special marker)
|
|
||||||
}
|
|
||||||
uint32_t mask = meta[i].markerMask;
|
uint32_t mask = meta[i].markerMask;
|
||||||
for (int m = M_CONT; m <= M_STRUCT_BG; m++) {
|
for (int m = M_CONT; m <= M_STRUCT_BG; m++) {
|
||||||
if (mask & (1u << m)) {
|
if (mask & (1u << m)) {
|
||||||
@@ -636,7 +633,11 @@ void RcxEditor::applyBaseAddressColoring(const QVector<LineMeta>& meta) {
|
|||||||
if (meta.isEmpty() || meta[0].lineKind != LineKind::CommandRow) return;
|
if (meta.isEmpty() || meta[0].lineKind != LineKind::CommandRow) return;
|
||||||
|
|
||||||
clearIndicatorLine(IND_BASE_ADDR, 0);
|
clearIndicatorLine(IND_BASE_ADDR, 0);
|
||||||
// Address in command bar is not colored green (only field values get green)
|
// Override lexer's green number coloring on the address with default text color
|
||||||
|
QString t = getLineText(m_sci, 0);
|
||||||
|
ColumnSpan addr = commandRowAddrSpan(t);
|
||||||
|
if (addr.valid)
|
||||||
|
fillIndicatorCols(IND_BASE_ADDR, 0, addr.start, addr.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RcxEditor::applyCommandRowPills() {
|
void RcxEditor::applyCommandRowPills() {
|
||||||
@@ -645,20 +646,10 @@ void RcxEditor::applyCommandRowPills() {
|
|||||||
constexpr int line = 0;
|
constexpr int line = 0;
|
||||||
QString t = getLineText(m_sci, line);
|
QString t = getLineText(m_sci, line);
|
||||||
|
|
||||||
clearIndicatorLine(IND_CMD_PILL, line);
|
|
||||||
clearIndicatorLine(IND_HEX_DIM, line);
|
clearIndicatorLine(IND_HEX_DIM, line);
|
||||||
|
clearIndicatorLine(IND_CLASS_NAME, line);
|
||||||
|
|
||||||
auto fillPadded = [&](ColumnSpan s) {
|
// Dim label text: source arrow/placeholder + its ▾ dropdown arrow
|
||||||
if (!s.valid) return;
|
|
||||||
int a = qMax(0, s.start - 1);
|
|
||||||
int b = qMin(t.size(), s.end + 1);
|
|
||||||
fillIndicatorCols(IND_CMD_PILL, line, a, b);
|
|
||||||
};
|
|
||||||
|
|
||||||
fillPadded(commandRowSrcSpan(t));
|
|
||||||
fillPadded(commandRowAddrSpan(t));
|
|
||||||
|
|
||||||
// Dim label text: source arrow/placeholder and ", address="
|
|
||||||
ColumnSpan srcSpan = commandRowSrcSpan(t);
|
ColumnSpan srcSpan = commandRowSrcSpan(t);
|
||||||
if (srcSpan.valid) {
|
if (srcSpan.valid) {
|
||||||
int quotePos = t.indexOf('\'', srcSpan.start);
|
int quotePos = t.indexOf('\'', srcSpan.start);
|
||||||
@@ -666,35 +657,37 @@ void RcxEditor::applyCommandRowPills() {
|
|||||||
while (kindEnd > srcSpan.start && t[kindEnd - 1].isSpace()) kindEnd--;
|
while (kindEnd > srcSpan.start && t[kindEnd - 1].isSpace()) kindEnd--;
|
||||||
if (kindEnd > srcSpan.start)
|
if (kindEnd > srcSpan.start)
|
||||||
fillIndicatorCols(IND_HEX_DIM, line, srcSpan.start, kindEnd);
|
fillIndicatorCols(IND_HEX_DIM, line, srcSpan.start, kindEnd);
|
||||||
|
// Dim the source ▾ dropdown arrow to match (like struct▾)
|
||||||
|
int srcDrop = t.indexOf(QChar(0x25BE));
|
||||||
|
int rootStart = commandRowRootStart(t);
|
||||||
|
if (srcDrop >= 0 && (rootStart < 0 || srcDrop < rootStart))
|
||||||
|
fillIndicatorCols(IND_HEX_DIM, line, srcDrop, srcDrop + 1);
|
||||||
}
|
}
|
||||||
int addrTag = t.indexOf(QStringLiteral(" \u203A"));
|
// Dim all " · " separators
|
||||||
if (addrTag >= 0)
|
int searchFrom = 0;
|
||||||
fillIndicatorCols(IND_HEX_DIM, line, addrTag, addrTag + 3);
|
while (true) {
|
||||||
|
int tag = t.indexOf(QStringLiteral(" \u00B7"), searchFrom);
|
||||||
// Style CommandRow2 (line kCommandRow2Line) if present
|
if (tag < 0) break;
|
||||||
if (m_meta.size() > kCommandRow2Line && m_meta[kCommandRow2Line].lineKind == LineKind::CommandRow2) {
|
fillIndicatorCols(IND_HEX_DIM, line, tag, tag + 3);
|
||||||
int line2 = kCommandRow2Line;
|
searchFrom = tag + 3;
|
||||||
QString t2 = getLineText(m_sci, line2);
|
|
||||||
|
|
||||||
clearIndicatorLine(IND_HEX_DIM, line2);
|
|
||||||
clearIndicatorLine(IND_CLASS_NAME, line2);
|
|
||||||
clearIndicatorLine(IND_CLASS_ULINE, line2);
|
|
||||||
|
|
||||||
ColumnSpan typeSpan = commandRow2TypeSpan(t2);
|
|
||||||
if (typeSpan.valid)
|
|
||||||
fillIndicatorCols(IND_HEX_DIM, line2, typeSpan.start, typeSpan.end);
|
|
||||||
|
|
||||||
ColumnSpan nameSpan = commandRow2NameSpan(t2);
|
|
||||||
if (nameSpan.valid) {
|
|
||||||
fillIndicatorCols(IND_CLASS_NAME, line2, nameSpan.start, nameSpan.end);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dim the dotted separator line
|
// Dim base address to match source/struct grey
|
||||||
if (m_meta.size() > kBlankLine && m_meta[kBlankLine].lineKind == LineKind::Blank) {
|
ColumnSpan addrSpan = commandRowAddrSpan(t);
|
||||||
QString sep = getLineText(m_sci, kBlankLine);
|
if (addrSpan.valid)
|
||||||
if (!sep.isEmpty())
|
fillIndicatorCols(IND_HEX_DIM, line, addrSpan.start, addrSpan.end);
|
||||||
fillIndicatorCols(IND_HEX_DIM, kBlankLine, 0, sep.size());
|
|
||||||
|
// Root class styling (type dim + class-name teal, no underline)
|
||||||
|
ColumnSpan rt = commandRowRootTypeSpan(t);
|
||||||
|
if (rt.valid) {
|
||||||
|
fillIndicatorCols(IND_HEX_DIM, line, rt.start, rt.end);
|
||||||
|
int drop = t.indexOf(QChar(0x25BE), rt.start);
|
||||||
|
if (drop >= 0)
|
||||||
|
fillIndicatorCols(IND_HEX_DIM, line, drop, qMin(drop + 2, t.size()));
|
||||||
|
}
|
||||||
|
ColumnSpan rn = commandRowRootNameSpan(t);
|
||||||
|
if (rn.valid) {
|
||||||
|
fillIndicatorCols(IND_CLASS_NAME, line, rn.start, rn.end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -841,26 +834,17 @@ bool RcxEditor::resolvedSpanFor(int line, EditTarget t,
|
|||||||
const LineMeta* lm = metaForLine(line);
|
const LineMeta* lm = metaForLine(line);
|
||||||
if (!lm) return false;
|
if (!lm) return false;
|
||||||
|
|
||||||
// CommandRow: BaseAddress (ADDR) and Source (SRC) editing
|
// CommandRow: Source / BaseAddress / Root class (type+name) editing
|
||||||
if (lm->lineKind == LineKind::CommandRow) {
|
if (lm->lineKind == LineKind::CommandRow) {
|
||||||
if (t != EditTarget::BaseAddress && t != EditTarget::Source) return false;
|
if (t != EditTarget::BaseAddress && t != EditTarget::Source
|
||||||
QString lineText = getLineText(m_sci, line);
|
&& t != EditTarget::RootClassType && t != EditTarget::RootClassName) return false;
|
||||||
ColumnSpan s = (t == EditTarget::Source)
|
|
||||||
? commandRowSrcSpan(lineText)
|
|
||||||
: commandRowAddrSpan(lineText);
|
|
||||||
out = normalizeSpan(s, lineText, t, /*skipPrefixes=*/(t == EditTarget::BaseAddress));
|
|
||||||
if (lineTextOut) *lineTextOut = lineText;
|
|
||||||
return out.valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommandRow2: root class type and name
|
|
||||||
if (lm->lineKind == LineKind::CommandRow2) {
|
|
||||||
if (t != EditTarget::RootClassType && t != EditTarget::RootClassName) return false;
|
|
||||||
QString lineText = getLineText(m_sci, line);
|
QString lineText = getLineText(m_sci, line);
|
||||||
ColumnSpan s;
|
ColumnSpan s;
|
||||||
if (t == EditTarget::RootClassType) s = commandRow2TypeSpan(lineText);
|
if (t == EditTarget::Source) s = commandRowSrcSpan(lineText);
|
||||||
else s = commandRow2NameSpan(lineText);
|
else if (t == EditTarget::BaseAddress) s = commandRowAddrSpan(lineText);
|
||||||
out = normalizeSpan(s, lineText, t, false);
|
else if (t == EditTarget::RootClassType) s = commandRowRootTypeSpan(lineText);
|
||||||
|
else s = commandRowRootNameSpan(lineText);
|
||||||
|
out = normalizeSpan(s, lineText, t, /*skipPrefixes=*/(t == EditTarget::BaseAddress));
|
||||||
if (lineTextOut) *lineTextOut = lineText;
|
if (lineTextOut) *lineTextOut = lineText;
|
||||||
return out.valid;
|
return out.valid;
|
||||||
}
|
}
|
||||||
@@ -974,21 +958,17 @@ static bool hitTestTarget(QsciScintilla* sci,
|
|||||||
return s.valid && col >= s.start && col < s.end;
|
return s.valid && col >= s.start && col < s.end;
|
||||||
};
|
};
|
||||||
|
|
||||||
// CommandRow: SRC and ADDR fields are interactive
|
// CommandRow: interactive SRC/ADDR + root class (type+name)
|
||||||
if (lm.lineKind == LineKind::CommandRow) {
|
if (lm.lineKind == LineKind::CommandRow) {
|
||||||
ColumnSpan ss = commandRowSrcSpan(lineText);
|
ColumnSpan ss = commandRowSrcSpan(lineText);
|
||||||
if (inSpan(ss)) { outTarget = EditTarget::Source; outLine = line; return true; }
|
if (inSpan(ss)) { outTarget = EditTarget::Source; outLine = line; return true; }
|
||||||
ColumnSpan as = commandRowAddrSpan(lineText);
|
ColumnSpan as = commandRowAddrSpan(lineText);
|
||||||
if (inSpan(as)) { outTarget = EditTarget::BaseAddress; outLine = line; return true; }
|
if (inSpan(as)) { outTarget = EditTarget::BaseAddress; outLine = line; return true; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommandRow2: root class type and name
|
ColumnSpan rts = commandRowRootTypeSpan(lineText);
|
||||||
if (lm.lineKind == LineKind::CommandRow2) {
|
if (inSpan(rts)) { outTarget = EditTarget::RootClassType; outLine = line; return true; }
|
||||||
ColumnSpan ts = commandRow2TypeSpan(lineText);
|
ColumnSpan rns = commandRowRootNameSpan(lineText);
|
||||||
if (inSpan(ts)) { outTarget = EditTarget::RootClassType; outLine = line; return true; }
|
if (inSpan(rns)) { outTarget = EditTarget::RootClassName; outLine = line; return true; }
|
||||||
ColumnSpan ns = commandRow2NameSpan(lineText);
|
|
||||||
if (inSpan(ns)) { outTarget = EditTarget::RootClassName; outLine = line; return true; }
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1122,7 +1102,7 @@ bool RcxEditor::eventFilter(QObject* obj, QEvent* event) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// CommandRow: try ADDR edit or consume
|
// CommandRow: try ADDR edit or consume
|
||||||
if (h.nodeId == kCommandRowId || h.nodeId == kCommandRow2Id) {
|
if (h.nodeId == kCommandRowId) {
|
||||||
int tLine; EditTarget t;
|
int tLine; EditTarget t;
|
||||||
if (hitTestTarget(m_sci, m_meta, me->pos(), tLine, t))
|
if (hitTestTarget(m_sci, m_meta, me->pos(), tLine, t))
|
||||||
beginInlineEdit(t, tLine);
|
beginInlineEdit(t, tLine);
|
||||||
@@ -1217,7 +1197,7 @@ bool RcxEditor::eventFilter(QObject* obj, QEvent* event) {
|
|||||||
m_pendingClickNodeId = 0; // cancel deferred selection change
|
m_pendingClickNodeId = 0; // cancel deferred selection change
|
||||||
// Narrow selection to this node before editing
|
// Narrow selection to this node before editing
|
||||||
auto h = hitTest(me->pos());
|
auto h = hitTest(me->pos());
|
||||||
if (h.nodeId != 0 && h.nodeId != kCommandRowId && h.nodeId != kCommandRow2Id)
|
if (h.nodeId != 0 && h.nodeId != kCommandRowId)
|
||||||
emit nodeClicked(h.line, h.nodeId, Qt::NoModifier);
|
emit nodeClicked(h.line, h.nodeId, Qt::NoModifier);
|
||||||
return beginInlineEdit(t, line);
|
return beginInlineEdit(t, line);
|
||||||
}
|
}
|
||||||
@@ -1403,11 +1383,10 @@ bool RcxEditor::beginInlineEdit(EditTarget target, int line) {
|
|||||||
m_sci->getCursorPosition(&line, &col);
|
m_sci->getCursorPosition(&line, &col);
|
||||||
auto* lm = metaForLine(line);
|
auto* lm = metaForLine(line);
|
||||||
if (!lm) return false;
|
if (!lm) return false;
|
||||||
// Allow nodeIdx=-1 only for CommandRow/CommandRow2 editing
|
// Allow nodeIdx=-1 only for CommandRow editing (command bar)
|
||||||
if (lm->nodeIdx < 0 && !(lm->lineKind == LineKind::CommandRow &&
|
if (lm->nodeIdx < 0 && !(lm->lineKind == LineKind::CommandRow &&
|
||||||
(target == EditTarget::BaseAddress || target == EditTarget::Source))
|
(target == EditTarget::BaseAddress || target == EditTarget::Source
|
||||||
&& !(lm->lineKind == LineKind::CommandRow2 &&
|
|| target == EditTarget::RootClassType || target == EditTarget::RootClassName)))
|
||||||
(target == EditTarget::RootClassType || target == EditTarget::RootClassName)))
|
|
||||||
return false;
|
return false;
|
||||||
// Padding: reject value editing (display-only hex bytes)
|
// Padding: reject value editing (display-only hex bytes)
|
||||||
if (target == EditTarget::Value && lm->nodeKind == NodeKind::Padding)
|
if (target == EditTarget::Value && lm->nodeKind == NodeKind::Padding)
|
||||||
@@ -1493,10 +1472,15 @@ bool RcxEditor::beginInlineEdit(EditTarget target, int line) {
|
|||||||
m_sci->viewport()->setCursor(Qt::IBeamCursor);
|
m_sci->viewport()->setCursor(Qt::IBeamCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable selection rendering for inline edit
|
// Re-enable selection rendering for inline edit (skip for picker-based targets)
|
||||||
|
bool isPicker = (target == EditTarget::Type || target == EditTarget::Source
|
||||||
|
|| target == EditTarget::ArrayElementType
|
||||||
|
|| target == EditTarget::PointerTarget
|
||||||
|
|| target == EditTarget::RootClassType);
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSELFORE, (long)0, (long)0);
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSELFORE, (long)0, (long)0);
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSELBACK, (long)1,
|
if (!isPicker)
|
||||||
QColor("#264f78"));
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSELBACK, (long)1,
|
||||||
|
QColor("#264f78"));
|
||||||
|
|
||||||
// Use correct UTF-8 position conversion (not lineStart + col!)
|
// Use correct UTF-8 position conversion (not lineStart + col!)
|
||||||
m_editState.posStart = posFromCol(m_sci, line, norm.start);
|
m_editState.posStart = posFromCol(m_sci, line, norm.start);
|
||||||
@@ -1671,8 +1655,15 @@ void RcxEditor::showSourcePicker() {
|
|||||||
if (!m_editState.active || m_editState.target != EditTarget::Source)
|
if (!m_editState.active || m_editState.target != EditTarget::Source)
|
||||||
return;
|
return;
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
menu.addAction("File");
|
QFont menuFont = editorFont();
|
||||||
menu.addAction("Process");
|
int zoom = (int)m_sci->SendScintilla(QsciScintillaBase::SCI_GETZOOM);
|
||||||
|
menuFont.setPointSize(menuFont.pointSize() + zoom);
|
||||||
|
menu.setFont(menuFont);
|
||||||
|
menu.setStyleSheet(QStringLiteral(
|
||||||
|
"QMenu { background: #252526; color: #d4d4d4; }"
|
||||||
|
"QMenu::item:selected { background: #232323; }"));
|
||||||
|
menu.addAction("file");
|
||||||
|
menu.addAction("process");
|
||||||
|
|
||||||
// Saved sources below separator (with checkmarks)
|
// Saved sources below separator (with checkmarks)
|
||||||
if (!m_savedSourceDisplay.isEmpty()) {
|
if (!m_savedSourceDisplay.isEmpty()) {
|
||||||
@@ -1795,18 +1786,13 @@ void RcxEditor::updatePointerTargetFilter() {
|
|||||||
void RcxEditor::paintEditableSpans(int line) {
|
void RcxEditor::paintEditableSpans(int line) {
|
||||||
const LineMeta* lm = metaForLine(line);
|
const LineMeta* lm = metaForLine(line);
|
||||||
if (!lm) return;
|
if (!lm) return;
|
||||||
// CommandRow: paint Source and BaseAddress spans
|
// CommandRow: paint Source/BaseAddress + root class (type+name) spans
|
||||||
if (lm->lineKind == LineKind::CommandRow) {
|
if (lm->lineKind == LineKind::CommandRow) {
|
||||||
NormalizedSpan norm;
|
NormalizedSpan norm;
|
||||||
if (resolvedSpanFor(line, EditTarget::Source, norm))
|
if (resolvedSpanFor(line, EditTarget::Source, norm))
|
||||||
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
|
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
|
||||||
if (resolvedSpanFor(line, EditTarget::BaseAddress, norm))
|
if (resolvedSpanFor(line, EditTarget::BaseAddress, norm))
|
||||||
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
|
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
|
||||||
return;
|
|
||||||
}
|
|
||||||
// CommandRow2: paint RootClassType and RootClassName spans
|
|
||||||
if (lm->lineKind == LineKind::CommandRow2) {
|
|
||||||
NormalizedSpan norm;
|
|
||||||
if (resolvedSpanFor(line, EditTarget::RootClassType, norm))
|
if (resolvedSpanFor(line, EditTarget::RootClassType, norm))
|
||||||
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
|
fillIndicatorCols(IND_EDITABLE, line, norm.start, norm.end);
|
||||||
if (resolvedSpanFor(line, EditTarget::RootClassName, norm))
|
if (resolvedSpanFor(line, EditTarget::RootClassName, norm))
|
||||||
@@ -1991,8 +1977,8 @@ void RcxEditor::setEditComment(const QString& comment) {
|
|||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_REPLACETARGET,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_REPLACETARGET,
|
||||||
(uintptr_t)utf8.size(), utf8.constData());
|
(uintptr_t)utf8.size(), utf8.constData());
|
||||||
|
|
||||||
// Apply green color to hint text (reuse IND_BASE_ADDR which is green)
|
// Apply green color to hint text
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETINDICATORCURRENT, IND_BASE_ADDR);
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETINDICATORCURRENT, IND_HINT_GREEN);
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICATORFILLRANGE, posA, posB - posA);
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICATORFILLRANGE, posA, posB - posA);
|
||||||
|
|
||||||
m_updatingComment = false;
|
m_updatingComment = false;
|
||||||
@@ -2065,42 +2051,6 @@ void RcxEditor::setCommandRowText(const QString& line) {
|
|||||||
applyCommandRowPills();
|
applyCommandRowPills();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RcxEditor::setCommandRow2Text(const QString& line) {
|
|
||||||
if (m_sci->lines() <= kCommandRow2Line) return;
|
|
||||||
QString s = line;
|
|
||||||
s.replace('\n', ' ');
|
|
||||||
s.replace('\r', ' ');
|
|
||||||
|
|
||||||
bool wasReadOnly = m_sci->isReadOnly();
|
|
||||||
bool wasModified = m_sci->SendScintilla(QsciScintillaBase::SCI_GETMODIFY);
|
|
||||||
long savedPos = m_sci->SendScintilla(QsciScintillaBase::SCI_GETCURRENTPOS);
|
|
||||||
long savedAnchor = m_sci->SendScintilla(QsciScintillaBase::SCI_GETANCHOR);
|
|
||||||
|
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETUNDOCOLLECTION, 0);
|
|
||||||
m_sci->setReadOnly(false);
|
|
||||||
|
|
||||||
long start = m_sci->SendScintilla(QsciScintillaBase::SCI_POSITIONFROMLINE, kCommandRow2Line);
|
|
||||||
long end = m_sci->SendScintilla(QsciScintillaBase::SCI_GETLINEENDPOSITION, kCommandRow2Line);
|
|
||||||
long oldLen = end - start;
|
|
||||||
QByteArray utf8 = s.toUtf8();
|
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETSTART, start);
|
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETEND, end);
|
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_REPLACETARGET, (uintptr_t)utf8.size(), utf8.constData());
|
|
||||||
|
|
||||||
// Adjust saved cursor/anchor for length change in CommandRow2 line
|
|
||||||
long delta = (long)utf8.size() - oldLen;
|
|
||||||
if (savedPos > end) savedPos += delta;
|
|
||||||
if (savedAnchor > end) savedAnchor += delta;
|
|
||||||
|
|
||||||
if (wasReadOnly) m_sci->setReadOnly(true);
|
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETUNDOCOLLECTION, 1);
|
|
||||||
if (!wasModified) m_sci->SendScintilla(QsciScintillaBase::SCI_SETSAVEPOINT);
|
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETCURRENTPOS, savedPos);
|
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETANCHOR, savedAnchor);
|
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_COLOURISE, start, start + utf8.size());
|
|
||||||
applyCommandRowPills();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RcxEditor::setEditorFont(const QString& fontName) {
|
void RcxEditor::setEditorFont(const QString& fontName) {
|
||||||
g_fontName = fontName;
|
g_fontName = fontName;
|
||||||
QFont f = editorFont();
|
QFont f = editorFont();
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ public:
|
|||||||
|
|
||||||
void applySelectionOverlay(const QSet<uint64_t>& selIds);
|
void applySelectionOverlay(const QSet<uint64_t>& selIds);
|
||||||
void setCommandRowText(const QString& line);
|
void setCommandRowText(const QString& line);
|
||||||
void setCommandRow2Text(const QString& line);
|
|
||||||
void setEditorFont(const QString& fontName);
|
void setEditorFont(const QString& fontName);
|
||||||
static void setGlobalFontName(const QString& fontName);
|
static void setGlobalFontName(const QString& fontName);
|
||||||
|
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ QString indent(int depth) {
|
|||||||
// ── Offset margin ──
|
// ── Offset margin ──
|
||||||
|
|
||||||
QString fmtOffsetMargin(uint64_t absoluteOffset, bool isContinuation) {
|
QString fmtOffsetMargin(uint64_t absoluteOffset, bool isContinuation) {
|
||||||
if (isContinuation) return QStringLiteral(" \u00B7");
|
if (isContinuation) return QStringLiteral(" \u00B7 ");
|
||||||
return QString::number(absoluteOffset, 16).toUpper();
|
return QString::number(absoluteOffset, 16).toUpper() + QChar(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Struct type name (for width calculation) ──
|
// ── Struct type name (for width calculation) ──
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ static QString buildCommandRow(const Provider& prov, uint64_t baseAddress) {
|
|||||||
QString src = buildSourceLabel(prov);
|
QString src = buildSourceLabel(prov);
|
||||||
QString addr = QStringLiteral("0x") +
|
QString addr = QStringLiteral("0x") +
|
||||||
QString::number(baseAddress, 16).toUpper();
|
QString::number(baseAddress, 16).toUpper();
|
||||||
return QStringLiteral(" %1 \u203A %2").arg(src, addr);
|
return QStringLiteral(" %1 \u00B7 %2").arg(src, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Replicate commandRowSrcSpan for testing
|
// -- Replicate commandRowSrcSpan for testing
|
||||||
@@ -32,7 +32,7 @@ struct TestColumnSpan {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static TestColumnSpan commandRowSrcSpan(const QString& lineText) {
|
static TestColumnSpan commandRowSrcSpan(const QString& lineText) {
|
||||||
int idx = lineText.indexOf(QStringLiteral(" \u203A"));
|
int idx = lineText.indexOf(QStringLiteral(" \u00B7"));
|
||||||
if (idx < 0) return {};
|
if (idx < 0) return {};
|
||||||
int start = 0;
|
int start = 0;
|
||||||
while (start < idx && !lineText[start].isLetterOrNumber()
|
while (start < idx && !lineText[start].isLetterOrNumber()
|
||||||
@@ -77,13 +77,13 @@ private slots:
|
|||||||
void row_nullProvider() {
|
void row_nullProvider() {
|
||||||
NullProvider p;
|
NullProvider p;
|
||||||
QString row = buildCommandRow(p, 0);
|
QString row = buildCommandRow(p, 0);
|
||||||
QCOMPARE(row, QStringLiteral(" source\u25BE \u203A 0x0"));
|
QCOMPARE(row, QStringLiteral(" source\u25BE \u00B7 0x0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void row_fileProvider() {
|
void row_fileProvider() {
|
||||||
BufferProvider p(QByteArray(4, '\0'), "test.bin");
|
BufferProvider p(QByteArray(4, '\0'), "test.bin");
|
||||||
QString row = buildCommandRow(p, 0x140000000ULL);
|
QString row = buildCommandRow(p, 0x140000000ULL);
|
||||||
QCOMPARE(row, QStringLiteral(" 'test.bin'\u25BE \u203A 0x140000000"));
|
QCOMPARE(row, QStringLiteral(" 'test.bin'\u25BE \u00B7 0x140000000"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
@@ -110,7 +110,7 @@ private slots:
|
|||||||
void span_processProvider_simulated() {
|
void span_processProvider_simulated() {
|
||||||
// Simulate a process provider without needing Windows APIs
|
// Simulate a process provider without needing Windows APIs
|
||||||
// by building the string directly
|
// by building the string directly
|
||||||
QString row = QStringLiteral(" 'notepad.exe'\u25BE \u203A 0x7FF600000000");
|
QString row = QStringLiteral(" 'notepad.exe'\u25BE \u00B7 0x7FF600000000");
|
||||||
auto span = commandRowSrcSpan(row);
|
auto span = commandRowSrcSpan(row);
|
||||||
QVERIFY(span.valid);
|
QVERIFY(span.valid);
|
||||||
QString extracted = row.mid(span.start, span.end - span.start);
|
QString extracted = row.mid(span.start, span.end - span.start);
|
||||||
|
|||||||
@@ -35,30 +35,24 @@ private slots:
|
|||||||
NullProvider prov;
|
NullProvider prov;
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + 2 fields + root footer = 6
|
// CommandRow + 2 fields + root footer = 4
|
||||||
QCOMPARE(result.meta.size(), 6);
|
QCOMPARE(result.meta.size(), 4);
|
||||||
|
|
||||||
// Line 0 is CommandRow
|
// Line 0 is CommandRow
|
||||||
QCOMPARE(result.meta[0].lineKind, LineKind::CommandRow);
|
QCOMPARE(result.meta[0].lineKind, LineKind::CommandRow);
|
||||||
|
|
||||||
// Line 1 is Blank separator
|
|
||||||
QCOMPARE(result.meta[1].lineKind, LineKind::Blank);
|
|
||||||
|
|
||||||
// Line 2 is CommandRow2
|
|
||||||
QCOMPARE(result.meta[2].lineKind, LineKind::CommandRow2);
|
|
||||||
|
|
||||||
// Fields at depth 1
|
// Fields at depth 1
|
||||||
QVERIFY(!result.meta[3].foldHead);
|
QVERIFY(!result.meta[1].foldHead);
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
QVERIFY(!result.meta[4].foldHead);
|
QVERIFY(!result.meta[2].foldHead);
|
||||||
QCOMPARE(result.meta[4].depth, 1);
|
QCOMPARE(result.meta[2].depth, 1);
|
||||||
|
|
||||||
// Offset text
|
// Offset text
|
||||||
QCOMPARE(result.meta[3].offsetText, QString("0"));
|
QCOMPARE(result.meta[1].offsetText, QString("0"));
|
||||||
QCOMPARE(result.meta[4].offsetText, QString("4"));
|
QCOMPARE(result.meta[2].offsetText, QString("4"));
|
||||||
|
|
||||||
// Line 5 is root footer
|
// Line 3 is root footer
|
||||||
QCOMPARE(result.meta[5].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[3].lineKind, LineKind::Footer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testVec3SingleLine() {
|
void testVec3SingleLine() {
|
||||||
@@ -82,17 +76,17 @@ private slots:
|
|||||||
NullProvider prov;
|
NullProvider prov;
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + 1 Vec3 line + root footer = 5
|
// CommandRow + 1 Vec3 line + root footer = 3
|
||||||
QCOMPARE(result.meta.size(), 5);
|
QCOMPARE(result.meta.size(), 3);
|
||||||
|
|
||||||
// Line 3: single Vec3 line, not continuation, depth 1
|
// Line 1: single Vec3 line, not continuation, depth 1
|
||||||
QVERIFY(!result.meta[3].isContinuation);
|
QVERIFY(!result.meta[1].isContinuation);
|
||||||
QCOMPARE(result.meta[3].offsetText, QString("0"));
|
QCOMPARE(result.meta[1].offsetText, QString("0"));
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
QCOMPARE(result.meta[3].nodeKind, NodeKind::Vec3);
|
QCOMPARE(result.meta[1].nodeKind, NodeKind::Vec3);
|
||||||
|
|
||||||
// Line 4 is root footer
|
// Line 2 is root footer
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[2].lineKind, LineKind::Footer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testPaddingMarker() {
|
void testPaddingMarker() {
|
||||||
@@ -116,13 +110,13 @@ private slots:
|
|||||||
NullProvider prov;
|
NullProvider prov;
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + padding + root footer = 5
|
// CommandRow + padding + root footer = 3
|
||||||
QCOMPARE(result.meta.size(), 5);
|
QCOMPARE(result.meta.size(), 3);
|
||||||
QVERIFY(result.meta[3].markerMask & (1u << M_PAD));
|
QVERIFY(result.meta[1].markerMask & (1u << M_PAD));
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
|
|
||||||
// Line 4 is root footer
|
// Line 2 is root footer
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[2].lineKind, LineKind::Footer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testNullPointerMarker() {
|
void testNullPointerMarker() {
|
||||||
@@ -148,14 +142,14 @@ private slots:
|
|||||||
BufferProvider prov(data);
|
BufferProvider prov(data);
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + ptr + root footer = 5
|
// CommandRow + ptr + root footer = 3
|
||||||
QCOMPARE(result.meta.size(), 5);
|
QCOMPARE(result.meta.size(), 3);
|
||||||
// No ambient validation markers — M_PTR0 is no longer set
|
// No ambient validation markers — M_PTR0 is no longer set
|
||||||
QVERIFY(!(result.meta[3].markerMask & (1u << M_PTR0)));
|
QVERIFY(!(result.meta[1].markerMask & (1u << M_PTR0)));
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
|
|
||||||
// Line 4 is root footer
|
// Line 2 is root footer
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[2].lineKind, LineKind::Footer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testCollapsedStruct() {
|
void testCollapsedStruct() {
|
||||||
@@ -181,11 +175,11 @@ private slots:
|
|||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// Collapsed root: isRootHeader overrides collapse, so children + footer still render
|
// Collapsed root: isRootHeader overrides collapse, so children + footer still render
|
||||||
// CommandRow + Blank + CommandRow2 + field + root footer = 5
|
// CommandRow + field + root footer = 3
|
||||||
QCOMPARE(result.meta.size(), 5);
|
QCOMPARE(result.meta.size(), 3);
|
||||||
QCOMPARE(result.meta[3].lineKind, LineKind::Field);
|
QCOMPARE(result.meta[1].lineKind, LineKind::Field);
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[2].lineKind, LineKind::Footer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testUnreadablePointerNoRead() {
|
void testUnreadablePointerNoRead() {
|
||||||
@@ -212,15 +206,15 @@ private slots:
|
|||||||
BufferProvider prov(data);
|
BufferProvider prov(data);
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + ptr + root footer = 5
|
// CommandRow + ptr + root footer = 3
|
||||||
QCOMPARE(result.meta.size(), 5);
|
QCOMPARE(result.meta.size(), 3);
|
||||||
// No ambient validation markers
|
// No ambient validation markers
|
||||||
QVERIFY(!(result.meta[3].markerMask & (1u << M_ERR)));
|
QVERIFY(!(result.meta[1].markerMask & (1u << M_ERR)));
|
||||||
QVERIFY(!(result.meta[3].markerMask & (1u << M_PTR0)));
|
QVERIFY(!(result.meta[1].markerMask & (1u << M_PTR0)));
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
|
|
||||||
// Line 4 is root footer
|
// Line 2 is root footer
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[2].lineKind, LineKind::Footer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testFoldLevels() {
|
void testFoldLevels() {
|
||||||
@@ -253,13 +247,13 @@ private slots:
|
|||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// Child header (depth 1, fold head) — root header no longer emitted
|
// Child header (depth 1, fold head) — root header no longer emitted
|
||||||
QCOMPARE(result.meta[3].foldLevel, 0x401 | 0x2000);
|
QCOMPARE(result.meta[1].foldLevel, 0x401 | 0x2000);
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
QVERIFY(result.meta[3].foldHead);
|
QVERIFY(result.meta[1].foldHead);
|
||||||
|
|
||||||
// Leaf (depth 2, not head)
|
// Leaf (depth 2, not head)
|
||||||
QCOMPARE(result.meta[4].foldLevel, 0x402);
|
QCOMPARE(result.meta[2].foldLevel, 0x402);
|
||||||
QCOMPARE(result.meta[4].depth, 2);
|
QCOMPARE(result.meta[2].depth, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testNestedStruct() {
|
void testNestedStruct() {
|
||||||
@@ -306,31 +300,31 @@ private slots:
|
|||||||
NullProvider prov;
|
NullProvider prov;
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + flags + Inner header + x + y + Inner footer + root footer = 9
|
// CommandRow + flags + Inner header + x + y + Inner footer + root footer = 7
|
||||||
QCOMPARE(result.meta.size(), 9);
|
QCOMPARE(result.meta.size(), 7);
|
||||||
|
|
||||||
// flags field (depth 1)
|
// flags field (depth 1)
|
||||||
QCOMPARE(result.meta[3].lineKind, LineKind::Field);
|
QCOMPARE(result.meta[1].lineKind, LineKind::Field);
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
|
|
||||||
// Inner header (depth 1, fold head)
|
// Inner header (depth 1, fold head)
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Header);
|
QCOMPARE(result.meta[2].lineKind, LineKind::Header);
|
||||||
QCOMPARE(result.meta[4].depth, 1);
|
QCOMPARE(result.meta[2].depth, 1);
|
||||||
QVERIFY(result.meta[4].foldHead);
|
QVERIFY(result.meta[2].foldHead);
|
||||||
QCOMPARE(result.meta[4].foldLevel, 0x401 | 0x2000);
|
QCOMPARE(result.meta[2].foldLevel, 0x401 | 0x2000);
|
||||||
|
|
||||||
// Inner fields at depth 2
|
// Inner fields at depth 2
|
||||||
QCOMPARE(result.meta[5].depth, 2);
|
QCOMPARE(result.meta[3].depth, 2);
|
||||||
QCOMPARE(result.meta[5].foldLevel, 0x402);
|
QCOMPARE(result.meta[3].foldLevel, 0x402);
|
||||||
QCOMPARE(result.meta[6].depth, 2);
|
QCOMPARE(result.meta[4].depth, 2);
|
||||||
|
|
||||||
// Inner footer
|
// Inner footer
|
||||||
QCOMPARE(result.meta[7].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[5].lineKind, LineKind::Footer);
|
||||||
QCOMPARE(result.meta[7].depth, 1);
|
QCOMPARE(result.meta[5].depth, 1);
|
||||||
|
|
||||||
// Root footer
|
// Root footer
|
||||||
QCOMPARE(result.meta[8].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[6].lineKind, LineKind::Footer);
|
||||||
QCOMPARE(result.meta[8].depth, 0);
|
QCOMPARE(result.meta[6].depth, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testPointerDerefExpansion() {
|
void testPointerDerefExpansion() {
|
||||||
@@ -398,28 +392,28 @@ private slots:
|
|||||||
|
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + magic + ptr(merged fold header) + fn1 + fn2 + ptr footer + Main footer = 9
|
// CommandRow + magic + ptr(merged fold header) + fn1 + fn2 + ptr footer + Main footer = 7
|
||||||
// VTable standalone: header + fn1 + fn2 + footer = 4
|
// VTable standalone: header + fn1 + fn2 + footer = 4
|
||||||
// Total = 13
|
// Total = 11
|
||||||
QCOMPARE(result.meta.size(), 13);
|
QCOMPARE(result.meta.size(), 11);
|
||||||
|
|
||||||
// magic field (depth 1)
|
// magic field (depth 1)
|
||||||
QCOMPARE(result.meta[3].lineKind, LineKind::Field);
|
QCOMPARE(result.meta[1].lineKind, LineKind::Field);
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
|
|
||||||
// Pointer as merged fold header: "VTable* ptr {"
|
// Pointer as merged fold header: "VTable* ptr {"
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Header);
|
QCOMPARE(result.meta[2].lineKind, LineKind::Header);
|
||||||
QCOMPARE(result.meta[4].depth, 1);
|
QCOMPARE(result.meta[2].depth, 1);
|
||||||
QVERIFY(result.meta[4].foldHead);
|
QVERIFY(result.meta[2].foldHead);
|
||||||
QCOMPARE(result.meta[4].nodeKind, NodeKind::Pointer64);
|
QCOMPARE(result.meta[2].nodeKind, NodeKind::Pointer64);
|
||||||
|
|
||||||
// Expanded fields at depth 2 (struct header merged into pointer)
|
// Expanded fields at depth 2 (struct header merged into pointer)
|
||||||
QCOMPARE(result.meta[5].depth, 2);
|
QCOMPARE(result.meta[3].depth, 2);
|
||||||
QCOMPARE(result.meta[6].depth, 2);
|
QCOMPARE(result.meta[4].depth, 2);
|
||||||
|
|
||||||
// Pointer fold footer
|
// Pointer fold footer
|
||||||
QCOMPARE(result.meta[7].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[5].lineKind, LineKind::Footer);
|
||||||
QCOMPARE(result.meta[7].depth, 1);
|
QCOMPARE(result.meta[5].depth, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testPointerDerefNull() {
|
void testPointerDerefNull() {
|
||||||
@@ -463,22 +457,22 @@ private slots:
|
|||||||
|
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + ptr(merged fold header) + target field + ptr footer + Main footer = 7
|
// CommandRow + ptr(merged fold header) + target field + ptr footer + Main footer = 5
|
||||||
// Target standalone: header + field + footer = 3
|
// Target standalone: header + field + footer = 3
|
||||||
// Total = 10 (null ptr still shows template preview)
|
// Total = 8 (null ptr still shows template preview)
|
||||||
QCOMPARE(result.meta.size(), 10);
|
QCOMPARE(result.meta.size(), 8);
|
||||||
|
|
||||||
// Pointer as merged fold header (expanded — shows template at offset 0)
|
// Pointer as merged fold header (expanded — shows template at offset 0)
|
||||||
QCOMPARE(result.meta[3].lineKind, LineKind::Header);
|
QCOMPARE(result.meta[1].lineKind, LineKind::Header);
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
QVERIFY(result.meta[3].foldHead);
|
QVERIFY(result.meta[1].foldHead);
|
||||||
|
|
||||||
// Target field shown as template preview
|
// Target field shown as template preview
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Field);
|
QCOMPARE(result.meta[2].lineKind, LineKind::Field);
|
||||||
QCOMPARE(result.meta[4].depth, 2);
|
QCOMPARE(result.meta[2].depth, 2);
|
||||||
|
|
||||||
// Pointer fold footer
|
// Pointer fold footer
|
||||||
QCOMPARE(result.meta[5].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[3].lineKind, LineKind::Footer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testPointerDerefCollapsed() {
|
void testPointerDerefCollapsed() {
|
||||||
@@ -525,14 +519,14 @@ private slots:
|
|||||||
|
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + ptr(fold head, collapsed) + Main footer = 5
|
// CommandRow + ptr(fold head, collapsed) + Main footer = 3
|
||||||
// Target standalone: header + field + footer = 3
|
// Target standalone: header + field + footer = 3
|
||||||
// Total = 8
|
// Total = 6
|
||||||
QCOMPARE(result.meta.size(), 8);
|
QCOMPARE(result.meta.size(), 6);
|
||||||
|
|
||||||
// Pointer is fold head (depth 1)
|
// Pointer is fold head (depth 1)
|
||||||
QVERIFY(result.meta[3].foldHead);
|
QVERIFY(result.meta[1].foldHead);
|
||||||
QCOMPARE(result.meta[3].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testPointerDerefCycle() {
|
void testPointerDerefCycle() {
|
||||||
@@ -595,12 +589,12 @@ private slots:
|
|||||||
QVERIFY(result.meta.size() > 0);
|
QVERIFY(result.meta.size() > 0);
|
||||||
QVERIFY(result.meta.size() < 100); // sanity: bounded output
|
QVERIFY(result.meta.size() < 100); // sanity: bounded output
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + ptr merged header + data + self merged header
|
// CommandRow + ptr merged header + data + self merged header
|
||||||
// Second expansion blocked by cycle guard: no children under self
|
// Second expansion blocked by cycle guard: no children under self
|
||||||
// Then: self footer + ptr footer + Main footer + standalone Recursive rendering
|
// Then: self footer + ptr footer + Main footer + standalone Recursive rendering
|
||||||
QVERIFY(result.meta[3].foldHead); // ptr merged fold head
|
QVERIFY(result.meta[1].foldHead); // ptr merged fold head
|
||||||
QCOMPARE(result.meta[3].lineKind, LineKind::Header); // ptr merged header
|
QCOMPARE(result.meta[1].lineKind, LineKind::Header); // ptr merged header
|
||||||
QCOMPARE(result.meta[4].lineKind, LineKind::Field); // data field (first child of Recursive)
|
QCOMPARE(result.meta[2].lineKind, LineKind::Field); // data field (first child of Recursive)
|
||||||
}
|
}
|
||||||
|
|
||||||
void testStructFooterSimple() {
|
void testStructFooterSimple() {
|
||||||
@@ -664,21 +658,12 @@ private slots:
|
|||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
for (int i = 0; i < result.meta.size(); i++) {
|
for (int i = 0; i < result.meta.size(); i++) {
|
||||||
// Skip CommandRow / Blank / CommandRow2 (synthetic lines with sentinel nodeId)
|
// Skip CommandRow (synthetic line with sentinel nodeId)
|
||||||
if (result.meta[i].lineKind == LineKind::CommandRow) {
|
if (result.meta[i].lineKind == LineKind::CommandRow) {
|
||||||
QCOMPARE(result.meta[i].nodeId, kCommandRowId);
|
QCOMPARE(result.meta[i].nodeId, kCommandRowId);
|
||||||
QCOMPARE(result.meta[i].nodeIdx, -1);
|
QCOMPARE(result.meta[i].nodeIdx, -1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (result.meta[i].lineKind == LineKind::Blank) {
|
|
||||||
QCOMPARE(result.meta[i].nodeIdx, -1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (result.meta[i].lineKind == LineKind::CommandRow2) {
|
|
||||||
QCOMPARE(result.meta[i].nodeId, kCommandRow2Id);
|
|
||||||
QCOMPARE(result.meta[i].nodeIdx, -1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QVERIFY2(result.meta[i].nodeId != 0,
|
QVERIFY2(result.meta[i].nodeId != 0,
|
||||||
qPrintable(QString("Line %1 has nodeId=0").arg(i)));
|
qPrintable(QString("Line %1 has nodeId=0").arg(i)));
|
||||||
int ni = result.meta[i].nodeIdx;
|
int ni = result.meta[i].nodeIdx;
|
||||||
@@ -955,16 +940,16 @@ private slots:
|
|||||||
NullProvider prov;
|
NullProvider prov;
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + Array header(collapsed) + root footer = 5
|
// CommandRow + Array header(collapsed) + root footer = 3
|
||||||
QCOMPARE(result.meta.size(), 5);
|
QCOMPARE(result.meta.size(), 3);
|
||||||
|
|
||||||
// Array header is collapsed (at index 3)
|
// Array header is collapsed (at index 1)
|
||||||
int arrLine = -1;
|
int arrLine = -1;
|
||||||
for (int i = 0; i < result.meta.size(); i++) {
|
for (int i = 0; i < result.meta.size(); i++) {
|
||||||
if (result.meta[i].isArrayHeader) { arrLine = i; break; }
|
if (result.meta[i].isArrayHeader) { arrLine = i; break; }
|
||||||
}
|
}
|
||||||
QVERIFY(arrLine >= 0);
|
QVERIFY(arrLine >= 0);
|
||||||
QCOMPARE(arrLine, 3);
|
QCOMPARE(arrLine, 1);
|
||||||
QVERIFY(result.meta[arrLine].foldCollapsed);
|
QVERIFY(result.meta[arrLine].foldCollapsed);
|
||||||
|
|
||||||
// Header text should NOT contain "{"
|
// Header text should NOT contain "{"
|
||||||
@@ -1826,10 +1811,10 @@ private slots:
|
|||||||
QCOMPARE(tree.computeStructAlignment(rootId), 1);
|
QCOMPARE(tree.computeStructAlignment(rootId), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testCommandRow2NameSpan() {
|
void testCommandRowRootNameSpan() {
|
||||||
// Name span should cover the class name
|
// Name span should cover the class name in the merged command row
|
||||||
QString text = "struct\u25BE MyClass";
|
QString text = "source\u25BE \u00B7 0x0 \u00B7 struct\u25BE MyClass {";
|
||||||
ColumnSpan nameSpan = commandRow2NameSpan(text);
|
ColumnSpan nameSpan = commandRowRootNameSpan(text);
|
||||||
QVERIFY(nameSpan.valid);
|
QVERIFY(nameSpan.valid);
|
||||||
|
|
||||||
QString nameText = text.mid(nameSpan.start, nameSpan.end - nameSpan.start);
|
QString nameText = text.mid(nameSpan.start, nameSpan.end - nameSpan.start);
|
||||||
@@ -1881,11 +1866,10 @@ private slots:
|
|||||||
QVERIFY2(result.meta.size() >= 5,
|
QVERIFY2(result.meta.size() >= 5,
|
||||||
qPrintable(QString("Expected >= 5 lines, got %1").arg(result.meta.size())));
|
qPrintable(QString("Expected >= 5 lines, got %1").arg(result.meta.size())));
|
||||||
|
|
||||||
// Every non-blank line should have text content
|
// Every line should have text content
|
||||||
QStringList lines = result.text.split('\n');
|
QStringList lines = result.text.split('\n');
|
||||||
QCOMPARE(lines.size(), result.meta.size());
|
QCOMPARE(lines.size(), result.meta.size());
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
if (result.meta[i].lineKind == LineKind::Blank) continue;
|
|
||||||
QVERIFY2(!lines[i].isEmpty(),
|
QVERIFY2(!lines[i].isEmpty(),
|
||||||
qPrintable(QString("Line %1 is empty").arg(i)));
|
qPrintable(QString("Line %1 is empty").arg(i)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ private slots:
|
|||||||
|
|
||||||
// Set CommandRow text with an ADDR value (simulates controller.updateCommandRow)
|
// Set CommandRow text with an ADDR value (simulates controller.updateCommandRow)
|
||||||
m_editor->setCommandRowText(
|
m_editor->setCommandRowText(
|
||||||
QStringLiteral("source\u25BE \u203A 0xD87B5E5000"));
|
QStringLiteral("source\u25BE \u00B7 0xD87B5E5000"));
|
||||||
|
|
||||||
// BaseAddress should be ALLOWED on CommandRow (ADDR field)
|
// BaseAddress should be ALLOWED on CommandRow (ADDR field)
|
||||||
bool ok = m_editor->beginInlineEdit(EditTarget::BaseAddress, 0);
|
bool ok = m_editor->beginInlineEdit(EditTarget::BaseAddress, 0);
|
||||||
@@ -394,7 +394,7 @@ private slots:
|
|||||||
|
|
||||||
// ── Test: inline edit lifecycle (begin → commit → re-edit) ──
|
// ── Test: inline edit lifecycle (begin → commit → re-edit) ──
|
||||||
void testInlineEditReEntry() {
|
void testInlineEditReEntry() {
|
||||||
// Move cursor to line 3 (first field; 0=CommandRow, 1=Blank, 2=CommandRow2, root header suppressed)
|
// Move cursor to first data line (0=CommandRow, root header suppressed)
|
||||||
m_editor->scintilla()->setCursorPosition(kFirstDataLine, 0);
|
m_editor->scintilla()->setCursorPosition(kFirstDataLine, 0);
|
||||||
|
|
||||||
// Should not be editing
|
// Should not be editing
|
||||||
@@ -712,7 +712,7 @@ private slots:
|
|||||||
void testSelectedNodeIndices() {
|
void testSelectedNodeIndices() {
|
||||||
m_editor->applyDocument(m_result);
|
m_editor->applyDocument(m_result);
|
||||||
|
|
||||||
// Put cursor on first field line (kFirstDataLine; 0=CommandRow, 1=Blank, 2=CommandRow2)
|
// Put cursor on first field line (kFirstDataLine; 0=CommandRow)
|
||||||
m_editor->scintilla()->setCursorPosition(kFirstDataLine, 0);
|
m_editor->scintilla()->setCursorPosition(kFirstDataLine, 0);
|
||||||
QSet<int> sel = m_editor->selectedNodeIndices();
|
QSet<int> sel = m_editor->selectedNodeIndices();
|
||||||
QCOMPARE(sel.size(), 1);
|
QCOMPARE(sel.size(), 1);
|
||||||
@@ -750,7 +750,7 @@ private slots:
|
|||||||
|
|
||||||
// Set CommandRow text with ADDR value (simulates controller)
|
// Set CommandRow text with ADDR value (simulates controller)
|
||||||
m_editor->setCommandRowText(
|
m_editor->setCommandRowText(
|
||||||
QStringLiteral("source\u25BE \u203A 0xD87B5E5000"));
|
QStringLiteral("source\u25BE \u00B7 0xD87B5E5000"));
|
||||||
|
|
||||||
// Line 0 is CommandRow
|
// Line 0 is CommandRow
|
||||||
const LineMeta* lm = m_editor->metaForLine(0);
|
const LineMeta* lm = m_editor->metaForLine(0);
|
||||||
@@ -902,7 +902,7 @@ private slots:
|
|||||||
|
|
||||||
// Set CommandRow text with ADDR value (simulates controller)
|
// Set CommandRow text with ADDR value (simulates controller)
|
||||||
m_editor->setCommandRowText(
|
m_editor->setCommandRowText(
|
||||||
QStringLiteral("source\u25BE \u203A 0xD87B5E5000"));
|
QStringLiteral("source\u25BE \u00B7 0xD87B5E5000"));
|
||||||
|
|
||||||
// Begin base address edit on line 0 (CommandRow ADDR field)
|
// Begin base address edit on line 0 (CommandRow ADDR field)
|
||||||
bool ok = m_editor->beginInlineEdit(EditTarget::BaseAddress, 0);
|
bool ok = m_editor->beginInlineEdit(EditTarget::BaseAddress, 0);
|
||||||
@@ -978,7 +978,7 @@ private slots:
|
|||||||
m_editor->applyDocument(m_result);
|
m_editor->applyDocument(m_result);
|
||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
// Root header (line 2) has fold suppressed; find a nested struct with foldHead
|
// Root header is suppressed; find a nested struct with foldHead
|
||||||
int foldLine = -1;
|
int foldLine = -1;
|
||||||
for (int i = 0; i < m_result.meta.size(); i++) {
|
for (int i = 0; i < m_result.meta.size(); i++) {
|
||||||
if (m_result.meta[i].foldHead && m_result.meta[i].lineKind == LineKind::Header) {
|
if (m_result.meta[i].foldHead && m_result.meta[i].lineKind == LineKind::Header) {
|
||||||
@@ -1033,57 +1033,54 @@ private slots:
|
|||||||
QVERIFY(!m_editor->isEditing());
|
QVERIFY(!m_editor->isEditing());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Test: CommandRow2 exists at kCommandRow2Line ──
|
// ── Test: CommandRow root class edits on line 0 ──
|
||||||
void testCommandRow2Exists() {
|
void testCommandRowRootClassEdits() {
|
||||||
m_editor->applyDocument(m_result);
|
m_editor->applyDocument(m_result);
|
||||||
|
|
||||||
// kCommandRow2Line should be CommandRow2
|
// Set CommandRow text with root class (simulates controller.updateCommandRow)
|
||||||
const LineMeta* lm = m_editor->metaForLine(kCommandRow2Line);
|
m_editor->setCommandRowText(
|
||||||
QVERIFY(lm);
|
QStringLiteral("source\u25BE \u00B7 0xD87B5E5000 \u00B7 struct\u25BE _PEB64 {"));
|
||||||
QCOMPARE(lm->lineKind, LineKind::CommandRow2);
|
|
||||||
QCOMPARE(lm->nodeId, kCommandRow2Id);
|
|
||||||
QCOMPARE(lm->nodeIdx, -1);
|
|
||||||
|
|
||||||
// Type/Name/Value should be rejected on CommandRow2
|
// RootClassName should be allowed on CommandRow (line 0)
|
||||||
QVERIFY(!m_editor->beginInlineEdit(EditTarget::Type, kCommandRow2Line));
|
bool ok = m_editor->beginInlineEdit(EditTarget::RootClassName, 0);
|
||||||
QVERIFY(!m_editor->beginInlineEdit(EditTarget::Name, kCommandRow2Line));
|
QVERIFY2(ok, "RootClassName edit should be allowed on CommandRow");
|
||||||
QVERIFY(!m_editor->beginInlineEdit(EditTarget::Value, kCommandRow2Line));
|
QVERIFY(m_editor->isEditing());
|
||||||
QVERIFY(!m_editor->isEditing());
|
m_editor->cancelInlineEdit();
|
||||||
|
|
||||||
// RootClassName should be allowed on CommandRow2
|
// RootClassType should be allowed on CommandRow (line 0)
|
||||||
m_editor->setCommandRow2Text(QStringLiteral("struct\u25BE _PEB64"));
|
ok = m_editor->beginInlineEdit(EditTarget::RootClassType, 0);
|
||||||
bool ok = m_editor->beginInlineEdit(EditTarget::RootClassName, kCommandRow2Line);
|
QVERIFY2(ok, "RootClassType edit should be allowed on CommandRow");
|
||||||
QVERIFY2(ok, "RootClassName edit should be allowed on CommandRow2");
|
|
||||||
QVERIFY(m_editor->isEditing());
|
QVERIFY(m_editor->isEditing());
|
||||||
m_editor->cancelInlineEdit();
|
m_editor->cancelInlineEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Test: CommandRow2 has class type and name but no alignas ──
|
// ── Test: CommandRow root class name editable ──
|
||||||
void testCommandRow2NoAlignas() {
|
void testCommandRowRootClassName() {
|
||||||
m_editor->applyDocument(m_result);
|
m_editor->applyDocument(m_result);
|
||||||
|
|
||||||
// Set CommandRow2 without alignas
|
// Set CommandRow with root class
|
||||||
m_editor->setCommandRow2Text(QStringLiteral("struct\u25BE _PEB64"));
|
m_editor->setCommandRowText(
|
||||||
|
QStringLiteral("source\u25BE \u00B7 0xD87B5E5000 \u00B7 struct\u25BE _PEB64 {"));
|
||||||
|
|
||||||
// kCommandRow2Line is CommandRow2
|
// Line 0 is CommandRow
|
||||||
const LineMeta* lm = m_editor->metaForLine(kCommandRow2Line);
|
const LineMeta* lm = m_editor->metaForLine(0);
|
||||||
QVERIFY(lm);
|
QVERIFY(lm);
|
||||||
QCOMPARE(lm->lineKind, LineKind::CommandRow2);
|
QCOMPARE(lm->lineKind, LineKind::CommandRow);
|
||||||
|
|
||||||
// RootClassName should work
|
// RootClassName should work
|
||||||
QVERIFY(m_editor->beginInlineEdit(EditTarget::RootClassName, kCommandRow2Line));
|
QVERIFY(m_editor->beginInlineEdit(EditTarget::RootClassName, 0));
|
||||||
QVERIFY(m_editor->isEditing());
|
QVERIFY(m_editor->isEditing());
|
||||||
m_editor->cancelInlineEdit();
|
m_editor->cancelInlineEdit();
|
||||||
|
|
||||||
m_editor->applyDocument(m_result);
|
m_editor->applyDocument(m_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Test: root header/footer are suppressed (CommandRow2 replaces them) ──
|
// ── Test: root header/footer are suppressed (CommandRow replaces them) ──
|
||||||
void testRootFoldSuppressed() {
|
void testRootFoldSuppressed() {
|
||||||
m_editor->applyDocument(m_result);
|
m_editor->applyDocument(m_result);
|
||||||
|
|
||||||
// Root struct header is completely suppressed from output.
|
// Root struct header is completely suppressed from output.
|
||||||
// Line 0 = CommandRow, Line 1 = Blank, Line 2 = CommandRow2, Line 3 = first field.
|
// Line 0 = CommandRow, Line 1 = first field.
|
||||||
const LineMeta* lm2 = m_editor->metaForLine(kFirstDataLine);
|
const LineMeta* lm2 = m_editor->metaForLine(kFirstDataLine);
|
||||||
QVERIFY(lm2);
|
QVERIFY(lm2);
|
||||||
QCOMPARE(lm2->lineKind, LineKind::Field);
|
QCOMPARE(lm2->lineKind, LineKind::Field);
|
||||||
|
|||||||
@@ -441,11 +441,9 @@ private slots:
|
|||||||
NullProvider prov;
|
NullProvider prov;
|
||||||
ComposeResult result = compose(tree, prov, 99999);
|
ComposeResult result = compose(tree, prov, 99999);
|
||||||
|
|
||||||
// Only command rows + blank
|
// Only command row
|
||||||
QCOMPARE(result.meta.size(), 3);
|
QCOMPARE(result.meta.size(), 1);
|
||||||
QCOMPARE(result.meta[0].lineKind, LineKind::CommandRow);
|
QCOMPARE(result.meta[0].lineKind, LineKind::CommandRow);
|
||||||
QCOMPARE(result.meta[1].lineKind, LineKind::Blank);
|
|
||||||
QCOMPARE(result.meta[2].lineKind, LineKind::CommandRow2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testCompose_viewRootId_singleRoot() {
|
void testCompose_viewRootId_singleRoot() {
|
||||||
@@ -882,13 +880,13 @@ private slots:
|
|||||||
NullProvider prov;
|
NullProvider prov;
|
||||||
ComposeResult result = compose(tree, prov);
|
ComposeResult result = compose(tree, prov);
|
||||||
|
|
||||||
// CommandRow + Blank + CommandRow2 + 1 Vec4 line + footer = 5
|
// CommandRow + 1 Vec4 line + footer = 3
|
||||||
QCOMPARE(result.meta.size(), 5);
|
QCOMPARE(result.meta.size(), 3);
|
||||||
|
|
||||||
// The Vec4 line (index 3) is a single field line, not continuation
|
// The Vec4 line (index 1) is a single field line, not continuation
|
||||||
QCOMPARE(result.meta[3].lineKind, LineKind::Field);
|
QCOMPARE(result.meta[1].lineKind, LineKind::Field);
|
||||||
QCOMPARE(result.meta[3].nodeKind, NodeKind::Vec4);
|
QCOMPARE(result.meta[1].nodeKind, NodeKind::Vec4);
|
||||||
QVERIFY(!result.meta[3].isContinuation);
|
QVERIFY(!result.meta[1].isContinuation);
|
||||||
|
|
||||||
// Copy text (equivalent to editor's "Copy All as Text")
|
// Copy text (equivalent to editor's "Copy All as Text")
|
||||||
QString text = result.text;
|
QString text = result.text;
|
||||||
@@ -896,9 +894,9 @@ private slots:
|
|||||||
QVERIFY(text.contains("0.f, 0.f, 0.f, 0.f"));
|
QVERIFY(text.contains("0.f, 0.f, 0.f, 0.f"));
|
||||||
// Confirm type, name, and values all on the same line
|
// Confirm type, name, and values all on the same line
|
||||||
QStringList lines = text.split('\n');
|
QStringList lines = text.split('\n');
|
||||||
QVERIFY(lines[3].contains("vec4"));
|
QVERIFY(lines[1].contains("vec4"));
|
||||||
QVERIFY(lines[3].contains("position"));
|
QVERIFY(lines[1].contains("position"));
|
||||||
QVERIFY(lines[3].contains("0.f, 0.f, 0.f, 0.f"));
|
QVERIFY(lines[1].contains("0.f, 0.f, 0.f, 0.f"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user