Move Painter Methods to a new class TerminalPainter

This commit is contained in:
Gustavo Carneiro
2020-10-02 18:06:17 -03:00
committed by Kurt Hindenburg
parent 5c9c22fab3
commit 68ab8f55b4
7 changed files with 699 additions and 544 deletions

View File

@@ -179,6 +179,7 @@ set(konsoleprivate_SRCS ${windowadaptors_SRCS}
widgets/RenameTabWidget.cpp
widgets/TabTitleFormatButton.cpp
terminalDisplay/TerminalDisplay.cpp
terminalDisplay/TerminalPainter.cpp
widgets/TerminalDisplayAccessible.cpp
widgets/TerminalHeaderBar.cpp
widgets/ViewContainer.cpp

View File

@@ -77,7 +77,7 @@ uint ExtendedCharTable::createExtendedChar(const uint *unicodePoints, ushort len
const QList<Session *> sessionsList = SessionManager::instance()->sessions();
for (const Session *s : sessionsList) {
const QList<TerminalDisplay *> displayList = s->views();
for (const TerminalDisplay *display : displayList) {
for (const QPointer<TerminalDisplay> display : displayList) {
usedExtendedChars += display->screenWindow()->screen()->usedExtendedChars();
}
}

View File

@@ -38,7 +38,7 @@ void EscapeSequenceUrlFilter::process()
if (!_window->screenWindow() && _window->screenWindow()->screen()) {
return;
}
auto *sWindow = _window->screenWindow();
auto sWindow = _window->screenWindow();
const auto urls = sWindow->screen()->urlExtractor()->history();
for (const auto &escapedUrl : urls) {

View File

@@ -86,6 +86,8 @@
#include "../widgets/KonsolePrintManager.h"
#include "EscapeSequenceUrlExtractor.h"
#include "TerminalPainter.hpp"
using namespace Konsole;
#define REPCHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
@@ -126,10 +128,6 @@ inline int TerminalDisplay::loc(int x, int y) const {
IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White
*/
ScreenWindow* TerminalDisplay::screenWindow() const
{
return _screenWindow;
}
void TerminalDisplay::setScreenWindow(ScreenWindow* window)
{
// disconnect existing screen window if any
@@ -596,6 +594,8 @@ TerminalDisplay::TerminalDisplay(QWidget* parent)
#endif
connect(KonsoleSettings::self(), &KonsoleSettings::configChanged, this, &TerminalDisplay::setupHeaderVisibility);
_terminalPainter = new TerminalPainter(this);
}
TerminalDisplay::~TerminalDisplay()
@@ -610,6 +610,8 @@ TerminalDisplay::~TerminalDisplay()
_readOnlyMessageWidget = nullptr;
_outputSuspendedMessageWidget = nullptr;
delete _terminalPainter;
}
void TerminalDisplay::setupHeaderVisibility()
@@ -652,26 +654,6 @@ void TerminalDisplay::showDragTarget(const QPoint& cursorPos)
/* */
/* ------------------------------------------------------------------------- */
void TerminalDisplay::drawLineCharString(QPainter& painter, int x, int y, const QString& str,
const Character* attributes)
{
// only turn on anti-aliasing during this short time for the "text"
// for the normal text we have TextAntialiasing on demand on
// otherwise we have rendering artifacts
// set https://bugreports.qt.io/browse/QTBUG-66036
painter.setRenderHint(QPainter::Antialiasing, _antialiasText);
const bool useBoldPen = (attributes->rendition & RE_BOLD) != 0 && _boldIntense;
QRect cellRect = {x, y, _fontWidth, _fontHeight};
for (int i = 0 ; i < str.length(); i++) {
LineBlockCharacters::draw(painter, cellRect.translated(i * _fontWidth, 0), str[i],
useBoldPen);
}
painter.setRenderHint(QPainter::Antialiasing, false);
}
void TerminalDisplay::setKeyboardCursorShape(Enum::CursorShapeEnum shape)
{
_cursorShape = shape;
@@ -732,210 +714,6 @@ void TerminalDisplay::setWallpaper(const ColorSchemeWallpaper::Ptr &p)
_wallpaper = p;
}
void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting)
{
// the area of the widget showing the contents of the terminal display is drawn
// using the background color from the color scheme set with setColorTable()
//
// the area of the widget behind the scroll-bar is drawn using the background
// brush from the scroll-bar's palette, to give the effect of the scroll-bar
// being outside of the terminal display and visual consistency with other KDE
// applications.
if (useOpacitySetting && !_wallpaper->isNull() &&
_wallpaper->draw(painter, rect, _opacity)) {
} else if (qAlpha(_blendColor) < 0xff && useOpacitySetting) {
#if defined(Q_OS_MACOS)
// TODO - On MacOS, using CompositionMode doesn't work. Altering the
// transparency in the color scheme alters the brightness.
painter.fillRect(rect, backgroundColor);
#else
QColor color(backgroundColor);
color.setAlpha(qAlpha(_blendColor));
const QPainter::CompositionMode originalMode = painter.compositionMode();
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.fillRect(rect, color);
painter.setCompositionMode(originalMode);
#endif
} else {
painter.fillRect(rect, backgroundColor);
}
}
void TerminalDisplay::drawCursor(QPainter& painter,
const QRect& rect,
const QColor& foregroundColor,
const QColor& backgroundColor,
QColor& characterColor)
{
// don't draw cursor which is currently blinking
if (_cursorBlinking) {
return;
}
// shift rectangle top down one pixel to leave some space
// between top and bottom
QRectF cursorRect = rect.adjusted(0, 1, 0, 0);
QColor cursorColor = _cursorColor.isValid() ? _cursorColor : foregroundColor;
QPen pen(cursorColor);
// TODO: the relative pen width to draw the cursor is a bit hacky
// and set to 1/12 of the font width. Visually it seems to work at
// all scales but there must be better ways to do it
const qreal width = qMax(_fontWidth / 12.0, 1.0);
const qreal halfWidth = width / 2.0;
pen.setWidthF(width);
painter.setPen(pen);
if (_cursorShape == Enum::BlockCursor) {
// draw the cursor outline, adjusting the area so that it is draw entirely inside 'rect'
painter.drawRect(cursorRect.adjusted(halfWidth, halfWidth, -halfWidth, -halfWidth));
// draw the cursor body only when the widget has focus
if (hasFocus()) {
painter.fillRect(cursorRect, cursorColor);
// if the cursor text color is valid then use it to draw the character under the cursor,
// otherwise invert the color used to draw the text to ensure that the character at
// the cursor position is readable
characterColor = _cursorTextColor.isValid() ? _cursorTextColor : backgroundColor;
}
} else if (_cursorShape == Enum::UnderlineCursor) {
QLineF line(cursorRect.left() + halfWidth,
cursorRect.bottom() - halfWidth,
cursorRect.right() - halfWidth,
cursorRect.bottom() - halfWidth);
painter.drawLine(line);
} else if (_cursorShape == Enum::IBeamCursor) {
QLineF line(cursorRect.left() + halfWidth,
cursorRect.top() + halfWidth,
cursorRect.left() + halfWidth,
cursorRect.bottom() - halfWidth);
painter.drawLine(line);
}
}
void TerminalDisplay::drawCharacters(QPainter& painter,
const QRect& rect,
const QString& text,
const Character* style,
const QColor& characterColor)
{
// don't draw text which is currently blinking
if (_textBlinking && ((style->rendition & RE_BLINK) != 0)) {
return;
}
// don't draw concealed characters
if ((style->rendition & RE_CONCEAL) != 0) {
return;
}
static constexpr int MaxFontWeight = 99; // https://doc.qt.io/qt-5/qfont.html#Weight-enum
const int normalWeight = font().weight();
// +26 makes "bold" from "normal", "normal" from "light", etc. It is 26 instead of not 25 to prefer
// bolder weight when 25 falls in the middle between two weights. See QFont::Weight
const int boldWeight = qMin(normalWeight + 26, MaxFontWeight);
const auto isBold = [boldWeight](const QFont &font) { return font.weight() >= boldWeight; };
const bool useBold = (((style->rendition & RE_BOLD) != 0) && _boldIntense);
const bool useUnderline = ((style->rendition & RE_UNDERLINE) != 0) || font().underline();
const bool useItalic = ((style->rendition & RE_ITALIC) != 0) || font().italic();
const bool useStrikeOut = ((style->rendition & RE_STRIKEOUT) != 0) || font().strikeOut();
const bool useOverline = ((style->rendition & RE_OVERLINE) != 0) || font().overline();
QFont currentFont = painter.font();
if (isBold(currentFont) != useBold
|| currentFont.underline() != useUnderline
|| currentFont.italic() != useItalic
|| currentFont.strikeOut() != useStrikeOut
|| currentFont.overline() != useOverline) {
currentFont.setWeight(useBold ? boldWeight : normalWeight);
currentFont.setUnderline(useUnderline);
currentFont.setItalic(useItalic);
currentFont.setStrikeOut(useStrikeOut);
currentFont.setOverline(useOverline);
painter.setFont(currentFont);
}
// setup pen
const QColor foregroundColor = style->foregroundColor.color(_colorTable);
const QColor color = characterColor.isValid() ? characterColor : foregroundColor;
QPen pen = painter.pen();
if (pen.color() != color) {
pen.setColor(color);
painter.setPen(color);
}
const bool origClipping = painter.hasClipping();
const auto origClipRegion = painter.clipRegion();
painter.setClipRect(rect);
// draw text
if (isLineCharString(text) && !_useFontLineCharacters) {
drawLineCharString(painter, rect.x(), rect.y(), text, style);
} else {
// Force using LTR as the document layout for the terminal area, because
// there is no use cases for RTL emulator and RTL terminal application.
//
// This still allows RTL characters to be rendered in the RTL way.
painter.setLayoutDirection(Qt::LeftToRight);
if (_bidiEnabled) {
painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, text);
} else {
painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, LTR_OVERRIDE_CHAR + text);
}
}
painter.setClipRegion(origClipRegion);
painter.setClipping(origClipping);
}
void TerminalDisplay::drawTextFragment(QPainter& painter ,
const QRect& rect,
const QString& text,
const Character* style)
{
// setup painter
const QColor foregroundColor = style->foregroundColor.color(_colorTable);
const QColor backgroundColor = style->backgroundColor.color(_colorTable);
// draw background if different from the display's background color
if (backgroundColor != getBackgroundColor()) {
drawBackground(painter, rect, backgroundColor,
false /* do not use transparency */);
}
// draw cursor shape if the current character is the cursor
// this may alter the foreground and background colors
QColor characterColor;
if ((style->rendition & RE_CURSOR) != 0) {
drawCursor(painter, rect, foregroundColor, backgroundColor, characterColor);
}
// draw text
drawCharacters(painter, rect, text, style, characterColor);
}
void TerminalDisplay::drawPrinterFriendlyTextFragment(QPainter& painter,
const QRect& rect,
const QString& text,
const Character* style)
{
// Set the colors used to draw to black foreground and white
// background for printer friendly output when printing
Character print_style = *style;
print_style.foregroundColor = CharacterColor(COLOR_SPACE_RGB, 0x00000000);
print_style.backgroundColor = CharacterColor(COLOR_SPACE_RGB, 0xFFFFFFFF);
// draw text
drawCharacters(painter, rect, text, &print_style, QColor());
}
void TerminalDisplay::setRandomSeed(uint randomSeed)
{
_randomSeed = randomSeed;
@@ -1260,7 +1038,7 @@ void TerminalDisplay::updateImage()
}
if (_highlightScrolledLinesControl.enabled) {
dirtyRegion |= highlightScrolledLinesRegion(dirtyRegion.isEmpty());
dirtyRegion |= _terminalPainter->highlightScrolledLinesRegion(dirtyRegion.isEmpty());
}
_screenWindow->resetScrollCount();
@@ -1319,7 +1097,7 @@ void TerminalDisplay::paintEvent(QPaintEvent* pe)
for (const QRect &rect : region) {
dirtyImageRegion += widgetToImage(rect);
drawBackground(paint, rect, getBackgroundColor(), true /* use opacity setting */);
_terminalPainter->drawBackground(paint, rect, getBackgroundColor(), true /* use opacity setting */);
}
if (_displayVerticalLine) {
@@ -1335,11 +1113,11 @@ void TerminalDisplay::paintEvent(QPaintEvent* pe)
paint.setRenderHint(QPainter::TextAntialiasing, _antialiasText);
for (const QRect &rect : qAsConst(dirtyImageRegion)) {
drawContents(paint, rect);
_terminalPainter->drawContents(paint, rect);
}
drawCurrentResultRect(paint);
highlightScrolledLines(paint);
drawInputMethodPreeditString(paint, preeditRect());
_terminalPainter->drawCurrentResultRect(paint);
_terminalPainter->highlightScrolledLines(paint);
_terminalPainter->drawInputMethodPreeditString(paint, preeditRect());
paintFilters(paint);
const bool drawDimmed = _dimWhenInactive && !hasFocus();
@@ -1378,10 +1156,10 @@ void TerminalDisplay::printContent(QPainter& painter, bool friendly)
_printerFriendly = friendly;
if (!friendly) {
drawBackground(painter, rect, getBackgroundColor(),
_terminalPainter->drawBackground(painter, rect, getBackgroundColor(),
true /* use opacity setting */);
}
drawContents(painter, rect);
_terminalPainter->drawContents(painter, rect);
_printerFriendly = false;
setVTFont(savedFont);
}
@@ -1416,291 +1194,6 @@ void TerminalDisplay::paintFilters(QPainter& painter)
_filterChain->paint(this, painter);
}
static uint baseCodePoint(const Character &ch) {
if (ch.rendition & RE_EXTENDED_CHAR) {
// sequence of characters
ushort extendedCharLength = 0;
const uint* chars = ExtendedCharTable::instance.lookupExtendedChar(ch.character, extendedCharLength);
return chars[0];
} else {
return ch.character;
}
}
void TerminalDisplay::drawContents(QPainter& paint, const QRect& rect)
{
const int numberOfColumns = _usedColumns;
QVector<uint> univec;
univec.reserve(numberOfColumns);
for (int y = rect.y(); y <= rect.bottom(); y++) {
int x = rect.x();
if ((_image[loc(rect.x(), y)].character == 0u) && (x != 0)) {
x--; // Search for start of multi-column character
}
for (; x <= rect.right(); x++) {
int len = 1;
int p = 0;
// reset our buffer to the number of columns
int bufferSize = numberOfColumns;
univec.resize(bufferSize);
uint *disstrU = univec.data();
// is this a single character or a sequence of characters ?
if ((_image[loc(x, y)].rendition & RE_EXTENDED_CHAR) != 0) {
// sequence of characters
ushort extendedCharLength = 0;
const uint* chars = ExtendedCharTable::instance.lookupExtendedChar(_image[loc(x, y)].character, extendedCharLength);
if (chars != nullptr) {
Q_ASSERT(extendedCharLength > 1);
bufferSize += extendedCharLength - 1;
univec.resize(bufferSize);
disstrU = univec.data();
for (int index = 0 ; index < extendedCharLength ; index++) {
Q_ASSERT(p < bufferSize);
disstrU[p++] = chars[index];
}
}
} else {
// single character
const uint c = _image[loc(x, y)].character;
if (c != 0u) {
Q_ASSERT(p < bufferSize);
disstrU[p++] = c;
}
}
const bool lineDraw = LineBlockCharacters::canDraw(_image[loc(x, y)].character);
const bool doubleWidth = (_image[qMin(loc(x, y) + 1, _imageSize - 1)].character == 0);
const CharacterColor currentForeground = _image[loc(x, y)].foregroundColor;
const CharacterColor currentBackground = _image[loc(x, y)].backgroundColor;
const RenditionFlags currentRendition = _image[loc(x, y)].rendition;
const QChar::Script currentScript = QChar::script(baseCodePoint(_image[loc(x, y)]));
const auto isInsideDrawArea = [&](int column) { return column <= rect.right(); };
const auto hasSameColors = [&](int column) {
return _image[loc(column, y)].foregroundColor == currentForeground
&& _image[loc(column, y)].backgroundColor == currentBackground;
};
const auto hasSameRendition = [&](int column) {
return (_image[loc(column, y)].rendition & ~RE_EXTENDED_CHAR)
== (currentRendition & ~RE_EXTENDED_CHAR);
};
const auto hasSameWidth = [&](int column) {
const int characterLoc = qMin(loc(column, y) + 1, _imageSize - 1);
return (_image[characterLoc].character == 0) == doubleWidth;
};
const auto hasSameLineDrawStatus = [&](int column) {
return LineBlockCharacters::canDraw(_image[loc(column, y)].character)
== lineDraw;
};
const auto isSameScript = [&](int column) {
const QChar::Script script = QChar::script(baseCodePoint(_image[loc(column, y)]));
if (currentScript == QChar::Script_Common || script == QChar::Script_Common
|| currentScript == QChar::Script_Inherited || script == QChar::Script_Inherited) {
return true;
}
return currentScript == script;
};
const auto canBeGrouped = [&](int column) {
return _image[loc(column, y)].character <= 0x7e
|| (_image[loc(column, y)].rendition & RE_EXTENDED_CHAR)
|| (_bidiEnabled && !doubleWidth);
};
if (canBeGrouped(x)) {
while (isInsideDrawArea(x + len) && hasSameColors(x + len)
&& hasSameRendition(x + len) && hasSameWidth(x + len)
&& hasSameLineDrawStatus(x + len) && isSameScript(x + len)
&& canBeGrouped(x + len)) {
const uint c = _image[loc(x + len, y)].character;
if ((_image[loc(x + len, y)].rendition & RE_EXTENDED_CHAR) != 0) {
// sequence of characters
ushort extendedCharLength = 0;
const uint* chars = ExtendedCharTable::instance.lookupExtendedChar(c, extendedCharLength);
if (chars != nullptr) {
Q_ASSERT(extendedCharLength > 1);
bufferSize += extendedCharLength - 1;
univec.resize(bufferSize);
disstrU = univec.data();
for (int index = 0 ; index < extendedCharLength ; index++) {
Q_ASSERT(p < bufferSize);
disstrU[p++] = chars[index];
}
}
} else {
// single character
if (c != 0u) {
Q_ASSERT(p < bufferSize);
disstrU[p++] = c;
}
}
if (doubleWidth) { // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition
len++; // Skip trailing part of multi-column character
}
len++;
}
} else {
// Group spaces following any non-wide character with the character. This allows for
// rendering ambiguous characters with wide glyphs without clipping them.
while (!doubleWidth && isInsideDrawArea(x + len)
&& _image[loc(x + len, y)].character == ' ' && hasSameColors(x + len)
&& hasSameRendition(x + len)) {
// disstrU intentionally not modified - trailing spaces are meaningless
len++;
}
}
if ((x + len < _usedColumns) && (_image[loc(x + len, y)].character == 0u)) {
len++; // Adjust for trailing part of multi-column character
}
const bool save__fixedFont = _fixedFont;
if (lineDraw) {
_fixedFont = false;
}
if (doubleWidth) {
_fixedFont = false;
}
univec.resize(p);
// Create a text scaling matrix for double width and double height lines.
QMatrix textScale;
if (y < _lineProperties.size()) {
if ((_lineProperties[y] & LINE_DOUBLEWIDTH) != 0) {
textScale.scale(2, 1);
}
if ((_lineProperties[y] & LINE_DOUBLEHEIGHT) != 0) {
textScale.scale(1, 2);
}
}
//Apply text scaling matrix.
paint.setWorldTransform(QTransform(textScale), true);
//calculate the area in which the text will be drawn
QRect textArea = QRect(_contentRect.left() + contentsRect().left() + _fontWidth * x,
_contentRect.top() + contentsRect().top() + _fontHeight * y,
_fontWidth * len,
_fontHeight);
//move the calculated area to take account of scaling applied to the painter.
//the position of the area from the origin (0,0) is scaled
//by the opposite of whatever
//transformation has been applied to the painter. this ensures that
//painting does actually start from textArea.topLeft()
//(instead of textArea.topLeft() * painter-scale)
textArea.moveTopLeft(textScale.inverted().map(textArea.topLeft()));
QString unistr = QString::fromUcs4(univec.data(), univec.length());
//paint text fragment
if (_printerFriendly) {
drawPrinterFriendlyTextFragment(paint,
textArea,
unistr,
&_image[loc(x, y)]);
} else {
drawTextFragment(paint,
textArea,
unistr,
&_image[loc(x, y)]);
}
_fixedFont = save__fixedFont;
//reset back to single-width, single-height _lines
paint.setWorldTransform(QTransform(textScale.inverted()), true);
if (y < _lineProperties.size() - 1) {
//double-height _lines are represented by two adjacent _lines
//containing the same characters
//both _lines will have the LINE_DOUBLEHEIGHT attribute.
//If the current line has the LINE_DOUBLEHEIGHT attribute,
//we can therefore skip the next line
if ((_lineProperties[y] & LINE_DOUBLEHEIGHT) != 0) {
y++;
}
}
x += len - 1;
}
}
}
void TerminalDisplay::drawCurrentResultRect(QPainter& painter)
{
if(_screenWindow->currentResultLine() == -1) {
return;
}
_searchResultRect.setRect(0, _contentRect.top() + (_screenWindow->currentResultLine() - _screenWindow->currentLine()) * _fontHeight,
_columns * _fontWidth, _fontHeight);
painter.fillRect(_searchResultRect, QColor(0, 0, 255, 80));
}
void TerminalDisplay::highlightScrolledLines(QPainter& painter)
{
if (!_highlightScrolledLinesControl.enabled) {
return;
}
QColor color = QColor(_colorTable[Color4Index]);
color.setAlpha(_highlightScrolledLinesControl.timer->isActive() ? 255 : 150);
painter.fillRect(_highlightScrolledLinesControl.rect, color);
}
QRegion TerminalDisplay::highlightScrolledLinesRegion(bool nothingChanged)
{
QRegion dirtyRegion;
const int highlightLeftPosition = _scrollbarLocation == Enum::ScrollBarLeft ? _scrollBar->width() : 0;
int start = 0;
int nb_lines = abs(_screenWindow->scrollCount());
if (nb_lines > 0 && _scrollBar->maximum() > 0) {
QRect new_highlight;
bool addToCurrentHighlight = _highlightScrolledLinesControl.timer->isActive() &&
(_screenWindow->scrollCount() * _highlightScrolledLinesControl.previousScrollCount > 0);
if (addToCurrentHighlight) {
if (_screenWindow->scrollCount() > 0) {
start = -1 * (_highlightScrolledLinesControl.previousScrollCount + _screenWindow->scrollCount()) + _screenWindow->windowLines();
} else {
start = -1 * _highlightScrolledLinesControl.previousScrollCount;
}
_highlightScrolledLinesControl.previousScrollCount += _screenWindow->scrollCount();
} else {
start = _screenWindow->scrollCount() > 0 ? _screenWindow->windowLines() - nb_lines : 0;
_highlightScrolledLinesControl.previousScrollCount = _screenWindow->scrollCount();
}
new_highlight.setRect(highlightLeftPosition, _contentRect.top() + start * _fontHeight, HIGHLIGHT_SCROLLED_LINES_WIDTH, nb_lines * _fontHeight);
new_highlight.setTop(std::max(new_highlight.top(), _contentRect.top()));
new_highlight.setBottom(std::min(new_highlight.bottom(), _contentRect.bottom()));
if (!new_highlight.isValid()) {
new_highlight = QRect(0, 0, 0, 0);
}
dirtyRegion = new_highlight;
if (addToCurrentHighlight) {
_highlightScrolledLinesControl.rect |= new_highlight;
} else {
dirtyRegion |= _highlightScrolledLinesControl.rect;
_highlightScrolledLinesControl.rect = new_highlight;
}
_highlightScrolledLinesControl.timer->start();
} else if (!nothingChanged || _highlightScrolledLinesControl.needToClear) {
dirtyRegion = _highlightScrolledLinesControl.rect;
_highlightScrolledLinesControl.rect.setRect(0, 0, 0, 0);
_highlightScrolledLinesControl.needToClear = false;
}
return dirtyRegion;
}
void TerminalDisplay::highlightScrolledLinesEvent()
{
update(_highlightScrolledLinesControl.rect);
@@ -3380,26 +2873,6 @@ QRect TerminalDisplay::preeditRect() const
return stringRect.intersected(_contentRect);
}
void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect)
{
if (_inputMethodData.preeditString.isEmpty() || !isCursorOnDisplay()) {
return;
}
const QPoint cursorPos = cursorPosition();
QColor characterColor;
const QColor background = _colorTable[DEFAULT_BACK_COLOR];
const QColor foreground = _colorTable[DEFAULT_FORE_COLOR];
const Character* style = &_image[loc(cursorPos.x(), cursorPos.y())];
drawBackground(painter, rect, background, true);
drawCursor(painter, rect, foreground, background, characterColor);
drawCharacters(painter, rect, _inputMethodData.preeditString, style, characterColor);
_inputMethodData.previousPreeditRect = rect;
}
/* ------------------------------------------------------------------------- */
/* */
/* Keyboard */

View File

@@ -50,6 +50,7 @@ class QTimerEvent;
class KMessageWidget;
namespace Konsole {
class TerminalPainter;
class FilterChain;
class TerminalImageFilterChain;
class SessionController;
@@ -337,7 +338,10 @@ public:
*/
void setScreenWindow(ScreenWindow *window);
/** Returns the terminal screen section which is displayed in this widget. See setScreenWindow() */
ScreenWindow *screenWindow() const;
QPointer<ScreenWindow> screenWindow() const
{
return _screenWindow;
}
// Select the current line.
void selectCurrentLine();
@@ -393,6 +397,8 @@ public:
// a character which left edge is closest to the point.
void getCharacterPosition(const QPoint &widgetPoint, int &line, int &column, bool edge) const;
friend class TerminalPainter;
public Q_SLOTS:
/**
* Scrolls current ScreenWindow
@@ -921,6 +927,7 @@ private:
int _displayVerticalLineAtChar;
QKeySequence _peekPrimaryShortcut;
TerminalPainter *_terminalPainter;
};
}

View File

@@ -0,0 +1,568 @@
/*
Copyright 2020-2020 by Gustavo Carneiro <gcarneiroa@hotmail.com>
Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.
*/
// Own
#include "TerminalPainter.hpp"
// Konsole
#include "TerminalDisplay.h"
#include "ExtendedCharTable.h"
#include "LineBlockCharacters.h"
// Qt
#include <QRect>
#include <QColor>
#include <QRegion>
#include <QPainter>
#include <QString>
#include <QVector>
#include <QChar>
#include <QMatrix>
#include <QTransform>
#include <QTimer>
#include <QPen>
#include <QDebug>
const QChar LTR_OVERRIDE_CHAR(0x202D);
namespace Konsole
{
TerminalPainter::TerminalPainter(TerminalDisplay *display)
: _display(display)
{}
static inline bool isLineCharString(const QString &string)
{
if (string.length() == 0) {
return false;
}
return LineBlockCharacters::canDraw(string.at(0).unicode());
}
static int baseCodePoint(const Character &ch)
{
if (ch.rendition & RE_EXTENDED_CHAR) {
ushort extendedCharLength = 0;
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(ch.character, extendedCharLength);
return chars[0];
} else {
return ch.character;
}
}
void TerminalPainter::drawContents(QPainter &paint, const QRect &rect)
{
const int numberOfColumns = _display->_usedColumns;
QVector<uint> univec;
univec.reserve(numberOfColumns);
for (int y = rect.y(); y <= rect.bottom(); y++) {
int x = rect.x();
if ((_display->_image[loc(rect.x(), y)].character == 0u) && (x != 0)) {
x--; // Search for start of multi-column character
}
for (; x <= rect.right(); x++) {
int len = 1;
int p = 0;
// reset our buffer to the number of columns
int bufferSize = numberOfColumns;
univec.resize(bufferSize);
uint *disstrU = univec.data();
// is this a single character or a sequence of characters ?
if ((_display->_image[loc(x, y)].rendition & RE_EXTENDED_CHAR) != 0) {
// sequence of characters
ushort extendedCharLength = 0;
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(_display->_image[loc(x, y)].character, extendedCharLength);
if (chars != nullptr) {
Q_ASSERT(extendedCharLength > 1);
bufferSize += extendedCharLength - 1;
univec.resize(bufferSize);
disstrU = univec.data();
for (int index = 0; index < extendedCharLength; index++) {
Q_ASSERT(p < bufferSize);
disstrU[p++] = chars[index];
}
}
} else {
const uint c = _display->_image[loc(x, y)].character;
if (c != 0u) {
Q_ASSERT(p < bufferSize);
disstrU[p++] = c;
}
}
const bool lineDraw = LineBlockCharacters::canDraw(_display->_image[loc(x, y)].character);
const bool doubleWidth = (_display->_image[qMin(loc(x, y) + 1, _display->_imageSize - 1)].character == 0);
const CharacterColor currentForeground = _display->_image[loc(x, y)].foregroundColor;
const CharacterColor currentBackground = _display->_image[loc(x, y)].backgroundColor;
const RenditionFlags currentRendition = _display->_image[loc(x, y)].rendition;
const QChar::Script currentScript = QChar::script(baseCodePoint(_display->_image[loc(x, y)]));
const auto isInsideDrawArea = [&](int column) { return column <= rect.right(); };
const auto hasSameColors = [&](int column) {
return _display->_image[loc(column, y)].foregroundColor == currentForeground
&& _display->_image[loc(column, y)].backgroundColor == currentBackground;
};
const auto hasSameRendition = [&](int column) {
return (_display->_image[loc(column, y)].rendition & ~RE_EXTENDED_CHAR)
== (currentRendition & ~RE_EXTENDED_CHAR);
};
const auto hasSameWidth = [&](int column) {
const int characterLoc = qMin(loc(column, y) + 1, _display->_imageSize - 1);
return (_display->_image[characterLoc].character == 0) == doubleWidth;
};
const auto hasSameLineDrawStatus = [&](int column) {
return LineBlockCharacters::canDraw(_display->_image[loc(column, y)].character)
== lineDraw;
};
const auto isSameScript = [&](int column) {
const QChar::Script script = QChar::script(baseCodePoint(_display->_image[loc(column, y)]));
if (currentScript == QChar::Script_Common || script == QChar::Script_Common
|| currentScript == QChar::Script_Inherited || script == QChar::Script_Inherited) {
return true;
}
return currentScript == script;
};
const auto canBeGrouped = [&](int column) {
return _display->_image[loc(column, y)].character <= 0x7e
|| (_display->_image[loc(column, y)].rendition & RE_EXTENDED_CHAR)
|| (_display->_bidiEnabled && !doubleWidth);
};
if (canBeGrouped(x)) {
while (isInsideDrawArea(x + len) && hasSameColors(x + len)
&& hasSameRendition(x + len) && hasSameWidth(x + len)
&& hasSameLineDrawStatus(x + len) && isSameScript(x + len)
&& canBeGrouped(x + len)) {
const uint c = _display->_image[loc(x + len, y)].character;
if ((_display->_image[loc(x + len, y)].rendition & RE_EXTENDED_CHAR) != 0) {
// sequence of characters
ushort extendedCharLength = 0;
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(c, extendedCharLength);
if (chars != nullptr) {
Q_ASSERT(extendedCharLength > 1);
bufferSize += extendedCharLength - 1;
univec.resize(bufferSize);
disstrU = univec.data();
for (int index = 0; index < extendedCharLength; index++) {
Q_ASSERT(p < bufferSize);
disstrU[p++] = chars[index];
}
}
} else {
// single character
if (c != 0u) {
Q_ASSERT(p < bufferSize);
disstrU[p++] = c;
}
}
if (doubleWidth) {
len++;
}
len++;
}
} else {
// Group spaces following any non-wide character with the character. This allows for
// rendering ambiguous characters with wide glyphs without clipping them.
while (!doubleWidth && isInsideDrawArea(x + len)
&& _display->_image[loc(x + len, y)].character == ' ' && hasSameColors(x + len)
&& hasSameRendition(x + len)) {
// disstrU intentionally not modified - trailing spaces are meaningless
len++;
}
}
if ((x + len < _display->_usedColumns) && (_display->_image[loc(x + len, y)].character == 0u)) {
len++; // Adjust for trailing part of multi-column character
}
const bool save__fixedFont = _display->_fixedFont;
if (lineDraw) {
_display->_fixedFont = false;
}
if (doubleWidth) {
_display->_fixedFont = false;
}
univec.resize(p);
QMatrix textScale;
if (y < _display->_lineProperties.size()) {
if ((_display->_lineProperties[y] & LINE_DOUBLEWIDTH) != 0) {
textScale.scale(2, 1);
}
if ((_display->_lineProperties[y] & LINE_DOUBLEHEIGHT) != 0) {
textScale.scale(1, 2);
}
}
// Apply text scalling matrix
paint.setWorldTransform(QTransform(textScale), true);
// Calculate the area in which the text will be drawn
QRect textArea = QRect(_display->contentRect().left() + _display->contentsRect().left() + _display->fontWidth() * x,
_display->contentRect().top() + _display->contentsRect().top() + _display->fontHeight() * y,
_display->fontWidth() * len,
_display->fontHeight());
//move the calculated area to take account of scaling applied to the painter.
//the position of the area from the origin (0,0) is scaled
//by the opposite of whatever
//transformation has been applied to the painter. this ensures that
//painting does actually start from textArea.topLeft()
//(instead of textArea.topLeft() * painter-scale)
textArea.moveTopLeft(textScale.inverted().map(textArea.topLeft()));
QString unistr = QString::fromUcs4(univec.data(), univec.length());
// paint text fragment
if (_display->_printerFriendly) {
drawPrinterFriendlyTextFragment(paint,
textArea,
unistr,
&_display->_image[loc(x, y)]);
} else {
drawTextFragment(paint,
textArea,
unistr,
&_display->_image[loc(x, y)]);
}
_display->_fixedFont = save__fixedFont;
paint.setWorldTransform(QTransform(textScale.inverted()), true);
if (y < _display->_lineProperties.size() - 1) {
if ((_display->_lineProperties[y] & LINE_DOUBLEHEIGHT) != 0) {
y++;
}
}
x += len - 1;
}
}
}
void TerminalPainter::drawCurrentResultRect(QPainter &painter)
{
if (_display->_screenWindow->currentResultLine() == -1) {
return;
}
_display->_searchResultRect.setRect(0, _display->contentRect().top() + (_display->_screenWindow->currentResultLine() - _display->_screenWindow->currentLine()) * _display->fontHeight(),
_display->columns() * _display->fontWidth(), _display->fontHeight());
painter.fillRect(_display->_searchResultRect, QColor(0, 0, 255, 80));
}
void TerminalPainter::highlightScrolledLines(QPainter& painter)
{
if (!_display->_highlightScrolledLinesControl.enabled) {
return;
}
QColor color = QColor(_display->_colorTable[Color4Index]);
color.setAlpha(_display->_highlightScrolledLinesControl.timer->isActive() ? 255 : 150);
painter.fillRect(_display->_highlightScrolledLinesControl.rect, color);
}
QRegion TerminalPainter::highlightScrolledLinesRegion(bool nothingChanged)
{
QRegion dirtyRegion;
const int highlightLeftPosition = _display->scrollBarPosition() == Enum::ScrollBarLeft ? _display->_scrollBar->width() : 0;
int start = 0;
int nb_lines = abs(_display->_screenWindow->scrollCount());
if (nb_lines > 0 && _display->_scrollBar->maximum() > 0) {
QRect new_highlight;
bool addToCurrentHighlight = _display->_highlightScrolledLinesControl.timer->isActive() &&
(_display->_screenWindow->scrollCount() * _display->_highlightScrolledLinesControl.previousScrollCount > 0);
if (addToCurrentHighlight) {
if (_display->_screenWindow->scrollCount() > 0) {
start = -1 * (_display->_highlightScrolledLinesControl.previousScrollCount + _display->screenWindow()->scrollCount()) + _display->screenWindow()->windowLines();
} else {
start = -1 * _display->_highlightScrolledLinesControl.previousScrollCount;
}
_display->_highlightScrolledLinesControl.previousScrollCount += _display->_screenWindow->scrollCount();
} else {
start = _display->_screenWindow->scrollCount() > 0 ? _display->_screenWindow->windowLines() - nb_lines : 0;
_display->_highlightScrolledLinesControl.previousScrollCount = _display->_screenWindow->scrollCount();
}
new_highlight.setRect(highlightLeftPosition, _display->contentRect().top() + start * _display->fontHeight(), _display->HIGHLIGHT_SCROLLED_LINES_WIDTH, nb_lines * _display->fontHeight());
new_highlight.setTop(std::max(new_highlight.top(), _display->contentRect().top()));
new_highlight.setBottom(std::min(new_highlight.bottom(), _display->contentRect().bottom()));
if (!new_highlight.isValid()) {
new_highlight = QRect(0, 0, 0, 0);
}
if (addToCurrentHighlight) {
_display->_highlightScrolledLinesControl.rect |= new_highlight;
} else {
dirtyRegion |= _display->_highlightScrolledLinesControl.rect;
_display->_highlightScrolledLinesControl.rect = new_highlight;
}
_display->_highlightScrolledLinesControl.timer->start();
} else if (!nothingChanged || _display->_highlightScrolledLinesControl.needToClear) {
dirtyRegion = _display->_highlightScrolledLinesControl.rect;
_display->_highlightScrolledLinesControl.rect.setRect(0, 0, 0, 0);
_display->_highlightScrolledLinesControl.needToClear = false;
}
return dirtyRegion;
}
void TerminalPainter::drawTextFragment(QPainter &painter, const QRect &rect, const QString &text,
const Character *style)
{
// setup painter
const QColor foregroundColor = style->foregroundColor.color(_display->_colorTable);
const QColor backgroundColor = style->backgroundColor.color(_display->_colorTable);
if (backgroundColor != _display->_colorTable[DEFAULT_BACK_COLOR]) {
drawBackground(painter, rect, backgroundColor, false);
}
QColor characterColor;
if ((style->rendition & RE_CURSOR) != 0) {
drawCursor(painter, rect, foregroundColor, backgroundColor, characterColor);
}
// draw text
drawCharacters(painter, rect, text, style, characterColor);
}
void TerminalPainter::drawPrinterFriendlyTextFragment(QPainter &painter, const QRect &rect, const QString &text,
const Character *style)
{
Character print_style = *style;
print_style.foregroundColor = CharacterColor(COLOR_SPACE_RGB, 0x00000000);
print_style.backgroundColor = CharacterColor(COLOR_SPACE_RGB, 0xFFFFFFFF);
drawCharacters(painter, rect, text, &print_style, QColor());
}
void TerminalPainter::drawBackground(QPainter &painter, const QRect &rect, const QColor &backgroundColor,
bool useOpacitySetting)
{
if (useOpacitySetting && !_display->_wallpaper->isNull() &&
_display->_wallpaper->draw(painter, rect, _display->_opacity)) {
} else if (qAlpha(_display->_blendColor) < 0xff && useOpacitySetting) {
#if defined(Q_OS_MACOS)
// TODO: On MacOS, using CompositionMode doesn't work. Altering the
// transparency in the color scheme alters the brightness.
painter.fillRect(rect, backgroundColor);
#else
QColor color(backgroundColor);
color.setAlpha(qAlpha(_display->_blendColor));
const QPainter::CompositionMode originalMode = painter.compositionMode();
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.fillRect(rect, color);
painter.setCompositionMode(originalMode);
#endif
} else {
painter.fillRect(rect, backgroundColor);
}
}
void TerminalPainter::drawCursor(QPainter &painter, const QRect &rect, const QColor &foregroundColor,
const QColor &backgroundColor, QColor &characterColor)
{
if (_display->_cursorBlinking) {
return;
}
QRectF cursorRect = rect.adjusted(0, 1, 0, 0);
QColor cursorColor = _display->_cursorColor.isValid() ? _display->_cursorColor : foregroundColor;
QPen pen(cursorColor);
// TODO: the relative pen width to draw the cursor is a bit hacky
// and set to 1/12 of the font width. Visually it seems to work at
// all scales but there must be better ways to do it
const qreal width = qMax(_display->fontWidth() / 12.0, 1.0);
const qreal halfWidth = width / 2.0;
pen.setWidthF(width);
painter.setPen(pen);
if (_display->_cursorShape == Enum::BlockCursor) {
painter.drawRect(cursorRect.adjusted(halfWidth, halfWidth, -halfWidth, -halfWidth));
if (_display->hasFocus()) {
painter.fillRect(cursorRect, cursorColor);
characterColor = _display->_cursorTextColor.isValid() ? _display->_cursorTextColor : backgroundColor;
}
} else if (_display->_cursorShape == Enum::UnderlineCursor) {
QLineF line(cursorRect.left() + halfWidth,
cursorRect.bottom() - halfWidth,
cursorRect.right() - halfWidth,
cursorRect.bottom() - halfWidth);
painter.drawLine(line);
} else if (_display->_cursorShape == Enum::IBeamCursor) {
QLineF line(cursorRect.left() + halfWidth,
cursorRect.top() + halfWidth,
cursorRect.left() + halfWidth,
cursorRect.bottom() - halfWidth);
painter.drawLine(line);
}
}
void TerminalPainter::drawCharacters(QPainter &painter, const QRect &rect, const QString &text,
const Character *style, const QColor &characterColor)
{
if (_display->_textBlinking && ((style->rendition & RE_BLINK) != 0)) {
return;
}
if ((style->rendition & RE_CONCEAL) != 0) {
return;
}
static constexpr int MaxFontWeight = 99;
const int normalWeight = _display->font().weight();
const int boldWeight = qMin(normalWeight + 26, MaxFontWeight);
const auto isBold = [boldWeight](const QFont &font) { return font.weight() >= boldWeight; };
const bool useBold = (((style->rendition & RE_BOLD) != 0) && _display->_boldIntense);
const bool useUnderline = ((style->rendition & RE_UNDERLINE) != 0) || _display->font().underline();
const bool useItalic = ((style->rendition * RE_ITALIC) != 0) || _display->font().italic();
const bool useStrikeOut = ((style->rendition & RE_STRIKEOUT) != 0) || _display->font().strikeOut();
const bool useOverline = ((style->rendition & RE_OVERLINE) != 0) || _display->font().overline();
QFont currentFont = painter.font();
if (isBold(currentFont) != useBold
|| currentFont.underline() != useUnderline
|| currentFont.italic() != useItalic
|| currentFont.strikeOut() != useStrikeOut
|| currentFont.overline() != useOverline) {
currentFont.setWeight(useBold ? boldWeight : normalWeight);
currentFont.setUnderline(useUnderline);
currentFont.setItalic(useItalic);
currentFont.setStrikeOut(useStrikeOut);
currentFont.setOverline(useOverline);
painter.setFont(currentFont);
}
// setup pen
const QColor foregroundColor = style->foregroundColor.color(_display->_colorTable);
const QColor color = characterColor.isValid() ? characterColor : foregroundColor;
QPen pen = painter.pen();
if (pen.color() != color) {
pen.setColor(color);
painter.setPen(color);
}
const bool origClipping = painter.hasClipping();
const auto origClipRegion = painter.clipRegion();
painter.setClipRect(rect);
// draw text
if (isLineCharString(text) && !_display->_useFontLineCharacters) {
drawLineCharString(painter, rect.x(), rect.y(), text, style);
} else {
painter.setLayoutDirection(Qt::LeftToRight);
if (_display->_bidiEnabled) {
painter.drawText(rect.x(), rect.y() + _display->_fontAscent + _display->lineSpacing(), text);
} else {
painter.drawText(rect.x(), rect.y() + _display->_fontAscent + _display->lineSpacing(), LTR_OVERRIDE_CHAR + text);
}
}
painter.setClipRegion(origClipRegion);
painter.setClipping(origClipping);
}
void TerminalPainter::drawLineCharString(QPainter &painter, int x, int y, const QString &str,
const Character *attributes)
{
painter.setRenderHint(QPainter::Antialiasing, _display->_antialiasText);
const bool useBoldPen = (attributes->rendition & RE_BOLD) != 0 && _display->_boldIntense;
QRect cellRect = {x, y, _display->fontWidth(), _display->fontHeight()};
for (int i = 0; i < str.length(); i++) {
LineBlockCharacters::draw(painter, cellRect.translated(i * _display->fontWidth(), 0), str[i], useBoldPen);
}
painter.setRenderHint(QPainter::Antialiasing, false);
}
void TerminalPainter::drawInputMethodPreeditString(QPainter &painter, const QRect &rect)
{
if (_display->_inputMethodData.preeditString.isEmpty() || !isCursorOnDisplay()) {
return;
}
const QPoint cursorPos = cursorPosition();
QColor characterColor;
const QColor background = _display->_colorTable[DEFAULT_BACK_COLOR];
const QColor foreground = _display->_colorTable[DEFAULT_FORE_COLOR];
const Character *style = &_display->_image[loc(cursorPos.x(), cursorPos.y())];
drawBackground(painter, rect, background, true);
drawCursor(painter, rect, foreground, background, characterColor);
drawCharacters(painter, rect, _display->_inputMethodData.preeditString, style, characterColor);
_display->_inputMethodData.previousPreeditRect = rect;
}
inline int TerminalPainter::loc(int x, int y) const
{
if (y < 0 || y > _display->lines()) {
qDebug() << "Y: " << y << "Lines" << _display->lines();
}
if (x < 0 || x > _display->columns()) {
qDebug() << "X" << x << "Columns" << _display->columns();
}
Q_ASSERT(y >= 0 && y < _display->lines());
Q_ASSERT(x >= 0 && x < _display->columns());
x = qBound(0, x, _display->columns() - 1);
y = qBound(0, y, _display->lines() - 1);
return y * _display->columns() + x;
}
QPoint TerminalPainter::cursorPosition() const
{
if (!_display->_screenWindow.isNull()) {
return _display->screenWindow()->cursorPosition();
} else {
return {0, 0};
}
}
inline bool TerminalPainter::isCursorOnDisplay() const
{
return cursorPosition().x() < _display->columns() &&
cursorPosition().y() < _display->lines();
}
}

View File

@@ -0,0 +1,106 @@
/*
Copyright 2020-2020 by Gustavo Carneiro <gcarneiroa@hotmail.com>
Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.
*/
#ifndef TERMINALPAINTER_HPP
#define TERMINALPAINTER_HPP
// Qt
#include <QVector>
#include <QWidget>
#include <QPointer>
#include <QScrollBar>
// Konsole
#include "Character.h"
#include "ScreenWindow.h"
#include "Enumeration.h"
#include "colorscheme/ColorSchemeWallpaper.h"
class QRect;
class QColor;
class QRegion;
class QPainter;
class QString;
class QTimer;
namespace Konsole
{
class Character;
class TerminalDisplay;
class TerminalPainter
{
public:
explicit TerminalPainter(TerminalDisplay *display);
~TerminalPainter() = default;
// -- Drawing helpers --
// divides the part of the display specified by 'rect' into
// fragments according to their colors and styles and calls
// drawTextFragment() or drawPrinterFriendlyTextFragment()
// to draw the fragments
void drawContents(QPainter &paint, const QRect &rect);
// draw a transparent rectangle over the line of the current match
void drawCurrentResultRect(QPainter &painter);
// draw a thin highlight on the left of the screen for lines that have been scrolled into view
void highlightScrolledLines(QPainter& painter);
// compute which region need to be repainted for scrolled lines highlight
QRegion highlightScrolledLinesRegion(bool nothingChanged);
// draws a section of text, all the text in this section
// has a common color and style
void drawTextFragment(QPainter &painter, const QRect &rect, const QString &text,
const Character *style);
void drawPrinterFriendlyTextFragment(QPainter &painter, const QRect &rect, const QString &text,
const Character *style);
// draws the background for a text fragment
// if useOpacitySetting is true then the color's alpha value will be set to
// the display's transparency (set with setOpacity()), otherwise the background
// will be drawn fully opaque
void drawBackground(QPainter &painter, const QRect &rect, const QColor &backgroundColor,
bool useOpacitySetting);
// draws the cursor character
void drawCursor(QPainter &painter, const QRect &rect, const QColor &foregroundColor,
const QColor &backgroundColor, QColor &characterColor);
// draws the characters or line graphics in a text fragment
void drawCharacters(QPainter &painter, const QRect &rect, const QString &text,
const Character *style, const QColor &characterColor);
// draws a string of line graphics
void drawLineCharString(QPainter &painter, int x, int y, const QString &str,
const Character *attributes);
// draws the preedit string for input methods
void drawInputMethodPreeditString(QPainter &painter, const QRect &rect);
private:
int loc(int x, int y) const;
QPoint cursorPosition() const;
bool isCursorOnDisplay() const;
TerminalDisplay *_display;
};
}
#endif