tdesktop/Telegram/SourceFiles/ui/widgets/labels.cpp

781 lines
23 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/popup_menu.h"
#include "mainwindow.h"
#include "lang.h"
namespace Ui {
namespace {
TextParseOptions _labelOptions = {
TextParseMultiline, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _labelMarkedOptions = {
TextParseMultiline | TextParseRichText | TextParseLinks | TextParseHashtags | TextParseMentions | TextParseBotCommands | TextParseMono, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
} // namespace
CrossFadeAnimation::CrossFadeAnimation(style::color bg) : _bg(bg) {
}
void CrossFadeAnimation::addLine(Part was, Part now) {
_lines.push_back(Line(std_::move(was), std_::move(now)));
}
void CrossFadeAnimation::paintFrame(Painter &p, float64 positionReady, float64 alphaWas, float64 alphaNow) {
if (_lines.isEmpty()) return;
for_const (auto &line, _lines) {
paintLine(p, line, positionReady, alphaWas, alphaNow);
}
}
void CrossFadeAnimation::paintLine(Painter &p, const Line &line, float64 positionReady, float64 alphaWas, float64 alphaNow) {
auto &snapshotWas = line.was.snapshot;
auto &snapshotNow = line.now.snapshot;
t_assert(!snapshotWas.isNull() || !snapshotNow.isNull());
auto positionWas = line.was.position;
auto positionNow = line.now.position;
auto left = anim::interpolate(positionWas.x(), positionNow.x(), positionReady);
auto topDelta = (snapshotNow.height() / cIntRetinaFactor()) - (snapshotWas.height() / cIntRetinaFactor());
auto widthDelta = (snapshotNow.width() / cIntRetinaFactor()) - (snapshotWas.width() / cIntRetinaFactor());
auto topWas = anim::interpolate(positionWas.y(), positionNow.y() + topDelta, positionReady);
auto topNow = topWas - topDelta;
p.setOpacity(alphaWas);
if (!snapshotWas.isNull()) {
p.drawPixmap(left, topWas, snapshotWas);
if (topDelta > 0) {
p.fillRect(left, topWas - topDelta, snapshotWas.width() / cIntRetinaFactor(), topDelta, _bg);
}
}
if (widthDelta > 0) {
p.fillRect(left + (snapshotWas.width() / cIntRetinaFactor()), topNow, widthDelta, snapshotNow.height() / cIntRetinaFactor(), _bg);
}
p.setOpacity(alphaNow);
if (!snapshotNow.isNull()) {
p.drawPixmap(left, topNow, snapshotNow);
if (topDelta < 0) {
p.fillRect(left, topNow + topDelta, snapshotNow.width() / cIntRetinaFactor(), -topDelta, _bg);
}
}
if (widthDelta < 0) {
p.fillRect(left + (snapshotNow.width() / cIntRetinaFactor()), topWas, -widthDelta, snapshotWas.height() / cIntRetinaFactor(), _bg);
}
}
LabelSimple::LabelSimple(QWidget *parent, const style::LabelSimple &st, const QString &value) : TWidget(parent)
, _st(st) {
setText(value);
}
void LabelSimple::setText(const QString &value, bool *outTextChanged) {
if (_fullText == value) {
if (outTextChanged) *outTextChanged = false;
return;
}
_fullText = value;
_fullTextWidth = _st.font->width(_fullText);
if (!_st.maxWidth || _fullTextWidth <= _st.maxWidth) {
_text = _fullText;
_textWidth = _fullTextWidth;
} else {
auto newText = _st.font->elided(_fullText, _st.maxWidth);
if (newText == _text) {
if (outTextChanged) *outTextChanged = false;
return;
}
_text = newText;
_textWidth = _st.font->width(_text);
}
resize(_textWidth, _st.font->height);
update();
if (outTextChanged) *outTextChanged = true;
}
void LabelSimple::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setFont(_st.font);
p.setPen(_st.textFg);
p.drawTextLeft(0, 0, width(), _text, _textWidth);
}
FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st) : TWidget(parent)
, _text(st.width ? st.width : QFIXED_MAX)
, _st(st)
, _contextCopyText(lang(lng_context_copy_text)) {
init();
}
FlatLabel::FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::FlatLabel &st) : TWidget(parent)
, _text(st.width ? st.width : QFIXED_MAX)
, _st(st)
, _contextCopyText(lang(lng_context_copy_text)) {
if (initType == InitType::Rich) {
setRichText(text);
} else {
setText(text);
}
init();
}
void FlatLabel::init() {
_trippleClickTimer.setSingleShot(true);
_touchSelectTimer.setSingleShot(true);
connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
}
void FlatLabel::textUpdated() {
refreshSize();
setMouseTracking(_selectable || _text.hasLinks());
update();
}
void FlatLabel::setText(const QString &text) {
_text.setText(_st.style, text, _labelOptions);
textUpdated();
}
void FlatLabel::setRichText(const QString &text) {
_text.setRichText(_st.style, text, _labelOptions);
textUpdated();
}
void FlatLabel::setMarkedText(const TextWithEntities &textWithEntities) {
_text.setMarkedText(_st.style, textWithEntities, _labelMarkedOptions);
textUpdated();
}
void FlatLabel::setSelectable(bool selectable) {
_selectable = selectable;
setMouseTracking(_selectable || _text.hasLinks());
}
void FlatLabel::setDoubleClickSelectsParagraph(bool doubleClickSelectsParagraph) {
_doubleClickSelectsParagraph = doubleClickSelectsParagraph;
}
void FlatLabel::setContextCopyText(const QString &copyText) {
_contextCopyText = copyText;
}
void FlatLabel::setExpandLinksMode(ExpandLinksMode mode) {
_contextExpandLinksMode = mode;
}
void FlatLabel::setBreakEverywhere(bool breakEverywhere) {
_breakEverywhere = breakEverywhere;
}
int FlatLabel::resizeGetHeight(int newWidth) {
_allowedWidth = newWidth;
int textWidth = countTextWidth();
int textHeight = countTextHeight(textWidth);
return _st.margin.top() + textHeight + _st.margin.bottom();
}
int FlatLabel::naturalWidth() const {
return _text.maxWidth();
}
int FlatLabel::countTextWidth() const {
return _allowedWidth ? (_allowedWidth - _st.margin.left() - _st.margin.right()) : (_st.width ? _st.width : _text.maxWidth());
}
int FlatLabel::countTextHeight(int textWidth) {
_fullTextHeight = _text.countHeight(textWidth);
return _st.maxHeight ? qMin(_fullTextHeight, _st.maxHeight) : _fullTextHeight;
}
void FlatLabel::refreshSize() {
int textWidth = countTextWidth();
int textHeight = countTextHeight(textWidth);
int fullWidth = _st.margin.left() + textWidth + _st.margin.right();
int fullHeight = _st.margin.top() + textHeight + _st.margin.bottom();
resize(fullWidth, fullHeight);
}
void FlatLabel::setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk) {
_text.setLink(lnkIndex, lnk);
}
void FlatLabel::setClickHandlerHook(ClickHandlerHook &&hook) {
_clickHandlerHook = std_::move(hook);
}
void FlatLabel::mouseMoveEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
dragActionUpdate();
}
void FlatLabel::mousePressEvent(QMouseEvent *e) {
if (_contextMenu) {
e->accept();
return; // ignore mouse press, that was hiding context menu
}
dragActionStart(e->globalPos(), e->button());
}
Text::StateResult FlatLabel::dragActionStart(const QPoint &p, Qt::MouseButton button) {
_lastMousePos = p;
auto state = dragActionUpdate();
if (button != Qt::LeftButton) return state;
ClickHandler::pressed();
_dragAction = NoDrag;
_dragWasInactive = App::wnd()->inactivePress();
if (_dragWasInactive) App::wnd()->inactivePress(false);
if (ClickHandler::getPressed()) {
_dragStartPosition = mapFromGlobal(_lastMousePos);
_dragAction = PrepareDrag;
}
if (!_selectable || _dragAction != NoDrag) {
return state;
}
if (_trippleClickTimer.isActive() && (_lastMousePos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) {
if (state.uponSymbol) {
_selection = { state.symbol, state.symbol };
_savedSelection = { 0, 0 };
_dragSymbol = state.symbol;
_dragAction = Selecting;
_selectionType = TextSelectType::Paragraphs;
updateHover(state);
_trippleClickTimer.start(QApplication::doubleClickInterval());
update();
}
}
if (_selectionType != TextSelectType::Paragraphs) {
_dragSymbol = state.symbol;
bool uponSelected = state.uponSymbol;
if (uponSelected) {
if (_dragSymbol < _selection.from || _dragSymbol >= _selection.to) {
uponSelected = false;
}
}
if (uponSelected) {
_dragStartPosition = mapFromGlobal(_lastMousePos);
_dragAction = PrepareDrag; // start text drag
} else if (!_dragWasInactive) {
if (state.afterSymbol) ++_dragSymbol;
_selection = { _dragSymbol, _dragSymbol };
_savedSelection = { 0, 0 };
_dragAction = Selecting;
update();
}
}
return state;
}
Text::StateResult FlatLabel::dragActionFinish(const QPoint &p, Qt::MouseButton button) {
_lastMousePos = p;
auto state = dragActionUpdate();
ClickHandlerPtr activated = ClickHandler::unpressed();
if (_dragAction == Dragging) {
activated.clear();
} else if (_dragAction == PrepareDrag) {
_selection = { 0, 0 };
_savedSelection = { 0, 0 };
update();
}
_dragAction = NoDrag;
_selectionType = TextSelectType::Letters;
if (activated) {
if (!_clickHandlerHook || _clickHandlerHook(activated, button)) {
App::activateClickHandler(activated, button);
}
}
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
if (!_selection.empty()) {
QApplication::clipboard()->setText(_text.originalText(_selection, _contextExpandLinksMode), QClipboard::Selection);
}
#endif // Q_OS_LINUX32 || Q_OS_LINUX64
return state;
}
void FlatLabel::mouseReleaseEvent(QMouseEvent *e) {
dragActionFinish(e->globalPos(), e->button());
if (!rect().contains(e->pos())) {
leaveEvent(e);
}
}
void FlatLabel::mouseDoubleClickEvent(QMouseEvent *e) {
auto state = dragActionStart(e->globalPos(), e->button());
if (((_dragAction == Selecting) || (_dragAction == NoDrag)) && _selectionType == TextSelectType::Letters) {
if (state.uponSymbol) {
_dragSymbol = state.symbol;
_selectionType = _doubleClickSelectsParagraph ? TextSelectType::Paragraphs : TextSelectType::Words;
if (_dragAction == NoDrag) {
_dragAction = Selecting;
_selection = { state.symbol, state.symbol };
_savedSelection = { 0, 0 };
}
mouseMoveEvent(e);
_trippleClickPoint = e->globalPos();
_trippleClickTimer.start(QApplication::doubleClickInterval());
}
}
}
void FlatLabel::enterEventHook(QEvent *e) {
_lastMousePos = QCursor::pos();
dragActionUpdate();
}
void FlatLabel::leaveEventHook(QEvent *e) {
ClickHandler::clearActive(this);
}
void FlatLabel::focusOutEvent(QFocusEvent *e) {
if (!_selection.empty()) {
if (_contextMenu) {
_savedSelection = _selection;
}
_selection = { 0, 0 };
update();
}
}
void FlatLabel::focusInEvent(QFocusEvent *e) {
if (!_savedSelection.empty()) {
_selection = _savedSelection;
_savedSelection = { 0, 0 };
update();
}
}
void FlatLabel::keyPressEvent(QKeyEvent *e) {
e->ignore();
if (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier))) {
if (!_selection.empty()) {
onCopySelectedText();
e->accept();
}
#ifdef Q_OS_MAC
} else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) {
auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection;
if (!selection.empty()) {
QApplication::clipboard()->setText(_text.originalText(selection, _contextExpandLinksMode), QClipboard::FindBuffer);
}
#endif // Q_OS_MAC
}
}
void FlatLabel::contextMenuEvent(QContextMenuEvent *e) {
if (!_selectable) return;
showContextMenu(e, ContextMenuReason::FromEvent);
}
bool FlatLabel::event(QEvent *e) {
if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
QTouchEvent *ev = static_cast<QTouchEvent*>(e);
if (ev->device()->type() == QTouchDevice::TouchScreen) {
touchEvent(ev);
return true;
}
}
return QWidget::event(e);
}
void FlatLabel::touchEvent(QTouchEvent *e) {
const Qt::TouchPointStates &states(e->touchPointStates());
if (e->type() == QEvent::TouchCancel) { // cancel
if (!_touchInProgress) return;
_touchInProgress = false;
_touchSelectTimer.stop();
_touchSelect = false;
_dragAction = NoDrag;
return;
}
if (!e->touchPoints().isEmpty()) {
_touchPrevPos = _touchPos;
_touchPos = e->touchPoints().cbegin()->screenPos().toPoint();
}
switch (e->type()) {
case QEvent::TouchBegin:
if (_contextMenu) {
e->accept();
return; // ignore mouse press, that was hiding context menu
}
if (_touchInProgress) return;
if (e->touchPoints().isEmpty()) return;
_touchInProgress = true;
_touchSelectTimer.start(QApplication::startDragTime());
_touchSelect = false;
_touchStart = _touchPrevPos = _touchPos;
break;
case QEvent::TouchUpdate:
if (!_touchInProgress) return;
if (_touchSelect) {
_lastMousePos = _touchPos;
dragActionUpdate();
}
break;
case QEvent::TouchEnd:
if (!_touchInProgress) return;
_touchInProgress = false;
if (_touchSelect) {
dragActionFinish(_touchPos, Qt::RightButton);
QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos);
showContextMenu(&contextMenu, ContextMenuReason::FromTouch);
} else { // one short tap -- like mouse click
dragActionStart(_touchPos, Qt::LeftButton);
dragActionFinish(_touchPos, Qt::LeftButton);
}
_touchSelectTimer.stop();
_touchSelect = false;
break;
}
}
void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason) {
if (_contextMenu) {
_contextMenu->deleteLater();
_contextMenu = nullptr;
}
if (e->reason() == QContextMenuEvent::Mouse) {
_lastMousePos = e->globalPos();
} else {
_lastMousePos = QCursor::pos();
}
auto state = dragActionUpdate();
bool hasSelection = !_selection.empty();
bool uponSelection = state.uponSymbol && (state.symbol >= _selection.from) && (state.symbol < _selection.to);
bool fullSelection = _text.isFullSelection(_selection);
if (reason == ContextMenuReason::FromTouch && hasSelection && !uponSelection) {
uponSelection = hasSelection;
}
_contextMenu = new Ui::PopupMenu(nullptr);
_contextMenuClickHandler = ClickHandler::getActive();
if (fullSelection && !_contextCopyText.isEmpty()) {
_contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText()))->setEnabled(true);
} else if (uponSelection && !fullSelection) {
_contextMenu->addAction(lang(lng_context_copy_selected), this, SLOT(onCopySelectedText()))->setEnabled(true);
} else if (!hasSelection && !_contextCopyText.isEmpty()) {
_contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText()))->setEnabled(true);
}
QString linkCopyToClipboardText = _contextMenuClickHandler ? _contextMenuClickHandler->copyToClipboardContextItemText() : QString();
if (!linkCopyToClipboardText.isEmpty()) {
_contextMenu->addAction(linkCopyToClipboardText, this, SLOT(onCopyContextUrl()))->setEnabled(true);
}
if (_contextMenu->actions().isEmpty()) {
delete _contextMenu;
_contextMenu = nullptr;
} else {
connect(_contextMenu, SIGNAL(destroyed(QObject*)), this, SLOT(onContextMenuDestroy(QObject*)));
_contextMenu->popup(e->globalPos());
e->accept();
}
}
void FlatLabel::onCopySelectedText() {
auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection;
if (!selection.empty()) {
QApplication::clipboard()->setText(_text.originalText(selection, _contextExpandLinksMode));
}
}
void FlatLabel::onCopyContextText() {
QApplication::clipboard()->setText(_text.originalText({ 0, 0xFFFF }, _contextExpandLinksMode));
}
void FlatLabel::onCopyContextUrl() {
if (_contextMenuClickHandler) {
_contextMenuClickHandler->copyToClipboard();
}
}
void FlatLabel::onTouchSelect() {
_touchSelect = true;
dragActionStart(_touchPos, Qt::LeftButton);
}
void FlatLabel::onContextMenuDestroy(QObject *obj) {
if (obj == _contextMenu) {
_contextMenu = nullptr;
}
}
void FlatLabel::onExecuteDrag() {
if (_dragAction != Dragging) return;
auto state = getTextState(_dragStartPosition);
bool uponSelected = state.uponSymbol && _selection.from <= state.symbol;
if (uponSelected) {
if (_dragSymbol < _selection.from || _dragSymbol >= _selection.to) {
uponSelected = false;
}
}
ClickHandlerPtr pressedHandler = ClickHandler::getPressed();
QString selectedText;
if (uponSelected) {
selectedText = _text.originalText(_selection, ExpandLinksAll);
} else if (pressedHandler) {
selectedText = pressedHandler->dragText();
}
if (!selectedText.isEmpty()) {
auto mimeData = new QMimeData();
mimeData->setText(selectedText);
auto drag = new QDrag(App::wnd());
drag->setMimeData(mimeData);
drag->exec(Qt::CopyAction);
// We don't receive mouseReleaseEvent when drag is finished.
ClickHandler::unpressed();
}
}
void FlatLabel::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) {
update();
}
void FlatLabel::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool active) {
update();
}
std_::unique_ptr<CrossFadeAnimation> FlatLabel::CrossFade(FlatLabel *from, FlatLabel *to, style::color bg, QPoint fromPosition, QPoint toPosition) {
auto result = std_::make_unique<CrossFadeAnimation>(bg);
struct Data {
QImage full;
QVector<int> lineWidths;
int lineHeight = 0;
int lineAddTop = 0;
};
auto prepareData = [&bg](FlatLabel *label) {
auto result = Data();
result.full = myGrabImage(label, QRect(), bg->c);
auto textWidth = label->width() - label->_st.margin.left() - label->_st.margin.right();
label->_text.countLineWidths(textWidth, &result.lineWidths);
result.lineHeight = label->_st.style.font->height;
auto addedHeight = (label->_st.style.lineHeight - result.lineHeight);
if (addedHeight > 0) {
result.lineAddTop = addedHeight / 2;
result.lineHeight += addedHeight;
}
return std_::move(result);
};
auto was = prepareData(from);
auto now = prepareData(to);
auto maxLines = qMax(was.lineWidths.size(), now.lineWidths.size());
auto fillDataTill = [maxLines](Data &data) {
for (auto i = data.lineWidths.size(); i != maxLines; ++i) {
data.lineWidths.push_back(-1);
}
};
fillDataTill(was);
fillDataTill(now);
auto preparePart = [](FlatLabel *label, QPoint position, Data &data, int index, Data &other) {
auto result = CrossFadeAnimation::Part();
auto lineWidth = data.lineWidths[index];
if (lineWidth < 0) {
lineWidth = other.lineWidths[index];
}
auto fullWidth = data.full.width() / cIntRetinaFactor();
auto top = index * data.lineHeight + data.lineAddTop;
auto left = 0;
if (label->_st.align & Qt::AlignHCenter) {
left += (fullWidth - lineWidth) / 2;
} else if (label->_st.align & Qt::AlignRight) {
left += (fullWidth - lineWidth);
}
auto snapshotRect = data.full.rect().intersected(QRect(left * cIntRetinaFactor(), top * cIntRetinaFactor(), lineWidth * cIntRetinaFactor(), label->_st.style.font->height * cIntRetinaFactor()));
if (!snapshotRect.isEmpty()) {
result.snapshot = App::pixmapFromImageInPlace(data.full.copy(snapshotRect));
result.snapshot.setDevicePixelRatio(cRetinaFactor());
}
auto positionBase = position + label->pos();
result.position = positionBase + QPoint(label->_st.margin.left() + left, label->_st.margin.top() + top);
return std_::move(result);
};
for (int i = 0; i != maxLines; ++i) {
result->addLine(preparePart(from, fromPosition, was, i, now), preparePart(to, toPosition, now, i, was));
}
return std_::move(result);
}
Text::StateResult FlatLabel::dragActionUpdate() {
auto m = mapFromGlobal(_lastMousePos);
auto state = getTextState(m);
updateHover(state);
if (_dragAction == PrepareDrag && (m - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
_dragAction = Dragging;
QTimer::singleShot(1, this, SLOT(onExecuteDrag()));
}
return state;
}
void FlatLabel::updateHover(const Text::StateResult &state) {
bool lnkChanged = ClickHandler::setActive(state.link, this);
if (!_selectable) {
refreshCursor(state.uponSymbol);
return;
}
Qt::CursorShape cur = style::cur_default;
if (_dragAction == NoDrag) {
if (state.link) {
cur = style::cur_pointer;
} else if (state.uponSymbol) {
cur = style::cur_text;
}
} else {
if (_dragAction == Selecting) {
uint16 second = state.symbol;
if (state.afterSymbol && _selectionType == TextSelectType::Letters) {
++second;
}
auto selection = _text.adjustSelection({ qMin(second, _dragSymbol), qMax(second, _dragSymbol) }, _selectionType);
if (_selection != selection) {
_selection = selection;
_savedSelection = { 0, 0 };
setFocus();
update();
}
} else if (_dragAction == Dragging) {
}
if (ClickHandler::getPressed()) {
cur = style::cur_pointer;
} else if (_dragAction == Selecting) {
cur = style::cur_text;
}
}
if (_dragAction == Selecting) {
// checkSelectingScroll();
} else {
// noSelectingScroll();
}
if (_dragAction == NoDrag && (lnkChanged || cur != _cursor)) {
setCursor(_cursor = cur);
}
}
void FlatLabel::refreshCursor(bool uponSymbol) {
if (_dragAction != NoDrag) {
return;
}
bool needTextCursor = _selectable && uponSymbol;
style::cursor newCursor = needTextCursor ? style::cur_text : style::cur_default;
if (ClickHandler::getActive()) {
newCursor = style::cur_pointer;
}
if (newCursor != _cursor) {
_cursor = newCursor;
setCursor(_cursor);
}
}
Text::StateResult FlatLabel::getTextState(const QPoint &m) const {
Text::StateRequestElided request;
request.align = _st.align;
if (_selectable) {
request.flags |= Text::StateRequest::Flag::LookupSymbol;
}
int textWidth = width() - _st.margin.left() - _st.margin.right();
Text::StateResult state;
bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth());
bool renderElided = _breakEverywhere || heightExceeded;
if (renderElided) {
auto lineHeight = qMax(_st.style.lineHeight, _st.style.font->height);
auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2);
request.lines = lines;
if (_breakEverywhere) {
request.flags |= Text::StateRequest::Flag::BreakEverywhere;
}
state = _text.getStateElided(m.x() - _st.margin.left(), m.y() - _st.margin.top(), textWidth, request);
} else {
state = _text.getState(m.x() - _st.margin.left(), m.y() - _st.margin.top(), textWidth, request);
}
return state;
}
void FlatLabel::setOpacity(float64 o) {
_opacity = o;
update();
}
void FlatLabel::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setOpacity(_opacity);
p.setPen(_st.textFg);
p.setTextPalette(_st.palette);
int textWidth = width() - _st.margin.left() - _st.margin.right();
auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection;
bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth());
bool renderElided = _breakEverywhere || heightExceeded;
if (renderElided) {
auto lineHeight = qMax(_st.style.lineHeight, _st.style.font->height);
auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2);
_text.drawElided(p, _st.margin.left(), _st.margin.top(), textWidth, lines, _st.align, e->rect().y(), e->rect().bottom(), 0, _breakEverywhere, selection);
} else {
_text.draw(p, _st.margin.left(), _st.margin.top(), textWidth, _st.align, e->rect().y(), e->rect().bottom(), selection);
}
}
} // namespace Ui