Vertikale Linien zur Visualisierung von Einsprüngen implementiert

This commit is contained in:
2026-05-25 02:50:52 +02:00
parent 250783f63b
commit 5b81ef9c12
2 changed files with 121 additions and 0 deletions

View File

@@ -194,6 +194,126 @@ void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
}
}
void CodeEditor::paintEvent(QPaintEvent *event)
{
// Zuerst den normalen Editor-Inhalt zeichnen
QPlainTextEdit::paintEvent(event);
// Danach die Einrück-Führungslinien darüber legen
const int tabSize = m_settings->tabSize();
if (tabSize <= 0)
{
return;
}
QPainter painter(viewport());
// Farbe: subtil, passt zu Hell- und Dunkeltheme
QColor guideColor = palette().color(QPalette::Text);
guideColor.setAlpha(30);
painter.setPen(QPen(guideColor, 1, Qt::SolidLine));
const QFontMetrics fm(font());
const int spaceWidth = fm.horizontalAdvance(' ');
const int tabPixels = tabSize * spaceWidth;
if (tabPixels <= 0)
{
return;
}
// X-Startposition des Textes direkt aus dem Layout des ersten Blocks holen.
// Das ist der einzige zuverlässige Weg — Qt berücksichtigt intern Margins,
// Gutter und Frame-Abstände die sich nicht sauber manuell nachrechnen lassen.
int textOriginX = 0;
{
QTextBlock firstBlock = firstVisibleBlock();
if (!firstBlock.isValid())
{
firstBlock = document()->begin();
}
if (firstBlock.isValid())
{
const QRectF blockRect = blockBoundingGeometry(firstBlock)
.translated(contentOffset());
// Position des ersten Zeichens im Layout
const QTextLayout *layout = firstBlock.layout();
if (layout && layout->lineCount() > 0)
{
textOriginX = static_cast<int>(blockRect.left()
+ layout->lineAt(0).position().x());
}
else
{
textOriginX = static_cast<int>(blockRect.left());
}
}
}
// Horizontalen Scroll-Offset berücksichtigen
const int scrollX = horizontalScrollBar()->value();
// Sichtbaren Zeilenbereich bestimmen
QTextBlock block = firstVisibleBlock();
const int bottom = event->rect().bottom();
while (block.isValid())
{
const QRectF blockRect = blockBoundingGeometry(block).translated(contentOffset());
if (blockRect.top() > bottom)
{
break;
}
if (block.isVisible())
{
const QString text = block.text();
// Einrückungstiefe der Zeile zählen (Leerzeichen / Tabs)
int indentSpaces = 0;
for (const QChar &ch : text)
{
if (ch == ' ')
{
++indentSpaces;
}
else if (ch == '\t')
{
// Tab auffüllen auf nächsten Tab-Stop
indentSpaces = ((indentSpaces / tabSize) + 1) * tabSize;
}
else
{
break;
}
}
const int indentStops = indentSpaces / tabSize;
// Für jeden Einrückungslevel eine vertikale Linie zeichnen
for (int stop = 1; stop <= indentStops; ++stop)
{
const int xPixel = textOriginX + stop * tabPixels - scrollX;
// Nur im sichtbaren Bereich zeichnen
if (xPixel < lineNumberAreaWidth() || xPixel > viewport()->width())
{
continue;
}
const int y1 = static_cast<int>(blockRect.top());
const int y2 = static_cast<int>(blockRect.bottom());
painter.drawLine(xPixel, y1, xPixel, y2);
}
}
block = block.next();
}
}
void CodeEditor::resizeEvent(QResizeEvent *event)
{
QPlainTextEdit::resizeEvent(event);