mirror of
https://github.com/KDE/konsole.git
synced 2025-12-23 23:38:08 -05:00
Draw characters in exact positions
QT can't be made to draw monospaced text (if the font does not cooperate), so avoid combining characters, using a QPainter::drawText() call for each character. For bidi text support this change requires konsole to reorder and reshape the characters. This is done using the ICU library (which QT also uses). This change allows for some improvements related to text rendering: - More precise bidi reordering, which is no longer changed by characters' attributes and selection. - underlines drawn separately from the text, allowing for differing underline modes (double, curly, dashed, dotted, colored). - Overriding font for emoji characters. This commit fixes a few bugs and addresses a lot more: Feature requests: More standard conforming RTL and various underlines: BUG: 403729 BUG: 387811 Using non-monospace font: BUG: 416508 BUG: 452087 BUG: 425973 BUG: 430822 BUG: 442742 BUG: 441037 BUG: 430822 Emoji: BUG: 440070 CCBUG: 450017 CCBUG: 445846 CCBUG: 453086 Regression: devanagari rendering CCBUG: 381593 CCBUG: 451716
This commit is contained in:
committed by
Kurt Hindenburg
parent
4e875cdfdd
commit
76f879cd70
@@ -102,6 +102,8 @@ set_package_properties(KF5DocTools PROPERTIES DESCRIPTION
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
find_package(ICU 61.0 COMPONENTS uc i18n REQUIRED)
|
||||
|
||||
if(NOT APPLE)
|
||||
option(WITHOUT_X11 "Build without X11 integration (skips finding X11)" OFF)
|
||||
if (NOT WITHOUT_X11)
|
||||
|
||||
@@ -92,6 +92,7 @@ add_library(konsoleprivate_core STATIC ${konsoleprivate_core_SRCS})
|
||||
# Needed to link this static lib to shared libs
|
||||
set_target_properties(konsoleprivate_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(konsoleprivate_core ${konsole_LIBS})
|
||||
target_link_libraries(konsoleprivate_core ICU::uc ICU::i18n)
|
||||
|
||||
set(konsolehelpers_SRCS
|
||||
LabelsAligner.cpp
|
||||
|
||||
@@ -17,45 +17,62 @@
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
FontDialog::FontDialog(QWidget *parent)
|
||||
FontDialog::FontDialog(QWidget *parent, bool emoji, const QFont font)
|
||||
: QDialog(parent)
|
||||
, _fontChooser(nullptr)
|
||||
, _showAllFonts(nullptr)
|
||||
, _buttonBox(nullptr)
|
||||
, _emoji(emoji)
|
||||
{
|
||||
setWindowTitle(i18nc("@title:window", "Select font"));
|
||||
|
||||
KFontChooser::DisplayFlag onlyFixed = _emoji ? KFontChooser::FixedFontsOnly : KFontChooser::FixedFontsOnly;
|
||||
|
||||
#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 86, 0)
|
||||
_fontChooser = new KFontChooser(KFontChooser::FixedFontsOnly, this);
|
||||
_fontChooser = new KFontChooser(onlyFixed, this);
|
||||
if (_emoji) {
|
||||
QStringList list = KFontChooser::createFontList(0).filter(QStringLiteral("emoji"), Qt::CaseInsensitive);
|
||||
_fontChooser->setFont(font);
|
||||
_fontChooser->setFontListItems(KFontChooser::createFontList(0).filter(QStringLiteral("emoji"), Qt::CaseInsensitive));
|
||||
_fontChooser->setFont(font);
|
||||
}
|
||||
#else
|
||||
_fontChooser = new KFontChooser(this, KFontChooser::FixedFontsOnly);
|
||||
_fontChooser = new KFontChooser(this, onlyFixed);
|
||||
#endif
|
||||
|
||||
_showAllFonts = new QCheckBox(i18nc("@action:button", "Show all fonts"), this);
|
||||
_showAllFontsWarningButton = new QToolButton(this);
|
||||
_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
|
||||
|
||||
_fontChooser->setSampleText(
|
||||
QStringLiteral("0OQ 1Il!| 5S 8B rnm :; ,. \"'` ~-= ({[<>]})\n"
|
||||
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789\n"
|
||||
"abcdefghijklmnopqrstuvwxyz"));
|
||||
_showAllFontsWarningButton->setIcon(QIcon::fromTheme(QStringLiteral("emblem-warning")));
|
||||
_showAllFontsWarningButton->setAutoRaise(true);
|
||||
if (_emoji) {
|
||||
_fontChooser->setSampleText(
|
||||
/* clang-format off */
|
||||
QStringLiteral(" 🏴🤘🚬🌍🌎🌏🥆💣🗡🔫⚗️⚛️☢️☣️🌿🎱🏧💉💊🕴️📡🤻🦑🇦🇶👩🔬🪤🚱✊🏿🔬🧬🏴☠️🤽\n"
|
||||
"0123456789\n"
|
||||
"👆🏻 👆🏼 👆🏽 👆🏾 👆🏿 👨❤️👨 👨❤️💋👨 👩👩👧👧 👩🏻🤝👨🏿 👨👨👧👦\n"
|
||||
"🇧🇲 🇨🇭 🇨🇿 🇪🇺 🇬🇱 🇲🇬 🇲🇹 🇸🇿 🇿🇲"));
|
||||
/* clang-format on */
|
||||
} else {
|
||||
_fontChooser->setSampleText(
|
||||
QStringLiteral("0OQ 1Il!| 5S 8B rnm :; ,. \"'` ~-= ({[<>]})\n"
|
||||
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789\n"
|
||||
"abcdefghijklmnopqrstuvwxyz"));
|
||||
_showAllFontsWarningButton->setIcon(QIcon::fromTheme(QStringLiteral("emblem-warning")));
|
||||
_showAllFontsWarningButton->setAutoRaise(true);
|
||||
|
||||
connect(_fontChooser, &KFontChooser::fontSelected, this, &FontDialog::fontChanged);
|
||||
connect(_showAllFonts, &QCheckBox::toggled, this, [this](bool enable) {
|
||||
_fontChooser->setFont(_fontChooser->font(), !enable);
|
||||
});
|
||||
connect(_showAllFontsWarningButton, &QToolButton::clicked, this, [this](bool) {
|
||||
const QString message = i18nc("@info:status",
|
||||
"By its very nature, a terminal program requires font characters that are equal width (monospace). Any non monospaced "
|
||||
"font may cause display issues. This should not be necessary except in rare cases.");
|
||||
const QPoint pos = QPoint(_showAllFonts->width() / 2, _showAllFonts->height());
|
||||
QWhatsThis::showText(_showAllFonts->mapToGlobal(pos), message, _showAllFonts);
|
||||
});
|
||||
connect(_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(_showAllFonts, &QCheckBox::toggled, this, [this](bool enable) {
|
||||
_fontChooser->setFont(_fontChooser->font(), !enable);
|
||||
});
|
||||
connect(_showAllFontsWarningButton, &QToolButton::clicked, this, [this](bool) {
|
||||
const QString message =
|
||||
i18nc("@info:status",
|
||||
"By its very nature, a terminal program requires font characters that are equal width (monospace). Any non monospaced "
|
||||
"font may cause display issues. This should not be necessary except in rare cases.");
|
||||
const QPoint pos = QPoint(_showAllFonts->width() / 2, _showAllFonts->height());
|
||||
QWhatsThis::showText(_showAllFonts->mapToGlobal(pos), message, _showAllFonts);
|
||||
});
|
||||
}
|
||||
|
||||
auto *showAllFontsLayout = new QHBoxLayout();
|
||||
showAllFontsLayout->addWidget(_showAllFonts);
|
||||
@@ -66,11 +83,16 @@ FontDialog::FontDialog(QWidget *parent)
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->addWidget(_fontChooser, 1);
|
||||
layout->addLayout(showAllFontsLayout);
|
||||
if (!_emoji) {
|
||||
layout->addLayout(showAllFontsLayout);
|
||||
}
|
||||
layout->addWidget(_buttonBox);
|
||||
connect(_fontChooser, &KFontChooser::fontSelected, this, &FontDialog::fontChanged);
|
||||
connect(_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void FontDialog::setFont(const QFont &font)
|
||||
{
|
||||
_fontChooser->setFont(font, !_showAllFonts->isChecked());
|
||||
_fontChooser->setFont(font, !_showAllFonts->isChecked() && !_emoji);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class FontDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FontDialog(QWidget *parent = nullptr);
|
||||
explicit FontDialog(QWidget *parent = nullptr, bool emoji = false, const QFont font = QFont());
|
||||
|
||||
QFont font() const
|
||||
{
|
||||
@@ -37,6 +37,7 @@ private:
|
||||
QCheckBox *_showAllFonts;
|
||||
QToolButton *_showAllFontsWarningButton;
|
||||
QDialogButtonBox *_buttonBox;
|
||||
bool _emoji;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
150
src/Screen.cpp
150
src/Screen.cpp
@@ -70,7 +70,10 @@ Screen::Screen(int lines, int columns)
|
||||
, _cuY(0)
|
||||
, _currentForeground(CharacterColor())
|
||||
, _currentBackground(CharacterColor())
|
||||
, _currentRendition(DEFAULT_RENDITION)
|
||||
, _currentRendition({DEFAULT_RENDITION})
|
||||
, _ulColorQueueStart(0)
|
||||
, _ulColorQueueEnd(0)
|
||||
, _currentULColor(0)
|
||||
, _topMargin(0)
|
||||
, _bottomMargin(0)
|
||||
, _replMode(REPL_None)
|
||||
@@ -83,7 +86,7 @@ Screen::Screen(int lines, int columns)
|
||||
, _blockSelectionMode(false)
|
||||
, _effectiveForeground(CharacterColor())
|
||||
, _effectiveBackground(CharacterColor())
|
||||
, _effectiveRendition(DEFAULT_RENDITION)
|
||||
, _effectiveRendition({DEFAULT_RENDITION})
|
||||
, _lastPos(-1)
|
||||
, _lastDrawnChar(0)
|
||||
, _escapeSequenceUrlExtractor(nullptr)
|
||||
@@ -286,7 +289,7 @@ void Screen::deleteChars(int n)
|
||||
_screenLines[_cuY].remove(_cuX, n);
|
||||
|
||||
// Append space(s) with current attributes
|
||||
Character spaceWithCurrentAttrs(' ', _effectiveForeground, _effectiveBackground, _effectiveRendition, 0);
|
||||
Character spaceWithCurrentAttrs(' ', _effectiveForeground, _effectiveBackground, _effectiveRendition.all, 0);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
_screenLines[_cuY].append(spaceWithCurrentAttrs);
|
||||
@@ -646,7 +649,7 @@ void Screen::reverseRendition(Character &p) const
|
||||
void Screen::updateEffectiveRendition()
|
||||
{
|
||||
_effectiveRendition = _currentRendition;
|
||||
if ((_currentRendition & RE_REVERSE) != 0) {
|
||||
if ((_currentRendition.f.reverse) != 0) {
|
||||
_effectiveForeground = _currentBackground;
|
||||
_effectiveBackground = _currentForeground;
|
||||
} else {
|
||||
@@ -654,12 +657,12 @@ void Screen::updateEffectiveRendition()
|
||||
_effectiveBackground = _currentBackground;
|
||||
}
|
||||
|
||||
if ((_currentRendition & RE_BOLD) != 0) {
|
||||
if ((_currentRendition & RE_FAINT) == 0) {
|
||||
if ((_currentRendition.f.bold) != 0) {
|
||||
if ((_currentRendition.f.faint) == 0) {
|
||||
_effectiveForeground.setIntensive();
|
||||
}
|
||||
} else {
|
||||
if ((_currentRendition & RE_FAINT) != 0) {
|
||||
if ((_currentRendition.f.faint) != 0) {
|
||||
_effectiveForeground.setFaint();
|
||||
}
|
||||
}
|
||||
@@ -688,7 +691,7 @@ void Screen::copyFromHistory(Character *dest, int startLine, int count) const
|
||||
if (_selBegin != -1) {
|
||||
for (int column = 0; column < lastColumn; ++column) {
|
||||
if (isSelected(column, line)) {
|
||||
dest[destLineOffset + column].rendition |= RE_SELECTED;
|
||||
dest[destLineOffset + column].rendition.f.selected = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -720,7 +723,7 @@ void Screen::copyFromScreen(Character *dest, int startLine, int count) const
|
||||
if (_selBegin != -1) {
|
||||
for (int column = 0; column < lastColumn; ++column) {
|
||||
if (isSelected(column, line + historyLines)) {
|
||||
dest[destLineOffset + column].rendition |= RE_SELECTED;
|
||||
dest[destLineOffset + column].rendition.f.selected = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -761,7 +764,7 @@ void Screen::getImage(Character *dest, int size, int startLine, int endLine) con
|
||||
// mark the character at the current cursor position
|
||||
int cursorIndex = loc(visX, _cuY + linesInHistoryBuffer);
|
||||
if (getMode(MODE_Cursor) && cursorIndex < _columns * mergedLines) {
|
||||
dest[cursorIndex].rendition |= RE_CURSOR;
|
||||
dest[cursorIndex].rendition.f.cursor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -947,13 +950,14 @@ void Screen::displayCharacter(uint c)
|
||||
// putting the cursor one right to the last column of the screen.
|
||||
|
||||
int w = Character::width(c);
|
||||
|
||||
const QChar::Category category = QChar::category(c);
|
||||
if (w < 0) {
|
||||
// Non-printable character
|
||||
return;
|
||||
} else if (w == 0) {
|
||||
const QChar::Category category = QChar::category(c);
|
||||
if (category != QChar::Mark_NonSpacing && category != QChar::Letter_Other && category != QChar::Other_Format) {
|
||||
} else if (category == QChar::Mark_SpacingCombining || w == 0 || Character::emoji(c) || c == 0x20E3) {
|
||||
bool emoji = Character::emoji(c);
|
||||
if (category != QChar::Mark_SpacingCombining && category != QChar::Mark_NonSpacing && category != QChar::Letter_Other && category != QChar::Other_Format
|
||||
&& !emoji && c != 0x20E3) {
|
||||
return;
|
||||
}
|
||||
// Find previous "real character" to try to combine with
|
||||
@@ -980,6 +984,9 @@ void Screen::displayCharacter(uint c)
|
||||
} while (_screenLines.at(charToCombineWithY).at(charToCombineWithX).isRightHalfOfDoubleWide());
|
||||
|
||||
if (!previousChar) {
|
||||
if (emoji) {
|
||||
goto notcombine;
|
||||
}
|
||||
if (!Hangul::isHangul(c)) {
|
||||
return;
|
||||
} else {
|
||||
@@ -990,24 +997,88 @@ void Screen::displayCharacter(uint c)
|
||||
|
||||
Character ¤tChar = _screenLines[charToCombineWithY][charToCombineWithX];
|
||||
|
||||
if (c == 0x20E3) {
|
||||
// Combining Enclosing Keycap - only combines with presentation mode #,*,0-9
|
||||
if ((currentChar.character != 0x23 && currentChar.character != 0x2A && (currentChar.character < '0' || currentChar.character > '9'))
|
||||
|| (currentChar.flags & EF_EMOJI_REPRESENTATION) == 0) {
|
||||
// Is this the right thing TODO?
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (c == 0xFE0F) {
|
||||
// Emoji presentation - should not be included
|
||||
currentChar.flags |= EF_EMOJI_REPRESENTATION;
|
||||
return;
|
||||
}
|
||||
if (c == 0x200D) {
|
||||
// Zero width joiner
|
||||
currentChar.flags |= EF_EMOJI_REPRESENTATION;
|
||||
}
|
||||
if (c >= 0xE0020 && c <= 0xE007F) {
|
||||
// Tags - used for some flags
|
||||
currentChar.flags |= EF_EMOJI_REPRESENTATION;
|
||||
}
|
||||
|
||||
if (c >= 0x1f3fb && c <= 0x1f3ff) {
|
||||
// Emoji modifier Fitzpatrick - changes skin color
|
||||
uint currentUcs4 = currentChar.character;
|
||||
if (currentChar.rendition.f.extended == 1) {
|
||||
ushort extendedCharLength;
|
||||
const uint *oldChars = ExtendedCharTable::instance.lookupExtendedChar(currentChar.character, extendedCharLength);
|
||||
currentUcs4 = oldChars[extendedCharLength - 1];
|
||||
}
|
||||
if (currentUcs4 < 0x261d || (currentUcs4 > 0x270d && currentUcs4 < 0x1efff) || currentUcs4 > 0x1faff) {
|
||||
goto notcombine;
|
||||
}
|
||||
currentChar.flags |= EF_EMOJI_REPRESENTATION;
|
||||
} else if (c >= 0x1f1e6 && c <= 0x1f1ff) {
|
||||
// Regional indicators - flag components
|
||||
if (currentChar.rendition.f.extended == 1 || currentChar.character < 0x1f1e6 || currentChar.character > 0x1f1ff) {
|
||||
goto notcombine;
|
||||
}
|
||||
currentChar.flags |= EF_EMOJI_REPRESENTATION;
|
||||
} else if (emoji) {
|
||||
if (currentChar.rendition.f.extended == 0) {
|
||||
goto notcombine;
|
||||
}
|
||||
ushort extendedCharLength;
|
||||
const uint *oldChars = ExtendedCharTable::instance.lookupExtendedChar(currentChar.character, extendedCharLength);
|
||||
if (oldChars[extendedCharLength - 1] != 0x200d) {
|
||||
goto notcombine;
|
||||
}
|
||||
}
|
||||
|
||||
if (Hangul::isHangul(c) && !Hangul::combinesWith(currentChar, c)) {
|
||||
w = 2;
|
||||
goto notcombine;
|
||||
}
|
||||
|
||||
if ((currentChar.rendition & RE_EXTENDED_CHAR) == 0) {
|
||||
if (currentChar.rendition.f.extended == 0) {
|
||||
const uint chars[2] = {currentChar.character, c};
|
||||
currentChar.rendition |= RE_EXTENDED_CHAR;
|
||||
currentChar.rendition.f.extended = 1;
|
||||
auto extChars = [this]() {
|
||||
return usedExtendedChars();
|
||||
};
|
||||
currentChar.character = ExtendedCharTable::instance.createExtendedChar(chars, 2, extChars);
|
||||
if (category == QChar::Mark_SpacingCombining) {
|
||||
// ensure current line vector has enough elements
|
||||
if (_screenLines[_cuY].size() < _cuX + w) {
|
||||
_screenLines[_cuY].resize(_cuX + w);
|
||||
}
|
||||
Character &ch = _screenLines[_cuY][_cuX];
|
||||
ch.setRightHalfOfDoubleWide();
|
||||
ch.foregroundColor = _effectiveForeground;
|
||||
ch.backgroundColor = _effectiveBackground;
|
||||
ch.rendition = _effectiveRendition;
|
||||
ch.flags = setRepl(EF_UNREAL, _replMode);
|
||||
_cuX += 1;
|
||||
}
|
||||
} else {
|
||||
ushort extendedCharLength;
|
||||
const uint *oldChars = ExtendedCharTable::instance.lookupExtendedChar(currentChar.character, extendedCharLength);
|
||||
Q_ASSERT(extendedCharLength > 1);
|
||||
Q_ASSERT(oldChars);
|
||||
if (((oldChars) != nullptr) && extendedCharLength < 8) {
|
||||
Q_ASSERT(extendedCharLength > 1);
|
||||
if (((oldChars) != nullptr) && extendedCharLength < 10) {
|
||||
Q_ASSERT(extendedCharLength < 65535); // redundant due to above check
|
||||
auto chars = std::make_unique<uint[]>(extendedCharLength + 1);
|
||||
std::copy_n(oldChars, extendedCharLength, chars.get());
|
||||
@@ -1051,7 +1122,10 @@ notcombine:
|
||||
currentChar.foregroundColor = _effectiveForeground;
|
||||
currentChar.backgroundColor = _effectiveBackground;
|
||||
currentChar.rendition = _effectiveRendition;
|
||||
currentChar.flags = setRepl(EF_REAL, _replMode);
|
||||
currentChar.flags = setRepl(EF_REAL, _replMode) | SetULColor(0, _currentULColor);
|
||||
if (Character::emojiPresentation(c)) {
|
||||
currentChar.flags |= EF_EMOJI_REPRESENTATION;
|
||||
}
|
||||
|
||||
_lastDrawnChar = c;
|
||||
|
||||
@@ -1416,13 +1490,19 @@ void Screen::clearEntireLine()
|
||||
|
||||
void Screen::setRendition(RenditionFlags rendition)
|
||||
{
|
||||
_currentRendition |= rendition;
|
||||
_currentRendition.all |= rendition;
|
||||
updateEffectiveRendition();
|
||||
}
|
||||
|
||||
void Screen::setUnderlineType(int type)
|
||||
{
|
||||
_currentRendition.f.underline = type;
|
||||
updateEffectiveRendition();
|
||||
}
|
||||
|
||||
void Screen::resetRendition(RenditionFlags rendition)
|
||||
{
|
||||
_currentRendition &= ~rendition;
|
||||
_currentRendition.all &= ~rendition;
|
||||
updateEffectiveRendition();
|
||||
}
|
||||
|
||||
@@ -1430,7 +1510,7 @@ void Screen::setDefaultRendition()
|
||||
{
|
||||
setForeColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR);
|
||||
setBackColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR);
|
||||
_currentRendition = DEFAULT_RENDITION;
|
||||
_currentRendition = {DEFAULT_RENDITION};
|
||||
updateEffectiveRendition();
|
||||
}
|
||||
|
||||
@@ -1456,6 +1536,31 @@ void Screen::setBackColor(int space, int color)
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::setULColor(int space, int color)
|
||||
{
|
||||
CharacterColor col(quint8(space), color);
|
||||
if (col.isValid()) {
|
||||
int end = _ulColorQueueEnd;
|
||||
if (end < _ulColorQueueStart) {
|
||||
end += 15;
|
||||
}
|
||||
for (int i = _ulColorQueueStart; i < end; i++) {
|
||||
if (col == _ulColors[i % 15]) {
|
||||
_currentULColor = i % 15 + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ulColors[_ulColorQueueEnd] = col;
|
||||
_currentULColor = _ulColorQueueEnd + 1;
|
||||
_ulColorQueueEnd = (_ulColorQueueEnd + 1) % 15;
|
||||
if (_ulColorQueueEnd == _ulColorQueueStart) {
|
||||
_ulColorQueueStart = (_ulColorQueueStart + 1) % 15;
|
||||
}
|
||||
} else {
|
||||
_currentULColor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::clearSelection()
|
||||
{
|
||||
_selBottomRight = -1;
|
||||
@@ -1920,7 +2025,6 @@ int Screen::copyLineToStream(int line,
|
||||
int p = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
Character c = characterBuffer[spacesCount + i];
|
||||
// fprintf(stderr, "copy %i %i %i\n", i, c.character, c.flags);
|
||||
if (((options & ExcludePrompt) != 0 && (c.flags & EF_REPL) == EF_REPL_PROMPT)
|
||||
|| ((options & ExcludeInput) != 0 && (c.flags & EF_REPL) == EF_REPL_INPUT)
|
||||
|| ((options & ExcludeOutput) != 0 && (c.flags & EF_REPL) == EF_REPL_OUTPUT)) {
|
||||
|
||||
23
src/Screen.h
23
src/Screen.h
@@ -314,6 +314,7 @@ public:
|
||||
* @see Character::rendition
|
||||
*/
|
||||
void setRendition(RenditionFlags rendition);
|
||||
void setUnderlineType(int type);
|
||||
/**
|
||||
* Disables the given @p rendition flag. Rendition flags control the appearance
|
||||
* of characters on the screen.
|
||||
@@ -346,6 +347,13 @@ public:
|
||||
*/
|
||||
void setDefaultRendition();
|
||||
|
||||
void setULColor(int space, int color);
|
||||
|
||||
CharacterColor const *ulColorTable() const
|
||||
{
|
||||
return _ulColors;
|
||||
};
|
||||
|
||||
/** Returns the column which the cursor is positioned at. */
|
||||
int getCursorX() const;
|
||||
/** Returns the line which the cursor is positioned on. */
|
||||
@@ -642,7 +650,7 @@ public:
|
||||
for (int i = 0; i < _lines; ++i) {
|
||||
const ImageLine &il = _screenLines[i];
|
||||
for (int j = 0; j < il.length(); ++j) {
|
||||
if (il[j].rendition & RE_EXTENDED_CHAR) {
|
||||
if (il[j].rendition.f.extended) {
|
||||
result << il[j].character;
|
||||
}
|
||||
}
|
||||
@@ -800,7 +808,12 @@ private:
|
||||
// cursor color and rendition info
|
||||
CharacterColor _currentForeground;
|
||||
CharacterColor _currentBackground;
|
||||
RenditionFlags _currentRendition;
|
||||
RenditionFlagsC _currentRendition;
|
||||
|
||||
CharacterColor _ulColors[15];
|
||||
int _ulColorQueueStart;
|
||||
int _ulColorQueueEnd;
|
||||
int _currentULColor;
|
||||
|
||||
// margins ----------------
|
||||
int _topMargin;
|
||||
@@ -829,7 +842,7 @@ private:
|
||||
// effective colors and rendition ------------
|
||||
CharacterColor _effectiveForeground; // These are derived from
|
||||
CharacterColor _effectiveBackground; // the cu_* variables above
|
||||
RenditionFlags _effectiveRendition; // to speed up operation
|
||||
RenditionFlagsC _effectiveRendition; // to speed up operation
|
||||
|
||||
class SavedState
|
||||
{
|
||||
@@ -838,7 +851,7 @@ private:
|
||||
: cursorColumn(0)
|
||||
, cursorLine(0)
|
||||
, originMode(0)
|
||||
, rendition(0)
|
||||
, rendition({0})
|
||||
, foreground(CharacterColor())
|
||||
, background(CharacterColor())
|
||||
{
|
||||
@@ -847,7 +860,7 @@ private:
|
||||
int cursorColumn;
|
||||
int cursorLine;
|
||||
int originMode;
|
||||
RenditionFlags rendition;
|
||||
RenditionFlagsC rendition;
|
||||
CharacterColor foreground;
|
||||
CharacterColor background;
|
||||
};
|
||||
|
||||
@@ -511,7 +511,7 @@ void Vt102Emulation::csi_dispatch(const uint cc)
|
||||
processToken(token_csi_pq(cc), 0, 0);
|
||||
} else if (tokenBufferPos != 0 && tokenBuffer[0] == '>') {
|
||||
processToken(token_csi_pg(cc), 0, 0);
|
||||
} else if (cc == 'm' && !params.sub[i].count && params.count - i >= 4 && (params.value[i] == 38 || params.value[i] == 48)
|
||||
} else if (cc == 'm' && !params.sub[i].count && params.count - i >= 4 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
|
||||
&& params.value[i + 1] == 2) {
|
||||
// ESC[ ... 48;2;<red>;<green>;<blue> ... m -or- ESC[ ... 38;2;<red>;<green>;<blue> ... m
|
||||
i += 2;
|
||||
@@ -519,22 +519,27 @@ void Vt102Emulation::csi_dispatch(const uint cc)
|
||||
COLOR_SPACE_RGB,
|
||||
(params.value[i] << 16) | (params.value[i + 1] << 8) | params.value[i + 2]);
|
||||
i += 2;
|
||||
} else if (cc == 'm' && params.sub[i].count >= 5 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 2) {
|
||||
} else if (cc == 'm' && params.sub[i].count >= 5 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
|
||||
&& params.sub[i].value[1] == 2) {
|
||||
// ESC[ ... 48:2:<id>:<red>:<green>:<blue> ... m -or- ESC[ ... 38:2:<id>:<red>:<green>:<blue> ... m
|
||||
processToken(token_csi_ps(cc, params.value[i]),
|
||||
COLOR_SPACE_RGB,
|
||||
(params.sub[i].value[3] << 16) | (params.sub[i].value[4] << 8) | params.sub[i].value[5]);
|
||||
} else if (cc == 'm' && params.sub[i].count == 4 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 2) {
|
||||
} else if (cc == 'm' && params.sub[i].count == 4 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
|
||||
&& params.sub[i].value[1] == 2) {
|
||||
// ESC[ ... 48:2:<red>:<green>:<blue> ... m -or- ESC[ ... 38:2:<red>:<green>:<blue> ... m
|
||||
processToken(token_csi_ps(cc, params.value[i]),
|
||||
COLOR_SPACE_RGB,
|
||||
(params.sub[i].value[2] << 16) | (params.sub[i].value[3] << 8) | params.sub[i].value[4]);
|
||||
} else if (cc == 'm' && !params.sub[i].count && params.count - i >= 2 && (params.value[i] == 38 || params.value[i] == 48)
|
||||
} else if (cc == 'm' && params.sub[i].count == 1 && params.value[i] == 4) {
|
||||
processToken(token_csi_ps(cc, params.value[i]), params.sub[i].value[1], 1);
|
||||
} else if (cc == 'm' && !params.sub[i].count && params.count - i >= 2 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
|
||||
&& params.value[i + 1] == 5) {
|
||||
// ESC[ ... 48;5;<index> ... m -or- ESC[ ... 38;5;<index> ... m
|
||||
i += 2;
|
||||
processToken(token_csi_ps(cc, params.value[i - 2]), COLOR_SPACE_256, params.value[i]);
|
||||
} else if (cc == 'm' && params.sub[i].count >= 2 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 5) {
|
||||
} else if (cc == 'm' && params.sub[i].count >= 2 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
|
||||
&& params.sub[i].value[1] == 5) {
|
||||
// ESC[ ... 48:5:<index> ... m -or- ESC[ ... 38:5:<index> ... m
|
||||
processToken(token_csi_ps(cc, params.value[i]), COLOR_SPACE_256, params.sub[i].value[2]);
|
||||
} else if (_nIntermediate == 0) {
|
||||
@@ -985,16 +990,16 @@ void Vt102Emulation::processChecksumRequest([[maybe_unused]] int crargc, int cra
|
||||
// XXX: Apparently, VT520 uses 0x00 for uninitialized cells, konsole can't tell uninitialized cells from spaces
|
||||
Character c = image[y * _currentScreen->getColumns() + x];
|
||||
|
||||
if (c.rendition & RE_CONCEAL) {
|
||||
if (c.rendition.f.conceal) {
|
||||
checksum += 0x20; // don't reveal secrets
|
||||
} else {
|
||||
checksum += c.character;
|
||||
}
|
||||
|
||||
checksum += (c.rendition & RE_BOLD) / RE_BOLD * 0x80;
|
||||
checksum += (c.rendition & RE_BLINK) / RE_BLINK * 0x40;
|
||||
checksum += (c.rendition & RE_REVERSE) / RE_REVERSE * 0x20;
|
||||
checksum += (c.rendition & RE_UNDERLINE) / RE_UNDERLINE * 0x10;
|
||||
checksum += c.rendition.f.bold * 0x80;
|
||||
checksum += c.rendition.f.blink * 0x40;
|
||||
checksum += c.rendition.f.reverse * 0x20;
|
||||
checksum += !!(c.rendition.all & RE_UNDERLINE_MASK) * 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1353,7 +1358,13 @@ void Vt102Emulation::processToken(int token, int p, int q)
|
||||
case token_csi_ps('m', 1) : _currentScreen-> setRendition (RE_BOLD ); break; //VT100
|
||||
case token_csi_ps('m', 2) : _currentScreen-> setRendition (RE_FAINT ); break;
|
||||
case token_csi_ps('m', 3) : _currentScreen-> setRendition (RE_ITALIC ); break; //VT100
|
||||
case token_csi_ps('m', 4) : _currentScreen-> setRendition (RE_UNDERLINE); break; //VT100
|
||||
case token_csi_ps('m', 4) :
|
||||
if (q == 1) {
|
||||
_currentScreen->setUnderlineType(p);
|
||||
} else {
|
||||
_currentScreen->setUnderlineType(RE_UNDERLINE);
|
||||
}
|
||||
break; //VT100
|
||||
case token_csi_ps('m', 5) : _currentScreen-> setRendition (RE_BLINK ); break; //VT100
|
||||
case token_csi_ps('m', 7) : _currentScreen-> setRendition (RE_REVERSE ); break;
|
||||
case token_csi_ps('m', 8) : _currentScreen-> setRendition (RE_CONCEAL ); break;
|
||||
@@ -1362,11 +1373,11 @@ void Vt102Emulation::processToken(int token, int p, int q)
|
||||
case token_csi_ps('m', 10) : /* IGNORED: mapping related */ break; //LINUX
|
||||
case token_csi_ps('m', 11) : /* IGNORED: mapping related */ break; //LINUX
|
||||
case token_csi_ps('m', 12) : /* IGNORED: mapping related */ break; //LINUX
|
||||
case token_csi_ps('m', 21) : _currentScreen->resetRendition (RE_BOLD ); break;
|
||||
case token_csi_ps('m', 21) : _currentScreen->setUnderlineType(RE_UNDERLINE_DOUBLE); break;
|
||||
case token_csi_ps('m', 22) : _currentScreen->resetRendition (RE_BOLD );
|
||||
_currentScreen->resetRendition (RE_FAINT ); break;
|
||||
case token_csi_ps('m', 23) : _currentScreen->resetRendition (RE_ITALIC ); break; //VT100
|
||||
case token_csi_ps('m', 24) : _currentScreen->resetRendition (RE_UNDERLINE); break;
|
||||
case token_csi_ps('m', 24) : _currentScreen->resetRendition (RE_UNDERLINE_MASK); break;
|
||||
case token_csi_ps('m', 25) : _currentScreen->resetRendition (RE_BLINK ); break;
|
||||
case token_csi_ps('m', 27) : _currentScreen->resetRendition (RE_REVERSE ); break;
|
||||
case token_csi_ps('m', 28) : _currentScreen->resetRendition (RE_CONCEAL ); break;
|
||||
@@ -1399,6 +1410,10 @@ void Vt102Emulation::processToken(int token, int p, int q)
|
||||
|
||||
case token_csi_ps('m', 49) : _currentScreen->setBackColor (COLOR_SPACE_DEFAULT, 1); break;
|
||||
|
||||
case token_csi_ps('m', 58) : _currentScreen->setULColor (p, q); break;
|
||||
|
||||
case token_csi_ps('m', 59) : _currentScreen->setULColor (COLOR_SPACE_UNDEFINED, 0); break;
|
||||
|
||||
case token_csi_ps('m', 90) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 8); break;
|
||||
case token_csi_ps('m', 91) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 9); break;
|
||||
case token_csi_ps('m', 92) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 10); break;
|
||||
|
||||
@@ -10,33 +10,4 @@
|
||||
#include <QTest>
|
||||
#include <cstdint>
|
||||
|
||||
void Konsole::CharacterTest::testCanBeGrouped()
|
||||
{
|
||||
// Test for Capital Latin
|
||||
for (uint32_t c = U'A'; c <= U'Z'; ++c) {
|
||||
Konsole::Character uppercase_latin_char(c);
|
||||
const bool res = uppercase_latin_char.canBeGrouped(false, false);
|
||||
QVERIFY(res);
|
||||
}
|
||||
|
||||
// Test for Non Capital Latin
|
||||
for (uint32_t c = U'a'; c <= U'z'; ++c) {
|
||||
Konsole::Character lowercase_latin_char(c);
|
||||
const bool res = lowercase_latin_char.canBeGrouped(false, false);
|
||||
QVERIFY(res);
|
||||
}
|
||||
|
||||
// Test for Braille
|
||||
for (uint32_t c = 0x2800; c <= 0x28ff; ++c) {
|
||||
Konsole::Character braille_char(c);
|
||||
const bool res = braille_char.canBeGrouped(false, false);
|
||||
QCOMPARE(res, false);
|
||||
|
||||
// TEST FOR REGRESSION - This was failing with different bidirectional modes.
|
||||
Konsole::Character braille_char_bidi_enabled(c);
|
||||
const bool res2 = braille_char_bidi_enabled.canBeGrouped(true, false);
|
||||
QCOMPARE(res2, false);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(Konsole::CharacterTest)
|
||||
|
||||
@@ -15,7 +15,6 @@ class CharacterTest : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testCanBeGrouped();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ void TerminalCharacterDecoderTest::convertToCharacter(Character *charResult, con
|
||||
}
|
||||
for (int i = 0; i < text.size(); ++i) {
|
||||
charResult[i] = Character(text.at(i).unicode());
|
||||
charResult[i].rendition = renditions.at(i);
|
||||
charResult[i].rendition.all = renditions.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,27 @@ namespace Konsole
|
||||
{
|
||||
typedef quint32 LineProperty;
|
||||
|
||||
#pragma pack(1)
|
||||
typedef union {
|
||||
quint16 all;
|
||||
struct {
|
||||
uint bold : 1;
|
||||
uint blink : 1;
|
||||
uint transparent : 1;
|
||||
uint reverse : 1;
|
||||
uint italic : 1;
|
||||
uint cursor : 1;
|
||||
uint extended : 1;
|
||||
uint faint : 1;
|
||||
uint strikeout : 1;
|
||||
uint conceal : 1;
|
||||
uint overline : 1;
|
||||
uint selected : 1;
|
||||
uint underline : 4;
|
||||
} f;
|
||||
} RenditionFlagsC;
|
||||
typedef quint16 RenditionFlags;
|
||||
#pragma pack()
|
||||
|
||||
typedef quint16 ExtraFlags;
|
||||
|
||||
@@ -41,7 +61,7 @@ const int LINE_LEN_MASK = (0xfff << LINE_LEN_POS);
|
||||
const RenditionFlags DEFAULT_RENDITION = 0;
|
||||
const RenditionFlags RE_BOLD = (1 << 0);
|
||||
const RenditionFlags RE_BLINK = (1 << 1);
|
||||
const RenditionFlags RE_UNDERLINE = (1 << 2);
|
||||
const RenditionFlags RE_TRANSPARENT = (1 << 2);
|
||||
const RenditionFlags RE_REVERSE = (1 << 3); // Screen only
|
||||
const RenditionFlags RE_ITALIC = (1 << 4);
|
||||
const RenditionFlags RE_CURSOR = (1 << 5);
|
||||
@@ -51,7 +71,17 @@ const RenditionFlags RE_STRIKEOUT = (1 << 8);
|
||||
const RenditionFlags RE_CONCEAL = (1 << 9);
|
||||
const RenditionFlags RE_OVERLINE = (1 << 10);
|
||||
const RenditionFlags RE_SELECTED = (1 << 11);
|
||||
const RenditionFlags RE_TRANSPARENT = (1 << 12);
|
||||
const RenditionFlags RE_UNDERLINE_MASK = (15 << 12);
|
||||
const RenditionFlags RE_UNDERLINE_NONE = 0;
|
||||
const RenditionFlags RE_UNDERLINE = 1;
|
||||
const RenditionFlags RE_UNDERLINE_DOUBLE= 2;
|
||||
const RenditionFlags RE_UNDERLINE_CURL = 3;
|
||||
const RenditionFlags RE_UNDERLINE_DOT = 4;
|
||||
const RenditionFlags RE_UNDERLINE_DASH = 5;
|
||||
// Masks of flags that matter for drawing what is below/above the text
|
||||
const RenditionFlags RE_MASK_UNDER = RE_TRANSPARENT | RE_REVERSE | RE_CURSOR | RE_SELECTED;
|
||||
const RenditionFlags RE_MASK_ABOVE = RE_TRANSPARENT | RE_REVERSE | RE_CURSOR | RE_SELECTED | RE_STRIKEOUT | RE_CONCEAL | RE_OVERLINE | RE_UNDERLINE_MASK;
|
||||
|
||||
|
||||
const ExtraFlags EF_UNREAL = 0;
|
||||
const ExtraFlags EF_REAL = (1 << 0);
|
||||
@@ -60,7 +90,11 @@ const ExtraFlags EF_REPL_NONE = (0 << 1);
|
||||
const ExtraFlags EF_REPL_PROMPT = (1 << 1);
|
||||
const ExtraFlags EF_REPL_INPUT = (2 << 1);
|
||||
const ExtraFlags EF_REPL_OUTPUT = (3 << 1);
|
||||
const ExtraFlags EF_UNDERLINE_COLOR = (15 << 3);
|
||||
const ExtraFlags EF_UNDERLINE_COLOR_1 = (1 << 3);
|
||||
const ExtraFlags EF_EMOJI_REPRESENTATION = (1 << 7);
|
||||
|
||||
#define SetULColor(f, m) (((f) & ~EF_UNDERLINE_COLOR) | ((m) * EF_UNDERLINE_COLOR_1))
|
||||
#define setRepl(f, m) (((f) & ~EF_REPL) | ((m) * EF_REPL_PROMPT))
|
||||
#define LineLength(f) static_cast<int>(((f) & LINE_LEN_MASK) >> LINE_LEN_POS)
|
||||
#define SetLineLength(f, l) (((f) & ~LINE_LEN_MASK) | ((l) << LINE_LEN_POS))
|
||||
@@ -90,7 +124,7 @@ public:
|
||||
RenditionFlags _r = DEFAULT_RENDITION,
|
||||
ExtraFlags _flags = EF_REAL)
|
||||
: character(_c)
|
||||
, rendition(_r)
|
||||
, rendition({_r})
|
||||
, foregroundColor(_f)
|
||||
, backgroundColor(_b)
|
||||
, flags(_flags)
|
||||
@@ -106,7 +140,7 @@ public:
|
||||
uint character;
|
||||
|
||||
/** A combination of RENDITION flags which specify options for drawing the character. */
|
||||
RenditionFlags rendition;
|
||||
RenditionFlagsC rendition;
|
||||
|
||||
/** The foreground color used to draw this character. */
|
||||
CharacterColor foregroundColor;
|
||||
@@ -114,14 +148,10 @@ public:
|
||||
/** The color used to draw this character's background. */
|
||||
CharacterColor backgroundColor;
|
||||
|
||||
/** Indicate whether this character really exists, or exists simply as place holder.
|
||||
*
|
||||
* TODO: this boolean filed can be further improved to become a enum filed, which
|
||||
* indicates different roles:
|
||||
*
|
||||
* RealCharacter: a character which really exists
|
||||
* PlaceHolderCharacter: a character which exists as place holder
|
||||
* TabStopCharacter: a special place holder for HT("\t")
|
||||
/** Flags which are not specific to rendition
|
||||
* Indicate whether this character really exists, or exists simply as place holder.
|
||||
* REPL mode
|
||||
* Character type (script, etc.)
|
||||
*/
|
||||
ExtraFlags flags;
|
||||
|
||||
@@ -144,7 +174,7 @@ public:
|
||||
|
||||
constexpr bool isSpace() const
|
||||
{
|
||||
if (rendition & RE_EXTENDED_CHAR) {
|
||||
if (rendition.f.extended) {
|
||||
return false;
|
||||
} else {
|
||||
return QChar(character).isSpace();
|
||||
@@ -161,6 +191,79 @@ public:
|
||||
return flags & EF_REPL;
|
||||
}
|
||||
|
||||
static constexpr int emojiPresentation1Start = 8986;
|
||||
static constexpr int emojiPresentation1End = 11093;
|
||||
static constexpr int emojiPresentation2Start = 126980;
|
||||
static constexpr int emojiPresentation2End = 129782;
|
||||
/* clang-format off */
|
||||
static constexpr uint64_t emojiPresentation1Bits[] = {
|
||||
0x3, 0x0, 0x0, 0x2478000, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0xc00001800000000, 0x3ffc00000000000, 0x200002000000000, 0x4100c1800030080, 0x308090b010000,
|
||||
0x2e14000000004000, 0x3800000000000000, 0x2000400000, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x840000000000006
|
||||
};
|
||||
static constexpr uint64_t emojiPresentation2Bits[] = {
|
||||
0x1, 0x0, 0x0, 0x800, 0x0, 0x0, 0x7fe400, 0x2ffffffc00000000,
|
||||
0x77c80000400000, 0x3000, 0x0, 0xf000000000000000,
|
||||
0xfffbfe001fffffff, 0xfdffffffffffffff, 0xfffffffff000ffff, 0xfff11ffff000f87f,
|
||||
0xd7ffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xf9ffffffffffffff,
|
||||
0x3ffffffffffffff, 0x40000ffffff780, 0x100060000, 0xff80000000000000,
|
||||
0xffffffffffffffff, 0xf000000000000fff, 0xffffffffffffffff, 0x1ff01800e0e7103, 0x0, 0x0, 0x0, 0x10fff0000000,
|
||||
0x0, 0x0, 0x0, 0x0, 0xff7fffffffffff00, 0xfffffffffffffffb, 0xffffffffffffffff, 0xfffffffffffffff,
|
||||
0x0, 0xf1f1f00000000000, 0xf07ff1fffffff007, 0x7f00ff03ff003
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
static bool emojiPresentation(uint ucs4)
|
||||
{
|
||||
if (ucs4 >= emojiPresentation1Start && ucs4 <= emojiPresentation1End) {
|
||||
return (emojiPresentation1Bits[(ucs4 - emojiPresentation1Start) / 64] & (1l << ((ucs4 - emojiPresentation1Start) % 64))) != 0;
|
||||
} else if (ucs4 >= emojiPresentation2Start && ucs4 <= emojiPresentation2End) {
|
||||
return (emojiPresentation2Bits[(ucs4 - emojiPresentation2Start) / 64] & (1l << ((ucs4 - emojiPresentation2Start) % 64))) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr int emoji1Start = 8252;
|
||||
static constexpr int emoji1End = 12953;
|
||||
static constexpr int emoji2Start = 126980;
|
||||
static constexpr int emoji2End = 129782;
|
||||
/* clang-format off */
|
||||
static constexpr uint64_t emoji1Bits[] = {
|
||||
0x2001, 0x0, 0x0, 0x2000004000000000, 0x0, 0x60003f000000, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x1000c0000000, 0x0, 0x0, 0x70ffe00000080000, 0x0,
|
||||
0x0, 0x0, 0x40, 0x0, 0x0, 0x400c00000000000, 0x8000000000000010, 0x700c44d2132401f7,
|
||||
0x8000169800fff050, 0x30c831afc0000c, 0x7bf0600001ac1306, 0x1801022054bf242, 0x1800b850900, 0x1000200e000000, 0x8, 0x0,
|
||||
0x0, 0x0, 0x0, 0x300000000000000, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x180000e00, 0x2100000, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000,
|
||||
0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x28000000
|
||||
};
|
||||
static constexpr uint64_t emoji2Bits[] = {
|
||||
0x1, 0x0, 0x0, 0x800, 0x0, 0xc00300000000000, 0x7fe400, 0x6ffffffc00000000,
|
||||
0x7fc80000400000, 0x3000, 0x0, 0xf000000000000000,
|
||||
0xffffffff3fffffff, 0xffffffffffffffff, 0xfffffffffcecffff, 0xfffb9fffffffffff,
|
||||
0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfbffffffffffffff,
|
||||
0x3ffffffffffffff, 0x7f980ffffff7e0, 0xc1006013000613c8, 0xffc08810a700e001,
|
||||
0xffffffffffffffff, 0xf000000000000fff, 0xffffffffffffffff, 0x1ff91a3fe0e7f83, 0x0, 0x0, 0x0, 0x10fff0000000,
|
||||
0x0, 0x0, 0x0, 0x0, 0xff7fffffffffff00, 0xfffffffffffffffb, 0xffffffffffffffff, 0xfffffffffffffff,
|
||||
0x0, 0xf1f1f00000000000, 0xf07ff1fffffff007, 0x7f00ff03ff003
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
static bool emoji(uint ucs4)
|
||||
{
|
||||
if (ucs4 >= emoji1Start && ucs4 <= emoji1End) {
|
||||
return (emoji1Bits[(ucs4 - emoji1Start) / 64] & (1l << ((ucs4 - emoji1Start) % 64))) != 0;
|
||||
} else if (ucs4 >= emoji2Start && ucs4 <= emoji2End) {
|
||||
return (emoji2Bits[(ucs4 - emoji2Start) / 64] & (1l << ((ucs4 - emoji2Start) % 64))) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int width(uint ucs4)
|
||||
{
|
||||
// ASCII
|
||||
@@ -205,22 +308,9 @@ public:
|
||||
return stringWidth(ucs4Str.constData(), ucs4Str.length());
|
||||
}
|
||||
|
||||
inline bool canBeGrouped(bool bidirectionalEnabled, bool isDoubleWidth) const
|
||||
{
|
||||
if (character <= 0x7e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (QChar::script(character) == QChar::Script_Braille) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (rendition & RE_EXTENDED_CHAR) || (bidirectionalEnabled && !isDoubleWidth);
|
||||
}
|
||||
|
||||
inline uint baseCodePoint() const
|
||||
{
|
||||
if (rendition & RE_EXTENDED_CHAR) {
|
||||
if (rendition.f.extended) {
|
||||
ushort extendedCharLength = 0;
|
||||
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(character, extendedCharLength);
|
||||
// FIXME: Coverity-Dereferencing chars, which is known to be nullptr
|
||||
@@ -247,7 +337,7 @@ public:
|
||||
|
||||
inline bool hasSameRendition(Character lhs) const
|
||||
{
|
||||
return (lhs.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && lhs.flags == flags;
|
||||
return (lhs.rendition.all & ~RE_EXTENDED_CHAR) == (rendition.all & ~RE_EXTENDED_CHAR) && lhs.flags == flags;
|
||||
};
|
||||
|
||||
inline bool hasSameLineDrawStatus(Character lhs) const
|
||||
@@ -284,7 +374,7 @@ constexpr bool operator!=(const Character &a, const Character &b)
|
||||
|
||||
constexpr bool Character::equalsFormat(const Character &other) const
|
||||
{
|
||||
return backgroundColor == other.backgroundColor && foregroundColor == other.foregroundColor && rendition == other.rendition;
|
||||
return backgroundColor == other.backgroundColor && foregroundColor == other.foregroundColor && rendition.all == other.rendition.all;
|
||||
}
|
||||
}
|
||||
Q_DECLARE_TYPEINFO(Konsole::Character, Q_MOVABLE_TYPE);
|
||||
|
||||
@@ -133,7 +133,7 @@ bool Hangul::combinesWith(Character prevChar, uint c)
|
||||
{
|
||||
Hangul::SyllablePos syllablePos = Hangul::NotInSyllable;
|
||||
|
||||
if ((prevChar.rendition & RE_EXTENDED_CHAR) == 0) {
|
||||
if (prevChar.rendition.f.extended == 0) {
|
||||
const uint prev = prevChar.character;
|
||||
updateHangulSyllablePos(syllablePos, prev);
|
||||
} else {
|
||||
|
||||
@@ -100,13 +100,14 @@ void HTMLDecoder::decodeLine(const Character *const characters, int count, LineP
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
// check if appearance of character is different from previous char
|
||||
if (characters[i].rendition != _lastRendition || characters[i].foregroundColor != _lastForeColor || characters[i].backgroundColor != _lastBackColor) {
|
||||
if (characters[i].rendition.all != _lastRendition || characters[i].foregroundColor != _lastForeColor
|
||||
|| characters[i].backgroundColor != _lastBackColor) {
|
||||
if (_innerSpanOpen) {
|
||||
closeSpan(text);
|
||||
_innerSpanOpen = false;
|
||||
}
|
||||
|
||||
_lastRendition = characters[i].rendition;
|
||||
_lastRendition = characters[i].rendition.all;
|
||||
_lastForeColor = characters[i].foregroundColor;
|
||||
_lastBackColor = characters[i].backgroundColor;
|
||||
|
||||
@@ -118,7 +119,7 @@ void HTMLDecoder::decodeLine(const Character *const characters, int count, LineP
|
||||
style.append(QLatin1String("font-weight:bold;"));
|
||||
}
|
||||
|
||||
if ((_lastRendition & RE_UNDERLINE) != 0) {
|
||||
if ((_lastRendition & RE_UNDERLINE_MASK) != 0) {
|
||||
style.append(QLatin1String("font-decoration:underline;"));
|
||||
}
|
||||
|
||||
@@ -140,7 +141,7 @@ void HTMLDecoder::decodeLine(const Character *const characters, int count, LineP
|
||||
|
||||
// output current character
|
||||
if (spaceCount < 2) {
|
||||
if ((characters[i].rendition & RE_EXTENDED_CHAR) != 0) {
|
||||
if ((characters[i].rendition.all & RE_EXTENDED_CHAR) != 0) {
|
||||
ushort extendedCharLength = 0;
|
||||
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength);
|
||||
if (chars != nullptr) {
|
||||
|
||||
@@ -110,7 +110,7 @@ void PlainTextDecoder::decodeLine(const Character *const characters, int count,
|
||||
characterBuffer.reserve(count);
|
||||
|
||||
for (int i = start; i < outputCount;) {
|
||||
if ((characters[i].rendition & RE_EXTENDED_CHAR) != 0) {
|
||||
if (characters[i].rendition.f.extended != 0) {
|
||||
ushort extendedCharLength = 0;
|
||||
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength);
|
||||
if (chars != nullptr) {
|
||||
|
||||
@@ -90,6 +90,7 @@ const std::vector<Profile::PropertyInfo> Profile::DefaultProperties = {
|
||||
{DimValue, "DimmValue", APPEARANCE_GROUP, 128},
|
||||
{DimWhenInactive, "DimWhenInactive", GENERAL_GROUP, false},
|
||||
{InvertSelectionColors, "InvertSelectionColors", GENERAL_GROUP, false},
|
||||
{EmojiFont, "EmojiFont", APPEARANCE_GROUP, QFont()},
|
||||
|
||||
// Keyboard
|
||||
#ifdef Q_OS_MACOS
|
||||
@@ -112,6 +113,8 @@ const std::vector<Profile::PropertyInfo> Profile::DefaultProperties = {
|
||||
{BlinkingTextEnabled, "BlinkingTextEnabled", TERMINAL_GROUP, true},
|
||||
{FlowControlEnabled, "FlowControlEnabled", TERMINAL_GROUP, true},
|
||||
{BidiRenderingEnabled, "BidiRenderingEnabled", TERMINAL_GROUP, true},
|
||||
{BidiLineLTR, "BidiLineLTR", TERMINAL_GROUP, true},
|
||||
{BidiTableDirOverride, "BidiTableDirOverride", TERMINAL_GROUP, true},
|
||||
{BlinkingCursorEnabled, "BlinkingCursorEnabled", TERMINAL_GROUP, false},
|
||||
{BellMode, "BellMode", TERMINAL_GROUP, Enum::NotifyBell},
|
||||
{VerticalLine, "VerticalLine", TERMINAL_GROUP, false},
|
||||
|
||||
@@ -160,6 +160,14 @@ public:
|
||||
* text display
|
||||
*/
|
||||
BidiRenderingEnabled,
|
||||
/** (bool) If true, lines are always LTR. Otherwise, decided by
|
||||
* first strong charachter as defined by the BiDi algorithm.
|
||||
*/
|
||||
BidiLineLTR,
|
||||
/** (bool) Specifies whether the table drawing characters will be
|
||||
* considered strong LTR by the BiDi algorithm.
|
||||
*/
|
||||
BidiTableDirOverride,
|
||||
/** (bool) Specifies whether text in terminal displays is allowed
|
||||
* to blink.
|
||||
*/
|
||||
@@ -363,6 +371,9 @@ public:
|
||||
/** (bool) If true, move cursor with Left/Right keys when mouse clicks in input area
|
||||
*/
|
||||
SemanticInputClick,
|
||||
/** (QFont) Emoji font override
|
||||
*/
|
||||
EmojiFont,
|
||||
};
|
||||
|
||||
Q_ENUM(Property)
|
||||
@@ -569,6 +580,12 @@ public:
|
||||
return property<QFont>(Profile::Font);
|
||||
}
|
||||
|
||||
/** Convenience method for property<QFont>(Profile::EmojiFont) */
|
||||
QFont emojiFont() const
|
||||
{
|
||||
return property<QFont>(Profile::EmojiFont);
|
||||
}
|
||||
|
||||
/** Convenience method for property<QString>(Profile::ColorScheme) */
|
||||
QString colorScheme() const
|
||||
{
|
||||
@@ -599,6 +616,18 @@ public:
|
||||
return property<bool>(Profile::BidiRenderingEnabled);
|
||||
}
|
||||
|
||||
/** Convenience method for property<bool>(Profile::BidiLineLTR) */
|
||||
bool bidiLineLTR() const
|
||||
{
|
||||
return property<bool>(Profile::BidiLineLTR);
|
||||
}
|
||||
|
||||
/** Convenience method for property<bool>(Profile::BidiTableDirOverride) */
|
||||
bool bidiTableDirOverride() const
|
||||
{
|
||||
return property<bool>(Profile::BidiTableDirOverride);
|
||||
}
|
||||
|
||||
/** Convenience method for property<bool>(Profile::LineSpacing) */
|
||||
int lineSpacing() const
|
||||
{
|
||||
|
||||
@@ -78,6 +78,12 @@
|
||||
#include "TerminalPainter.h"
|
||||
#include "TerminalScrollBar.h"
|
||||
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/ubidi.h"
|
||||
#include "unicode/uchar.h"
|
||||
#include "unicode/ushape.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
inline int TerminalDisplay::loc(int x, int y) const
|
||||
@@ -148,6 +154,14 @@ void TerminalDisplay::setScreenWindow(ScreenWindow *window)
|
||||
}
|
||||
}
|
||||
|
||||
static UCharDirection BiDiClass([[maybe_unused]] const void *context, UChar32 c)
|
||||
{
|
||||
if (c >= 0x2500 && c <= 0x25ff) {
|
||||
return U_LEFT_TO_RIGHT;
|
||||
}
|
||||
return U_CHAR_DIRECTION_COUNT;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* */
|
||||
/* Accessibility */
|
||||
@@ -511,7 +525,7 @@ void TerminalDisplay::updateImage()
|
||||
|
||||
if (!_resizing) { // not while _resizing, we're expecting a paintEvent
|
||||
for (x = 0; x < columnsToUpdate; ++x) {
|
||||
_hasTextBlinker |= (newLine[x].rendition & RE_BLINK);
|
||||
_hasTextBlinker |= newLine[x].rendition.f.blink;
|
||||
|
||||
// Start drawing if this character or the next one differs.
|
||||
// We also take the next one into account to handle the situation
|
||||
@@ -522,7 +536,7 @@ void TerminalDisplay::updateImage()
|
||||
}
|
||||
const bool lineDraw = LineBlockCharacters::canDraw(newLine[x + 0].character);
|
||||
const bool doubleWidth = (x + 1 == columnsToUpdate) ? false : newLine[x + 1].isRightHalfOfDoubleWide();
|
||||
const RenditionFlags cr = newLine[x].rendition;
|
||||
const RenditionFlags cr = newLine[x].rendition.all;
|
||||
const CharacterColor clipboard = newLine[x].backgroundColor;
|
||||
if (newLine[x].foregroundColor != cf) {
|
||||
cf = newLine[x].foregroundColor;
|
||||
@@ -537,7 +551,7 @@ void TerminalDisplay::updateImage()
|
||||
|
||||
const bool nextIsDoubleWidth = (x + len + 1 == columnsToUpdate) ? false : newLine[x + len + 1].isRightHalfOfDoubleWide();
|
||||
|
||||
if (ch.foregroundColor != cf || ch.backgroundColor != clipboard || (ch.rendition & ~RE_EXTENDED_CHAR) != (cr & ~RE_EXTENDED_CHAR)
|
||||
if (ch.foregroundColor != cf || ch.backgroundColor != clipboard || (ch.rendition.all & ~RE_EXTENDED_CHAR) != (cr & ~RE_EXTENDED_CHAR)
|
||||
|| (dirtyMask[x + len] == 0) || LineBlockCharacters::canDraw(ch.character) != lineDraw || nextIsDoubleWidth != doubleWidth) {
|
||||
break;
|
||||
}
|
||||
@@ -683,7 +697,7 @@ void TerminalDisplay::paintEvent(QPaintEvent *pe)
|
||||
paint.setRenderHint(QPainter::TextAntialiasing, _terminalFont->antialiasText());
|
||||
|
||||
for (const QRect &rect : qAsConst(dirtyImageRegion)) {
|
||||
_terminalPainter->drawContents(_image, paint, rect, false, _imageSize, _bidiEnabled, _lineProperties);
|
||||
_terminalPainter->drawContents(_image, paint, rect, false, _imageSize, _bidiEnabled, _lineProperties, _screenWindow->screen()->ulColorTable());
|
||||
}
|
||||
|
||||
if (screenWindow()->currentResultLine() != -1) {
|
||||
@@ -1479,6 +1493,19 @@ QPair<int, int> TerminalDisplay::getCharacterPosition(const QPoint &widgetPoint,
|
||||
int column =
|
||||
qBound(0, (widgetPoint.x() + xOffset - contentsRect().left() - _contentRect.left()) / _terminalFont->fontWidth() / (doubleWidth ? 2 : 1), columnMax);
|
||||
|
||||
// Visual column to logical
|
||||
if (_bidiEnabled && column < _usedColumns) {
|
||||
#define MAX_LINE_WIDTH 1024
|
||||
int log2line[MAX_LINE_WIDTH];
|
||||
int line2log[MAX_LINE_WIDTH];
|
||||
uint16_t shapemap[MAX_LINE_WIDTH];
|
||||
int32_t vis2line[MAX_LINE_WIDTH];
|
||||
const int pos = loc(0, line);
|
||||
QString line;
|
||||
bidiMap(_image + pos, line, log2line, line2log, shapemap, vis2line, false);
|
||||
column = line2log[vis2line[column]];
|
||||
}
|
||||
|
||||
return qMakePair(line, column);
|
||||
}
|
||||
|
||||
@@ -1874,7 +1901,7 @@ QPoint TerminalDisplay::findWordEnd(const QPoint &pnt)
|
||||
out:
|
||||
y -= curLine;
|
||||
// In word selection mode don't select @ (64) if at end of word.
|
||||
if (((image[j].rendition & RE_EXTENDED_CHAR) == 0) && (QChar(image[j].character) == QLatin1Char('@')) && (y > pnt.y() || x > pnt.x())) {
|
||||
if ((image[j].rendition.f.extended == 0) && (QChar(image[j].character) == QLatin1Char('@')) && (y > pnt.y() || x > pnt.x())) {
|
||||
if (x > 0) {
|
||||
x--;
|
||||
} else {
|
||||
@@ -1992,7 +2019,7 @@ bool TerminalDisplay::focusNextPrevChild(bool next)
|
||||
|
||||
QChar TerminalDisplay::charClass(const Character &ch) const
|
||||
{
|
||||
if ((ch.rendition & RE_EXTENDED_CHAR) != 0) {
|
||||
if (ch.rendition.f.extended != 0) {
|
||||
ushort extendedCharLength = 0;
|
||||
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(ch.character, extendedCharLength);
|
||||
if ((chars != nullptr) && extendedCharLength > 0) {
|
||||
@@ -2832,6 +2859,8 @@ void TerminalDisplay::applyProfile(const Profile::Ptr &profile)
|
||||
_ctrlRequiredForDrag = profile->property<bool>(Profile::CtrlRequiredForDrag);
|
||||
_dropUrlsAsText = profile->property<bool>(Profile::DropUrlsAsText);
|
||||
_bidiEnabled = profile->bidiRenderingEnabled();
|
||||
_bidiLineLTR = profile->bidiLineLTR();
|
||||
_bidiTableDirOverride = profile->bidiTableDirOverride();
|
||||
_semanticUpDown = profile->semanticUpDown();
|
||||
_semanticHints = profile->semanticHints();
|
||||
_semanticInputClick = profile->semanticInputClick();
|
||||
@@ -2899,3 +2928,69 @@ int TerminalDisplay::selectionState() const
|
||||
{
|
||||
return _actSel;
|
||||
}
|
||||
|
||||
int TerminalDisplay::bidiMap(Character *screenline, QString &line, int *log2line, int *line2log, uint16_t *shapemap, int32_t *vis2line, bool shape, bool bidi)
|
||||
const
|
||||
{
|
||||
const int linewidth = _usedColumns;
|
||||
uint64_t notSkipped[MAX_LINE_WIDTH / 64] = {};
|
||||
int i;
|
||||
int lastNonSpace = 0;
|
||||
for (i = 0; i < linewidth; i++) {
|
||||
log2line[i] = line.size();
|
||||
line2log[line.size()] = i;
|
||||
notSkipped[line.size() / 64] |= 1ul << (line.size() % 64);
|
||||
const Character char_value = screenline[i];
|
||||
if (char_value.rendition.f.extended != 0) {
|
||||
// sequence of characters
|
||||
ushort extendedCharLength = 0;
|
||||
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(char_value.character, extendedCharLength);
|
||||
if (chars != nullptr) {
|
||||
Q_ASSERT(extendedCharLength > 1);
|
||||
line += QString::fromUcs4(chars, extendedCharLength);
|
||||
}
|
||||
lastNonSpace = i;
|
||||
} else {
|
||||
line += QString::fromUcs4(&char_value.character, 1);
|
||||
if (!line[line.size() - 1].isSpace()) {
|
||||
lastNonSpace = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
log2line[i] = line.size();
|
||||
// line.truncate(lastNonSpace + 1);
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
if (shape) {
|
||||
UChar shaped_line[MAX_LINE_WIDTH];
|
||||
u_shapeArabic(reinterpret_cast<const UChar *>(line.utf16()),
|
||||
line.length(),
|
||||
shaped_line,
|
||||
1024,
|
||||
U_SHAPE_AGGREGATE_TASHKEEL_NOOP | U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_LETTERS_SHAPE,
|
||||
&errorCode);
|
||||
for (int i = 0; i < line.length(); i++) {
|
||||
if (line[i] != shaped_line[i]) {
|
||||
shapemap[i] = shaped_line[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bidi) {
|
||||
return lastNonSpace;
|
||||
}
|
||||
UBiDi *ubidi = ubidi_open();
|
||||
UBiDiLevel paraLevel = _bidiLineLTR ? 0 : UBIDI_DEFAULT_LTR;
|
||||
if (_bidiTableDirOverride) {
|
||||
ubidi_setClassCallback(ubidi, BiDiClass, nullptr, nullptr, nullptr, &errorCode);
|
||||
}
|
||||
ubidi_setPara(ubidi, reinterpret_cast<const UChar *>(line.utf16()), line.length(), paraLevel, nullptr, &errorCode);
|
||||
int len = ubidi_getProcessedLength(ubidi);
|
||||
int32_t semi_vis2line[MAX_LINE_WIDTH];
|
||||
ubidi_getVisualMap(ubidi, semi_vis2line, &errorCode);
|
||||
int p = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if ((notSkipped[i / 64] & (1ul << (i % 64))) != 0) {
|
||||
vis2line[p++] = semi_vis2line[i];
|
||||
}
|
||||
}
|
||||
return lastNonSpace;
|
||||
}
|
||||
|
||||
@@ -398,6 +398,11 @@ public:
|
||||
// Used to show/hide the message widget
|
||||
void updateReadOnlyState(bool readonly);
|
||||
|
||||
// Get mapping between visual and logical positions in line
|
||||
// returns the index of the last non space character.
|
||||
int bidiMap(Character *screenline, QString &line, int *log2line, int *line2log, uint16_t *shapemap, int32_t *vis2line, bool shape = true, bool bidi = true)
|
||||
const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Causes the terminal display to fetch the latest character image from the associated
|
||||
@@ -669,6 +674,8 @@ private:
|
||||
bool _resizing = false;
|
||||
bool _showTerminalSizeHint = true;
|
||||
bool _bidiEnabled = false;
|
||||
bool _bidiLineLTR = true;
|
||||
bool _bidiTableDirOverride = true;
|
||||
bool _usesMouseTracking = false;
|
||||
bool _allowMouseTracking = true;
|
||||
bool _bracketedPasteMode = false;
|
||||
|
||||
@@ -35,6 +35,15 @@ void TerminalFont::applyProfile(const Profile::Ptr &profile)
|
||||
m_useFontLineCharacters = profile->useFontLineCharacters();
|
||||
m_lineSpacing = uint(profile->lineSpacing());
|
||||
setVTFont(profile->font());
|
||||
extraFonts[0] = profile->emojiFont();
|
||||
if (extraFonts[0] == QFont()) {
|
||||
extraFonts[0] = QFont(QStringLiteral("Noto Color Emoji"));
|
||||
// extraFonts[0] = QFont(QStringLiteral("Apple Color Emoji"));
|
||||
// extraFonts[0] = QFont(QStringLiteral("Emoji One"));
|
||||
if (extraFonts[0] == QFont()) {
|
||||
extraFonts.remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalFont::setVTFont(const QFont &f)
|
||||
@@ -196,6 +205,26 @@ int TerminalFont::fontAscent() const
|
||||
return m_fontAscent;
|
||||
}
|
||||
|
||||
int TerminalFont::lineWidth() const
|
||||
{
|
||||
return m_lineWidth;
|
||||
}
|
||||
|
||||
int TerminalFont::underlinePos() const
|
||||
{
|
||||
return m_underlinePos;
|
||||
}
|
||||
|
||||
int TerminalFont::strikeOutPos() const
|
||||
{
|
||||
return m_strikeOutPos;
|
||||
}
|
||||
|
||||
int TerminalFont::overlinePos() const
|
||||
{
|
||||
return m_overlinePos;
|
||||
}
|
||||
|
||||
bool TerminalFont::boldIntense() const
|
||||
{
|
||||
return m_boldIntense;
|
||||
@@ -225,8 +254,21 @@ void TerminalFont::fontChange(const QFont &)
|
||||
}
|
||||
|
||||
m_fontAscent = fm.ascent();
|
||||
m_lineWidth = fm.lineWidth();
|
||||
m_underlinePos = fm.underlinePos();
|
||||
m_strikeOutPos = fm.strikeOutPos();
|
||||
m_overlinePos = fm.overlinePos();
|
||||
|
||||
qobject_cast<TerminalDisplay *>(m_parent)->propagateSize();
|
||||
}
|
||||
|
||||
bool TerminalFont::hasExtraFont(int i) const
|
||||
{
|
||||
return extraFonts.contains(i);
|
||||
}
|
||||
|
||||
QFont TerminalFont::getExtraFont(int i) const
|
||||
{
|
||||
return extraFonts[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,17 @@ public:
|
||||
int fontHeight() const;
|
||||
int fontWidth() const;
|
||||
int fontAscent() const;
|
||||
int lineWidth() const;
|
||||
int underlinePos() const;
|
||||
int strikeOutPos() const;
|
||||
int overlinePos() const;
|
||||
bool boldIntense() const;
|
||||
bool antialiasText() const;
|
||||
bool useFontLineCharacters() const;
|
||||
|
||||
bool hasExtraFont(int i) const;
|
||||
QFont getExtraFont(int i) const;
|
||||
|
||||
protected:
|
||||
void fontChange(const QFont &);
|
||||
|
||||
@@ -51,9 +58,14 @@ private:
|
||||
int m_fontHeight = 1;
|
||||
int m_fontWidth = 1;
|
||||
int m_fontAscent = 1;
|
||||
int m_lineWidth = 1;
|
||||
int m_underlinePos = 1;
|
||||
int m_strikeOutPos = 1;
|
||||
int m_overlinePos = 1;
|
||||
bool m_boldIntense = false;
|
||||
bool m_antialiasText = true;
|
||||
bool m_useFontLineCharacters = false;
|
||||
QMap<int, QFont> extraFonts;
|
||||
|
||||
Profile::Ptr m_profile;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QChar>
|
||||
#include <QColor>
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QRect>
|
||||
@@ -68,9 +69,7 @@ static inline bool isLineCharString(const QString &string)
|
||||
bool isInvertedRendition(const TerminalDisplay *display)
|
||||
{
|
||||
auto currentProfile = SessionManager::instance()->sessionProfile(display->session());
|
||||
const bool isInvert = currentProfile ? currentProfile->property<bool>(Profile::InvertSelectionColors) : false;
|
||||
|
||||
return isInvert;
|
||||
return currentProfile ? currentProfile->property<bool>(Profile::InvertSelectionColors) : false;
|
||||
}
|
||||
|
||||
void TerminalPainter::drawContents(Character *image,
|
||||
@@ -79,167 +78,165 @@ void TerminalPainter::drawContents(Character *image,
|
||||
bool printerFriendly,
|
||||
int imageSize,
|
||||
bool bidiEnabled,
|
||||
QVector<LineProperty> lineProperties)
|
||||
QVector<LineProperty> lineProperties,
|
||||
CharacterColor const *ulColorTable)
|
||||
{
|
||||
const bool invertedRendition = isInvertedRendition(m_parentDisplay);
|
||||
|
||||
QVector<uint> univec;
|
||||
univec.reserve(m_parentDisplay->usedColumns());
|
||||
int placementIdx = 0;
|
||||
|
||||
const int leftPadding = m_parentDisplay->contentRect().left() + m_parentDisplay->contentsRect().left();
|
||||
const int topPadding = m_parentDisplay->contentRect().top() + m_parentDisplay->contentsRect().top();
|
||||
const int fontWidth = m_parentDisplay->terminalFont()->fontWidth();
|
||||
const int fontHeight = m_parentDisplay->terminalFont()->fontHeight();
|
||||
const QRect textArea(QPoint(leftPadding + fontWidth * rect.x(), topPadding + rect.y() * fontHeight),
|
||||
QSize(rect.width() * fontWidth, rect.height() * fontHeight));
|
||||
if (!printerFriendly) {
|
||||
drawImagesBelowText(paint, textArea, fontWidth, fontHeight, placementIdx);
|
||||
}
|
||||
|
||||
static const QFont::Weight FontWeights[] = {
|
||||
QFont::Thin,
|
||||
QFont::Light,
|
||||
QFont::Normal,
|
||||
QFont::Bold,
|
||||
QFont::Black,
|
||||
};
|
||||
const auto normalWeight = m_parentDisplay->font().weight();
|
||||
auto it = std::upper_bound(std::begin(FontWeights), std::end(FontWeights), normalWeight);
|
||||
const QFont::Weight boldWeight = it != std::end(FontWeights) ? *it : QFont::Black;
|
||||
paint.setRenderHint(QPainter::Antialiasing, m_parentDisplay->terminalFont()->antialiasText());
|
||||
paint.setLayoutDirection(Qt::LeftToRight);
|
||||
|
||||
for (int y = rect.y(); y <= rect.bottom(); y++) {
|
||||
const int textY = topPadding + fontHeight * y;
|
||||
int x = rect.x();
|
||||
bool doubleHeightLinePair = false;
|
||||
int x = rect.x();
|
||||
LineProperty lineProperty = y < lineProperties.size() ? lineProperties[y] : 0;
|
||||
|
||||
// Search for start of multi-column character
|
||||
if (image[m_parentDisplay->loc(rect.x(), y)].isRightHalfOfDoubleWide() && (x != 0)) {
|
||||
x--;
|
||||
}
|
||||
QTransform textScale;
|
||||
bool doubleHeight = false;
|
||||
bool doubleWidthLine = false;
|
||||
|
||||
if ((lineProperty & LINE_DOUBLEWIDTH) != 0) {
|
||||
textScale.scale(2, 1);
|
||||
doubleWidthLine = true;
|
||||
}
|
||||
|
||||
doubleHeight = lineProperty & (LINE_DOUBLEHEIGHT_TOP | LINE_DOUBLEHEIGHT_BOTTOM);
|
||||
if (doubleHeight) {
|
||||
textScale.scale(1, 2);
|
||||
}
|
||||
|
||||
if (y < lineProperties.size() - 1) {
|
||||
if (((lineProperties[y] & LINE_DOUBLEHEIGHT_TOP) != 0) && ((lineProperties[y + 1] & LINE_DOUBLEHEIGHT_BOTTOM) != 0)) {
|
||||
doubleHeightLinePair = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply text scaling matrix
|
||||
paint.setWorldTransform(textScale, true);
|
||||
// Calculate the area in which the text will be drawn
|
||||
const int textX = leftPadding + fontWidth * rect.x() * (doubleWidthLine ? 2 : 1);
|
||||
const int textWidth = fontWidth * rect.width();
|
||||
const int textHeight = doubleHeight && !doubleHeightLinePair ? fontHeight / 2 : fontHeight;
|
||||
int pos = m_parentDisplay->loc(0, y);
|
||||
|
||||
// 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)
|
||||
QString line;
|
||||
#define MAX_LINE_WIDTH 1024
|
||||
int log2line[MAX_LINE_WIDTH];
|
||||
int line2log[MAX_LINE_WIDTH];
|
||||
uint16_t shapemap[MAX_LINE_WIDTH];
|
||||
int32_t vis2line[MAX_LINE_WIDTH];
|
||||
int lastNonSpace = m_parentDisplay->bidiMap(image + pos, line, log2line, line2log, shapemap, vis2line, bidiEnabled, bidiEnabled);
|
||||
const QRect textArea(textScale.inverted().map(QPoint(textX, textY)), QSize(textWidth, textHeight));
|
||||
if (!printerFriendly) {
|
||||
drawBelowText(paint,
|
||||
textArea,
|
||||
image + pos,
|
||||
rect.x(),
|
||||
rect.width(),
|
||||
fontWidth,
|
||||
m_parentDisplay->terminalColor()->colorTable(),
|
||||
invertedRendition,
|
||||
vis2line,
|
||||
line2log,
|
||||
bidiEnabled);
|
||||
}
|
||||
|
||||
RenditionFlags oldRendition = -1;
|
||||
QColor oldColor = QColor();
|
||||
for (; x <= rect.right(); x++) {
|
||||
int len = 1;
|
||||
const int pos = m_parentDisplay->loc(x, y);
|
||||
const Character char_value = image[pos];
|
||||
|
||||
// is this a single character or a sequence of characters ?
|
||||
if ((char_value.rendition & RE_EXTENDED_CHAR) != 0) {
|
||||
// sequence of characters
|
||||
ushort extendedCharLength = 0;
|
||||
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(char_value.character, extendedCharLength);
|
||||
if (chars != nullptr) {
|
||||
Q_ASSERT(extendedCharLength > 1);
|
||||
for (int index = 0; index < extendedCharLength; index++) {
|
||||
univec << chars[index];
|
||||
}
|
||||
}
|
||||
if (x > lastNonSpace) {
|
||||
// What about the cursor?
|
||||
// break;
|
||||
}
|
||||
int log_x;
|
||||
int line_x;
|
||||
if (bidiEnabled) {
|
||||
line_x = vis2line[x];
|
||||
log_x = line2log[line_x];
|
||||
} else {
|
||||
if (!char_value.isRightHalfOfDoubleWide()) {
|
||||
univec << char_value.character;
|
||||
}
|
||||
log_x = x;
|
||||
}
|
||||
|
||||
// TODO: Move all those lambdas to Character, so it's easy to test.
|
||||
const bool doubleWidth = image[qMin(pos + 1, imageSize - 1)].isRightHalfOfDoubleWide();
|
||||
const Character char_value = image[pos + log_x];
|
||||
|
||||
const auto isInsideDrawArea = [rectRight = rect.right()](int column) {
|
||||
return column <= rectRight;
|
||||
};
|
||||
|
||||
const auto hasSameWidth = [imageSize, image, doubleWidth](int nextPos) {
|
||||
const int characterLoc = qMin(nextPos + 1, imageSize - 1);
|
||||
return image[characterLoc].isRightHalfOfDoubleWide() == doubleWidth;
|
||||
};
|
||||
|
||||
if (char_value.canBeGrouped(bidiEnabled, doubleWidth)) {
|
||||
while (isInsideDrawArea(x + len)) {
|
||||
const int nextPos = m_parentDisplay->loc(x + len, y);
|
||||
const Character next_char = image[nextPos];
|
||||
|
||||
if (!hasSameWidth(nextPos) || !next_char.canBeGrouped(bidiEnabled, doubleWidth) || !char_value.hasSameAttributes(next_char)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const uint c = next_char.character;
|
||||
if ((next_char.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);
|
||||
for (int index = 0; index < extendedCharLength; index++) {
|
||||
univec << chars[index];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// single character
|
||||
if (c != 0u) {
|
||||
univec << 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)) {
|
||||
const Character next_char = image[m_parentDisplay->loc(x + len, y)];
|
||||
if (next_char.character == ' ' && char_value.hasSameColors(next_char) && char_value.hasSameRendition(next_char)) {
|
||||
univec << next_char.character;
|
||||
len++;
|
||||
} else {
|
||||
// break otherwise, we don't want to be stuck in this loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!printerFriendly && char_value.isSpace() && char_value.rendition.f.cursor == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust for trailing part of multi-column character
|
||||
if ((x + len < m_parentDisplay->usedColumns()) && image[m_parentDisplay->loc(x + len, y)].isRightHalfOfDoubleWide()) {
|
||||
len++;
|
||||
}
|
||||
|
||||
QTransform textScale;
|
||||
bool doubleHeight = false;
|
||||
bool doubleWidthLine = false;
|
||||
|
||||
if ((lineProperty & LINE_DOUBLEWIDTH) != 0) {
|
||||
textScale.scale(2, 1);
|
||||
doubleWidthLine = true;
|
||||
}
|
||||
|
||||
doubleHeight = lineProperty & (LINE_DOUBLEHEIGHT_TOP | LINE_DOUBLEHEIGHT_BOTTOM);
|
||||
if (doubleHeight) {
|
||||
textScale.scale(1, 2);
|
||||
}
|
||||
|
||||
if (y < lineProperties.size() - 1) {
|
||||
if (((lineProperties[y] & LINE_DOUBLEHEIGHT_TOP) != 0) && ((lineProperties[y + 1] & LINE_DOUBLEHEIGHT_BOTTOM) != 0)) {
|
||||
doubleHeightLinePair = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply text scaling matrix
|
||||
paint.setWorldTransform(textScale, true);
|
||||
|
||||
// Calculate the area in which the text will be drawn
|
||||
const int textX = leftPadding + fontWidth * x * (doubleWidthLine ? 2 : 1);
|
||||
const int textWidth = fontWidth * len;
|
||||
const int textHeight = doubleHeight && !doubleHeightLinePair ? fontHeight / 2 : 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)
|
||||
const QRect textArea(textScale.inverted().map(QPoint(textX, textY)), QSize(textWidth, textHeight));
|
||||
|
||||
const QString unistr = QString::fromUcs4(univec.data(), univec.length());
|
||||
|
||||
univec.clear();
|
||||
const QString unistr = line.mid(log2line[log_x], log2line[log_x + 1] - log2line[log_x]);
|
||||
|
||||
// paint text fragment
|
||||
if (printerFriendly) {
|
||||
drawPrinterFriendlyTextFragment(paint, textArea, unistr, image[pos], lineProperty);
|
||||
} else {
|
||||
drawTextFragment(paint, textArea, unistr, image[pos], m_parentDisplay->terminalColor()->colorTable(), invertedRendition, lineProperty);
|
||||
if (unistr.length() && unistr[0] != QChar(0)) {
|
||||
int textWidth = fontWidth * 1;
|
||||
int textX = leftPadding + fontWidth * x * (doubleWidthLine ? 2 : 1);
|
||||
const QRect textAreaOneChar(textScale.inverted().map(QPoint(textX, textY)), QSize(textWidth, textHeight));
|
||||
drawTextCharacters(paint,
|
||||
textAreaOneChar,
|
||||
unistr,
|
||||
image[pos + log_x],
|
||||
m_parentDisplay->terminalColor()->colorTable(),
|
||||
invertedRendition,
|
||||
lineProperty,
|
||||
printerFriendly,
|
||||
oldRendition,
|
||||
oldColor,
|
||||
normalWeight,
|
||||
boldWeight);
|
||||
}
|
||||
|
||||
paint.setWorldTransform(textScale.inverted(), true);
|
||||
|
||||
x += len - 1;
|
||||
}
|
||||
if ((lineProperty & LINE_PROMPT_START) != 0
|
||||
if (!printerFriendly) {
|
||||
drawAboveText(paint,
|
||||
textArea,
|
||||
image + pos,
|
||||
rect.x(),
|
||||
rect.width(),
|
||||
fontWidth,
|
||||
m_parentDisplay->terminalColor()->colorTable(),
|
||||
invertedRendition,
|
||||
vis2line,
|
||||
line2log,
|
||||
bidiEnabled,
|
||||
ulColorTable);
|
||||
}
|
||||
|
||||
paint.setRenderHint(QPainter::Antialiasing, false);
|
||||
paint.setWorldTransform(textScale.inverted(), true);
|
||||
if ((lineProperty & LINE_PROMPT_START)
|
||||
&& ((m_parentDisplay->semanticHints() == Enum::SemanticHintsURL && m_parentDisplay->filterChain()->showUrlHint())
|
||||
|| m_parentDisplay->semanticHints() == Enum::SemanticHintsAlways)) {
|
||||
QPen pen(m_parentDisplay->terminalColor()->foregroundColor());
|
||||
@@ -251,6 +248,9 @@ void TerminalPainter::drawContents(Character *image,
|
||||
y++;
|
||||
}
|
||||
}
|
||||
if (!printerFriendly) {
|
||||
drawImagesAboveText(paint, textArea, fontWidth, fontHeight, placementIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPainter::drawCurrentResultRect(QPainter &painter, const QRect &searchResultRect)
|
||||
@@ -395,129 +395,6 @@ static void reverseRendition(Character &p)
|
||||
p.backgroundColor = f;
|
||||
}
|
||||
|
||||
void TerminalPainter::drawTextFragment(QPainter &painter,
|
||||
const QRect &rect,
|
||||
const QString &text,
|
||||
Character style,
|
||||
const QColor *colorTable,
|
||||
const bool invertedRendition,
|
||||
const LineProperty lineProperty)
|
||||
{
|
||||
// setup painter
|
||||
|
||||
// Sets the text selection colors, either:
|
||||
// - using reverseRendition(), which inverts the foreground/background
|
||||
// colors OR
|
||||
// - blending the foreground/background colors
|
||||
if (style.rendition & RE_SELECTED) {
|
||||
if (invertedRendition) {
|
||||
reverseRendition(style);
|
||||
}
|
||||
}
|
||||
|
||||
QColor foregroundColor = style.foregroundColor.color(colorTable);
|
||||
QColor backgroundColor = style.backgroundColor.color(colorTable);
|
||||
|
||||
if (style.rendition & RE_SELECTED) {
|
||||
if (!invertedRendition) {
|
||||
backgroundColor = calculateBackgroundColor(style, colorTable).value_or(foregroundColor);
|
||||
if (backgroundColor == foregroundColor) {
|
||||
foregroundColor = style.backgroundColor.color(colorTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Screen *screen = m_parentDisplay->screenWindow()->screen();
|
||||
|
||||
int placementIdx = 0;
|
||||
qreal opacity = painter.opacity();
|
||||
int scrollDelta = m_parentDisplay->terminalFont()->fontHeight() * (m_parentDisplay->screenWindow()->currentLine() - screen->getHistLines());
|
||||
const bool origClipping = painter.hasClipping();
|
||||
const auto origClipRegion = painter.clipRegion();
|
||||
if (screen->hasGraphics()) {
|
||||
painter.setClipRect(rect);
|
||||
while (1) {
|
||||
TerminalGraphicsPlacement_t *p = screen->getGraphicsPlacement(placementIdx);
|
||||
if (!p || p->z >= 0) {
|
||||
break;
|
||||
}
|
||||
int x = p->col * m_parentDisplay->terminalFont()->fontWidth() + p->X + m_parentDisplay->contentRect().left();
|
||||
int y = p->row * m_parentDisplay->terminalFont()->fontHeight() + p->Y + m_parentDisplay->contentRect().top();
|
||||
QRectF srcRect(0, 0, p->pixmap.width(), p->pixmap.height());
|
||||
QRectF dstRect(x, y - scrollDelta, p->pixmap.width(), p->pixmap.height());
|
||||
painter.setOpacity(p->opacity);
|
||||
painter.drawPixmap(dstRect, p->pixmap, srcRect);
|
||||
placementIdx++;
|
||||
}
|
||||
painter.setOpacity(opacity);
|
||||
}
|
||||
|
||||
bool drawBG = backgroundColor != colorTable[DEFAULT_BACK_COLOR];
|
||||
if (screen->hasGraphics() && style.rendition == RE_TRANSPARENT) {
|
||||
drawBG = false;
|
||||
}
|
||||
|
||||
if (drawBG) {
|
||||
drawBackground(painter, rect, backgroundColor, false);
|
||||
}
|
||||
|
||||
QColor characterColor = foregroundColor;
|
||||
if ((style.rendition & RE_CURSOR) != 0) {
|
||||
drawCursor(painter, rect, foregroundColor, backgroundColor, characterColor);
|
||||
}
|
||||
if (m_parentDisplay->filterChain()->showUrlHint()) {
|
||||
if ((style.flags & EF_REPL) == EF_REPL_PROMPT) {
|
||||
int h, s, v;
|
||||
characterColor.getHsv(&h, &s, &v);
|
||||
s = s / 2;
|
||||
v = v / 2;
|
||||
characterColor.setHsv(h, s, v);
|
||||
}
|
||||
if ((style.flags & EF_REPL) == EF_REPL_INPUT) {
|
||||
int h, s, v;
|
||||
characterColor.getHsv(&h, &s, &v);
|
||||
s = (511 + s) / 3;
|
||||
v = (511 + v) / 3;
|
||||
characterColor.setHsv(h, s, v);
|
||||
}
|
||||
}
|
||||
|
||||
// draw text
|
||||
drawCharacters(painter, rect, text, style, characterColor, lineProperty);
|
||||
|
||||
if (screen->hasGraphics()) {
|
||||
while (1) {
|
||||
TerminalGraphicsPlacement_t *p = screen->getGraphicsPlacement(placementIdx);
|
||||
if (!p) {
|
||||
break;
|
||||
}
|
||||
QPixmap image = p->pixmap;
|
||||
int x = p->col * m_parentDisplay->terminalFont()->fontWidth() + p->X + m_parentDisplay->contentRect().left();
|
||||
int y = p->row * m_parentDisplay->terminalFont()->fontHeight() + p->Y + m_parentDisplay->contentRect().top();
|
||||
QRectF srcRect(0, 0, image.width(), image.height());
|
||||
QRectF dstRect(x, y - scrollDelta, image.width(), image.height());
|
||||
painter.setOpacity(p->opacity);
|
||||
painter.drawPixmap(dstRect, image, srcRect);
|
||||
placementIdx++;
|
||||
}
|
||||
painter.setOpacity(opacity);
|
||||
painter.setClipRegion(origClipRegion);
|
||||
painter.setClipping(origClipping);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPainter::drawPrinterFriendlyTextFragment(QPainter &painter,
|
||||
const QRect &rect,
|
||||
const QString &text,
|
||||
Character style,
|
||||
const LineProperty lineProperty)
|
||||
{
|
||||
style.foregroundColor = CharacterColor(COLOR_SPACE_RGB, 0x00000000);
|
||||
style.backgroundColor = CharacterColor(COLOR_SPACE_RGB, 0xFFFFFFFF);
|
||||
|
||||
drawCharacters(painter, rect, text, style, QColor(), lineProperty);
|
||||
}
|
||||
|
||||
void TerminalPainter::drawBackground(QPainter &painter, const QRect &rect, const QColor &backgroundColor, bool useOpacitySetting)
|
||||
{
|
||||
if (useOpacitySetting && !m_parentDisplay->wallpaper()->isNull()
|
||||
@@ -595,11 +472,11 @@ void TerminalPainter::drawCharacters(QPainter &painter,
|
||||
const QColor &characterColor,
|
||||
const LineProperty lineProperty)
|
||||
{
|
||||
if (m_parentDisplay->textBlinking() && ((style.rendition & RE_BLINK) != 0)) {
|
||||
if (m_parentDisplay->textBlinking() && (style.rendition.f.blink != 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((style.rendition & RE_CONCEAL) != 0) {
|
||||
if (style.rendition.f.conceal != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -619,11 +496,11 @@ void TerminalPainter::drawCharacters(QPainter &painter,
|
||||
auto it = std::upper_bound(std::begin(FontWeights), std::end(FontWeights), normalWeight);
|
||||
const QFont::Weight boldWeight = it != std::end(FontWeights) ? *it : QFont::Black;
|
||||
|
||||
const bool useBold = (((style.rendition & RE_BOLD) != 0) && m_parentDisplay->terminalFont()->boldIntense());
|
||||
const bool useUnderline = ((style.rendition & RE_UNDERLINE) != 0) || m_parentDisplay->font().underline();
|
||||
const bool useItalic = ((style.rendition & RE_ITALIC) != 0) || m_parentDisplay->font().italic();
|
||||
const bool useStrikeOut = ((style.rendition & RE_STRIKEOUT) != 0) || m_parentDisplay->font().strikeOut();
|
||||
const bool useOverline = ((style.rendition & RE_OVERLINE) != 0) || m_parentDisplay->font().overline();
|
||||
const bool useBold = ((style.rendition.f.bold != 0) && m_parentDisplay->terminalFont()->boldIntense());
|
||||
const bool useUnderline = (style.rendition.f.underline != 0) || m_parentDisplay->font().underline();
|
||||
const bool useItalic = (style.rendition.f.italic != 0) || m_parentDisplay->font().italic();
|
||||
const bool useStrikeOut = (style.rendition.f.strikeout != 0) || m_parentDisplay->font().strikeOut();
|
||||
const bool useOverline = (style.rendition.f.overline != 0) || m_parentDisplay->font().overline();
|
||||
|
||||
QFont currentFont = painter.font();
|
||||
|
||||
@@ -633,7 +510,8 @@ void TerminalPainter::drawCharacters(QPainter &painter,
|
||||
|| currentFont.underline() != useUnderline
|
||||
|| currentFont.italic() != useItalic
|
||||
|| currentFont.strikeOut() != useStrikeOut
|
||||
|| currentFont.overline() != useOverline)
|
||||
|| currentFont.overline() != useOverline
|
||||
)
|
||||
{ // clang-format on
|
||||
currentFont.setWeight(useBold ? boldWeight : normalWeight);
|
||||
currentFont.setUnderline(useUnderline);
|
||||
@@ -651,9 +529,6 @@ void TerminalPainter::drawCharacters(QPainter &painter,
|
||||
pen.setColor(color);
|
||||
painter.setPen(color);
|
||||
}
|
||||
const bool origClipping = painter.hasClipping();
|
||||
const auto origClipRegion = painter.clipRegion();
|
||||
painter.setClipRect(rect);
|
||||
// draw text
|
||||
if (isLineCharString(text) && !m_parentDisplay->terminalFont()->useFontLineCharacters()) {
|
||||
int y = rect.y();
|
||||
@@ -687,21 +562,16 @@ void TerminalPainter::drawCharacters(QPainter &painter,
|
||||
y += m_parentDisplay->terminalFont()->lineSpacing() - shifted;
|
||||
}
|
||||
}
|
||||
painter.setClipRegion(origClipRegion);
|
||||
painter.setClipping(origClipping);
|
||||
}
|
||||
|
||||
void TerminalPainter::drawLineCharString(TerminalDisplay *display, QPainter &painter, int x, int y, const QString &str, const Character attributes)
|
||||
{
|
||||
painter.setRenderHint(QPainter::Antialiasing, display->terminalFont()->antialiasText());
|
||||
|
||||
const bool useBoldPen = (attributes.rendition & RE_BOLD) != 0 && display->terminalFont()->boldIntense();
|
||||
const bool useBoldPen = (attributes.rendition.f.bold) != 0 && display->terminalFont()->boldIntense();
|
||||
QRect cellRect = {x, y, display->terminalFont()->fontWidth(), display->terminalFont()->fontHeight()};
|
||||
QVector<uint> ucs4str = str.toUcs4();
|
||||
for (int i = 0; i < ucs4str.length(); i++) {
|
||||
LineBlockCharacters::draw(painter, cellRect.translated(i * display->terminalFont()->fontWidth(), 0), ucs4str[i], useBoldPen);
|
||||
}
|
||||
painter.setRenderHint(QPainter::Antialiasing, false);
|
||||
}
|
||||
|
||||
void TerminalPainter::drawInputMethodPreeditString(QPainter &painter, const QRect &rect, TerminalDisplay::InputMethodData &inputMethodData, Character *image)
|
||||
@@ -724,4 +594,407 @@ void TerminalPainter::drawInputMethodPreeditString(QPainter &painter, const QRec
|
||||
inputMethodData.previousPreeditRect = rect;
|
||||
}
|
||||
|
||||
void TerminalPainter::drawBelowText(QPainter &painter,
|
||||
const QRect &rect,
|
||||
Character *style,
|
||||
int startX,
|
||||
int width,
|
||||
int fontWidth,
|
||||
const QColor *colorTable,
|
||||
const bool invertedRendition,
|
||||
int *vis2line,
|
||||
int *line2log,
|
||||
bool bidiEnabled)
|
||||
{
|
||||
// setup painter
|
||||
|
||||
bool first = true;
|
||||
QRect constRect(0, 0, 0, 0);
|
||||
QColor backgroundColor;
|
||||
QColor foregroundColor;
|
||||
bool drawBG = false;
|
||||
int lastX = 0;
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
int x;
|
||||
if (bidiEnabled && i < width) {
|
||||
x = line2log[vis2line[i + startX]];
|
||||
} else {
|
||||
x = i + startX;
|
||||
}
|
||||
|
||||
if (first || style[x].rendition.all != style[lastX].rendition.all || style[x].foregroundColor != style[lastX].foregroundColor
|
||||
|| style[x].backgroundColor != style[lastX].backgroundColor || i == width) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (drawBG) {
|
||||
painter.fillRect(constRect, backgroundColor);
|
||||
}
|
||||
}
|
||||
if (i == width) {
|
||||
return;
|
||||
}
|
||||
// Sets the text selection colors, either:
|
||||
// - using reverseRendition(), which inverts the foreground/background
|
||||
// colors OR
|
||||
// - blending the foreground/background colors
|
||||
if (style[x].rendition.f.selected && invertedRendition) {
|
||||
backgroundColor = style[x].foregroundColor.color(colorTable);
|
||||
foregroundColor = style[x].backgroundColor.color(colorTable);
|
||||
} else {
|
||||
foregroundColor = style[x].foregroundColor.color(colorTable);
|
||||
backgroundColor = style[x].backgroundColor.color(colorTable);
|
||||
}
|
||||
|
||||
if (style[x].rendition.f.selected) {
|
||||
if (!invertedRendition) {
|
||||
backgroundColor = calculateBackgroundColor(style[x], colorTable).value_or(foregroundColor);
|
||||
if (backgroundColor == foregroundColor) {
|
||||
foregroundColor = style[x].backgroundColor.color(colorTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
drawBG = backgroundColor != colorTable[DEFAULT_BACK_COLOR];
|
||||
if (style[x].rendition.f.transparent) {
|
||||
drawBG = false;
|
||||
}
|
||||
constRect = QRect(rect.x() + fontWidth * i, rect.y(), fontWidth, rect.height());
|
||||
} else {
|
||||
constRect.setWidth(constRect.width() + fontWidth);
|
||||
}
|
||||
lastX = x;
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPainter::drawAboveText(QPainter &painter,
|
||||
const QRect &rect,
|
||||
Character *style,
|
||||
int startX,
|
||||
int width,
|
||||
int fontWidth,
|
||||
const QColor *colorTable,
|
||||
const bool invertedRendition,
|
||||
int *vis2line,
|
||||
int *line2log,
|
||||
bool bidiEnabled,
|
||||
CharacterColor const *ulColorTable)
|
||||
{
|
||||
bool first = true;
|
||||
QRect constRect(0, 0, 0, 0);
|
||||
QColor backgroundColor;
|
||||
QColor foregroundColor;
|
||||
int lastX = 0;
|
||||
int startUnderline = -1;
|
||||
int startOverline = -1;
|
||||
int startStrikeOut = -1;
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
int x;
|
||||
if (bidiEnabled && i < width) {
|
||||
x = line2log[vis2line[i + startX]];
|
||||
} else {
|
||||
x = i + startX;
|
||||
}
|
||||
|
||||
if (first || ((style[x].rendition.all ^ style[lastX].rendition.all) & RE_MASK_ABOVE) || ((style[x].flags ^ style[lastX].flags) & EF_UNDERLINE_COLOR)
|
||||
|| style[x].foregroundColor != style[lastX].foregroundColor || style[x].backgroundColor != style[lastX].backgroundColor || i == width) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if ((i == width || style[x].rendition.f.strikeout == 0) && startStrikeOut >= 0) {
|
||||
QPen pen(foregroundColor);
|
||||
int y = rect.y() + m_parentDisplay->terminalFont()->fontAscent();
|
||||
pen.setWidth(m_parentDisplay->terminalFont()->lineWidth());
|
||||
painter.setPen(pen);
|
||||
painter.drawLine(rect.x() + fontWidth * startStrikeOut,
|
||||
y - m_parentDisplay->terminalFont()->strikeOutPos(),
|
||||
rect.x() + fontWidth * i - 1,
|
||||
y - m_parentDisplay->terminalFont()->strikeOutPos());
|
||||
startStrikeOut = -1;
|
||||
}
|
||||
if ((i == width || style[x].rendition.f.overline == 0) && startOverline >= 0) {
|
||||
QPen pen(foregroundColor);
|
||||
int y = rect.y() + m_parentDisplay->terminalFont()->fontAscent();
|
||||
pen.setWidth(m_parentDisplay->terminalFont()->lineWidth());
|
||||
painter.setPen(pen);
|
||||
painter.drawLine(rect.x() + fontWidth * startOverline,
|
||||
y - m_parentDisplay->terminalFont()->overlinePos(),
|
||||
rect.x() + fontWidth * i - 1,
|
||||
y - m_parentDisplay->terminalFont()->overlinePos());
|
||||
startOverline = -1;
|
||||
}
|
||||
int underline = style[lastX].rendition.f.underline;
|
||||
if ((i == width || style[x].rendition.f.underline != underline || ((style[x].flags ^ style[lastX].flags) & EF_UNDERLINE_COLOR))
|
||||
&& startUnderline >= 0) {
|
||||
QPen pen(foregroundColor);
|
||||
if (ulColorTable != nullptr && (style[lastX].flags & EF_UNDERLINE_COLOR) != 0) {
|
||||
pen.setColor(ulColorTable[((style[lastX].flags & EF_UNDERLINE_COLOR)) / EF_UNDERLINE_COLOR_1 - 1].color(colorTable));
|
||||
}
|
||||
int y = rect.y() + m_parentDisplay->terminalFont()->fontAscent() + m_parentDisplay->terminalFont()->underlinePos()
|
||||
+ m_parentDisplay->terminalFont()->lineWidth();
|
||||
int lw = m_parentDisplay->terminalFont()->lineWidth();
|
||||
if (underline == RE_UNDERLINE_DOUBLE || underline == RE_UNDERLINE_CURL) {
|
||||
y = rect.bottom() - 1;
|
||||
lw = 1;
|
||||
} else {
|
||||
if (lw + lw + m_parentDisplay->terminalFont()->fontAscent() + m_parentDisplay->terminalFont()->underlinePos() > rect.height()) {
|
||||
lw = rect.height() - m_parentDisplay->terminalFont()->fontAscent() - m_parentDisplay->terminalFont()->underlinePos() - lw;
|
||||
}
|
||||
}
|
||||
pen.setWidth(lw);
|
||||
if (underline == RE_UNDERLINE_DOT) {
|
||||
pen.setStyle(Qt::DotLine);
|
||||
} else if (underline == RE_UNDERLINE_DASH) {
|
||||
pen.setStyle(Qt::DashLine);
|
||||
}
|
||||
if (underline == RE_UNDERLINE_CURL) {
|
||||
QVector<qreal> dashes(2, 2);
|
||||
pen.setDashPattern(dashes);
|
||||
}
|
||||
painter.setPen(pen);
|
||||
painter.drawLine(rect.x() + fontWidth * startUnderline, y, rect.x() + fontWidth * i - 1, y);
|
||||
if (underline == RE_UNDERLINE_DOUBLE) {
|
||||
painter.drawLine(rect.x() + fontWidth * startUnderline, y - 2, rect.x() + fontWidth * i - 1, y - 2);
|
||||
}
|
||||
if (underline == RE_UNDERLINE_CURL) {
|
||||
painter.drawLine(rect.x() + fontWidth * startUnderline + 2, y - 1, rect.x() + fontWidth * i - 1, y - 1);
|
||||
}
|
||||
|
||||
startUnderline = -1;
|
||||
}
|
||||
}
|
||||
if (i == width) {
|
||||
return;
|
||||
}
|
||||
// Sets the text selection colors, either:
|
||||
// - using reverseRendition(), which inverts the foreground/background
|
||||
// colors OR
|
||||
// - blending the foreground/background colors
|
||||
if (style[x].rendition.f.selected && invertedRendition) {
|
||||
backgroundColor = style[x].foregroundColor.color(colorTable);
|
||||
foregroundColor = style[x].backgroundColor.color(colorTable);
|
||||
} else {
|
||||
foregroundColor = style[x].foregroundColor.color(colorTable);
|
||||
backgroundColor = style[x].backgroundColor.color(colorTable);
|
||||
}
|
||||
|
||||
if (style[x].rendition.f.selected) {
|
||||
if (!invertedRendition) {
|
||||
backgroundColor = calculateBackgroundColor(style[x], colorTable).value_or(foregroundColor);
|
||||
if (backgroundColor == foregroundColor) {
|
||||
foregroundColor = style[x].backgroundColor.color(colorTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (style[x].rendition.f.strikeout && startStrikeOut == -1) {
|
||||
startStrikeOut = i;
|
||||
}
|
||||
if (style[x].rendition.f.overline && startOverline == -1) {
|
||||
startOverline = i;
|
||||
}
|
||||
if (style[x].rendition.f.underline && startUnderline == -1) {
|
||||
startUnderline = i;
|
||||
}
|
||||
lastX = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPainter::drawImagesBelowText(QPainter &painter, const QRect &rect, int fontWidth, int fontHeight, int &placementIdx)
|
||||
{
|
||||
Screen *screen = m_parentDisplay->screenWindow()->screen();
|
||||
|
||||
placementIdx = 0;
|
||||
qreal opacity = painter.opacity();
|
||||
int scrollDelta = m_parentDisplay->terminalFont()->fontHeight() * (m_parentDisplay->screenWindow()->currentLine() - screen->getHistLines());
|
||||
const bool origClipping = painter.hasClipping();
|
||||
const auto origClipRegion = painter.clipRegion();
|
||||
if (screen->hasGraphics()) {
|
||||
painter.setClipRect(rect);
|
||||
while (1) {
|
||||
TerminalGraphicsPlacement_t *p = screen->getGraphicsPlacement(placementIdx);
|
||||
if (!p || p->z >= 0) {
|
||||
break;
|
||||
}
|
||||
int x = p->col * fontWidth + p->X + m_parentDisplay->contentRect().left();
|
||||
int y = p->row * fontHeight + p->Y + m_parentDisplay->contentRect().top();
|
||||
QRectF srcRect(0, 0, p->pixmap.width(), p->pixmap.height());
|
||||
QRectF dstRect(x, y - scrollDelta, p->pixmap.width(), p->pixmap.height());
|
||||
painter.setOpacity(p->opacity);
|
||||
painter.drawPixmap(dstRect, p->pixmap, srcRect);
|
||||
placementIdx++;
|
||||
}
|
||||
painter.setOpacity(opacity);
|
||||
painter.setClipRegion(origClipRegion);
|
||||
painter.setClipping(origClipping);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPainter::drawImagesAboveText(QPainter &painter, const QRect &rect, int fontWidth, int fontHeight, int &placementIdx)
|
||||
{
|
||||
// setup painter
|
||||
Screen *screen = m_parentDisplay->screenWindow()->screen();
|
||||
|
||||
qreal opacity = painter.opacity();
|
||||
int scrollDelta = fontHeight * (m_parentDisplay->screenWindow()->currentLine() - screen->getHistLines());
|
||||
const bool origClipping = painter.hasClipping();
|
||||
const auto origClipRegion = painter.clipRegion();
|
||||
|
||||
if (screen->hasGraphics()) {
|
||||
painter.setClipRect(rect);
|
||||
while (1) {
|
||||
TerminalGraphicsPlacement_t *p = screen->getGraphicsPlacement(placementIdx);
|
||||
if (!p) {
|
||||
break;
|
||||
}
|
||||
QPixmap image = p->pixmap;
|
||||
int x = p->col * fontWidth + p->X + m_parentDisplay->contentRect().left();
|
||||
int y = p->row * fontHeight + p->Y + m_parentDisplay->contentRect().top();
|
||||
QRectF srcRect(0, 0, image.width(), image.height());
|
||||
QRectF dstRect(x, y - scrollDelta, image.width(), image.height());
|
||||
painter.setOpacity(p->opacity);
|
||||
painter.drawPixmap(dstRect, image, srcRect);
|
||||
placementIdx++;
|
||||
}
|
||||
painter.setOpacity(opacity);
|
||||
painter.setClipRegion(origClipRegion);
|
||||
painter.setClipping(origClipping);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPainter::drawTextCharacters(QPainter &painter,
|
||||
const QRect &rect,
|
||||
const QString &text,
|
||||
Character style,
|
||||
const QColor *colorTable,
|
||||
const bool invertedRendition,
|
||||
const LineProperty lineProperty,
|
||||
bool printerFriendly,
|
||||
RenditionFlags &oldRendition,
|
||||
QColor oldColor,
|
||||
int normalWeight,
|
||||
QFont::Weight boldWeight)
|
||||
{
|
||||
// setup painter
|
||||
if (style.rendition.f.conceal != 0) {
|
||||
return;
|
||||
}
|
||||
QColor characterColor;
|
||||
if (!printerFriendly) {
|
||||
// Sets the text selection colors, either:
|
||||
// - invertedRendition, which inverts the foreground/background colors OR
|
||||
// - blending the foreground/background colors
|
||||
if (m_parentDisplay->textBlinking() && (style.rendition.f.blink != 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (style.rendition.f.selected) {
|
||||
if (invertedRendition) {
|
||||
reverseRendition(style);
|
||||
}
|
||||
}
|
||||
|
||||
QColor foregroundColor = style.foregroundColor.color(colorTable);
|
||||
QColor backgroundColor = style.backgroundColor.color(colorTable);
|
||||
|
||||
if (style.rendition.f.selected) {
|
||||
if (!invertedRendition) {
|
||||
backgroundColor = calculateBackgroundColor(style, colorTable).value_or(foregroundColor);
|
||||
if (backgroundColor == foregroundColor) {
|
||||
foregroundColor = style.backgroundColor.color(colorTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
characterColor = foregroundColor;
|
||||
if (style.rendition.f.cursor != 0) {
|
||||
drawCursor(painter, rect, foregroundColor, backgroundColor, characterColor);
|
||||
}
|
||||
if (m_parentDisplay->filterChain()->showUrlHint()) {
|
||||
if ((style.flags & EF_REPL) == EF_REPL_PROMPT) {
|
||||
int h, s, v;
|
||||
characterColor.getHsv(&h, &s, &v);
|
||||
s = s / 2;
|
||||
v = v / 2;
|
||||
characterColor.setHsv(h, s, v);
|
||||
}
|
||||
if ((style.flags & EF_REPL) == EF_REPL_INPUT) {
|
||||
int h, s, v;
|
||||
characterColor.getHsv(&h, &s, &v);
|
||||
s = (511 + s) / 3;
|
||||
v = (511 + v) / 3;
|
||||
characterColor.setHsv(h, s, v);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
characterColor = QColor(0, 0, 0);
|
||||
}
|
||||
|
||||
// The weight used as bold depends on selected font's weight.
|
||||
// "Regular" will use "Bold", but e.g. "Thin" will use "Light".
|
||||
// Note that QFont::weight/setWeight() returns/takes an int in Qt5,
|
||||
// and a QFont::Weight in Qt6
|
||||
QFont savedFont;
|
||||
bool restoreFont = false;
|
||||
if ((style.flags & EF_EMOJI_REPRESENTATION) && m_parentDisplay->terminalFont()->hasExtraFont(0)) {
|
||||
savedFont = painter.font();
|
||||
restoreFont = true;
|
||||
painter.setFont(m_parentDisplay->terminalFont()->getExtraFont(0));
|
||||
} else {
|
||||
if (oldRendition != style.rendition.all) {
|
||||
const bool useBold = ((style.rendition.f.bold != 0) && m_parentDisplay->terminalFont()->boldIntense());
|
||||
const bool useItalic = (style.rendition.f.italic != 0) || m_parentDisplay->font().italic();
|
||||
|
||||
QFont currentFont = painter.font();
|
||||
|
||||
const bool isCurrentBold = currentFont.weight() >= boldWeight;
|
||||
if (isCurrentBold != useBold || currentFont.italic() != useItalic) {
|
||||
currentFont.setWeight(useBold ? boldWeight : normalWeight);
|
||||
currentFont.setItalic(useItalic);
|
||||
painter.setFont(currentFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (characterColor != oldColor) {
|
||||
QPen pen = painter.pen();
|
||||
if (pen.color() != characterColor) {
|
||||
painter.setPen(characterColor);
|
||||
}
|
||||
}
|
||||
// const bool origClipping = painter.hasClipping();
|
||||
// const auto origClipRegion = painter.clipRegion();
|
||||
// painter.setClipRect(rect);
|
||||
// draw text
|
||||
if (isLineCharString(text) && !m_parentDisplay->terminalFont()->useFontLineCharacters()) {
|
||||
int y = rect.y();
|
||||
|
||||
if (lineProperty & LINE_DOUBLEHEIGHT_BOTTOM) {
|
||||
y -= m_parentDisplay->terminalFont()->fontHeight() / 2;
|
||||
}
|
||||
|
||||
drawLineCharString(m_parentDisplay, painter, rect.x(), y, text, style);
|
||||
} else {
|
||||
int y = rect.y() + m_parentDisplay->terminalFont()->fontAscent();
|
||||
|
||||
if (lineProperty & LINE_DOUBLEHEIGHT_BOTTOM) {
|
||||
y -= m_parentDisplay->terminalFont()->fontHeight() / 2;
|
||||
} else {
|
||||
// We shift half way down here to center
|
||||
y += m_parentDisplay->terminalFont()->lineSpacing() / 2;
|
||||
}
|
||||
painter.drawText(rect.x(), y, text);
|
||||
if (0 && text.toUcs4().length() > 1) {
|
||||
fprintf(stderr, " %i ", text.toUcs4().length());
|
||||
for (int i = 0; i < text.toUcs4().length(); i++) {
|
||||
fprintf(stderr, " %04x ", text.toUcs4()[i]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
if (restoreFont) {
|
||||
painter.setFont(savedFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,8 @@ public Q_SLOTS:
|
||||
bool PrinterFriendly,
|
||||
int imageSize,
|
||||
bool bidiEnabled,
|
||||
QVector<LineProperty> lineProperties);
|
||||
QVector<LineProperty> lineProperties,
|
||||
CharacterColor const *ulColorTable = nullptr);
|
||||
|
||||
// draw a transparent rectangle over the line of the current match
|
||||
void drawCurrentResultRect(QPainter &painter, const QRect &searchResultRect);
|
||||
@@ -86,22 +87,48 @@ private:
|
||||
// draws a string of line graphics
|
||||
void drawLineCharString(TerminalDisplay *display, QPainter &painter, int x, int y, const QString &str, const Character attributes);
|
||||
|
||||
// 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,
|
||||
Character style,
|
||||
const QColor *colorTable,
|
||||
const bool invertedRendition,
|
||||
const LineProperty lineProperty);
|
||||
|
||||
void drawPrinterFriendlyTextFragment(QPainter &painter, const QRect &rect, const QString &text, Character style, const LineProperty lineProperty);
|
||||
|
||||
// draws the cursor character
|
||||
void drawCursor(QPainter &painter, const QRect &rect, const QColor &foregroundColor, const QColor &backgroundColor, QColor &characterColor);
|
||||
|
||||
TerminalDisplay *m_parentDisplay = nullptr;
|
||||
void drawBelowText(QPainter &painter,
|
||||
const QRect &rect,
|
||||
Character *style,
|
||||
int startX,
|
||||
int width,
|
||||
int fontWidth,
|
||||
const QColor *colorTable,
|
||||
const bool invertedRendition,
|
||||
int *vis2line,
|
||||
int *line2log,
|
||||
bool bidiEnabled);
|
||||
void drawAboveText(QPainter &painter,
|
||||
const QRect &rect,
|
||||
Character *style,
|
||||
int startX,
|
||||
int width,
|
||||
int fontWidth,
|
||||
const QColor *colorTable,
|
||||
const bool invertedRendition,
|
||||
int *vis2line,
|
||||
int *line2log,
|
||||
bool bidiEnabled,
|
||||
CharacterColor const *ulColorTable);
|
||||
void drawImagesBelowText(QPainter &painter, const QRect &rect, int fontWidth, int fontHeight, int &placementIdx);
|
||||
void drawImagesAboveText(QPainter &painter, const QRect &rect, int fontWidth, int fontHeight, int &placementIdx);
|
||||
|
||||
void drawTextCharacters(QPainter &painter,
|
||||
const QRect &rect,
|
||||
const QString &text,
|
||||
Character style,
|
||||
const QColor *colorTable,
|
||||
const bool invertedRendition,
|
||||
const LineProperty lineProperty,
|
||||
bool printerFriendly,
|
||||
RenditionFlags &oldRendition,
|
||||
QColor oldColor,
|
||||
int normalWeight,
|
||||
QFont::Weight boldWeight);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<!--
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="enableBidiRenderingButton">
|
||||
<property name="sizePolicy">
|
||||
@@ -234,6 +235,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
-->
|
||||
<item row="6" column="1">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>9</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="colorSchemeTab">
|
||||
<attribute name="title">
|
||||
@@ -56,7 +56,7 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout name="colorSchemesBtnLayout" class="QVBoxLayout">
|
||||
<layout class="QVBoxLayout" name="colorSchemesBtnLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
@@ -755,6 +755,116 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="CTLtab">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Complex Text Layout</p></body></html></string>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Complex Text Layout</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>3</x>
|
||||
<y>3</y>
|
||||
<width>611</width>
|
||||
<height>441</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="CTL_2">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Emoji Font:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="emojiFontPreview">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="enableBidiRenderingButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enable Bi-Directional display on terminals (valid for Arabic, Farsi or Hebrew only)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bi-Directional text rendering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="bidiLineLTR">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Lines are always LTR, rather than determined by first strong character</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>force LTR line direction</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="enableBidiTableDirOverrideButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Treat table drawing characters as strong LTR.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Table characters BiDi mode override</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -833,7 +943,7 @@
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="cursorShape"/>
|
||||
<buttongroup name="cursorColor"/>
|
||||
<buttongroup name="cursorShape"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
@@ -697,6 +697,8 @@ void EditProfileDialog::setupAppearancePage(const Profile::Ptr &profile)
|
||||
|
||||
connect(_appearanceUi->chooseFontButton, &QAbstractButton::clicked, this, &EditProfileDialog::showFontDialog);
|
||||
|
||||
connect(_appearanceUi->emojiFontPreview, &QAbstractButton::clicked, this, &EditProfileDialog::showEmojiFontDialog);
|
||||
|
||||
// setup font preview
|
||||
const bool antialias = profile->antiAliasFonts();
|
||||
|
||||
@@ -706,6 +708,10 @@ void EditProfileDialog::setupAppearancePage(const Profile::Ptr &profile)
|
||||
_appearanceUi->fontPreview->setFont(profileFont);
|
||||
_appearanceUi->fontPreview->setText(QStringLiteral("%1 %2pt").arg(profileFont.family()).arg(profileFont.pointSize()));
|
||||
|
||||
QFont emojiFont = profile->emojiFont();
|
||||
_appearanceUi->emojiFontPreview->setFont(profileFont);
|
||||
_appearanceUi->emojiFontPreview->setText(QStringLiteral("%1 %2pt").arg(emojiFont.family()).arg(emojiFont.pointSize()));
|
||||
|
||||
// setup font smoothing
|
||||
_appearanceUi->antialiasTextButton->setChecked(antialias);
|
||||
connect(_appearanceUi->antialiasTextButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setAntialiasText);
|
||||
@@ -791,6 +797,17 @@ void EditProfileDialog::setupAppearancePage(const Profile::Ptr &profile)
|
||||
getNewButton->setText(QStringLiteral("Get New..."));
|
||||
getNewButton->setConfigFile(QStringLiteral("konsole.knsrc"));
|
||||
_appearanceUi->colorSchemesBtnLayout->addWidget(getNewButton);
|
||||
|
||||
_appearanceUi->enableBidiRenderingButton->setChecked(profile->bidiRenderingEnabled());
|
||||
connect(_appearanceUi->enableBidiRenderingButton, &QPushButton::toggled, this, &EditProfileDialog::togglebidiRendering);
|
||||
|
||||
_appearanceUi->enableBidiTableDirOverrideButton->setChecked(profile->property<bool>(Profile::BidiTableDirOverride));
|
||||
connect(_appearanceUi->enableBidiTableDirOverrideButton, &QPushButton::toggled, this, &EditProfileDialog::togglebidiTableDirOverride);
|
||||
_appearanceUi->enableBidiTableDirOverrideButton->setEnabled(profile->bidiRenderingEnabled());
|
||||
|
||||
_appearanceUi->bidiLineLTR->setChecked(profile->property<bool>(Profile::BidiLineLTR));
|
||||
connect(_appearanceUi->bidiLineLTR, &QPushButton::toggled, this, &EditProfileDialog::togglebidiLineLTR);
|
||||
_appearanceUi->bidiLineLTR->setEnabled(profile->bidiRenderingEnabled());
|
||||
}
|
||||
|
||||
void EditProfileDialog::setAntialiasText(bool enable)
|
||||
@@ -1076,11 +1093,33 @@ void EditProfileDialog::showFontDialog()
|
||||
updateFontPreview(_profile->font());
|
||||
});
|
||||
}
|
||||
|
||||
_fontDialog->setFont(_profile->font());
|
||||
_fontDialog->show();
|
||||
}
|
||||
|
||||
void EditProfileDialog::showEmojiFontDialog()
|
||||
{
|
||||
if (_emojiFontDialog == nullptr) {
|
||||
_emojiFontDialog = new FontDialog(this, true, _profile->emojiFont());
|
||||
_emojiFontDialog->setModal(true);
|
||||
connect(_emojiFontDialog, &FontDialog::fontChanged, this, [this](const QFont &font) {
|
||||
preview(Profile::EmojiFont, font);
|
||||
updateEmojiFontPreview(font);
|
||||
});
|
||||
connect(_emojiFontDialog, &FontDialog::accepted, this, [this]() {
|
||||
const QFont font = _emojiFontDialog->font();
|
||||
preview(Profile::EmojiFont, font);
|
||||
updateTempProfileProperty(Profile::EmojiFont, font);
|
||||
updateEmojiFontPreview(font);
|
||||
});
|
||||
connect(_emojiFontDialog, &FontDialog::rejected, this, [this]() {
|
||||
unpreview(Profile::EmojiFont);
|
||||
updateEmojiFontPreview(_profile->emojiFont());
|
||||
});
|
||||
}
|
||||
_emojiFontDialog->show();
|
||||
}
|
||||
|
||||
void EditProfileDialog::updateFontPreview(QFont font)
|
||||
{
|
||||
bool aa = _profile->antiAliasFonts();
|
||||
@@ -1090,6 +1129,15 @@ void EditProfileDialog::updateFontPreview(QFont font)
|
||||
_appearanceUi->fontPreview->setText(QStringLiteral("%1 %2pt").arg(font.family()).arg(font.pointSize()));
|
||||
}
|
||||
|
||||
void EditProfileDialog::updateEmojiFontPreview(QFont font)
|
||||
{
|
||||
bool aa = _profile->antiAliasFonts();
|
||||
font.setStyleStrategy(aa ? QFont::PreferAntialias : QFont::NoAntialias);
|
||||
QFont emojiFont = _profile->emojiFont();
|
||||
_appearanceUi->emojiFontPreview->setFont(_profile->font());
|
||||
_appearanceUi->emojiFontPreview->setText(QStringLiteral("%1 %2pt").arg(emojiFont.family()).arg(emojiFont.pointSize()));
|
||||
}
|
||||
|
||||
void EditProfileDialog::removeColorScheme()
|
||||
{
|
||||
const QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes();
|
||||
@@ -1732,8 +1780,6 @@ void EditProfileDialog::setupAdvancedPage(const Profile::Ptr &profile)
|
||||
connect(_advancedUi->enableFlowControlButton, &QPushButton::toggled, this, &EditProfileDialog::toggleFlowControl);
|
||||
_appearanceUi->enableBlinkingCursorButton->setChecked(profile->property<bool>(Profile::BlinkingCursorEnabled));
|
||||
connect(_appearanceUi->enableBlinkingCursorButton, &QPushButton::toggled, this, &EditProfileDialog::toggleBlinkingCursor);
|
||||
_advancedUi->enableBidiRenderingButton->setChecked(profile->property<bool>(Profile::BidiRenderingEnabled));
|
||||
connect(_advancedUi->enableBidiRenderingButton, &QPushButton::toggled, this, &EditProfileDialog::togglebidiRendering);
|
||||
_advancedUi->enableReverseUrlHints->setChecked(profile->property<bool>(Profile::ReverseUrlHints));
|
||||
connect(_advancedUi->enableReverseUrlHints, &QPushButton::toggled, this, &EditProfileDialog::toggleReverseUrlHints);
|
||||
|
||||
@@ -1820,6 +1866,18 @@ void EditProfileDialog::wordCharactersChanged(const QString &text)
|
||||
void EditProfileDialog::togglebidiRendering(bool enable)
|
||||
{
|
||||
updateTempProfileProperty(Profile::BidiRenderingEnabled, enable);
|
||||
_appearanceUi->enableBidiTableDirOverrideButton->setEnabled(enable);
|
||||
_appearanceUi->bidiLineLTR->setEnabled(enable);
|
||||
}
|
||||
|
||||
void EditProfileDialog::togglebidiTableDirOverride(bool enable)
|
||||
{
|
||||
updateTempProfileProperty(Profile::BidiTableDirOverride, enable);
|
||||
}
|
||||
|
||||
void EditProfileDialog::togglebidiLineLTR(bool enable)
|
||||
{
|
||||
updateTempProfileProperty(Profile::BidiLineLTR, enable);
|
||||
}
|
||||
|
||||
void EditProfileDialog::toggleUnderlineLinks(bool enable)
|
||||
|
||||
@@ -151,6 +151,9 @@ private Q_SLOTS:
|
||||
void terminalMarginChanged(int margin);
|
||||
void lineSpacingChanged(int);
|
||||
void setTerminalCenter(bool enable);
|
||||
void togglebidiRendering(bool);
|
||||
void togglebidiTableDirOverride(bool);
|
||||
void togglebidiLineLTR(bool);
|
||||
|
||||
#if KNEWSTUFF_VERSION >= QT_VERSION_CHECK(5, 91, 0)
|
||||
void gotNewColorSchemes(const QList<KNSCore::EntryInternal> &changedEntries);
|
||||
@@ -168,6 +171,7 @@ private Q_SLOTS:
|
||||
void colorSchemeSelected();
|
||||
void previewColorScheme(const QModelIndex &index);
|
||||
void showFontDialog();
|
||||
void showEmojiFontDialog();
|
||||
void toggleMouseWheelZoom(bool enable);
|
||||
|
||||
// scrolling page
|
||||
@@ -210,7 +214,6 @@ private Q_SLOTS:
|
||||
// advanced page
|
||||
void toggleBlinkingText(bool);
|
||||
void toggleFlowControl(bool);
|
||||
void togglebidiRendering(bool);
|
||||
void updateUrlHintsModifier(bool);
|
||||
void toggleReverseUrlHints(bool);
|
||||
|
||||
@@ -280,6 +283,7 @@ private:
|
||||
void updateTransparencyWarning();
|
||||
|
||||
void updateFontPreview(QFont font);
|
||||
void updateEmojiFontPreview(QFont font);
|
||||
|
||||
// Update _tempProfile in a way of respecting the apply button.
|
||||
// When used with some previewed property, this method should
|
||||
@@ -347,6 +351,7 @@ private:
|
||||
ColorSchemeEditor *_colorDialog = nullptr;
|
||||
QDialogButtonBox *_buttonBox = nullptr;
|
||||
FontDialog *_fontDialog = nullptr;
|
||||
FontDialog *_emojiFontDialog = nullptr;
|
||||
|
||||
InitialProfileState _profileState = ExistingProfile;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user