max history width limited, custom tooltips replace QToolTip, keys with modifiers are not passed to MentionsDropdown

This commit is contained in:
John Preston 2016-02-07 18:38:49 +03:00
parent db9f47c6f2
commit 0b4ddb045e
16 changed files with 363 additions and 31 deletions

View File

@ -269,6 +269,21 @@ defaultPopupMenu: PopupMenu {
widthMin: 180px;
widthMax: 300px;
}
defaultTooltip: Tooltip {
textBg: #eef2f5;
textFg: #5d6c80;
textFont: normalFont;
textBorder: #c9d1db;
textPadding: margins(5px, 2px, 5px, 2px);
shift: point(-20px, 20px);
skip: 10px;
widthMax: 800px;
linesMax: 12;
}
almostTransparent: #ffffff0d;
boxScroll: flatScroll(solidScroll) {
width: 18px;
@ -999,6 +1014,8 @@ historyToEndSkip: 10px;
activeFadeInDuration: 500;
activeFadeOutDuration: 3000;
historyMaxWidth: 640px;
msgRadius: 3px;
msgMaxWidth: 430px;

View File

@ -272,6 +272,20 @@ PopupMenu {
widthMax: number;
}
Tooltip {
textBg: color;
textFg: color;
textFont: font;
textBorder: color;
textPadding: margins;
shift: point;
skip: number;
widthMax: number;
linesMax: number;
}
botKeyboardButton {
margin: number;
padding: number;

View File

@ -70,6 +70,13 @@ namespace {
App::wnd()->setWindowState(Qt::WindowMinimized);
return true;
}
} else {
if ((ev->key() == Qt::Key_W || ev->key() == Qt::Key_F4) && (ev->modifiers() & Qt::ControlModifier)) {
if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) {
App::wnd()->minimizeToTray();
return true;
}
}
}
if (ev->key() == Qt::Key_MediaPlay) {
if (App::main()) App::main()->player()->playPressed();
@ -279,7 +286,15 @@ void Application::singleInstanceChecked() {
if (status == SignalHandlers::CantOpen) {
new NotStartedWindow();
} else if (status == SignalHandlers::LastCrashed) {
new LastCrashedWindow();
if (Global::LastCrashDump().isEmpty()) { // don't handle bad closing for now
if (SignalHandlers::restart() == SignalHandlers::CantOpen) {
new NotStartedWindow();
} else {
Sandboxer::startSandbox();
}
} else {
new LastCrashedWindow();
}
} else {
Sandboxer::startSandbox();
}
@ -560,6 +575,12 @@ namespace Sandboxer {
return false;
}
void installEventFilter(QObject *filter) {
if (Application *a = application()) {
a->installEventFilter(filter);
}
}
void execExternal(const QString &cmd) {
DEBUG_LOG(("Application Info: executing external command '%1'").arg(cmd));
if (cmd == "show") {

View File

@ -112,6 +112,8 @@ namespace Sandboxer {
void setActiveWindow(QWidget *window);
bool isSavingSession();
void installEventFilter(QObject *filter);
void execExternal(const QString &cmd);
#ifndef TDESKTOP_DISABLE_AUTOUPDATE

View File

@ -4064,6 +4064,7 @@ bool MentionsInner::select() {
if (!_srows->isEmpty()) {
if (_sel >= 0 && _sel < _srows->size()) {
emit selected(_srows->at(_sel));
return true;
}
} else {
QString sel = getSelected();
@ -4636,10 +4637,12 @@ bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) {
if (isHidden()) return QWidget::eventFilter(obj, e);
if (e->type() == QEvent::KeyPress) {
QKeyEvent *ev = static_cast<QKeyEvent*>(e);
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.isEmpty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) {
return _inner.moveSel(ev->key());
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
return _inner.select();
if (!(ev->modifiers() & (Qt::AltModifier | Qt::ControlModifier | Qt::ShiftModifier | Qt::MetaModifier))) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.isEmpty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) {
return _inner.moveSel(ev->key());
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
return _inner.select();
}
}
}
return QWidget::eventFilter(obj, e);

View File

@ -520,3 +520,126 @@ PopupMenu::~PopupMenu() {
}
#endif
}
PopupTooltip *PopupTooltipInstance = 0;
PopupTooltip::PopupTooltip(const QPoint &p, const QString &text, const style::Tooltip &st) : TWidget(0)
, _st(0) {
if (PopupTooltip *instance = PopupTooltipInstance) {
hide();
deleteLater();
} else {
PopupTooltipInstance = this;
Sandboxer::installEventFilter(this);
_hideByLeaveTimer.setSingleShot(true);
connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));
}
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_NoSystemBackground, true);
PopupTooltipInstance->popup(p, text, &st);
}
bool PopupTooltip::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::Leave) {
_hideByLeaveTimer.start(10);
} else if (e->type() == QEvent::Enter) {
_hideByLeaveTimer.stop();
} else if (e->type() == QEvent::MouseMove) {
if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) {
Hide();
}
}
return TWidget::eventFilter(o, e);
}
void PopupTooltip::onHideByLeave() {
Hide();
}
PopupTooltip::~PopupTooltip() {
if (PopupTooltipInstance == this) {
PopupTooltipInstance = 0;
}
}
void PopupTooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) {
_point = m;
_st = st;
_text = Text(_st->textFont, text, _textPlainOptions, _st->widthMax, true);
int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right();
int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom();
// count tooltip size
QSize s(addw + _text.maxWidth(), addh + _text.minHeight());
if (s.width() > _st->widthMax) {
s.setWidth(addw + _text.countWidth(_st->widthMax - addw));
s.setHeight(addh + _text.countHeight(s.width() - addw));
}
int32 maxh = addh + (_st->linesMax * _st->textFont->height);
if (s.height() > maxh) {
s.setHeight(maxh);
}
// count tooltip position
QPoint p(m + _st->shift);
if (rtl()) {
p.setX(m.x() - s.width() - _st->shift.x());
}
if (s.width() < 2 * _st->shift.x()) {
p.setX(m.x() - (s.width() / 2));
}
// adjust tooltip position
QRect r(QApplication::desktop()->screenGeometry(m));
if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) {
p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width()));
}
if (r.x() + _st->skip > p.x() && p.x() < m.x()) {
p.setX(qMin(m.x(), r.x() + int32(_st->skip)));
}
if (r.y() + r.height() - _st->skip < p.y() + s.height()) {
p.setY(m.y() - s.height() - _st->skip);
}
if (r.y() > p.x()) {
p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height()));
}
setGeometry(QRect(p, s));
_hideByLeaveTimer.stop();
show();
}
void PopupTooltip::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(rect(), _st->textBg);
p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textFont->height);
p.setPen(_st->textFg);
_text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines);
}
void PopupTooltip::hideEvent(QHideEvent *e) {
if (PopupTooltipInstance == this) {
PopupTooltipInstance = 0;
deleteLater();
}
}
void PopupTooltip::Hide() {
if (PopupTooltip *instance = PopupTooltipInstance) {
PopupTooltipInstance = 0;
instance->hide();
instance->deleteLater();
}
}

View File

@ -17,6 +17,8 @@
*/
#pragma once
#include "text.h"
class PopupMenu : public TWidget {
Q_OBJECT
@ -105,3 +107,38 @@ private:
bool _deleteOnHide, _triggering, _deleteLater;
};
class PopupTooltip : public TWidget {
Q_OBJECT
public:
PopupTooltip(const QPoint &p, const QString &text, const style::Tooltip &st = st::defaultTooltip);
bool eventFilter(QObject *o, QEvent *e);
static void Hide();
~PopupTooltip();
public slots:
void onHideByLeave();
protected:
void paintEvent(QPaintEvent *e);
void hideEvent(QHideEvent *e);
private:
void popup(const QPoint &p, const QString &text, const style::Tooltip *st);
Text _text;
QPoint _point;
const style::Tooltip *_st;
QTimer _hideByLeaveTimer;
};

View File

@ -886,6 +886,8 @@ namespace {
void TextLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
PopupTooltip::Hide();
QString url = TextLink::encoded();
QRegularExpressionMatch telegramMeUser = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)/?(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
@ -912,6 +914,7 @@ void TextLink::onClick(Qt::MouseButton button) const {
void EmailLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
PopupTooltip::Hide();
QUrl url(qstr("mailto:") + _email);
if (!QDesktopServices::openUrl(url)) {
psOpenFile(url.toString(QUrl::FullyEncoded), true);
@ -2713,6 +2716,111 @@ void Text::removeSkipBlock() {
}
}
int32 Text::countWidth(int32 w) const {
QFixed width = w;
if (width < _minResizeWidth) width = _minResizeWidth;
if (width >= _maxWidth) {
return _maxWidth.ceil().toInt();
}
QFixed minWidthLeft = width, widthLeft = width, last_rBearing = 0, last_rPadding = 0;
bool longWordLine = true;
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) {
ITextBlock *b = *i;
TextBlockType _btype = b->type();
int32 blockHeight = _blockHeight(b, _font);
QFixed _rb = _blockRBearing(b);
if (_btype == TextBlockTNewline) {
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
if (widthLeft < minWidthLeft) {
minWidthLeft = widthLeft;
}
widthLeft = width - (b->f_width() - last_rBearing);
longWordLine = true;
continue;
}
QFixed lpadding = b->f_lpadding();
QFixed newWidthLeft = widthLeft - lpadding - last_rBearing - (last_rPadding + b->f_width() - _rb);
if (newWidthLeft >= 0) {
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
widthLeft = newWidthLeft;
longWordLine = false;
continue;
}
if (_btype == TextBlockTText) {
TextBlock *t = static_cast<TextBlock*>(b);
if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
last_rPadding += lpadding;
longWordLine = false;
continue;
}
QFixed f_wLeft = widthLeft;
for (TextBlock::TextWords::const_iterator j = t->_words.cbegin(), e = t->_words.cend(), f = j; j != e; ++j) {
bool wordEndsHere = (j->width >= 0);
QFixed j_width = wordEndsHere ? j->width : -j->width;
QFixed newWidthLeft = widthLeft - lpadding - last_rBearing - (last_rPadding + j_width - j->f_rbearing());
lpadding = 0;
if (newWidthLeft >= 0) {
last_rBearing = j->f_rbearing();
last_rPadding = j->rpadding;
widthLeft = newWidthLeft;
if (wordEndsHere) {
longWordLine = false;
}
if (wordEndsHere || longWordLine) {
f_wLeft = widthLeft;
f = j + 1;
}
continue;
}
if (f != j) {
j = f;
widthLeft = f_wLeft;
j_width = (j->width >= 0) ? j->width : -j->width;
}
last_rBearing = j->f_rbearing();
last_rPadding = j->rpadding;
if (widthLeft < minWidthLeft) {
minWidthLeft = widthLeft;
}
widthLeft = width - (j_width - last_rBearing);
longWordLine = true;
f = j + 1;
f_wLeft = widthLeft;
}
continue;
}
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
if (widthLeft < minWidthLeft) {
minWidthLeft = widthLeft;
}
widthLeft = width - (b->f_width() - last_rBearing);
longWordLine = true;
continue;
}
if (widthLeft < minWidthLeft) {
minWidthLeft = widthLeft;
}
return (width - minWidthLeft).ceil().toInt();
}
int32 Text::countHeight(int32 w) const {
QFixed width = w;
if (width < _minResizeWidth) width = _minResizeWidth;

View File

@ -549,6 +549,7 @@ public:
Text(const Text &other);
Text &operator=(const Text &other);
int32 countWidth(int32 width) const;
int32 countHeight(int32 width) const;
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
void setRichText(style::font font, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap());

View File

@ -4604,7 +4604,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo
if (!_caption.isEmpty()) {
p.setPen(st::black);
_caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw);
} else if (parent->getMedia() == this) {
} else if (parent->getMedia() == this && (_data->uploading() || App::hoveredItem() == parent)) {
int32 fullRight = skipx + width, fullBottom = skipy + height;
parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
}
@ -6209,17 +6209,22 @@ void HistoryMessage::initDimensions() {
}
void HistoryMessage::countPositionAndSize(int32 &left, int32 &width) const {
int32 mwidth = qMin(int(st::msgMaxWidth), _maxw);
int32 mwidth = qMin(int(st::msgMaxWidth), _maxw), hwidth = _history->width;
if (_media && _media->currentWidth() < mwidth) {
mwidth = qMax(_media->currentWidth(), qMin(mwidth, plainMaxWidth()));
}
left = (!fromChannel() && out()) ? st::msgMargin.right() : st::msgMargin.left();
left = 0;
if (hwidth > st::historyMaxWidth) {
left = (hwidth - st::historyMaxWidth) / 2;
hwidth = st::historyMaxWidth;
}
left += (!fromChannel() && out()) ? st::msgMargin.right() : st::msgMargin.left();
if (displayFromPhoto()) {
left += st::msgPhotoSkip;
}
width = _history->width - st::msgMargin.left() - st::msgMargin.right();
width = hwidth - st::msgMargin.left() - st::msgMargin.right();
if (width > mwidth) {
if (!fromChannel() && out()) {
left += width - mwidth;

View File

@ -2010,7 +2010,7 @@ public:
return drawBubble();
}
bool displayFromName() const {
return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || viaBot() || !_media->hideFromName());
return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || toHistoryForwarded() || viaBot() || !_media->hideFromName());
}
bool uploading() const {
return _media && _media->uploading();

View File

@ -458,6 +458,7 @@ void HistoryInner::touchScrollUpdated(const QPoint &screenPos) {
QPoint HistoryInner::mapMouseToItem(QPoint p, HistoryItem *item) {
int32 msgy = itemTop(item);
if (msgy < 0) return QPoint(0, 0);
p.setY(p.y() - msgy);
return p;
}
@ -1616,7 +1617,7 @@ void HistoryInner::onUpdateSelected() {
}
}
textlnkOver(lnk);
QToolTip::hideText();
PopupTooltip::Hide();
App::hoveredLinkItem((lnk && !lnkInDesc) ? item : 0);
if (textlnkOver()) {
if (HistoryItem *item = App::hoveredLinkItem()) {
@ -1631,7 +1632,7 @@ void HistoryInner::onUpdateSelected() {
_tooltipTimer.start(1000);
}
if (_dragCursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) {
QToolTip::hideText();
PopupTooltip::Hide();
}
if (_dragAction == NoDrag) {
@ -1870,14 +1871,11 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const {
void HistoryInner::showLinkTip() {
TextLinkPtr lnk = textlnkOver();
int32 dd = QApplication::startDragDistance();
QPoint dp(mapFromGlobal(_dragPos));
QRect r(dp.x() - dd, dp.y() - dd, 2 * dd, 2 * dd);
if (lnk && !lnk->fullDisplayed()) {
QToolTip::showText(_dragPos, lnk->readable(), this, r);
new PopupTooltip(_dragPos, lnk->readable());
} else if (_dragCursorState == HistoryInDateCursorState && _dragAction == NoDrag) {
if (App::hoveredItem()) {
QToolTip::showText(_dragPos, App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)), this, r);
new PopupTooltip(_dragPos, App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
}
}
}
@ -2265,10 +2263,7 @@ void BotKeyboard::showCommandTip() {
if (_sel >= 0) {
int row = (_sel / MatrixRowShift), col = _sel % MatrixRowShift;
if (!_btns.at(row).at(col).full) {
int32 dd = QApplication::startDragDistance();
QPoint dp(mapFromGlobal(_lastMousePos));
QRect r(dp.x() - dd, dp.y() - dd, 2 * dd, 2 * dd);
QToolTip::showText(_lastMousePos, _btns.at(row).at(col).cmd, this, r);
new PopupTooltip(_lastMousePos, _btns.at(row).at(col).cmd);
}
}
}
@ -2294,7 +2289,7 @@ void BotKeyboard::updateSelected() {
if (newSel >= 0) break;
}
if (newSel != _sel) {
QToolTip::hideText();
PopupTooltip::Hide();
if (newSel < 0) {
setCursor(style::cur_default);
} else if (_sel < 0) {

View File

@ -24,7 +24,12 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
// see https://blog.inventic.eu/2012/08/qt-and-google-breakpad/
#ifdef Q_OS_WIN
#pragma warning(push)
#pragma warning(disable:4091)
#include "client/windows/handler/exception_handler.h"
#pragma warning(pop)
#elif defined Q_OS_MAC
#ifdef MAC_USE_BREAKPAD

View File

@ -1000,7 +1000,7 @@ void OverviewInner::onUpdateSelected() {
}
}
textlnkOver(lnk);
QToolTip::hideText();
PopupTooltip::Hide();
App::hoveredLinkItem(lnk ? item : 0);
if (textlnkOver()) {
if (item && index >= 0) {
@ -1015,10 +1015,10 @@ void OverviewInner::onUpdateSelected() {
lnkChanged = true;
if (oldMousedItem) repaintItem(oldMousedItem, oldMousedItemIndex);
if (item) repaintItem(item);
QToolTip::hideText();
PopupTooltip::Hide();
}
if (_cursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) {
QToolTip::hideText();
PopupTooltip::Hide();
}
if (cursorState != _cursorState) {
_cursorState = cursorState;
@ -1142,14 +1142,11 @@ void OverviewInner::onUpdateSelected() {
void OverviewInner::showLinkTip() {
TextLinkPtr lnk = textlnkOver();
int32 dd = QApplication::startDragDistance();
QPoint dp(mapFromGlobal(_dragPos));
QRect r(dp.x() - dd, dp.y() - dd, 2 * dd, 2 * dd);
if (lnk && !lnk->fullDisplayed()) {
QToolTip::showText(_dragPos, lnk->readable(), this, r);
new PopupTooltip(_dragPos, lnk->readable());
} else if (_cursorState == HistoryInDateCursorState && _dragAction == NoDrag && _mousedItem) {
if (HistoryItem *item = App::histItemById(itemChannel(_mousedItem), itemMsgId(_mousedItem))) {
QToolTip::showText(_dragPos, item->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)), this, r);
new PopupTooltip(_dragPos, item->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
}
}
}

View File

@ -37,7 +37,11 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include <wrl\implements.h>
#include <windows.ui.notifications.h>
#pragma warning(push)
#pragma warning(disable:4091)
#include <dbghelp.h>
#pragma warning(pop)
#include <Shlwapi.h>
#include <Strsafe.h>
#include <shlobj.h>

View File

@ -2005,7 +2005,7 @@ LastCrashedWindow::LastCrashedWindow()
, _reportText(QString::fromUtf8(Global::LastCrashDump()))
, _reportShown(false)
, _reportSaved(false)
, _sendingState(Global::LastCrashDump().isEmpty() ? SendingNoReport : SendingUpdateCheck)
, _sendingState(((!cDevVersion() && !cBetaVersion()) || Global::LastCrashDump().isEmpty()) ? SendingNoReport : SendingUpdateCheck)
, _updating(this)
, _sendingProgress(0)
, _sendingTotal(0)