mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Replace Iosevka with JetBrains Mono, fix scrollbar and inline edit UX
- Replace embedded Iosevka font with JetBrains Mono as default font
- Fix wide horizontal scrollbar by enabling SCI_SETSCROLLWIDTHTRACKING
- Remove 28-char trailing whitespace padding from all lines
- Fix arrow keys not collapsing selection in inline edit mode
- Dim struct/array braces ({ and };) to match hex node styling
- Resize margin immediately on font change
This commit is contained in:
@@ -18,6 +18,7 @@ struct ComposeState {
|
|||||||
int currentLine = 0;
|
int currentLine = 0;
|
||||||
int typeW = kColType; // global type column width (fallback)
|
int typeW = kColType; // global type column width (fallback)
|
||||||
int nameW = kColName; // global name column width (fallback)
|
int nameW = kColName; // global name column width (fallback)
|
||||||
|
int offsetHexDigits = 8; // hex digit tier for offset margin
|
||||||
bool baseEmitted = false; // only first root struct shows base address
|
bool baseEmitted = false; // only first root struct shows base address
|
||||||
|
|
||||||
// Precomputed for O(1) lookups
|
// Precomputed for O(1) lookups
|
||||||
@@ -144,7 +145,7 @@ void composeLeaf(ComposeState& state, const NodeTree& tree,
|
|||||||
lm.isContinuation = isCont;
|
lm.isContinuation = isCont;
|
||||||
lm.lineKind = isCont ? LineKind::Continuation : LineKind::Field;
|
lm.lineKind = isCont ? LineKind::Continuation : LineKind::Field;
|
||||||
lm.nodeKind = node.kind;
|
lm.nodeKind = node.kind;
|
||||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, isCont);
|
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, isCont, state.offsetHexDigits);
|
||||||
lm.markerMask = computeMarkers(node, prov, absAddr, isCont, depth);
|
lm.markerMask = computeMarkers(node, prov, absAddr, isCont, depth);
|
||||||
lm.foldLevel = computeFoldLevel(depth, false);
|
lm.foldLevel = computeFoldLevel(depth, false);
|
||||||
lm.effectiveTypeW = typeW;
|
lm.effectiveTypeW = typeW;
|
||||||
@@ -191,7 +192,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
|||||||
lm.nodeId = node.id;
|
lm.nodeId = node.id;
|
||||||
lm.depth = depth;
|
lm.depth = depth;
|
||||||
lm.lineKind = LineKind::Field;
|
lm.lineKind = LineKind::Field;
|
||||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false);
|
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
|
||||||
lm.nodeKind = node.kind;
|
lm.nodeKind = node.kind;
|
||||||
lm.markerMask = (1u << M_CYCLE) | (1u << M_ERR);
|
lm.markerMask = (1u << M_CYCLE) | (1u << M_ERR);
|
||||||
lm.foldLevel = computeFoldLevel(depth, false);
|
lm.foldLevel = computeFoldLevel(depth, false);
|
||||||
@@ -208,7 +209,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
|||||||
lm.nodeId = node.id;
|
lm.nodeId = node.id;
|
||||||
lm.depth = depth;
|
lm.depth = depth;
|
||||||
lm.lineKind = LineKind::ArrayElementSeparator;
|
lm.lineKind = LineKind::ArrayElementSeparator;
|
||||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false);
|
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
|
||||||
lm.nodeKind = node.kind;
|
lm.nodeKind = node.kind;
|
||||||
lm.foldLevel = computeFoldLevel(depth, false);
|
lm.foldLevel = computeFoldLevel(depth, false);
|
||||||
lm.markerMask = 0;
|
lm.markerMask = 0;
|
||||||
@@ -234,7 +235,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
|||||||
lm.nodeId = node.id;
|
lm.nodeId = node.id;
|
||||||
lm.depth = depth;
|
lm.depth = depth;
|
||||||
lm.lineKind = LineKind::Header;
|
lm.lineKind = LineKind::Header;
|
||||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false);
|
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
|
||||||
lm.nodeKind = node.kind;
|
lm.nodeKind = node.kind;
|
||||||
lm.isRootHeader = false;
|
lm.isRootHeader = false;
|
||||||
lm.foldHead = true;
|
lm.foldHead = true;
|
||||||
@@ -288,10 +289,10 @@ void composeParent(ComposeState& state, const NodeTree& tree,
|
|||||||
lm.lineKind = LineKind::Footer;
|
lm.lineKind = LineKind::Footer;
|
||||||
lm.nodeKind = node.kind;
|
lm.nodeKind = node.kind;
|
||||||
lm.isRootHeader = isRootHeader; // root footer: flush left (no fold prefix)
|
lm.isRootHeader = isRootHeader; // root footer: flush left (no fold prefix)
|
||||||
lm.offsetText.clear();
|
|
||||||
lm.foldLevel = computeFoldLevel(depth, false);
|
lm.foldLevel = computeFoldLevel(depth, false);
|
||||||
lm.markerMask = 0;
|
lm.markerMask = 0;
|
||||||
int sz = tree.structSpan(node.id, &state.childMap);
|
int sz = tree.structSpan(node.id, &state.childMap);
|
||||||
|
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr + sz, false, state.offsetHexDigits);
|
||||||
state.emitLine(fmt::fmtStructFooter(node, depth, sz), lm);
|
state.emitLine(fmt::fmtStructFooter(node, depth, sz), lm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +323,7 @@ void composeNode(ComposeState& state, const NodeTree& tree,
|
|||||||
lm.nodeId = node.id;
|
lm.nodeId = node.id;
|
||||||
lm.depth = depth;
|
lm.depth = depth;
|
||||||
lm.lineKind = node.collapsed ? LineKind::Field : LineKind::Header;
|
lm.lineKind = node.collapsed ? LineKind::Field : LineKind::Header;
|
||||||
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false);
|
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress + absAddr, false, state.offsetHexDigits);
|
||||||
lm.nodeKind = node.kind;
|
lm.nodeKind = node.kind;
|
||||||
lm.foldHead = true;
|
lm.foldHead = true;
|
||||||
lm.foldCollapsed = node.collapsed;
|
lm.foldCollapsed = node.collapsed;
|
||||||
@@ -411,6 +412,19 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
|
|||||||
for (int i = 0; i < tree.nodes.size(); i++)
|
for (int i = 0; i < tree.nodes.size(); i++)
|
||||||
state.absOffsets[i] = tree.computeOffset(i);
|
state.absOffsets[i] = tree.computeOffset(i);
|
||||||
|
|
||||||
|
// Compute hex digit tier from max absolute address
|
||||||
|
{
|
||||||
|
uint64_t maxAddr = tree.baseAddress;
|
||||||
|
for (int i = 0; i < tree.nodes.size(); i++) {
|
||||||
|
uint64_t addr = tree.baseAddress + (uint64_t)state.absOffsets[i];
|
||||||
|
if (addr > maxAddr) maxAddr = addr;
|
||||||
|
}
|
||||||
|
if (maxAddr <= 0xFFFFULL) state.offsetHexDigits = 4;
|
||||||
|
else if (maxAddr <= 0xFFFFFFFFULL) state.offsetHexDigits = 8;
|
||||||
|
else if (maxAddr <= 0xFFFFFFFFFFFFULL) state.offsetHexDigits = 12;
|
||||||
|
else state.offsetHexDigits = 16;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper: compute the display type string for a node (for width calculation)
|
// Helper: compute the display type string for a node (for width calculation)
|
||||||
auto nodeTypeName = [&](const Node& n) -> QString {
|
auto nodeTypeName = [&](const Node& n) -> QString {
|
||||||
if (n.kind == NodeKind::Array)
|
if (n.kind == NodeKind::Array)
|
||||||
@@ -491,7 +505,7 @@ 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 = fmt::fmtOffsetMargin(tree.baseAddress, false);
|
lm.offsetText = fmt::fmtOffsetMargin(tree.baseAddress, false, state.offsetHexDigits);
|
||||||
lm.markerMask = 0;
|
lm.markerMask = 0;
|
||||||
lm.effectiveTypeW = state.typeW;
|
lm.effectiveTypeW = state.typeW;
|
||||||
lm.effectiveNameW = state.nameW;
|
lm.effectiveNameW = state.nameW;
|
||||||
@@ -510,7 +524,7 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR
|
|||||||
composeNode(state, tree, prov, idx, 0);
|
composeNode(state, tree, prov, idx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { state.text, state.meta, LayoutInfo{state.typeW, state.nameW} };
|
return { state.text, state.meta, LayoutInfo{state.typeW, state.nameW, state.offsetHexDigits} };
|
||||||
}
|
}
|
||||||
|
|
||||||
QSet<uint64_t> NodeTree::normalizePreferAncestors(const QSet<uint64_t>& ids) const {
|
QSet<uint64_t> NodeTree::normalizePreferAncestors(const QSet<uint64_t>& ids) const {
|
||||||
|
|||||||
@@ -1496,7 +1496,7 @@ void RcxController::showTypeSelectorPopup(RcxEditor* editor) {
|
|||||||
|
|
||||||
// Get font with zoom
|
// Get font with zoom
|
||||||
QSettings settings("ReclassX", "ReclassX");
|
QSettings settings("ReclassX", "ReclassX");
|
||||||
QString fontName = settings.value("font", "Consolas").toString();
|
QString fontName = settings.value("font", "JetBrains Mono").toString();
|
||||||
QFont font(fontName, 12);
|
QFont font(fontName, 12);
|
||||||
font.setFixedPitch(true);
|
font.setFixedPitch(true);
|
||||||
auto* sci = editor->scintilla();
|
auto* sci = editor->scintilla();
|
||||||
|
|||||||
@@ -439,6 +439,7 @@ inline bool isSyntheticLine(const LineMeta& lm) {
|
|||||||
struct LayoutInfo {
|
struct LayoutInfo {
|
||||||
int typeW = 14; // Effective type column width (default = kColType)
|
int typeW = 14; // Effective type column width (default = kColType)
|
||||||
int nameW = 22; // Effective name column width (default = kColName)
|
int nameW = 22; // Effective name column width (default = kColName)
|
||||||
|
int offsetHexDigits = 8; // Hex digits for offset margin (4/8/12/16)
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── ComposeResult ──
|
// ── ComposeResult ──
|
||||||
@@ -750,7 +751,7 @@ namespace fmt {
|
|||||||
uint64_t addr, int depth, int subLine = 0,
|
uint64_t addr, int depth, int subLine = 0,
|
||||||
const QString& comment = {}, int colType = kColType, int colName = kColName,
|
const QString& comment = {}, int colType = kColType, int colName = kColName,
|
||||||
const QString& typeOverride = {});
|
const QString& typeOverride = {});
|
||||||
QString fmtOffsetMargin(uint64_t absoluteOffset, bool isContinuation);
|
QString fmtOffsetMargin(uint64_t absoluteOffset, bool isContinuation, int hexDigits = 8);
|
||||||
QString fmtStructHeader(const Node& node, int depth, bool collapsed, int colType = kColType, int colName = kColName);
|
QString fmtStructHeader(const Node& node, int depth, bool collapsed, int colType = kColType, int colName = kColName);
|
||||||
QString fmtStructFooter(const Node& node, int depth, int totalSize = -1);
|
QString fmtStructFooter(const Node& node, int depth, int totalSize = -1);
|
||||||
QString fmtArrayHeader(const Node& node, int depth, int viewIdx, bool collapsed, int colType = kColType, int colName = kColName);
|
QString fmtArrayHeader(const Node& node, int depth, int viewIdx, bool collapsed, int colType = kColType, int colName = kColName);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ static constexpr int IND_DATA_CHANGED = 13; // Amber text for changed data value
|
|||||||
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_HINT_GREEN = 15; // Green text for hint/comment text
|
static constexpr int IND_HINT_GREEN = 15; // Green text for hint/comment text
|
||||||
|
|
||||||
static QString g_fontName = "Consolas";
|
static QString g_fontName = "JetBrains Mono";
|
||||||
|
|
||||||
static QFont editorFont() {
|
static QFont editorFont() {
|
||||||
QFont f(g_fontName, 12);
|
QFont f(g_fontName, 12);
|
||||||
@@ -139,6 +139,10 @@ void RcxEditor::setupScintilla() {
|
|||||||
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)0, (long)0);
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSELBACK, (long)0, (long)0);
|
||||||
|
|
||||||
|
// Auto-size horizontal scrollbar to actual content width (default is fixed 2000px)
|
||||||
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSCROLLWIDTHTRACKING, 1);
|
||||||
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSCROLLWIDTH, 1);
|
||||||
|
|
||||||
// Editable-field indicator - HIDDEN (no visual)
|
// Editable-field indicator - HIDDEN (no visual)
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICSETSTYLE,
|
||||||
IND_EDITABLE, 5 /*INDIC_HIDDEN*/);
|
IND_EDITABLE, 5 /*INDIC_HIDDEN*/);
|
||||||
@@ -240,7 +244,7 @@ void RcxEditor::setupMargins() {
|
|||||||
|
|
||||||
// Margin 0: Offset text
|
// Margin 0: Offset text
|
||||||
m_sci->setMarginType(0, QsciScintilla::TextMarginRightJustified);
|
m_sci->setMarginType(0, QsciScintilla::TextMarginRightJustified);
|
||||||
m_sci->setMarginWidth(0, " 0x00000000 ");
|
m_sci->setMarginWidth(0, " 00000000 "); // default 8-digit; resized dynamically in applyDocument()
|
||||||
m_sci->setMarginsBackgroundColor(kBgMargin);
|
m_sci->setMarginsBackgroundColor(kBgMargin);
|
||||||
m_sci->setMarginsForegroundColor(kFgMarginDim);
|
m_sci->setMarginsForegroundColor(kFgMarginDim);
|
||||||
m_sci->setMarginSensitivity(0, true);
|
m_sci->setMarginSensitivity(0, true);
|
||||||
@@ -346,10 +350,18 @@ void RcxEditor::applyDocument(const ComposeResult& result) {
|
|||||||
m_meta = result.meta;
|
m_meta = result.meta;
|
||||||
m_layout = result.layout;
|
m_layout = result.layout;
|
||||||
|
|
||||||
|
// Dynamically resize margin to fit the current hex digit tier
|
||||||
|
QString marginSizer = QString(" %1 ").arg(QString(m_layout.offsetHexDigits, '0'));
|
||||||
|
m_sci->setMarginWidth(0, marginSizer);
|
||||||
|
|
||||||
m_sci->setReadOnly(false);
|
m_sci->setReadOnly(false);
|
||||||
m_sci->setText(result.text);
|
m_sci->setText(result.text);
|
||||||
m_sci->setReadOnly(true);
|
m_sci->setReadOnly(true);
|
||||||
|
|
||||||
|
// Reset scroll width so tracking re-measures from current content
|
||||||
|
// (tracking never shrinks automatically — only grows)
|
||||||
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETSCROLLWIDTH, 1);
|
||||||
|
|
||||||
// Force full re-lex to fix stale syntax coloring after edits
|
// Force full re-lex to fix stale syntax coloring after edits
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_COLOURISE, (uintptr_t)0, (long)-1);
|
m_sci->SendScintilla(QsciScintillaBase::SCI_COLOURISE, (uintptr_t)0, (long)-1);
|
||||||
|
|
||||||
@@ -370,7 +382,6 @@ void RcxEditor::applyDocument(const ComposeResult& result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RcxEditor::applyMarginText(const QVector<LineMeta>& meta) {
|
void RcxEditor::applyMarginText(const QVector<LineMeta>& meta) {
|
||||||
// Clear all margin text
|
|
||||||
m_sci->clearMarginText(-1);
|
m_sci->clearMarginText(-1);
|
||||||
|
|
||||||
for (int i = 0; i < meta.size(); i++) {
|
for (int i = 0; i < meta.size(); i++) {
|
||||||
@@ -450,6 +461,21 @@ void RcxEditor::applyHexDimming(const QVector<LineMeta>& meta) {
|
|||||||
if (len > 0)
|
if (len > 0)
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICATORFILLRANGE, pos, len);
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICATORFILLRANGE, pos, len);
|
||||||
}
|
}
|
||||||
|
// Dim struct/array braces: entire footer line, trailing "{" on headers
|
||||||
|
if (meta[i].lineKind == LineKind::Footer) {
|
||||||
|
long pos, len; lineRangeNoEol(m_sci, i, pos, len);
|
||||||
|
if (len > 0)
|
||||||
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICATORFILLRANGE, pos, len);
|
||||||
|
} else if (meta[i].lineKind == LineKind::Header) {
|
||||||
|
long endPos = m_sci->SendScintilla(QsciScintillaBase::SCI_GETLINEENDPOSITION, (unsigned long)i);
|
||||||
|
for (long p = endPos - 1; p >= 0; --p) {
|
||||||
|
int ch = (int)m_sci->SendScintilla(QsciScintillaBase::SCI_GETCHARAT, (unsigned long)p);
|
||||||
|
if (ch == ' ' || ch == '\t') continue;
|
||||||
|
if (ch == '{')
|
||||||
|
m_sci->SendScintilla(QsciScintillaBase::SCI_INDICATORFILLRANGE, p, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1351,12 +1377,30 @@ bool RcxEditor::handleEditKey(QKeyEvent* ke) {
|
|||||||
if (lineText.mid(m_editState.spanStart, 2).startsWith(QStringLiteral("0x"), Qt::CaseInsensitive))
|
if (lineText.mid(m_editState.spanStart, 2).startsWith(QStringLiteral("0x"), Qt::CaseInsensitive))
|
||||||
minCol = m_editState.spanStart + 2;
|
minCol = m_editState.spanStart + 2;
|
||||||
}
|
}
|
||||||
|
// If there's an active selection, collapse it to the left end (Left only, not Backspace)
|
||||||
|
if (ke->key() == Qt::Key_Left) {
|
||||||
|
int sL, sC, eL, eC;
|
||||||
|
m_sci->getSelection(&sL, &sC, &eL, &eC);
|
||||||
|
if (sL >= 0 && (sL != eL || sC != eC)) {
|
||||||
|
int leftEnd = qMax(qMin(sC, eC), minCol);
|
||||||
|
m_sci->setCursorPosition(m_editState.line, leftEnd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (col <= minCol) return true;
|
if (col <= minCol) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case Qt::Key_Right: {
|
case Qt::Key_Right: {
|
||||||
int line, col;
|
int line, col;
|
||||||
m_sci->getCursorPosition(&line, &col);
|
m_sci->getCursorPosition(&line, &col);
|
||||||
|
// If there's an active selection, collapse it to the right end first
|
||||||
|
int sL, sC, eL, eC;
|
||||||
|
m_sci->getSelection(&sL, &sC, &eL, &eC);
|
||||||
|
if (sL >= 0 && (sL != eL || sC != eC)) {
|
||||||
|
int rightEnd = qMin(qMax(sC, eC), editEndCol());
|
||||||
|
m_sci->setCursorPosition(m_editState.line, rightEnd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (col >= editEndCol()) return true; // block past end
|
if (col >= editEndCol()) return true; // block past end
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1468,8 +1512,9 @@ bool RcxEditor::beginInlineEdit(EditTarget target, int line) {
|
|||||||
m_editState.editKind = NodeKind::Float;
|
m_editState.editKind = NodeKind::Float;
|
||||||
|
|
||||||
// Store fixed comment column position for value editing
|
// Store fixed comment column position for value editing
|
||||||
|
// Use large lineLength so commentCol is always computed (padding added dynamically)
|
||||||
if (target == EditTarget::Value) {
|
if (target == EditTarget::Value) {
|
||||||
ColumnSpan cs = commentSpanFor(*lm, lineText.size(), lm->effectiveTypeW, lm->effectiveNameW);
|
ColumnSpan cs = commentSpanFor(*lm, 9999, lm->effectiveTypeW, lm->effectiveNameW);
|
||||||
m_editState.commentCol = cs.valid ? cs.start : -1;
|
m_editState.commentCol = cs.valid ? cs.start : -1;
|
||||||
m_editState.lastValidationOk = true; // original value is always valid
|
m_editState.lastValidationOk = true; // original value is always valid
|
||||||
} else {
|
} else {
|
||||||
@@ -1480,6 +1525,26 @@ bool RcxEditor::beginInlineEdit(EditTarget target, int line) {
|
|||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETUNDOCOLLECTION, (long)0);
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETUNDOCOLLECTION, (long)0);
|
||||||
m_sci->SendScintilla(QsciScintillaBase::SCI_SETCARETWIDTH, 1);
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETCARETWIDTH, 1);
|
||||||
m_sci->setReadOnly(false);
|
m_sci->setReadOnly(false);
|
||||||
|
|
||||||
|
// For value editing: extend line with trailing spaces for the edit comment area
|
||||||
|
// (comment padding is no longer baked into every line to avoid unnecessary scroll width)
|
||||||
|
if (target == EditTarget::Value && m_editState.commentCol >= 0) {
|
||||||
|
int commentStart = norm.end + 2;
|
||||||
|
int neededLen = commentStart + kColComment;
|
||||||
|
int currentLen = (int)lineText.size();
|
||||||
|
if (currentLen < neededLen) {
|
||||||
|
int extend = neededLen - currentLen;
|
||||||
|
long lineEndPos = posFromCol(m_sci, line, currentLen);
|
||||||
|
QString pad(extend, ' ');
|
||||||
|
QByteArray padUtf8 = pad.toUtf8();
|
||||||
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETSTART, lineEndPos);
|
||||||
|
m_sci->SendScintilla(QsciScintillaBase::SCI_SETTARGETEND, lineEndPos);
|
||||||
|
m_sci->SendScintilla(QsciScintillaBase::SCI_REPLACETARGET,
|
||||||
|
(uintptr_t)padUtf8.size(), padUtf8.constData());
|
||||||
|
m_editState.linelenAfterReplace += extend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Switch to I-beam for editing (skip for picker-based targets)
|
// Switch to I-beam for editing (skip for picker-based targets)
|
||||||
if (target != EditTarget::Type && target != EditTarget::Source
|
if (target != EditTarget::Type && target != EditTarget::Source
|
||||||
&& target != EditTarget::ArrayElementType && target != EditTarget::PointerTarget
|
&& target != EditTarget::ArrayElementType && target != EditTarget::PointerTarget
|
||||||
@@ -2079,8 +2144,10 @@ void RcxEditor::setEditorFont(const QString& fontName) {
|
|||||||
m_lexer->setFont(f, i);
|
m_lexer->setFont(f, i);
|
||||||
m_sci->setMarginsFont(f);
|
m_sci->setMarginsFont(f);
|
||||||
|
|
||||||
// Re-apply margin styles with new font
|
// Re-apply margin styles and width with new font metrics
|
||||||
allocateMarginStyles();
|
allocateMarginStyles();
|
||||||
|
QString marginSizer = QString(" %1 ").arg(QString(m_layout.offsetHexDigits, '0'));
|
||||||
|
m_sci->setMarginWidth(0, marginSizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RcxEditor::setGlobalFontName(const QString& fontName) {
|
void RcxEditor::setGlobalFontName(const QString& fontName) {
|
||||||
|
|||||||
Binary file not shown.
BIN
src/fonts/JetBrainsMono.ttf
Normal file
BIN
src/fonts/JetBrainsMono.ttf
Normal file
Binary file not shown.
@@ -111,9 +111,10 @@ QString indent(int depth) {
|
|||||||
|
|
||||||
// ── Offset margin ──
|
// ── Offset margin ──
|
||||||
|
|
||||||
QString fmtOffsetMargin(uint64_t absoluteOffset, bool isContinuation) {
|
QString fmtOffsetMargin(uint64_t absoluteOffset, bool isContinuation, int hexDigits) {
|
||||||
if (isContinuation) return QStringLiteral(" \u00B7 ");
|
if (isContinuation) return QStringLiteral(" \u00B7 ");
|
||||||
return QString::number(absoluteOffset, 16).toUpper() + QChar(' ');
|
return QString::number(absoluteOffset, 16).toUpper()
|
||||||
|
.rightJustified(hexDigits, '0') + QChar(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Struct type name (for width calculation) ──
|
// ── Struct type name (for width calculation) ──
|
||||||
@@ -313,8 +314,8 @@ QString fmtNodeLine(const Node& node, const Provider& prov,
|
|||||||
// Blank prefix for continuation lines (same width as type+sep+name+sep)
|
// Blank prefix for continuation lines (same width as type+sep+name+sep)
|
||||||
const int prefixW = colType + colName + 2 * kSepWidth;
|
const int prefixW = colType + colName + 2 * kSepWidth;
|
||||||
|
|
||||||
// Comment suffix (padded or empty)
|
// Comment suffix (only present when a comment is provided; no trailing padding)
|
||||||
QString cmtSuffix = comment.isEmpty() ? QString(COL_COMMENT, ' ')
|
QString cmtSuffix = comment.isEmpty() ? QString()
|
||||||
: fit(comment, COL_COMMENT);
|
: fit(comment, COL_COMMENT);
|
||||||
|
|
||||||
// Mat4x4: subLine 0..3 = rows
|
// Mat4x4: subLine 0..3 = rows
|
||||||
|
|||||||
26
src/main.cpp
26
src/main.cpp
@@ -315,16 +315,16 @@ void MainWindow::createMenus() {
|
|||||||
auto* actConsolas = fontMenu->addAction("Consolas");
|
auto* actConsolas = fontMenu->addAction("Consolas");
|
||||||
actConsolas->setCheckable(true);
|
actConsolas->setCheckable(true);
|
||||||
actConsolas->setActionGroup(fontGroup);
|
actConsolas->setActionGroup(fontGroup);
|
||||||
auto* actIosevka = fontMenu->addAction("Iosevka");
|
auto* actJetBrains = fontMenu->addAction("JetBrains Mono");
|
||||||
actIosevka->setCheckable(true);
|
actJetBrains->setCheckable(true);
|
||||||
actIosevka->setActionGroup(fontGroup);
|
actJetBrains->setActionGroup(fontGroup);
|
||||||
// Load saved preference
|
// Load saved preference
|
||||||
QSettings settings("ReclassX", "ReclassX");
|
QSettings settings("ReclassX", "ReclassX");
|
||||||
QString savedFont = settings.value("font", "Consolas").toString();
|
QString savedFont = settings.value("font", "JetBrains Mono").toString();
|
||||||
if (savedFont == "Iosevka") actIosevka->setChecked(true);
|
if (savedFont == "JetBrains Mono") actJetBrains->setChecked(true);
|
||||||
else actConsolas->setChecked(true);
|
else actConsolas->setChecked(true);
|
||||||
connect(actConsolas, &QAction::triggered, this, [this]() { setEditorFont("Consolas"); });
|
connect(actConsolas, &QAction::triggered, this, [this]() { setEditorFont("Consolas"); });
|
||||||
connect(actIosevka, &QAction::triggered, this, [this]() { setEditorFont("Iosevka"); });
|
connect(actJetBrains, &QAction::triggered, this, [this]() { setEditorFont("JetBrains Mono"); });
|
||||||
|
|
||||||
view->addSeparator();
|
view->addSeparator();
|
||||||
view->addAction(m_workspaceDock->toggleViewAction());
|
view->addAction(m_workspaceDock->toggleViewAction());
|
||||||
@@ -353,7 +353,7 @@ void MainWindow::createStatusBar() {
|
|||||||
statusBar()->setStyleSheet("QStatusBar { background: #252526; color: #858585; }");
|
statusBar()->setStyleSheet("QStatusBar { background: #252526; color: #858585; }");
|
||||||
|
|
||||||
QSettings settings("ReclassX", "ReclassX");
|
QSettings settings("ReclassX", "ReclassX");
|
||||||
QString fontName = settings.value("font", "Consolas").toString();
|
QString fontName = settings.value("font", "JetBrains Mono").toString();
|
||||||
QFont f(fontName, 12);
|
QFont f(fontName, 12);
|
||||||
f.setFixedPitch(true);
|
f.setFixedPitch(true);
|
||||||
statusBar()->setFont(f);
|
statusBar()->setFont(f);
|
||||||
@@ -361,7 +361,7 @@ void MainWindow::createStatusBar() {
|
|||||||
|
|
||||||
void MainWindow::applyTabWidgetStyle(QTabWidget* tw) {
|
void MainWindow::applyTabWidgetStyle(QTabWidget* tw) {
|
||||||
QSettings settings("ReclassX", "ReclassX");
|
QSettings settings("ReclassX", "ReclassX");
|
||||||
QString fontName = settings.value("font", "Consolas").toString();
|
QString fontName = settings.value("font", "JetBrains Mono").toString();
|
||||||
QFont tabFont(fontName, 12);
|
QFont tabFont(fontName, 12);
|
||||||
tabFont.setFixedPitch(true);
|
tabFont.setFixedPitch(true);
|
||||||
tw->tabBar()->setFont(tabFont);
|
tw->tabBar()->setFont(tabFont);
|
||||||
@@ -813,7 +813,7 @@ void MainWindow::updateWindowTitle() {
|
|||||||
|
|
||||||
void MainWindow::setupRenderedSci(QsciScintilla* sci) {
|
void MainWindow::setupRenderedSci(QsciScintilla* sci) {
|
||||||
QSettings settings("ReclassX", "ReclassX");
|
QSettings settings("ReclassX", "ReclassX");
|
||||||
QString fontName = settings.value("font", "Consolas").toString();
|
QString fontName = settings.value("font", "JetBrains Mono").toString();
|
||||||
QFont f(fontName, 12);
|
QFont f(fontName, 12);
|
||||||
f.setFixedPitch(true);
|
f.setFixedPitch(true);
|
||||||
|
|
||||||
@@ -1112,7 +1112,7 @@ void MainWindow::createWorkspaceDock() {
|
|||||||
// Match editor font
|
// Match editor font
|
||||||
{
|
{
|
||||||
QSettings settings("ReclassX", "ReclassX");
|
QSettings settings("ReclassX", "ReclassX");
|
||||||
QString fontName = settings.value("font", "Consolas").toString();
|
QString fontName = settings.value("font", "JetBrains Mono").toString();
|
||||||
QFont f(fontName, 12);
|
QFont f(fontName, 12);
|
||||||
f.setFixedPitch(true);
|
f.setFixedPitch(true);
|
||||||
m_workspaceTree->setFont(f);
|
m_workspaceTree->setFont(f);
|
||||||
@@ -1299,13 +1299,13 @@ int main(int argc, char* argv[]) {
|
|||||||
app.setStyle("Fusion"); // Fusion style respects dark palette well
|
app.setStyle("Fusion"); // Fusion style respects dark palette well
|
||||||
|
|
||||||
// Load embedded fonts
|
// Load embedded fonts
|
||||||
int fontId = QFontDatabase::addApplicationFont(":/fonts/Iosevka-Regular.ttf");
|
int fontId = QFontDatabase::addApplicationFont(":/fonts/JetBrainsMono.ttf");
|
||||||
if (fontId == -1)
|
if (fontId == -1)
|
||||||
qWarning("Failed to load embedded Iosevka font");
|
qWarning("Failed to load embedded JetBrains Mono font");
|
||||||
// Apply saved font preference before creating any editors
|
// Apply saved font preference before creating any editors
|
||||||
{
|
{
|
||||||
QSettings settings("ReclassX", "ReclassX");
|
QSettings settings("ReclassX", "ReclassX");
|
||||||
QString savedFont = settings.value("font", "Consolas").toString();
|
QString savedFont = settings.value("font", "JetBrains Mono").toString();
|
||||||
rcx::RcxEditor::setGlobalFontName(savedFont);
|
rcx::RcxEditor::setGlobalFontName(savedFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<file alias="chevron-down.png">icons/chevron-down.png</file>
|
<file alias="chevron-down.png">icons/chevron-down.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/fonts">
|
<qresource prefix="/fonts">
|
||||||
<file alias="Iosevka-Regular.ttf">fonts/Iosevka-Regular.ttf</file>
|
<file alias="JetBrainsMono.ttf">fonts/JetBrainsMono.ttf</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/vsicons">
|
<qresource prefix="/vsicons">
|
||||||
<file alias="file.svg">vsicons/file.svg</file>
|
<file alias="file.svg">vsicons/file.svg</file>
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ private slots:
|
|||||||
QCOMPARE(result.meta[2].depth, 1);
|
QCOMPARE(result.meta[2].depth, 1);
|
||||||
|
|
||||||
// Offset text
|
// Offset text
|
||||||
QCOMPARE(result.meta[1].offsetText, QString("0"));
|
QCOMPARE(result.meta[1].offsetText, QString("0000 "));
|
||||||
QCOMPARE(result.meta[2].offsetText, QString("4"));
|
QCOMPARE(result.meta[2].offsetText, QString("0004 "));
|
||||||
|
|
||||||
// Line 3 is root footer
|
// Line 3 is root footer
|
||||||
QCOMPARE(result.meta[3].lineKind, LineKind::Footer);
|
QCOMPARE(result.meta[3].lineKind, LineKind::Footer);
|
||||||
@@ -81,7 +81,7 @@ private slots:
|
|||||||
|
|
||||||
// Line 1: single Vec3 line, not continuation, depth 1
|
// Line 1: single Vec3 line, not continuation, depth 1
|
||||||
QVERIFY(!result.meta[1].isContinuation);
|
QVERIFY(!result.meta[1].isContinuation);
|
||||||
QCOMPARE(result.meta[1].offsetText, QString("0"));
|
QCOMPARE(result.meta[1].offsetText, QString("0000 "));
|
||||||
QCOMPARE(result.meta[1].depth, 1);
|
QCOMPARE(result.meta[1].depth, 1);
|
||||||
QCOMPARE(result.meta[1].nodeKind, NodeKind::Vec3);
|
QCOMPARE(result.meta[1].nodeKind, NodeKind::Vec3);
|
||||||
|
|
||||||
|
|||||||
@@ -39,12 +39,21 @@ private slots:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void testFmtOffsetMargin_primary() {
|
void testFmtOffsetMargin_primary() {
|
||||||
QCOMPARE(fmt::fmtOffsetMargin(0x10, false), QString("10"));
|
QCOMPARE(fmt::fmtOffsetMargin(0x10, false), QString("00000010 "));
|
||||||
QCOMPARE(fmt::fmtOffsetMargin(0, false), QString("0"));
|
QCOMPARE(fmt::fmtOffsetMargin(0, false), QString("00000000 "));
|
||||||
}
|
}
|
||||||
|
|
||||||
void testFmtOffsetMargin_continuation() {
|
void testFmtOffsetMargin_continuation() {
|
||||||
QCOMPARE(fmt::fmtOffsetMargin(0x10, true), QString(" \u00B7"));
|
QCOMPARE(fmt::fmtOffsetMargin(0x10, true), QString(" \u00B7 "));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testFmtOffsetMargin_kernelAddr() {
|
||||||
|
QCOMPARE(fmt::fmtOffsetMargin(0xFFFFF80012345678ULL, false, 16),
|
||||||
|
QString("FFFFF80012345678 "));
|
||||||
|
QCOMPARE(fmt::fmtOffsetMargin(0x10, false, 16),
|
||||||
|
QString("0000000000000010 "));
|
||||||
|
QCOMPARE(fmt::fmtOffsetMargin(0x10, false, 4),
|
||||||
|
QString("0010 "));
|
||||||
}
|
}
|
||||||
|
|
||||||
void testFmtStructHeader() {
|
void testFmtStructHeader() {
|
||||||
|
|||||||
Reference in New Issue
Block a user