Type picker refactor: use SCI_USERLISTSHOW, fix coordinate handling

- Switch type picker from SCI_AUTOCSHOW to SCI_USERLISTSHOW for proper
  signal handling (userListActivated now fires correctly)
- Fix coordinate system mixing by using posFromCol() consistently
- Skip 0x prefix in value/base address edit selection
- Dynamic hint positioning: 2 spaces after value with // prefix
- Clearer validation errors ("too large! max=0x..." instead of "max")
- Green colored hint text using IND_BASE_ADDR indicator

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
sysadmin
2026-02-04 07:48:59 -07:00
parent 06c3251f74
commit 490486ca7c
7 changed files with 396 additions and 154 deletions

View File

@@ -455,87 +455,99 @@ private slots:
QVERIFY(sel.contains(lm->nodeIdx));
}
// ── Test: value edit echoes to comment column ──
void testValueEditCommentEcho() {
m_editor->applyDocument(m_result);
// ── Test: base address changes affect header display ──
void testBaseAddressDisplay() {
// Create tree with base address 0x10
NodeTree tree = makeTestTree();
tree.baseAddress = 0x10;
FileProvider prov = makeTestProvider();
ComposeResult result = compose(tree, prov);
// Begin value edit on line 1 (UInt16 field)
bool ok = m_editor->beginInlineEdit(EditTarget::Value, 1);
QVERIFY(ok);
QVERIFY(m_editor->isEditing());
m_editor->applyDocument(result);
// Get the line text before any typing
QString lineBefore;
int len = (int)m_editor->scintilla()->SendScintilla(
QsciScintillaBase::SCI_LINELENGTH, (unsigned long)1);
if (len > 0) {
QByteArray buf(len + 1, '\0');
m_editor->scintilla()->SendScintilla(
QsciScintillaBase::SCI_GETLINE, (unsigned long)1, (void*)buf.data());
lineBefore = QString::fromUtf8(buf.constData(), len).trimmed();
}
// Line 0 should be the struct header with isRootHeader=true
const LineMeta* lm = m_editor->metaForLine(0);
QVERIFY(lm);
QCOMPARE(lm->lineKind, LineKind::Header);
QVERIFY(lm->isRootHeader);
// Initial comment should contain "Enter=Save Esc=Cancel"
QVERIFY2(lineBefore.contains("Enter=Save"),
qPrintable("Initial comment missing, got: " + lineBefore));
// Type a digit to trigger validateEditLive
QKeyEvent key5(QEvent::KeyPress, Qt::Key_5, Qt::NoModifier, "5");
QApplication::sendEvent(m_editor->scintilla(), &key5);
QApplication::processEvents();
// Get line text after typing
QString lineAfter;
len = (int)m_editor->scintilla()->SendScintilla(
QsciScintillaBase::SCI_LINELENGTH, (unsigned long)1);
if (len > 0) {
QByteArray buf(len + 1, '\0');
m_editor->scintilla()->SendScintilla(
QsciScintillaBase::SCI_GETLINE, (unsigned long)1, (void*)buf.data());
lineAfter = QString::fromUtf8(buf.constData(), len).trimmed();
}
// Comment should show "!" prefix for invalid value
// Since "0x5a4d" + "5" = "0x5a4d5" = 370509 > 65535, it's invalid for UInt16
QVERIFY2(lineAfter.contains("! "),
qPrintable("Comment should show '!' for invalid value, got: " + lineAfter));
// Cancel and reset
m_editor->cancelInlineEdit();
m_editor->applyDocument(m_result);
}
// ── Test: value validation shows error indicator ──
void testValueValidationError() {
m_editor->applyDocument(m_result);
// Begin value edit on line 1 (UInt16 field, value = 23117)
bool ok = m_editor->beginInlineEdit(EditTarget::Value, 1);
QVERIFY(ok);
// Type "999" to make value invalid for UInt16 (appends to existing, making it too large)
// Original value 23117 -> typing "999" at end makes it invalid (23117999 > 65535)
const char* digits = "999";
for (int i = 0; digits[i]; i++) {
QKeyEvent key(QEvent::KeyPress, Qt::Key_9, Qt::NoModifier, QString(digits[i]));
QApplication::sendEvent(m_editor->scintilla(), &key);
QApplication::processEvents();
}
// Get line text - comment should show "! " prefix (error)
// Get header line text - should contain "0x10"
QString lineText;
int len = (int)m_editor->scintilla()->SendScintilla(
QsciScintillaBase::SCI_LINELENGTH, (unsigned long)1);
QsciScintillaBase::SCI_LINELENGTH, (unsigned long)0);
if (len > 0) {
QByteArray buf(len + 1, '\0');
m_editor->scintilla()->SendScintilla(
QsciScintillaBase::SCI_GETLINE, (unsigned long)1, (void*)buf.data());
QsciScintillaBase::SCI_GETLINE, (unsigned long)0, (void*)buf.data());
lineText = QString::fromUtf8(buf.constData(), len).trimmed();
}
// Comment should show "! " prefix for invalid value
QVERIFY2(lineText.contains("! "),
qPrintable("Comment should show '! ' for invalid value, got: " + lineText));
// Verify base address appears in header
QVERIFY2(lineText.contains("0x10") || lineText.contains("0X10"),
qPrintable("Header should contain base address 0x10, got: " + lineText));
// Verify struct keyword is present
QVERIFY2(lineText.contains("struct"),
qPrintable("Header should contain 'struct', got: " + lineText));
// Reset to original result
m_editor->applyDocument(m_result);
}
// ── Test: base address span is valid for root headers ──
void testBaseAddressSpan() {
NodeTree tree = makeTestTree();
tree.baseAddress = 0x140000000; // Large address to test span width
FileProvider prov = makeTestProvider();
ComposeResult result = compose(tree, prov);
m_editor->applyDocument(result);
// Line 0 should be root header
const LineMeta* lm = m_editor->metaForLine(0);
QVERIFY(lm);
QVERIFY(lm->isRootHeader);
// Get line text for span calculation
QString lineText;
int len = (int)m_editor->scintilla()->SendScintilla(
QsciScintillaBase::SCI_LINELENGTH, (unsigned long)0);
if (len > 0) {
QByteArray buf(len + 1, '\0');
m_editor->scintilla()->SendScintilla(
QsciScintillaBase::SCI_GETLINE, (unsigned long)0, (void*)buf.data());
lineText = QString::fromUtf8(buf.constData(), len);
while (lineText.endsWith('\n') || lineText.endsWith('\r'))
lineText.chop(1);
}
// Base address span should be valid
ColumnSpan bs = baseAddressSpanFor(*lm, lineText);
QVERIFY2(bs.valid, "Base address span should be valid for root header");
QVERIFY(bs.start < bs.end);
// The span should cover the hex address
QString spanText = lineText.mid(bs.start, bs.end - bs.start);
QVERIFY2(spanText.contains("0x") || spanText.startsWith("0X"),
qPrintable("Span should contain hex address, got: " + spanText));
// Reset
m_editor->applyDocument(m_result);
}
// ── Test: base address edit begins on root header ──
void testBaseAddressEditBegins() {
NodeTree tree = makeTestTree();
tree.baseAddress = 0x10;
FileProvider prov = makeTestProvider();
ComposeResult result = compose(tree, prov);
m_editor->applyDocument(result);
// Begin base address edit on line 0 (root header)
bool ok = m_editor->beginInlineEdit(EditTarget::BaseAddress, 0);
QVERIFY2(ok, "Should be able to begin base address edit on root header");
QVERIFY(m_editor->isEditing());
// Cancel and reset
m_editor->cancelInlineEdit();