diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index 9300efb8ec..ef0e69c5eb 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -90,6 +90,7 @@ void AbstractBox::animStep(float64 ms) { if (ms >= 1) { a_opacity.finish(); _cache = QPixmap(); + setAttribute(Qt::WA_OpaquePaintEvent); if (!_hiding) { showAll(); showDone(); @@ -129,6 +130,7 @@ void AbstractBox::startHide() { hideAll(); } a_opacity.start(0); + setAttribute(Qt::WA_OpaquePaintEvent, false); } ScrollableBox::ScrollableBox(const style::flatScroll &scroll) : AbstractBox(), diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index c4b9c98439..7978ebd01e 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -150,8 +150,12 @@ bool ContactsInner::addAdminFail(const RPCError &error, mtpRequestId req) { if (req != _addAdminRequestId) return true; _addAdminRequestId = 0; - if (_addAdminBox) _addAdminBox->onClose(); - emit adminAdded(); + if (error.type() == "USERS_TOO_MUCH") { + App::wnd()->replaceLayer(new MaxInviteBox(_channel->invitationUrl)); + } else { + if (_addAdminBox) _addAdminBox->onClose(); + emit adminAdded(); + } return true; } @@ -173,23 +177,29 @@ void ContactsInner::peerUpdated(PeerData *peer) { } } } + update(); } else { + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; ContactsData::iterator i = _contactsData.find(peer); if (i != _contactsData.cend()) { for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { - if (row->attached == i.value()) row->attached = 0; + if (row->attached == i.value()) { + row->attached = 0; + update(0, rh * row->pos, width(), rh); + } } if (!_filter.isEmpty()) { for (int32 j = 0, s = _filtered.size(); j < s; ++j) { - if (_filtered[j]->attached == i.value()) _filtered[j]->attached = 0; + if (_filtered[j]->attached == i.value()) { + _filtered[j]->attached = 0; + update(0, rh * j, width(), rh); + } } } delete i.value(); _contactsData.erase(i); } } - - parentWidget()->update(); } void ContactsInner::loadProfilePhotos(int32 yFrom) { @@ -338,10 +348,11 @@ void ContactsInner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); Painter p(this); + p.setClipRect(r); _time = unixtime(); p.fillRect(r, st::white->b); - int32 yFrom = r.top(), yTo = r.bottom(); + int32 yFrom = r.y(), yTo = r.y() + r.height(); int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; if (_filter.isEmpty()) { if (_contacts->list.count || !_byUsername.isEmpty()) { @@ -365,16 +376,12 @@ void ContactsInner::paintEvent(QPaintEvent *e) { yFrom -= _contacts->list.count * rh + st::searchedBarHeight; yTo -= _contacts->list.count * rh + st::searchedBarHeight; - int32 from = (yFrom >= 0) ? (yFrom / rh) : 0; - if (from < _byUsername.size()) { - int32 to = (yTo / rh) + 1; - if (to > _byUsername.size()) to = _byUsername.size(); - - p.translate(0, from * rh); - for (; from < to; ++from) { - paintDialog(p, _byUsername[from], d_byUsername[from], (_byUsernameSel == from)); - p.translate(0, rh); - } + int32 from = floorclamp(yFrom, rh, 0, _byUsername.size()); + int32 to = ceilclamp(yTo, rh, 0, _byUsername.size()); + p.translate(0, from * rh); + for (; from < to; ++from) { + paintDialog(p, _byUsername[from], d_byUsername[from], (_byUsernameSel == from)); + p.translate(0, rh); } } } else { @@ -405,16 +412,12 @@ void ContactsInner::paintEvent(QPaintEvent *e) { p.drawText(QRect(0, 0, width(), st::noContactsHeight), text, style::al_center); } else { if (!_filtered.isEmpty()) { - int32 from = (yFrom >= 0) ? (yFrom / rh) : 0; - if (from < _filtered.size()) { - int32 to = (yTo / rh) + 1; - if (to > _filtered.size()) to = _filtered.size(); - - p.translate(0, from * rh); - for (; from < to; ++from) { - paintDialog(p, _filtered[from]->history->peer, contactData(_filtered[from]), (_filteredSel == from)); - p.translate(0, rh); - } + int32 from = floorclamp(yFrom, rh, 0, _filtered.size()); + int32 to = ceilclamp(yTo, rh, 0, _filtered.size()); + p.translate(0, from * rh); + for (; from < to; ++from) { + paintDialog(p, _filtered[from]->history->peer, contactData(_filtered[from]), (_filteredSel == from)); + p.translate(0, rh); } } if (!_byUsernameFiltered.isEmpty()) { @@ -426,16 +429,12 @@ void ContactsInner::paintEvent(QPaintEvent *e) { yFrom -= _filtered.size() * rh + st::searchedBarHeight; yTo -= _filtered.size() * rh + st::searchedBarHeight; - int32 from = (yFrom >= 0) ? (yFrom / rh) : 0; - if (from < _byUsernameFiltered.size()) { - int32 to = (yTo / rh) + 1; - if (to > _byUsernameFiltered.size()) to = _byUsernameFiltered.size(); - - p.translate(0, from * rh); - for (; from < to; ++from) { - paintDialog(p, _byUsernameFiltered[from], d_byUsernameFiltered[from], (_byUsernameSel == from)); - p.translate(0, rh); - } + int32 from = floorclamp(yFrom, rh, 0, _byUsernameFiltered.size()); + int32 to = ceilclamp(yTo, rh, 0, _byUsernameFiltered.size()); + p.translate(0, from * rh); + for (; from < to; ++from) { + paintDialog(p, _byUsernameFiltered[from], d_byUsernameFiltered[from], (_byUsernameSel == from)); + p.translate(0, rh); } } } @@ -446,12 +445,31 @@ void ContactsInner::enterEvent(QEvent *e) { setMouseTracking(true); } +void ContactsInner::updateSelectedRow() { + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; + if (_filter.isEmpty()) { + if (_sel) { + update(0, _sel->pos * rh, width(), rh); + } + if (_byUsernameSel >= 0) { + update(0, _contacts->list.count * rh + st::searchedBarHeight + _byUsernameSel * rh, width(), rh); + } + } else { + if (_filteredSel >= 0) { + update(0, _filteredSel * rh, width(), rh); + } + if (_byUsernameSel >= 0) { + update(0, _filtered.size() * rh + st::searchedBarHeight + _byUsernameSel * rh, width(), rh); + } + } +} + void ContactsInner::leaveEvent(QEvent *e) { setMouseTracking(false); if (_sel || _filteredSel >= 0 || _byUsernameSel >= 0) { + updateSelectedRow(); _sel = 0; _filteredSel = _byUsernameSel = -1; - parentWidget()->update(); } } @@ -554,7 +572,7 @@ void ContactsInner::chooseParticipant() { } } } - parentWidget()->update(); + update(); } void ContactsInner::changeCheckState(DialogRow *row) { @@ -596,18 +614,20 @@ void ContactsInner::updateSel() { int32 byUsernameSel = (in && p.y() >= _contacts->list.count * rh + st::searchedBarHeight) ? ((p.y() - _contacts->list.count * rh - st::searchedBarHeight) / rh) : -1; if (byUsernameSel >= _byUsername.size()) byUsernameSel = -1; if (newSel != _sel || byUsernameSel != _byUsernameSel) { + updateSelectedRow(); _sel = newSel; _byUsernameSel = byUsernameSel; - parentWidget()->update(); + updateSelectedRow(); } } else { int32 newFilteredSel = (in && p.y() >= 0 && p.y() < _filtered.size() * rh) ? (p.y() / rh) : -1; int32 byUsernameSel = (in && p.y() >= _filtered.size() * rh + st::searchedBarHeight) ? ((p.y() - _filtered.size() * rh - st::searchedBarHeight) / rh) : -1; if (byUsernameSel >= _byUsernameFiltered.size()) byUsernameSel = -1; if (newFilteredSel != _filteredSel || byUsernameSel != _byUsernameSel) { + updateSelectedRow(); _filteredSel = newFilteredSel; _byUsernameSel = byUsernameSel; - parentWidget()->update(); + updateSelectedRow(); } } } @@ -745,7 +765,7 @@ void ContactsInner::updateFilter(QString filter) { emit searchByUsername(); } } - if (parentWidget()) parentWidget()->update(); + update(); loadProfilePhotos(0); } } @@ -984,7 +1004,7 @@ void ContactsInner::selectSkip(int32 dir) { emit mustScrollTo(skip + _byUsernameSel * rh, skip + (_byUsernameSel + 1) * rh); } } - parentWidget()->update(); + update(); } void ContactsInner::selectSkipPage(int32 h, int32 dir) { @@ -1441,7 +1461,7 @@ void MembersInner::paintEvent(QPaintEvent *e) { _time = unixtime(); p.fillRect(r, st::white->b); - int32 yFrom = r.top(), yTo = r.bottom(); + int32 yFrom = r.y() - st::membersPadding.top(), yTo = r.y() + r.height() - st::membersPadding.top(); int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; p.translate(0, st::membersPadding.top()); @@ -1450,19 +1470,15 @@ void MembersInner::paintEvent(QPaintEvent *e) { p.setPen(st::noContactsColor->p); p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); } else { - int32 from = (yFrom >= 0) ? (yFrom / rh) : 0; - if (from < _rows.size()) { - int32 to = (yTo / rh) + 1; - if (to > _rows.size()) to = _rows.size(); - - p.translate(0, from * rh); - for (; from < to; ++from) { - bool sel = (from == _sel); - bool kickSel = (from == _kickSel && (_kickDown < 0 || from == _kickDown)); - bool kickDown = kickSel && (from == _kickDown); - paintDialog(p, _rows[from], data(from), sel, kickSel, kickDown); - p.translate(0, rh); - } + int32 from = floorclamp(yFrom, rh, 0, _rows.size()); + int32 to = ceilclamp(yTo, rh, 0, _rows.size()); + p.translate(0, from * rh); + for (; from < to; ++from) { + bool sel = (from == _sel); + bool kickSel = (from == _kickSel && (_kickDown < 0 || from == _kickDown)); + bool kickDown = kickSel && (from == _kickDown); + paintDialog(p, _rows[from], data(from), sel, kickSel, kickDown); + p.translate(0, rh); } } } @@ -1474,8 +1490,8 @@ void MembersInner::enterEvent(QEvent *e) { void MembersInner::leaveEvent(QEvent *e) { setMouseTracking(false); if (_sel >= 0) { + updateSelectedRow(); _sel = -1; - parentWidget()->update(); } } @@ -1581,7 +1597,7 @@ void MembersInner::selectSkip(int32 dir) { emit mustScrollTo(_sel * rh, (_sel + 1) * rh); } - parentWidget()->update(); + update(); } void MembersInner::selectSkipPage(int32 h, int32 dir) { @@ -1700,23 +1716,32 @@ void MembersInner::updateSel() { newKickSel = -1; } if (newSel != _sel || newKickSel != _kickSel) { + updateSelectedRow(); _sel = newSel; _kickSel = newKickSel; - parentWidget()->update(); + updateSelectedRow(); setCursor(_kickSel >= 0 ? style::cur_pointer : style::cur_default); } } void MembersInner::peerUpdated(PeerData *peer) { - parentWidget()->update(); + update(); +} + +void MembersInner::updateSelectedRow() { + if (_sel >= 0) { + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; + update(0, st::membersPadding.top() + _sel * rh, width(), rh); + } } void MembersInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; for (int32 i = 0, l = _rows.size(); i < l; ++i) { if (_rows.at(i) == peer) { if (_datas.at(i)) { _datas.at(i)->name.setText(st::profileListNameFont, peer->name, _textNameOptions); - parentWidget()->update(); + update(0, st::membersPadding.top() + i * rh, width(), rh); } else { break; } diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index add3c8303b..45e872bb87 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -108,6 +108,7 @@ public slots: private: + void updateSelectedRow(); void addAdminDone(const MTPBool &result, mtpRequestId req); bool addAdminFail(const RPCError &error, mtpRequestId req); @@ -296,6 +297,7 @@ public slots: private: + void updateSelectedRow(); void clearSel(); MemberData *data(int32 index); diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index c5250bdf2c..2cf7fe6b17 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -38,30 +38,28 @@ void SessionsInner::paintEvent(QPaintEvent *e) { p.setFont(st::linkFont->f); int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPos.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip; int32 w = width(); - int32 from = (r.top() >= 0) ? qFloor(r.top() / st::sessionHeight) : 0, count = _list->size(); - if (from < count) { - int32 to = (r.bottom() >= 0 ? qFloor(r.bottom() / st::sessionHeight) : 0) + 1; - if (to > count) to = count; - p.translate(0, from * st::sessionHeight); - for (int32 i = from; i < to; ++i) { - const SessionData &auth(_list->at(i)); + int32 count = _list->size(); + int32 from = floorclamp(r.y(), st::sessionHeight, 0, count); + int32 to = ceilclamp(r.y() + r.height(), st::sessionHeight, 0, count); + p.translate(0, from * st::sessionHeight); + for (int32 i = from; i < to; ++i) { + const SessionData &auth(_list->at(i)); - p.setFont(st::sessionNameFont->f); - p.setPen(st::black->p); - p.drawTextLeft(x, st::sessionPadding.top(), w, auth.name, auth.nameWidth); + p.setFont(st::sessionNameFont->f); + p.setPen(st::black->p); + p.drawTextLeft(x, st::sessionPadding.top(), w, auth.name, auth.nameWidth); - p.setFont(st::sessionActiveFont->f); - p.setPen(st::sessionActiveColor->p); - p.drawTextRight(xact, st::sessionPadding.top(), w, auth.active, auth.activeWidth); + p.setFont(st::sessionActiveFont->f); + p.setPen(st::sessionActiveColor->p); + p.drawTextRight(xact, st::sessionPadding.top(), w, auth.active, auth.activeWidth); - p.setFont(st::sessionInfoFont->f); - p.setPen(st::black->p); - p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, auth.info, auth.infoWidth); - p.setPen(st::sessionInfoColor->p); - p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, auth.ip, auth.ipWidth); + p.setFont(st::sessionInfoFont->f); + p.setPen(st::black->p); + p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, auth.info, auth.infoWidth); + p.setPen(st::sessionInfoColor->p); + p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, auth.ip, auth.ipWidth); - p.translate(0, st::sessionHeight); - } + p.translate(0, st::sessionHeight); } } diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 5a8af1db08..5c05b7471b 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -26,9 +26,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "localstorage.h" +void StickerSetPanel::paintEvent(QPaintEvent *e) { + Painter p(this); + p.fillRect(e->rect(), st::emojiPanHeaderBg->b); +} + StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : _loaded(false), _setId(0), _setAccess(0), _setCount(0), _setHash(0), _setFlags(0), _bottom(0), -_input(set), _installRequest(0) { +_input(set), _installRequest(0), _panel(0) { switch (set.type()) { case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break; case mtpc_inputStickerSetShortName: _setShortName = qs(set.c_inputStickerSetShortName().vshort_name); break; @@ -68,6 +73,9 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) { } else { int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0); resize(st::stickersPadding + StickerPanPerRow * st::stickersSize.width(), rows * st::stickersSize.height() + st::stickersAddOrShare); + _panel = new StickerSetPanel(parentWidget()); + _panel->setGeometry(0, parentWidget()->height() - st::stickersAddOrShare, width(), st::stickersAddOrShare); + _panel->show(); } _loaded = true; @@ -178,16 +186,12 @@ void StickerSetInner::paintEvent(QPaintEvent *e) { } } } - p.fillRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare, st::emojiPanHeaderBg->b); } void StickerSetInner::setScrollBottom(int32 bottom) { if (bottom == _bottom) return; - QRegion upd = QRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare); _bottom = bottom; - upd += QRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare); - repaint(upd); } bool StickerSetInner::loaded() const { diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h index 8ac876990b..4adcf4550a 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.h +++ b/Telegram/SourceFiles/boxes/stickersetbox.h @@ -19,7 +19,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "abstractbox.h" -class StickerSetInner : public QWidget, public RPCSender { +class StickerSetPanel : public TWidget { +public: + + StickerSetPanel(QWidget *parent) : TWidget(parent) { + } + void paintEvent(QPaintEvent *e); + +}; + +class StickerSetInner : public TWidget, public RPCSender { Q_OBJECT public: @@ -64,6 +73,8 @@ private: MTPInputStickerSet _input; mtpRequestId _installRequest; + + StickerSetPanel *_panel; }; class StickerSetBox : public ScrollableBox, public RPCSender { diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 830a3a39b5..7bfe73c9be 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -69,11 +69,14 @@ int32 DialogsInner::searchedOffset() const { } void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingOther) { + QRegion original(rtl() ? region.translated(-otherWidth(), 0) : region); + if (App::wnd() && App::wnd()->contentOverlapped(this, original)) return; + if (!App::main()) return; QRect r(region.boundingRect()); - if (!paintingOther && !r.contains(rect())) { - p.setClipRect(rect().intersected(r)); + if (!paintingOther) { + p.setClipRect(r); } if (_state == DefaultState) { @@ -94,16 +97,12 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } } else if (_state == FilteredState || _state == SearchedState) { if (!hashtagResults.isEmpty()) { - int32 from = r.top() / int32(st::mentionHeight); - if (from < 0) { - from = 0; - } else if (from > hashtagResults.size()) { - from = hashtagResults.size(); - } + int32 from = floorclamp(r.y(), st::mentionHeight, 0, hashtagResults.size()); + int32 to = ceilclamp(r.y() + r.height(), st::mentionHeight, 0, hashtagResults.size()); p.translate(0, from * st::mentionHeight); if (from < hashtagResults.size()) { - int32 to = (r.bottom() / int32(st::mentionHeight)) + 1, w = fullWidth(), htagwidth = w - st::dlgPaddingHor * 2; - if (to > hashtagResults.size()) to = hashtagResults.size(); + int32 w = fullWidth(), htagwidth = w - st::dlgPaddingHor * 2; + p.setFont(st::mentionFont->f); p.setPen(st::black->p); for (; from < to; ++from) { @@ -142,16 +141,11 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } if (!filterResults.isEmpty()) { int32 skip = filteredOffset(); - int32 from = (r.top() - skip) / int32(st::dlgHeight); - if (from < 0) { - from = 0; - } else if (from > filterResults.size()) { - from = filterResults.size(); - } + int32 from = floorclamp(r.y() - skip, st::dlgHeight, 0, filterResults.size()); + int32 to = ceilclamp(r.y() + r.height() - skip, st::dlgHeight, 0, filterResults.size()); p.translate(0, from * st::dlgHeight); if (from < filterResults.size()) { - int32 to = (r.bottom() / int32(st::dlgHeight)) + 1, w = fullWidth(); - if (to > filterResults.size()) to = filterResults.size(); + int32 w = fullWidth(); for (; from < to; ++from) { bool active = (filterResults[from]->history->peer == App::main()->activePeer() && !App::main()->activeMsgId()); bool selected = (from == filteredSel); @@ -171,16 +165,11 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO p.translate(0, st::searchedBarHeight); int32 skip = peopleOffset(); - int32 from = (r.top() - skip) / int32(st::dlgHeight); - if (from < 0) { - from = 0; - } else if (from > peopleResults.size()) { - from = peopleResults.size(); - } + int32 from = floorclamp(r.y() - skip, st::dlgHeight, 0, peopleResults.size()); + int32 to = ceilclamp(r.y() + r.height() - skip, st::dlgHeight, 0, peopleResults.size()); p.translate(0, from * st::dlgHeight); if (from < peopleResults.size()) { - int32 to = ((r.bottom() - skip) / int32(st::dlgHeight)) + 1, w = fullWidth(); - if (to > peopleResults.size()) to = peopleResults.size(); + int32 w = fullWidth(); for (; from < to; ++from) { bool active = (peopleResults[from] == App::main()->activePeer() && !App::main()->activeMsgId()); bool selected = (from == peopleSel); @@ -215,16 +204,11 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO p.translate(0, st::searchedBarHeight); int32 skip = searchedOffset(); - int32 from = (r.top() - skip) / int32(st::dlgHeight); - if (from < 0) { - from = 0; - } else if (from > searchResults.size()) { - from = searchResults.size(); - } + int32 from = floorclamp(r.y() - skip, st::dlgHeight, 0, searchResults.size()); + int32 to = ceilclamp(r.y() + r.height() - skip, st::dlgHeight, 0, searchResults.size()); p.translate(0, from * st::dlgHeight); if (from < searchResults.size()) { - int32 to = ((r.bottom() - skip) / int32(st::dlgHeight)) + 1, w = fullWidth(); - if (to > searchResults.size()) to = searchResults.size(); + int32 w = fullWidth(); for (; from < to; ++from) { bool active = (searchResults[from]->_item->history()->peer == App::main()->activePeer() && searchResults[from]->_item->id == App::main()->activeMsgId()); bool selected = (from == searchedSel); @@ -941,7 +925,7 @@ void DialogsInner::refresh(bool toTop) { h = searchedOffset() + (searchResults.count() * st::dlgHeight); } } - setHeight(h ); + setHeight(h); if (toTop) { emit mustScrollTo(0, 0); loadPeerPhotos(0); @@ -2192,6 +2176,8 @@ void DialogsWidget::keyPressEvent(QKeyEvent *e) { } void DialogsWidget::paintEvent(QPaintEvent *e) { + if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; + QPainter p(this); QRect r(e->rect()); if (r != rect()) { diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index ba9e077893..de572f5849 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -444,7 +444,7 @@ bool DragArea::animStep(float64 ms) { return res; } -EmojiColorPicker::EmojiColorPicker(QWidget *parent) : TWidget(parent), +EmojiColorPicker::EmojiColorPicker() : _ignoreShow(false), _selected(-1), _pressedSel(-1), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow) { memset(_variants, 0, sizeof(_variants)); memset(_hovers, 0, sizeof(_hovers)); @@ -484,13 +484,16 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) { if (!_cache.isNull()) { p.setOpacity(a_opacity.current()); } + if (e->rect() != rect()) { + p.setClipRect(e->rect()); + } int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h); _shadow.paint(p, r, st::dropdownDef.shadowShift); if (_cache.isNull()) { - p.fillRect(r, st::white->b); + p.fillRect(e->rect().intersected(r), st::white->b); int32 x = w + 2 * st::emojiColorsPadding + st::emojiPanSize.width(); if (rtl()) x = width() - x - st::emojiColorsSep; @@ -541,7 +544,7 @@ void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) { } bool EmojiColorPicker::animStep(float64 ms) { - bool res1 = true, res2 = true; + bool res1 = false, res2 = false; if (!_cache.isNull()) { float64 dt = ms / st::dropdownDef.duration; if (dt >= 1) { @@ -554,13 +557,15 @@ bool EmojiColorPicker::animStep(float64 ms) { _lastMousePos = QCursor::pos(); updateSelected(); } - res1 = false; } else { a_opacity.update(dt, anim::linear); + res1 = true; } + update(); } if (!_emojiAnimations.isEmpty()) { uint64 now = getms(); + QRegion toUpdate; for (EmojiAnimations::iterator i = _emojiAnimations.begin(); i != _emojiAnimations.end();) { int index = qAbs(i.key()) - 1; float64 dt = float64(now - i.value()) / st::emojiPanDuration; @@ -571,10 +576,11 @@ bool EmojiColorPicker::animStep(float64 ms) { _hovers[index] = (i.key() > 0) ? dt : (1 - dt); ++i; } + toUpdate += QRect(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height()); } res2 = !_emojiAnimations.isEmpty(); + rtlupdate(toUpdate.boundingRect()); } - update(); return res1 || res2; } @@ -684,13 +690,13 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (esize / cIntRetinaFactor())) / 2, width(), App::emojisLarge(), QRect(_variants[variant]->x * esize, _variants[variant]->y * esize, esize, esize)); } -EmojiPanInner::EmojiPanInner(QWidget *parent) : TWidget(parent), _maxHeight(int(st::emojiPanMaxHeight)), -_top(0), _selected(-1), _pressedSel(-1), _pickerSel(-1), _picker(this), -_switcherHover(0), _stickersWidth(st::emojiPanHeaderFont->m.width(lang(lng_switch_stickers))) { - resize(st::emojiPanWidth, countHeight()); +EmojiPanInner::EmojiPanInner() : _maxHeight(int(st::emojiPanMaxHeight)), +_top(0), _selected(-1), _pressedSel(-1), _pickerSel(-1) { + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); + setAttribute(Qt::WA_OpaquePaintEvent); _picker.hide(); @@ -712,16 +718,13 @@ _switcherHover(0), _stickersWidth(st::emojiPanHeaderFont->m.width(lang(lng_switc void EmojiPanInner::setMaxHeight(int32 h) { _maxHeight = h; - resize(st::emojiPanWidth, countHeight()); + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); } void EmojiPanInner::setScrollTop(int top) { if (top == _top) return; - QRegion upd = QRect(0, _top, width(), st::emojiPanHeader); _top = top; - upd += QRect(0, _top, width(), st::emojiPanHeader); - repaint(upd); updateSelected(); } @@ -743,6 +746,14 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { } p.fillRect(r, st::white->b); + int32 fromcol = floorclamp(r.x() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); + int32 tocol = ceilclamp(r.x() + r.width() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); + if (rtl()) { + qSwap(fromcol, tocol); + fromcol = EmojiPanPerRow - fromcol; + tocol = EmojiPanPerRow - tocol; + } + int32 y, tilly = 0; for (int c = 0; c < emojiTabCount; ++c) { y = tilly; @@ -752,14 +763,6 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { if (r.top() >= tilly) continue; y += st::emojiPanHeader; - - if (r.bottom() <= y) { - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiPanHeaderColor->p); - p.drawTextLeft(st::emojiPanHeaderLeft, qMax(y - int(st::emojiPanHeader), _top) + st::emojiPanHeaderTop, width(), lang(LangKey(lng_emoji_category0 + c))); - break; - } - if (_emojis[c].isEmpty()) { _emojis[c] = emojiPack(emojiTabAtIndex(c)); if (emojiTabAtIndex(c) != dbietRecent) { @@ -779,9 +782,10 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { } } - int32 fromrow = (r.top() <= y) ? 0 : qMax(qFloor((r.top() - y) / st::emojiPanSize.height()), 0), torow = qMin(qCeil((r.bottom() - y) / st::emojiPanSize.height()) + 1, rows); + int32 fromrow = floorclamp(r.y() - y, st::emojiPanSize.height(), 0, rows); + int32 torow = ceilclamp(r.y() + r.height() - y, st::emojiPanSize.height(), 0, rows); for (int32 i = fromrow; i < torow; ++i) { - for (int32 j = 0; j < EmojiPanPerRow; ++j) { + for (int32 j = fromcol; j < tocol; ++j) { int32 index = i * EmojiPanPerRow + j; if (index >= size) break; @@ -798,34 +802,32 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (_esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (_esize / cIntRetinaFactor())) / 2, width(), App::emojisLarge(), QRect(_emojis[c][index]->x * _esize, _emojis[c][index]->y * _esize, _esize, _esize)); } } - - if (y - int(st::emojiPanHeader) < _top) { - p.fillRect(QRect(0, qMin(_top, tilly - int(st::emojiPanHeader)), width(), st::emojiPanHeader), st::emojiPanHeaderBg->b); - } - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiPanHeaderColor->p); - p.drawTextLeft(st::emojiPanHeaderLeft, qMin(qMax(y - int(st::emojiPanHeader), _top), tilly - int(st::emojiPanHeader)) + st::emojiPanHeaderTop, width(), lang(LangKey(lng_emoji_category0 + c))); } +} - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiSwitchColor->p); - p.drawTextRight(st::emojiSwitchSkip, _top + st::emojiPanHeaderTop, width(), lang(lng_switch_stickers), _stickersWidth); - p.drawSpriteRight(QPoint(st::emojiSwitchImgSkip - st::emojiSwitchStickers.pxWidth(), _top + (st::emojiPanHeader - st::emojiSwitchStickers.pxHeight()) / 2), width(), st::emojiSwitchStickers); +bool EmojiPanInner::checkPickerHide() { + if (!_picker.isHidden() && _selected == _pickerSel) { + _picker.hideStart(); + _pickerSel = -1; + updateSelected(); + return true; + } + return false; } void EmojiPanInner::mousePressEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateSelected(); - if (!_picker.isHidden() && _selected == _pickerSel) { - _picker.hideStart(); + if (checkPickerHide()) { return; } _pressedSel = _selected; - if (_selected >= 0 && _selected != SwitcherSelected) { + if (_selected >= 0) { int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { _pickerSel = _selected; + setCursor(style::cur_default); if (cEmojiVariants().constFind(_emojis[tab][sel]->code) == cEmojiVariants().cend()) { onShowPicker(); } else { @@ -848,6 +850,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) { _picker.hideStart(); + _pickerSel = -1; } } } @@ -861,10 +864,6 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { } if (_selected < 0 || _selected != pressed) return; - if (_selected == SwitcherSelected) { - emit switchToStickers(); - return; - } if (_selected >= emojiTabCount * MatrixRowShift) { return; @@ -930,8 +929,8 @@ void EmojiPanInner::onShowPicker() { int32 size = (c == tab) ? (sel - (sel % EmojiPanPerRow)) : _counts[c], rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); y += st::emojiPanHeader + (rows * st::emojiPanSize.height()); } - y -= _picker.height() - st::msgRadius; - if (y < _top) { + y -= _picker.height() - st::msgRadius + _top; + if (y < 0) { y += _picker.height() - st::msgRadius + st::emojiPanSize.height() - st::msgRadius; } int xmax = width() - _picker.width(); @@ -953,6 +952,23 @@ void EmojiPanInner::onPickerHidden() { updateSelected(); } +QRect EmojiPanInner::emojiRect(int tab, int sel) { + int x = 0, y = 0; + for (int i = 0; i < emojiTabCount; ++i) { + if (i == tab) { + int rows = (sel / EmojiPanPerRow); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + x = st::emojiPanPadding + ((sel % EmojiPanPerRow) * st::emojiPanSize.width()); + break; + } else { + int cnt = emojiPackCount(emojiTabAtIndex(i)); + int rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + } + return QRect(x, y, st::emojiPanSize.width(), st::emojiPanSize.height()); +} + void EmojiPanInner::onColorSelected(EmojiPtr emoji) { if (emoji->color) { cRefEmojiVariants().insert(emoji->code, emojiKey(emoji)); @@ -961,7 +977,7 @@ void EmojiPanInner::onColorSelected(EmojiPtr emoji) { int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; if (tab >= 0 && tab < emojiTabCount) { _emojis[tab][sel] = emoji; - update(); + rtlupdate(emojiRect(tab, sel)); } } selectEmoji(emoji); @@ -998,9 +1014,17 @@ void EmojiPanInner::clearSelection(bool fast) { if (fast) { for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - (index == SwitcherSelected ? _switcherHover : _hovers[tab][sel]) = 0; + _hovers[tab][sel] = 0; } _animations.clear(); + if (_selected >= 0) { + int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + _hovers[tab][sel] = 0; + } + if (_pressedSel >= 0) { + int index = qAbs(_pressedSel), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + _hovers[tab][sel] = 0; + } _selected = _pressedSel = -1; anim::stop(this); } else { @@ -1025,6 +1049,7 @@ void EmojiPanInner::hideFinish() { if (!_picker.isHidden()) { _picker.hideStart(true); _pickerSel = -1; + clearSelection(true); } } @@ -1034,7 +1059,42 @@ void EmojiPanInner::refreshRecent() { if (_hovers[0].size() != _counts[0]) _hovers[0] = QVector(_counts[0], 0); _emojis[0] = emojiPack(dbietRecent); int32 h = countHeight(); - if (h != height()) resize(width(), h); + if (h != height()) { + resize(width(), h); + emit needRefreshPanels(); + } +} + +void EmojiPanInner::fillPanels(QVector &panels) { + if (_picker.parentWidget() != parentWidget()) { + _picker.setParent(parentWidget()); + } + for (int32 i = 0; i < panels.size(); ++i) { + panels.at(i)->hide(); + panels.at(i)->deleteLater(); + } + panels.clear(); + + int y = 0; + panels.reserve(emojiTabCount); + for (int c = 0; c < emojiTabCount; ++c) { + panels.push_back(new EmojiPanel(parentWidget(), lang(LangKey(lng_emoji_category0 + c)), NoneStickerSetId, true, y)); + connect(panels.back(), SIGNAL(mousePressed()), this, SLOT(checkPickerHide())); + int cnt = emojiPackCount(emojiTabAtIndex(c)), rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + panels.back()->show(); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } +} + +void EmojiPanInner::refreshPanels(QVector &panels) { + if (panels.size() != emojiTabCount) return fillPanels(panels); + + int32 y = 0; + for (int c = 0; c < emojiTabCount; ++c) { + panels.at(c)->setWantedY(y); + int cnt = emojiPackCount(emojiTabAtIndex(c)), rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } } void EmojiPanInner::updateSelected() { @@ -1042,33 +1102,25 @@ void EmojiPanInner::updateSelected() { int32 selIndex = -1; QPoint p(mapFromGlobal(_lastMousePos)); - if (p.y() < _top + st::emojiPanHeader) { - bool upon1 = rtl() && p.x() >= 0 && p.x() < st::emojiSwitchSkip + _stickersWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - bool upon2 = !rtl() && p.x() < width() && p.x() >= width() - st::emojiSwitchSkip - _stickersWidth - (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - if (upon1 || upon2) { - selIndex = SwitcherSelected; - } - } else { - int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::emojiPanPadding; - for (int c = 0; c < emojiTabCount; ++c) { - int cnt = _counts[c]; - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); - if (p.y() >= y && p.y() < ytill) { - y += st::emojiPanHeader; - if (p.y() >= y && sx >= 0 && sx < EmojiPanPerRow * st::emojiPanSize.width()) { - selIndex = qFloor((p.y() - y) / st::emojiPanSize.height()) * EmojiPanPerRow + qFloor(sx / st::emojiPanSize.width()); - if (selIndex >= _emojis[c].size()) { - selIndex = -1; - } else { - selIndex += c * MatrixRowShift; - } + int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::emojiPanPadding; + for (int c = 0; c < emojiTabCount; ++c) { + int cnt = _counts[c]; + y = ytill; + ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); + if (p.y() >= y && p.y() < ytill) { + y += st::emojiPanHeader; + if (p.y() >= y && sx >= 0 && sx < EmojiPanPerRow * st::emojiPanSize.width()) { + selIndex = qFloor((p.y() - y) / st::emojiPanSize.height()) * EmojiPanPerRow + qFloor(sx / st::emojiPanSize.width()); + if (selIndex >= _emojis[c].size()) { + selIndex = -1; + } else { + selIndex += c * MatrixRowShift; } - break; } + break; } } - + bool startanim = false; int oldSel = _selected, newSel = selIndex; @@ -1103,18 +1155,20 @@ void EmojiPanInner::updateSelected() { bool EmojiPanInner::animStep(float64 ms) { uint64 now = getms(); + QRegion toUpdate; for (Animations::iterator i = _animations.begin(); i != _animations.end();) { int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; float64 dt = float64(now - i.value()) / st::emojiPanDuration; if (dt >= 1) { - (index == SwitcherSelected ? _switcherHover : _hovers[tab][sel]) = (i.key() > 0) ? 1 : 0; + _hovers[tab][sel] = (i.key() > 0) ? 1 : 0; i = _animations.erase(i); } else { - (index == SwitcherSelected ? _switcherHover : _hovers[tab][sel]) = (i.key() > 0) ? dt : (1 - dt); + _hovers[tab][sel] = (i.key() > 0) ? dt : (1 - dt); ++i; } + toUpdate += emojiRect(tab, sel); } - update(); + rtlupdate(toUpdate.boundingRect()); return !_animations.isEmpty(); } @@ -1137,13 +1191,13 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { update(); } -StickerPanInner::StickerPanInner(QWidget *parent) : TWidget(parent), _maxHeight(st::emojiPanMaxHeight), -_top(0), _selected(-1), _pressedSel(-1), -_switcherHover(0), _emojiWidth(st::emojiPanHeaderFont->m.width(lang(lng_switch_emoji))) { - resize(st::emojiPanWidth, countHeight()); +StickerPanInner::StickerPanInner() : _maxHeight(st::emojiPanMaxHeight), +_top(0), _selected(-1), _pressedSel(-1) { + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); + setAttribute(Qt::WA_OpaquePaintEvent); connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); @@ -1152,16 +1206,13 @@ _switcherHover(0), _emojiWidth(st::emojiPanHeaderFont->m.width(lang(lng_switch_e void StickerPanInner::setMaxHeight(int32 h) { _maxHeight = h; - resize(st::emojiPanWidth, countHeight()); + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); } void StickerPanInner::setScrollTop(int top) { if (top == _top) return; - QRegion upd = QRect(0, _top, width(), st::emojiPanHeader); _top = top; - upd += QRect(0, _top, width(), st::emojiPanHeader); - repaint(upd); updateSelected(); } @@ -1176,6 +1227,23 @@ int StickerPanInner::countHeight() { return result + st::stickerPanPadding; } +QRect StickerPanInner::stickerRect(int tab, int sel) { + int x = 0, y = 0; + for (int i = 0; i < _sets.size(); ++i) { + if (i == tab) { + int rows = (((sel >= _sets.at(i).pack.size()) ? (sel - _sets.at(i).pack.size()) : sel) / StickerPanPerRow); + y += st::emojiPanHeader + rows * st::stickerPanSize.height(); + x = st::stickerPanPadding + ((sel % StickerPanPerRow) * st::stickerPanSize.width()); + break; + } else { + int cnt = _sets.at(i).pack.size(); + int rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::stickerPanSize.height(); + } + } + return QRect(x, y, st::stickerPanSize.width(), st::stickerPanSize.height()); +} + void StickerPanInner::paintEvent(QPaintEvent *e) { Painter p(this); QRect r = e ? e->rect() : rect(); @@ -1183,6 +1251,15 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { p.setClipRect(r); } p.fillRect(r, st::white->b); + + int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); + int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); + if (rtl()) { + qSwap(fromcol, tocol); + fromcol = StickerPanPerRow - fromcol; + tocol = StickerPanPerRow - tocol; + } + int32 y, tilly = 0; for (int c = 0, l = _sets.size(); c < l; ++c) { y = tilly; @@ -1194,22 +1271,10 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { bool special = (_sets[c].flags & MTPDstickerSet_flag_official); y += st::emojiPanHeader; - QString title = _sets[c].title; - if (r.bottom() <= y) { - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiPanHeaderColor->p); - p.drawTextLeft(st::emojiPanHeaderLeft, qMax(y - int(st::emojiPanHeader), _top) + st::emojiPanHeaderTop, width(), title); - if (!special && y >= _top + 2 * st::emojiPanHeader) { - p.setOpacity(st::stickerPanDeleteOpacity + (1 - st::stickerPanDeleteOpacity) * _sets[c].hovers[size]); - p.drawSpriteRight(QPoint(st::emojiPanHeaderLeft, y - (st::emojiPanHeader + st::notifyClose.icon.pxHeight()) / 2), width(), st::notifyClose.icon); - p.setOpacity(1); - } - break; - } - - int32 fromrow = (r.top() <= y) ? 0 : qMax(qFloor((r.top() - y) / st::stickerPanSize.height()), 0), torow = qMin(qCeil((r.bottom() - y) / st::stickerPanSize.height()) + 1, rows); + int32 fromrow = floorclamp(r.y() - y, st::stickerPanSize.height(), 0, rows); + int32 torow = ceilclamp(r.y() + r.height() - y, st::stickerPanSize.height(), 0, rows); for (int32 i = fromrow; i < torow; ++i) { - for (int32 j = 0; j < StickerPanPerRow; ++j) { + for (int32 j = fromcol; j < tocol; ++j) { int32 index = i * StickerPanPerRow + j; if (index >= size) break; @@ -1266,24 +1331,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { } } } - - if (y - st::emojiPanHeader < _top) { - p.fillRect(QRect(0, qMin(_top, tilly - int(st::emojiPanHeader)), width(), st::emojiPanHeader), st::emojiPanHeaderBg->b); - } else if (!special && y >= _top + 2 * st::emojiPanHeader) { - p.setOpacity(st::stickerPanDeleteOpacity + (1 - st::stickerPanDeleteOpacity) * _sets[c].hovers[size]); - p.drawSpriteRight(QPoint(st::emojiPanHeaderLeft, y - (st::emojiPanHeader + st::notifyClose.icon.pxHeight()) / 2), width(), st::notifyClose.icon); - p.setOpacity(1); - } - - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiPanHeaderColor->p); - p.drawTextLeft(st::emojiPanHeaderLeft, qMin(qMax(y - int(st::emojiPanHeader), _top), tilly - int(st::emojiPanHeader)) + st::emojiPanHeaderTop, width(), title); } - - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiSwitchColor->p); - p.drawTextRight(st::emojiSwitchImgSkip - st::emojiSwitchEmoji.pxWidth(), _top + st::emojiPanHeaderTop, width(), lang(lng_switch_emoji), _emojiWidth); - p.drawSpriteRight(QPoint(st::emojiSwitchSkip + _emojiWidth - st::emojiSwitchEmoji.pxWidth(), _top + (st::emojiPanHeader - st::emojiSwitchEmoji.pxHeight()) / 2), width(), st::emojiSwitchEmoji); } void StickerPanInner::mousePressEvent(QMouseEvent *e) { @@ -1301,10 +1349,6 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { updateSelected(); if (_selected < 0 || _selected != pressed) return; - if (_selected == SwitcherSelected) { - emit switchToEmoji(); - return; - } if (_selected >= MatrixRowShift * _sets.size()) { return; } @@ -1347,8 +1391,6 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { } if (sel < _sets[tab].pack.size()) { emit selected(_sets[tab].pack[sel]); - } else if (sel == _sets[tab].pack.size()) { - emit removing(_sets[tab].id); } } @@ -1375,9 +1417,25 @@ void StickerPanInner::clearSelection(bool fast) { if (fast) { for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - (index == SwitcherSelected ? _switcherHover : _sets[tab].hovers[sel]) = 0; + _sets[tab].hovers[sel] = 0; } _animations.clear(); + if (_selected >= 0) { + int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + if (index >= 0 && tab < _sets.size() && _sets[tab].id == RecentStickerSetId && sel >= tab * MatrixRowShift + _sets[tab].pack.size()) { + _sets[tab].hovers[sel] = 0; + sel -= _sets[tab].pack.size(); + } + _sets[tab].hovers[sel] = 0; + } + if (_pressedSel >= 0) { + int index = qAbs(_pressedSel), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + if (index >= 0 && tab < _sets.size() && _sets[tab].id == RecentStickerSetId && sel >= tab * MatrixRowShift + _sets[tab].pack.size()) { + _sets[tab].hovers[sel] = 0; + sel -= _sets[tab].pack.size(); + } + _sets[tab].hovers[sel] = 0; + } _selected = _pressedSel = -1; anim::stop(this); } else { @@ -1457,9 +1515,7 @@ void StickerPanInner::appendSet(uint64 setId) { for (int32 i = 0, l = it->stickers.size(); i < l; ++i) { pack.push_back(it->stickers.at(i)); } - int32 availw = width() - st::emojiPanHeaderLeft - st::emojiSwitchSkip - _emojiWidth - (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - QString title = st::emojiPanHeaderFont->m.elidedText(it->title, Qt::ElideRight, availw); - _sets.push_back(DisplayedSet(it->id, it->flags, title, pack.size() + 1, pack)); + _sets.push_back(DisplayedSet(it->id, it->flags, it->title, pack.size() + 1, pack)); } void StickerPanInner::refreshRecent(bool performResize) { @@ -1501,7 +1557,10 @@ void StickerPanInner::refreshRecent(bool performResize) { if (performResize) { int32 h = countHeight(); - if (h != height()) resize(width(), h); + if (h != height()) { + resize(width(), h); + emit needRefreshPanels(); + } updateSelected(); } @@ -1532,49 +1591,71 @@ void StickerPanInner::fillIcons(QVector &icons) { } } +void StickerPanInner::fillPanels(QVector &panels) { + for (int32 i = 0; i < panels.size(); ++i) { + panels.at(i)->hide(); + panels.at(i)->deleteLater(); + } + panels.clear(); + if (_sets.isEmpty()) return; + + int y = 0; + panels.reserve(_sets.size()); + for (int32 i = 0, l = _sets.size(); i < l; ++i) { + bool special = (_sets.at(i).flags & MTPDstickerSet_flag_official); + panels.push_back(new EmojiPanel(parentWidget(), _sets.at(i).title, _sets.at(i).id, special, y)); + panels.back()->show(); + connect(panels.back(), SIGNAL(deleteClicked(quint64)), this, SIGNAL(removing(quint64))); + int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + y += h; + } +} + + +void StickerPanInner::refreshPanels(QVector &panels) { + if (panels.size() != _sets.size()) return fillPanels(panels); + + int32 y = 0; + for (int32 i = 0, l = _sets.size(); i < l; ++i) { + panels.at(i)->setWantedY(y); + int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + y += h; + } +} + void StickerPanInner::updateSelected() { if (_pressedSel >= 0) return; int32 selIndex = -1; QPoint p(mapFromGlobal(_lastMousePos)); - if (p.y() < _top + st::emojiPanHeader) { - bool upon1 = rtl() && p.x() >= 0 && p.x() < st::emojiSwitchSkip + _emojiWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - bool upon2 = !rtl() && p.x() < width() && p.x() >= width() - st::emojiSwitchSkip - _emojiWidth - (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - if (upon1 || upon2) { - selIndex = SwitcherSelected; - } - } else { - int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding; - for (int c = 0, l = _sets.size(); c < l; ++c) { - const DisplayedSet &set(_sets.at(c)); - int cnt = set.pack.size(); - bool special = (set.flags & MTPDstickerSet_flag_official); + int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding; + for (int c = 0, l = _sets.size(); c < l; ++c) { + const DisplayedSet &set(_sets.at(c)); + int cnt = set.pack.size(); + bool special = (set.flags & MTPDstickerSet_flag_official); - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); - if (p.y() >= y && p.y() < ytill) { - if (!special && p.y() >= y && p.y() < y + st::emojiPanHeader && sx + st::stickerPanPadding >= width() - st::emojiPanHeaderLeft - st::notifyClose.icon.pxWidth() && sx + st::stickerPanPadding < width() - st::emojiPanHeaderLeft) { - selIndex = c * MatrixRowShift + set.pack.size(); + y = ytill; + ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); + if (p.y() >= y && p.y() < ytill) { + y += st::emojiPanHeader; + if (p.y() >= y && sx >= 0 && sx < StickerPanPerRow * st::stickerPanSize.width()) { + selIndex = qFloor((p.y() - y) / st::stickerPanSize.height()) * StickerPanPerRow + qFloor(sx / st::stickerPanSize.width()); + if (selIndex >= set.pack.size()) { + selIndex = -1; } else { - y += st::emojiPanHeader; - if (p.y() >= y && sx >= 0 && sx < StickerPanPerRow * st::stickerPanSize.width()) { - selIndex = qFloor((p.y() - y) / st::stickerPanSize.height()) * StickerPanPerRow + qFloor(sx / st::stickerPanSize.width()); - if (selIndex >= set.pack.size()) { - selIndex = -1; - } else { - if (set.id == RecentStickerSetId && _custom[selIndex]) { - int32 inx = sx - (selIndex % StickerPanPerRow) * st::stickerPanSize.width(), iny = p.y() - y - ((selIndex / StickerPanPerRow) * st::stickerPanSize.height()); - if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.pxWidth() && iny < st::stickerPanDelete.pxHeight()) { - selIndex += set.pack.size(); - } - } - selIndex += c * MatrixRowShift; + if (set.id == RecentStickerSetId && _custom[selIndex]) { + int32 inx = sx - (selIndex % StickerPanPerRow) * st::stickerPanSize.width(), iny = p.y() - y - ((selIndex / StickerPanPerRow) * st::stickerPanSize.height()); + if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.pxWidth() && iny < st::stickerPanDelete.pxHeight()) { + selIndex += set.pack.size(); } } + selIndex += c * MatrixRowShift; } - break; } + break; } } @@ -1627,18 +1708,20 @@ void StickerPanInner::updateSelected() { bool StickerPanInner::animStep(float64 ms) { uint64 now = getms(); + QRegion toUpdate; for (Animations::iterator i = _animations.begin(); i != _animations.end();) { int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; float64 dt = float64(now - i.value()) / st::emojiPanDuration; if (dt >= 1) { - (index == SwitcherSelected ? _switcherHover : _sets[tab].hovers[sel]) = (i.key() > 0) ? 1 : 0; + _sets[tab].hovers[sel] = (i.key() > 0) ? 1 : 0; i = _animations.erase(i); } else { - (index == SwitcherSelected ? _switcherHover : _sets[tab].hovers[sel]) = (i.key() > 0) ? dt : (1 - dt); + _sets[tab].hovers[sel] = (i.key() > 0) ? dt : (1 - dt); ++i; } + toUpdate += stickerRect(tab, sel); } - update(); + rtlupdate(toUpdate.boundingRect()); return !_animations.isEmpty(); } @@ -1659,6 +1742,89 @@ void StickerPanInner::showStickerSet(uint64 setId) { update(); } +EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent), +_wantedY(wantedY), _setId(setId), _special(special), _deleteVisible(false), _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // NoneStickerSetId if in emoji + resize(st::emojiPanWidth, st::emojiPanHeader); + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + setText(text); + if (_delete) { + _delete->hide(); + _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::notifyClose.icon.pxWidth()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); + connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); + } +} + +void EmojiPanel::onDelete() { + emit deleteClicked(_setId); +} + +void EmojiPanel::setText(const QString &text) { + _fullText = text; + updateText(); +} + +void EmojiPanel::updateText() { + int32 availw = st::emojiPanWidth - st::emojiPanHeaderLeft * 2; + if (_deleteVisible) { + if (!_special && _setId != NoneStickerSetId) { + availw -= st::notifyClose.icon.pxWidth() + st::emojiPanHeaderLeft; + } + } else { + QString switchText = lang((_setId != NoneStickerSetId) ? lng_switch_emoji : lng_switch_stickers); + availw -= st::emojiSwitchSkip + st::emojiPanHeaderFont->m.width(switchText); + } + _text = st::emojiPanHeaderFont->m.elidedText(_fullText, Qt::ElideRight, availw); + update(); +} + +void EmojiPanel::setDeleteVisible(bool isVisible) { + if (_deleteVisible != isVisible) { + _deleteVisible = isVisible; + updateText(); + if (_delete) { + _delete->setVisible(_deleteVisible); + } + } +} + +void EmojiPanel::mousePressEvent(QMouseEvent *e) { + emit mousePressed(); +} + +void EmojiPanel::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_deleteVisible) { + p.fillRect(0, 0, width(), st::emojiPanHeader, st::emojiPanHeaderBg->b); + } + p.setFont(st::emojiPanHeaderFont->f); + p.setPen(st::emojiPanHeaderColor->p); + p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); +} + +EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent), +_toStickers(toStickers), _text(lang(_toStickers ? lng_switch_stickers : lng_switch_emoji)), +_textWidth(st::emojiPanHeaderFont->m.width(_text)) { + int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); + setCursor(style::cur_pointer); + resize(w, st::emojiPanHeader); +} + +void EmojiSwitchButton::paintEvent(QPaintEvent *e) { + Painter p(this); + + p.setFont(st::emojiPanHeaderFont->f); + p.setPen(st::emojiSwitchColor->p); + if (_toStickers) { + p.drawTextRight(st::emojiSwitchSkip, st::emojiPanHeaderTop, width(), _text, _textWidth); + p.drawSpriteRight(QPoint(st::emojiSwitchImgSkip - st::emojiSwitchStickers.pxWidth(), (st::emojiPanHeader - st::emojiSwitchStickers.pxHeight()) / 2), width(), st::emojiSwitchStickers); + } else { + p.drawTextRight(st::emojiSwitchImgSkip - st::emojiSwitchEmoji.pxWidth(), st::emojiPanHeaderTop, width(), lang(lng_switch_emoji), _textWidth); + p.drawSpriteRight(QPoint(st::emojiSwitchSkip + _textWidth - st::emojiSwitchEmoji.pxWidth(), (st::emojiPanHeader - st::emojiSwitchEmoji.pxHeight()) / 2), width(), st::emojiSwitchEmoji); + } +} + EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent), _maxHeight(st::emojiPanMaxHeight), _horizontal(false), _noTabUpdate(false), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow), _recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent), @@ -1673,7 +1839,8 @@ _iconOver(-1), _iconSel(0), _iconDown(-1), _iconsDragging(false), _iconAnim(animFunc(this, &EmojiPan::iconAnim)), _iconsLeft(0), _iconsTop(0), _iconsStartX(0), _iconsMax(0), _iconsX(0, 0), _iconSelX(0, 0), _iconsStartAnim(0), _stickersShown(false), _moveStart(0), -e_scroll(this, st::emojiScroll), e_inner(), s_scroll(this, st::emojiScroll), s_inner(), _removingSetId(0) { +e_scroll(this, st::emojiScroll), e_inner(), e_switch(&e_scroll, true), +s_scroll(this, st::emojiScroll), s_inner(), s_switch(&s_scroll, false), _removingSetId(0) { setFocusPolicy(Qt::NoFocus); e_scroll.setFocusPolicy(Qt::NoFocus); e_scroll.viewport()->setFocusPolicy(Qt::NoFocus); @@ -1692,10 +1859,8 @@ e_scroll(this, st::emojiScroll), e_inner(), s_scroll(this, st::emojiScroll), s_i s_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); s_scroll.setWidget(&s_inner); - e_inner.setAttribute(Qt::WA_OpaquePaintEvent); - e_scroll.setAutoFillBackground(true); - s_inner.setAttribute(Qt::WA_OpaquePaintEvent); - s_scroll.setAutoFillBackground(true); + e_inner.moveToLeft(0, 0, e_scroll.width()); + s_inner.moveToLeft(0, 0, s_scroll.width()); int32 left = _iconsLeft = st::dropdownDef.padding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2; int32 top = _iconsTop = st::dropdownDef.padding.top() + _maxHeight - st::rbEmoji.height; @@ -1707,6 +1872,8 @@ e_scroll(this, st::emojiScroll), e_inner(), s_scroll(this, st::emojiScroll), s_i prepareTab(left, top, _width, _activity); prepareTab(left, top, _width, _travel); prepareTab(left, top, _width, _objects); + e_inner.fillPanels(e_panels); + updatePanelsPositions(e_panels, 0); _hideTimer.setSingleShot(true); connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); @@ -1722,11 +1889,15 @@ e_scroll(this, st::emojiScroll), e_inner(), s_scroll(this, st::emojiScroll), s_i connect(&e_inner, SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr))); connect(&s_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*))); - connect(&e_inner, SIGNAL(switchToStickers()), this, SLOT(onSwitch())); - connect(&s_inner, SIGNAL(switchToEmoji()), this, SLOT(onSwitch())); + connect(&s_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); + connect(&e_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); + s_switch.moveToRight(0, 0, st::emojiPanWidth); + e_switch.moveToRight(0, 0, st::emojiPanWidth); - connect(&s_inner, SIGNAL(removing(uint64)), this, SLOT(onRemoveSet(uint64))); + connect(&s_inner, SIGNAL(removing(quint64)), this, SLOT(onRemoveSet(quint64))); connect(&s_inner, SIGNAL(refreshIcons()), this, SLOT(onRefreshIcons())); + connect(&e_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); + connect(&s_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); if (cPlatform() == dbipMac) { connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); @@ -1798,6 +1969,7 @@ void EmojiPan::paintEvent(QPaintEvent *e) { if (_toCache.isNull()) { if (_cache.isNull()) { + p.fillRect(rtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height(), width()), st::white->b); if (_stickersShown) { p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories->b); if (!_icons.isEmpty()) { @@ -2028,6 +2200,7 @@ void EmojiPan::onRefreshIcons() { _iconHovers.clear(); _iconAnimations.clear(); s_inner.fillIcons(_icons); + s_inner.fillPanels(s_panels); _iconsX = anim::ivalue(0, 0); _iconSelX.finish(); _iconsStartAnim = 0; @@ -2038,11 +2211,21 @@ void EmojiPan::onRefreshIcons() { _iconHovers = QVector(_icons.size(), 0); _iconsMax = qMax(int((_icons.size() - 8) * st::rbEmoji.width), 0); } + updatePanelsPositions(s_panels, s_scroll.scrollTop()); updateSelected(); - updateIcons(); } +void EmojiPan::onRefreshPanels() { + s_inner.refreshPanels(s_panels); + e_inner.refreshPanels(e_panels); + if (_stickersShown) { + updatePanelsPositions(s_panels, s_scroll.scrollTop()); + } else { + updatePanelsPositions(e_panels, e_scroll.scrollTop()); + } +} + void EmojiPan::leaveToChildEvent(QEvent *e) { if (!_stickersShown) return; _iconsMousePos = QCursor::pos(); @@ -2334,10 +2517,23 @@ void EmojiPan::onTabChange() { e_inner.showEmojiPack(newTab); } +void EmojiPan::updatePanelsPositions(const QVector &panels, int32 st) { + for (int32 i = 0, l = panels.size(); i < l; ++i) { + int32 y = panels.at(i)->wantedY() - st; + if (y < 0) { + y = (i + 1 < l) ? qMin(panels.at(i + 1)->wantedY() - st - int(st::emojiPanHeader), 0) : 0; + } + panels.at(i)->move(0, y); + panels.at(i)->setDeleteVisible(y >= st::emojiPanHeader); + } +} + void EmojiPan::onScroll() { - int top = e_scroll.scrollTop(); + int st = e_scroll.scrollTop(); if (!_stickersShown) { - DBIEmojiTab tab = e_inner.currentTab(top); + updatePanelsPositions(e_panels, st); + + DBIEmojiTab tab = e_inner.currentTab(st); FlatRadiobutton *check = 0; switch (tab) { case dbietRecent : check = &_recent ; break; @@ -2355,11 +2551,13 @@ void EmojiPan::onScroll() { _noTabUpdate = false; } } - e_inner.setScrollTop(top); + e_inner.setScrollTop(st); - top = s_scroll.scrollTop(); + st = s_scroll.scrollTop(); if (_stickersShown) { - uint64 setId = s_inner.currentSet(top); + updatePanelsPositions(s_panels, st); + + uint64 setId = s_inner.currentSet(st); int32 newSel = 0; for (int32 i = 0, l = _icons.size(); i < l; ++i) { if (_icons.at(i).setId == setId) { @@ -2377,7 +2575,7 @@ void EmojiPan::onScroll() { updateIcons(); } } - s_inner.setScrollTop(top); + s_inner.setScrollTop(st); } void EmojiPan::onSwitch() { @@ -2398,6 +2596,10 @@ void EmojiPan::onSwitch() { hideAll(); _moveStart = getms(); + if (_stickersShown) { + e_inner.hideFinish(); + } + a_toCoord = (_stickersShown != rtl()) ? anim::ivalue(st::emojiPanWidth, 0) : anim::ivalue(-st::emojiPanWidth, 0); a_toAlpha = anim::fvalue(0, 1); a_fromCoord = (_stickersShown != rtl()) ? anim::ivalue(0, -st::emojiPanWidth) : anim::ivalue(0, st::emojiPanWidth); @@ -2407,7 +2609,7 @@ void EmojiPan::onSwitch() { update(); } -void EmojiPan::onRemoveSet(uint64 setId) { +void EmojiPan::onRemoveSet(quint64 setId) { StickerSets::const_iterator it = cStickerSets().constFind(setId); if (it != cStickerSets().cend() && !(it->flags & MTPDstickerSet_flag_official)) { _removingSetId = it->id; @@ -2677,8 +2879,9 @@ void MentionsInner::leaveEvent(QEvent *e) { } void MentionsInner::setSel(int sel, bool scroll) { + if (_sel >= 0) update(0, _sel * st::mentionHeight, width(), st::mentionHeight); _sel = sel; - parentWidget()->update(); + if (_sel >= 0) update(0, _sel * st::mentionHeight, width(), st::mentionHeight); int32 maxSel = _rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size(); if (scroll && _sel >= 0 && _sel < maxSel) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight); } @@ -2936,6 +3139,7 @@ void MentionsDropdown::hideStart() { _scroll.hide(); _hiding = true; a_opacity.start(0); + setAttribute(Qt::WA_OpaquePaintEvent, false); anim::start(this); } } @@ -2959,6 +3163,7 @@ void MentionsDropdown::showStart() { _hiding = false; show(); a_opacity.start(1); + setAttribute(Qt::WA_OpaquePaintEvent, false); anim::start(this); } @@ -2968,6 +3173,7 @@ bool MentionsDropdown::animStep(float64 ms) { if (dt >= 1) { a_opacity.finish(); _cache = QPixmap(); + setAttribute(Qt::WA_OpaquePaintEvent); if (_hiding) { hideFinish(); } else { diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index 8da58424bd..e79ec8e0a4 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -47,6 +47,16 @@ public: bool eventFilter(QObject *obj, QEvent *e); + bool overlaps(const QRect &globalRect) { + if (isHidden() || animating()) return false; + + return QRect(_st.padding.left(), + _st.padding.top(), + _width - _st.padding.left() - _st.padding.right(), + _height - _st.padding.top() - _st.padding.bottom() + ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); + } + signals: void hiding(); @@ -108,6 +118,16 @@ public: bool animStep(float64 ms); + bool overlaps(const QRect &globalRect) { + if (isHidden() || animating()) return false; + + return QRect(st::dragPadding.left(), + st::dragPadding.top(), + width() - st::dragPadding.left() - st::dragPadding.right(), + height() - st::dragPadding.top() - st::dragPadding.bottom() + ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); + } + signals: void dropped(const QMimeData *data); @@ -132,6 +152,7 @@ private: }; +class EmojiPanel; static const int EmojiColorsCount = 5; class EmojiColorPicker : public TWidget, public Animated { @@ -139,7 +160,7 @@ class EmojiColorPicker : public TWidget, public Animated { public: - EmojiColorPicker(QWidget *parent); + EmojiColorPicker(); void showEmoji(uint32 code); @@ -193,14 +214,12 @@ private: }; -static const int32 SwitcherSelected = (INT_MAX / 2); - class EmojiPanInner : public TWidget, public Animated { Q_OBJECT public: - EmojiPanInner(QWidget *parent = 0); + EmojiPanInner(); void setMaxHeight(int32 h); void paintEvent(QPaintEvent *e); @@ -224,6 +243,9 @@ public: void refreshRecent(); void setScrollTop(int top); + + void fillPanels(QVector &panels); + void refreshPanels(QVector &panels); public slots: @@ -234,6 +256,8 @@ public slots: void onPickerHidden(); void onColorSelected(EmojiPtr emoji); + bool checkPickerHide(); + signals: void selected(EmojiPtr emoji); @@ -243,6 +267,8 @@ signals: void scrollToY(int y); void disableScroll(bool dis); + void needRefreshPanels(); + private: int32 _maxHeight; @@ -250,6 +276,8 @@ private: int32 countHeight(); void selectEmoji(EmojiPtr emoji); + QRect emojiRect(int tab, int sel); + typedef QMap Animations; // index - showing, -index - hiding Animations _animations; @@ -267,9 +295,6 @@ private: EmojiColorPicker _picker; QTimer _showPickerTimer; - - float64 _switcherHover; - int32 _stickersWidth; }; struct StickerIcon { @@ -287,7 +312,7 @@ class StickerPanInner : public TWidget, public Animated { public: - StickerPanInner(QWidget *parent = 0); + StickerPanInner(); void setMaxHeight(int32 h); void paintEvent(QPaintEvent *e); @@ -309,6 +334,8 @@ public: void refreshRecent(bool resize = true); void fillIcons(QVector &icons); + void fillPanels(QVector &panels); + void refreshPanels(QVector &panels); void setScrollTop(int top); void preloadImages(); @@ -322,7 +349,7 @@ public slots: signals: void selected(DocumentData *sticker); - void removing(uint64 setId); + void removing(quint64 setId); void refreshIcons(); @@ -330,6 +357,7 @@ signals: void scrollToY(int y); void disableScroll(bool dis); + void needRefreshPanels(); private: @@ -339,6 +367,7 @@ private: int32 countHeight(); void selectEmoji(EmojiPtr emoji); + QRect stickerRect(int tab, int sel); typedef QMap Animations; // index - showing, -index - hiding Animations _animations; @@ -359,9 +388,62 @@ private: int32 _selected, _pressedSel; QPoint _lastMousePos; +}; + +class EmojiPanel : public TWidget { + Q_OBJECT + +public: + + EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY); // NoneStickerSetId if in emoji + void setText(const QString &text); + void setDeleteVisible(bool isVisible); + + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e); + + int32 wantedY() const { + return _wantedY; + } + void setWantedY(int32 y) { + _wantedY = y; + } + +signals: + + void deleteClicked(quint64 setId); + void mousePressed(); + +public slots: + + void onDelete(); + +private: + + void updateText(); + + int32 _wantedY; + QString _text, _fullText; + uint64 _setId; + bool _special, _deleteVisible; + IconedButton *_delete; + +}; + +class EmojiSwitchButton : public Button { + Q_OBJECT + +public: + + EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji + void paintEvent(QPaintEvent *e); + +protected: + + bool _toStickers; + QString _text; + int32 _textWidth; - float64 _switcherHover; - int32 _emojiWidth; }; class EmojiPan : public TWidget, public Animated { @@ -397,6 +479,16 @@ public: bool eventFilter(QObject *obj, QEvent *e); void stickersInstalled(uint64 setId); + bool overlaps(const QRect &globalRect) { + if (isHidden() || !_cache.isNull()) return false; + + return QRect(st::dropdownDef.padding.left(), + st::dropdownDef.padding.top(), + _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), + _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() + ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); + } + public slots: void refreshStickers(); @@ -411,11 +503,12 @@ public slots: void onScroll(); void onSwitch(); - void onRemoveSet(uint64 setId); + void onRemoveSet(quint64 setId); void onRemoveSetSure(); void onDelayedHide(); void onRefreshIcons(); + void onRefreshPanels(); signals: @@ -434,6 +527,7 @@ private: void updateIcons(); void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab); + void updatePanelsPositions(const QVector &panels, int32 st); void showAll(); void hideAll(); @@ -472,8 +566,12 @@ private: ScrollArea e_scroll; EmojiPanInner e_inner; + QVector e_panels; + EmojiSwitchButton e_switch; ScrollArea s_scroll; StickerPanInner s_inner; + QVector s_panels; + EmojiSwitchButton s_switch; uint64 _removingSetId; @@ -557,6 +655,12 @@ public: bool eventFilter(QObject *obj, QEvent *e); QString getSelected() const; + bool overlaps(const QRect &globalRect) { + if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false; + + return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); + } + ~MentionsDropdown(); signals: diff --git a/Telegram/SourceFiles/gui/countryinput.cpp b/Telegram/SourceFiles/gui/countryinput.cpp index d0d2fa7f71..ec85de36e9 100644 --- a/Telegram/SourceFiles/gui/countryinput.cpp +++ b/Telegram/SourceFiles/gui/countryinput.cpp @@ -304,11 +304,8 @@ void CountryList::paintEvent(QPaintEvent *e) { int l = countriesNow->size(); if (l) { - int from = (r.top() > _st.verticalMargin) ? (r.top() - _st.verticalMargin) / _st.rowHeight : 0, to = from + (r.height() / _st.rowHeight) + 2; - if (to >= l) { - if (from >= l) return; - to = l; - } + int32 from = floorclamp(r.y() - _st.verticalMargin, _st.rowHeight, 0, l); + int32 to = ceilclamp(r.y() + r.height() - _st.verticalMargin, _st.rowHeight, 0, l); p.setFont(_st.font->f); QRectF textRect(_st.margin + _st.borderMargin, _st.verticalMargin + from * _st.rowHeight, width() - 2 * _st.margin - 2 * _st.borderMargin, _st.rowHeight - _st.borderWidth); for (int i = from; i < to; ++i) { diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index 67a704400a..a03cd513cd 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -526,6 +526,15 @@ LocalImage::LocalImage(const QPixmap &pixmap, QByteArray format) : Image(format) } } +LocalImage::LocalImage(const QByteArray &filecontent, QByteArray fmt, const QPixmap &pixmap) { + data = pixmap; + format = fmt; + saved = filecontent; + if (!data.isNull()) { + globalAquiredSize += int64(data.width()) * data.height() * 4; + } +} + const QPixmap &LocalImage::pixData() const { return data; } @@ -564,6 +573,10 @@ LocalImage *getImage(const QPixmap &pixmap, QByteArray format) { return new LocalImage(pixmap, format); } +LocalImage *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap) { + return new LocalImage(filecontent, format, pixmap); +} + void clearStorageImages() { for (StorageImages::const_iterator i = storageImages.cbegin(), e = storageImages.cend(); i != e; ++i) { delete i.value(); diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 34df39d390..a77ae6ee89 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -117,7 +117,8 @@ public: LocalImage(const QString &file, QByteArray format = QByteArray()); LocalImage(const QByteArray &filecontent, QByteArray format = QByteArray()); LocalImage(const QPixmap &pixmap, QByteArray format = QByteArray()); - + LocalImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); + int32 width() const; int32 height() const; @@ -143,6 +144,7 @@ private: LocalImage *getImage(const QString &file, QByteArray format); LocalImage *getImage(const QByteArray &filecontent, QByteArray format); LocalImage *getImage(const QPixmap &pixmap, QByteArray format); +LocalImage *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); typedef QPair StorageKey; inline uint64 storageMix32To64(int32 a, int32 b) { @@ -220,6 +222,8 @@ public: } ImagePtr(const QByteArray &filecontent, QByteArray format = QByteArray()) : Parent(getImage(filecontent, format)) { } + ImagePtr(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap) : Parent(getImage(filecontent, format, pixmap)) { + } ImagePtr(const QPixmap &pixmap, QByteArray format) : Parent(getImage(pixmap, format)) { } ImagePtr(const StorageImageLocation &location, int32 size = 0) : Parent(getImage(location, size)) { diff --git a/Telegram/SourceFiles/gui/scrollarea.cpp b/Telegram/SourceFiles/gui/scrollarea.cpp index cfa8a071b0..048e23035c 100644 --- a/Telegram/SourceFiles/gui/scrollarea.cpp +++ b/Telegram/SourceFiles/gui/scrollarea.cpp @@ -598,7 +598,7 @@ void ScrollArea::touchScrollUpdated(const QPoint &screenPos) { void ScrollArea::disableScroll(bool dis) { _disabled = dis; - if (_disabled) { + if (_disabled && _st.hiding) { hor.hideTimeout(0); vert.hideTimeout(0); } @@ -706,6 +706,7 @@ void ScrollArea::setWidget(QWidget *w) { } else if (!_other && splitted) { _other = new SplittedWidgetOther(this); _other->setAttribute(Qt::WA_OpaquePaintEvent); + _other->resize(vert.width(), _other->height()); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onVerticalScroll())); hor.raise(); vert.raise(); @@ -720,7 +721,7 @@ void ScrollArea::setWidget(QWidget *w) { } if (splitted) { splitted->setOtherWidth(vert.width()); - w->resize(width() - splitted->otherWidth(), w->height()); + w->setGeometry(rtl() ? splitted->otherWidth() : 0, 0, width() - splitted->otherWidth(), w->height()); connect(splitted, SIGNAL(resizeOther()), this, SLOT(onResizeOther())); connect(splitted, SIGNAL(updateOther(const QRect&)), this, SLOT(onUpdateOther(const QRect&))); connect(splitted, SIGNAL(updateOther(const QRegion&)), this, SLOT(onUpdateOther(const QRegion&))); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index ab6c4b0654..9ee6b8e893 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2114,7 +2114,7 @@ void History::updateShowFrom() { --i; for (HistoryBlock::Items::const_iterator j = (*i)->items.cend(); j != (*i)->items.cbegin();) { --j; - if ((*j)->type() == HistoryItemMsg && (*j)->id > 0) { + if ((*j)->type() == HistoryItemMsg && (*j)->id > 0 && (!(*j)->out() || !showFrom)) { if ((*j)->id >= inboxReadBefore) { showFrom = *j; } else { @@ -2896,11 +2896,11 @@ int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) { } const QString HistoryPhoto::inDialogsText() const { - return lang(lng_in_dlg_photo); + return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.original(0, 0xFFFF, false); } const QString HistoryPhoto::inHistoryText() const { - return qsl("[ ") + lang(lng_in_dlg_photo) + qsl(" ]"); + return qsl("[ ") + lang(lng_in_dlg_photo) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF))) + qsl(" ]"); } const Text &HistoryPhoto::captionForClone() const { @@ -3230,11 +3230,11 @@ void HistoryVideo::unregItem(HistoryItem *item) { } const QString HistoryVideo::inDialogsText() const { - return lang(lng_in_dlg_video); + return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, false); } const QString HistoryVideo::inHistoryText() const { - return qsl("[ ") + lang(lng_in_dlg_video) + qsl(" ]"); + return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF))) + qsl(" ]"); } bool HistoryVideo::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 7b25431485..f516c32315 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -96,6 +96,8 @@ void HistoryInner::updateMsg(const HistoryItem *msg) { } void HistoryInner::paintEvent(QPaintEvent *e) { + if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; + if (!App::main()) return; QRect r(e->rect()); @@ -107,7 +109,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { } if (!_firstLoading && botInfo && !botInfo->text.isEmpty() && botDescHeight > 0) { - if (r.top() < botDescRect.y() + botDescRect.height() && r.bottom() > botDescRect.y()) { + if (r.y() < botDescRect.y() + botDescRect.height() && r.y() + r.height() > botDescRect.y()) { textstyleSet(&st::inTextStyle); App::roundRect(p, botDescRect, st::msgInBg, MessageInCorners, &st::msgInShadow); @@ -131,7 +133,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { SelectedItems::const_iterator selEnd = _selected.cend(); bool hasSel = !_selected.isEmpty(); - int32 drawToY = r.bottom() - ySkip; + int32 drawToY = r.y() + r.height() - ySkip; int32 selfromy = 0, seltoy = 0; if (_dragSelFrom && _dragSelTo) { @@ -1746,8 +1748,8 @@ void BotKeyboard::paintEvent(QPaintEvent *e) { for (; j != s; ++j) { const Button &btn(_btns.at(i).at(j)); QRect rect(btn.rect); - if (rect.top() >= r.bottom()) break; - if (rect.bottom() < r.top()) continue; + if (rect.y() >= r.y() + r.height()) break; + if (rect.y() + rect.height() < r.y()) continue; if (rtl()) rect.moveLeft(width() - rect.left() - rect.width()); @@ -3077,6 +3079,14 @@ void HistoryWidget::updateNotifySettings() { _muteUnmute.setText(lang(_history->mute ? lng_channel_unmute : lng_channel_mute)); } +bool HistoryWidget::contentOverlapped(const QRect &globalRect) { + return (_attachDragDocument.overlaps(globalRect) || + _attachDragPhoto.overlaps(globalRect) || + _attachType.overlaps(globalRect) || + _attachMention.overlaps(globalRect) || + _emojiPan.overlaps(globalRect)); +} + void HistoryWidget::updateReportSpamStatus() { if (!_peer || (_peer->isUser() && (peerToUser(_peer->id) == MTP::authedId() || isNotificationsUser(_peer->id) || isServiceUser(_peer->id) || _peer->asUser()->botInfo))) { _reportSpamStatus = dbiprsNoButton; @@ -6202,6 +6212,8 @@ void HistoryWidget::drawRecording(Painter &p) { } void HistoryWidget::paintEvent(QPaintEvent *e) { + if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; + Painter p(this); QRect r(e->rect()); if (r != rect()) { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index db38cec394..f5b4c583f0 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -534,6 +534,8 @@ public: void updateNotifySettings(); + bool contentOverlapped(const QRect &globalRect); + ~HistoryWidget(); signals: diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 69795b3add..286ede18a0 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -54,7 +54,7 @@ void BackgroundWidget::paintEvent(QPaintEvent *e) { p.fillRect(rect(), st::layerBG->b); p.setOpacity(aBackground.current()); - shadow.paint(p, w->boxRect(), st::boxShadowShift); + shadow.paint(p, w->geometry(), st::boxShadowShift); } void BackgroundWidget::keyPressEvent(QKeyEvent *e) { @@ -105,6 +105,11 @@ void BackgroundWidget::setInnerFocus() { } } +bool BackgroundWidget::contentOverlapped(const QRect &globalRect) { + if (isHidden()) return false; + return w && w->overlaps(globalRect); +} + void BackgroundWidget::resizeEvent(QResizeEvent *e) { w->parentResized(); } diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index 0d4b57a71a..bf23ac43e5 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -38,16 +38,15 @@ public: emit resized(); } - virtual QRect boxRect() const { - QRect res(rect()); - res.moveTopLeft(geometry().topLeft()); - return res; - } - void mousePressEvent(QMouseEvent *e) { e->accept(); } + bool overlaps(const QRect &globalRect) { + if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false; + return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); + } + signals: void closed(); @@ -78,6 +77,8 @@ public: bool canSetFocus() const; void setInnerFocus(); + bool contentOverlapped(const QRect &globalRect); + ~BackgroundWidget(); public slots: diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 0aa38af556..ab33cec92b 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -2279,11 +2279,11 @@ namespace Local { } } - class ImageLoadTask : public Task { + class AbstractCachedLoadTask : public Task { public: - ImageLoadTask(const FileKey &key, const StorageKey &location, mtpFileLoader *loader) : - _key(key), _location(location), _loader(loader), _result(0) { + AbstractCachedLoadTask(const FileKey &key, const StorageKey &location, bool readImageFlag, mtpFileLoader *loader) : + _key(key), _location(location), _readImageFlag(readImageFlag), _loader(loader), _result(0) { } void process() { FileReadDescriptor image; @@ -2294,43 +2294,44 @@ namespace Local { QByteArray imageData; quint64 locFirst, locSecond; quint32 imageType; - image.stream >> locFirst >> locSecond >> imageType >> imageData; + readFromStream(image.stream, locFirst, locSecond, imageType, imageData); if (locFirst != _location.first || locSecond != _location.second) { return; } - _result = new Result(StorageFileType(imageType), imageData); + _result = new Result(StorageFileType(imageType), imageData, _readImageFlag); } void finish() { if (_result) { _loader->localLoaded(_result->image, _result->format, _result->pixmap); } else { - StorageMap::iterator j = _imagesMap.find(_location); - if (j != _imagesMap.cend() && j->first == _key) { - clearKey(_key, UserPath); - _storageImagesSize -= j->second; - _imagesMap.erase(j); - } + clearInMap(); _loader->localLoaded(StorageImageSaved()); } } + virtual void readFromStream(QDataStream &stream, quint64 &first, quint64 &second, quint32 &type, QByteArray &data) = 0; + virtual void clearInMap() = 0; - private: + protected: FileKey _key; StorageKey _location; + bool _readImageFlag; struct Result { - Result(StorageFileType type, const QByteArray &data) : image(type, data) { - QByteArray guessFormat; - switch (type) { - case mtpc_storage_fileGif: guessFormat = "GIF"; break; - case mtpc_storage_fileJpeg: guessFormat = "JPG"; break; - case mtpc_storage_filePng: guessFormat = "PNG"; break; - default: guessFormat = QByteArray(); break; - } - pixmap = QPixmap::fromImage(App::readImage(data, &guessFormat, false), Qt::ColorOnly); - if (!pixmap.isNull()) { - format = guessFormat; + Result(StorageFileType type, const QByteArray &data, bool readImageFlag) : image(type, data) { + if (readImageFlag) { + QByteArray guessFormat; + switch (type) { + case StorageFileGif: guessFormat = "GIF"; break; + case StorageFileJpeg: guessFormat = "JPG"; break; + case StorageFilePng: guessFormat = "PNG"; break; + case StorageFileWebp: guessFormat = "WEBP"; break; + default: guessFormat = QByteArray(); break; + } + pixmap = QPixmap::fromImage(App::readImage(data, &guessFormat, false), Qt::ColorOnly); + if (!pixmap.isNull()) { + format = guessFormat; + } } } StorageImageSaved image; @@ -2342,6 +2343,24 @@ namespace Local { }; + class ImageLoadTask : public AbstractCachedLoadTask { + public: + ImageLoadTask(const FileKey &key, const StorageKey &location, mtpFileLoader *loader) : + AbstractCachedLoadTask(key, location, true, loader) { + } + void readFromStream(QDataStream &stream, quint64 &first, quint64 &second, quint32 &type, QByteArray &data) { + stream >> first >> second >> type >> data; + } + void clearInMap() { + StorageMap::iterator j = _imagesMap.find(_location); + if (j != _imagesMap.cend() && j->first == _key) { + clearKey(_key, UserPath); + _storageImagesSize -= j->second; + _imagesMap.erase(j); + } + } + }; + TaskId startImageLoad(const StorageKey &location, mtpFileLoader *loader) { StorageMap::iterator j = _imagesMap.find(location); if (j == _imagesMap.cend() || !_localLoader) { @@ -2350,27 +2369,6 @@ namespace Local { return _localLoader->addTask(new ImageLoadTask(j->first, location, loader)); } - StorageImageSaved readImage(const StorageKey &location) { - StorageMap::iterator j = _imagesMap.find(location); - if (j == _imagesMap.cend()) { - return StorageImageSaved(); - } - FileReadDescriptor image; - if (!readEncryptedFile(image, j.value().first, UserPath)) { - clearKey(j.value().first, UserPath); - _storageImagesSize -= j.value().second; - _imagesMap.erase(j); - return StorageImageSaved(); - } - - QByteArray imageData; - quint64 locFirst, locSecond; - quint32 imageType; - image.stream >> locFirst >> locSecond >> imageType >> imageData; - - return (locFirst == location.first && locSecond == location.second) ? StorageImageSaved(StorageFileType(imageType), imageData) : StorageImageSaved(); - } - int32 hasImages() { return _imagesMap.size(); } @@ -2403,32 +2401,31 @@ namespace Local { } } + class StickerImageLoadTask : public AbstractCachedLoadTask { + public: + StickerImageLoadTask(const FileKey &key, const StorageKey &location, mtpFileLoader *loader) : + AbstractCachedLoadTask(key, location, true, loader) { + } + void readFromStream(QDataStream &stream, quint64 &first, quint64 &second, quint32 &type, QByteArray &data) { + stream >> first >> second >> data; + type = StorageFilePartial; + } + void clearInMap() { + StorageMap::iterator j = _stickerImagesMap.find(_location); + if (j != _stickerImagesMap.cend() && j->first == _key) { + clearKey(j.value().first, UserPath); + _storageStickersSize -= j.value().second; + _stickerImagesMap.erase(j); + } + } + }; + TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader) { StorageMap::iterator j = _stickerImagesMap.find(location); if (j == _stickerImagesMap.cend()) { return 0; } - return 0; - } - - QByteArray readStickerImage(const StorageKey &location) { - StorageMap::iterator j = _stickerImagesMap.find(location); - if (j == _stickerImagesMap.cend()) { - return QByteArray(); - } - FileReadDescriptor draft; - if (!readEncryptedFile(draft, j.value().first, UserPath)) { - clearKey(j.value().first, UserPath); - _storageStickersSize -= j.value().second; - _stickerImagesMap.erase(j); - return QByteArray(); - } - - QByteArray stickerData; - quint64 locFirst, locSecond; - draft.stream >> locFirst >> locSecond >> stickerData; - - return (locFirst == location.first && locSecond == location.second) ? stickerData : QByteArray(); + return _localLoader->addTask(new StickerImageLoadTask(j->first, location, loader)); } int32 hasStickers() { @@ -2463,32 +2460,31 @@ namespace Local { } } + class AudioLoadTask : public AbstractCachedLoadTask { + public: + AudioLoadTask(const FileKey &key, const StorageKey &location, mtpFileLoader *loader) : + AbstractCachedLoadTask(key, location, false, loader) { + } + void readFromStream(QDataStream &stream, quint64 &first, quint64 &second, quint32 &type, QByteArray &data) { + stream >> first >> second >> data; + type = StorageFilePartial; + } + void clearInMap() { + StorageMap::iterator j = _audiosMap.find(_location); + if (j != _audiosMap.cend() && j->first == _key) { + clearKey(j.value().first, UserPath); + _storageAudiosSize -= j.value().second; + _audiosMap.erase(j); + } + } + }; + TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader) { StorageMap::iterator j = _audiosMap.find(location); if (j == _audiosMap.cend()) { return 0; } - return 0; - } - - QByteArray readAudio(const StorageKey &location) { - StorageMap::iterator j = _audiosMap.find(location); - if (j == _audiosMap.cend()) { - return QByteArray(); - } - FileReadDescriptor draft; - if (!readEncryptedFile(draft, j.value().first, UserPath)) { - clearKey(j.value().first, UserPath); - _storageAudiosSize -= j.value().second; - _audiosMap.erase(j); - return QByteArray(); - } - - QByteArray audioData; - quint64 locFirst, locSecond; - draft.stream >> locFirst >> locSecond >> audioData; - - return (locFirst == location.first && locSecond == location.second) ? audioData : QByteArray(); + return _localLoader->addTask(new AudioLoadTask(j->first, location, loader)); } int32 hasAudios() { diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index 02361be3a7..b6789065a3 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -121,19 +121,16 @@ namespace Local { void writeImage(const StorageKey &location, const ImagePtr &img); void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true); TaskId startImageLoad(const StorageKey &location, mtpFileLoader *loader); - StorageImageSaved readImage(const StorageKey &location); int32 hasImages(); qint64 storageImagesSize(); void writeStickerImage(const StorageKey &location, const QByteArray &data, bool overwrite = true); TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader); - QByteArray readStickerImage(const StorageKey &location); int32 hasStickers(); qint64 storageStickersSize(); void writeAudio(const StorageKey &location, const QByteArray &data, bool overwrite = true); TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader); - QByteArray readAudio(const StorageKey &location); int32 hasAudios(); qint64 storageAudiosSize(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 899fd0f395..17b150faab 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3568,6 +3568,11 @@ void MainWidget::onSelfParticipantUpdated(ChannelData *channel) { } } +bool MainWidget::contentOverlapped(const QRect &globalRect) { + return (history.contentOverlapped(globalRect) || + _mediaType.overlaps(globalRect)); +} + void MainWidget::usernameResolveDone(QPair toProfileStartToken, const MTPcontacts_ResolvedPeer &result) { App::wnd()->hideLayer(); if (result.type() != mtpc_contacts_resolvedPeer) return; diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 009dc42fbf..b9a44d4ec0 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -398,6 +398,8 @@ public: void gotRangeDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff); void onSelfParticipantUpdated(ChannelData *channel); + bool contentOverlapped(const QRect &globalRect); + ~MainWidget(); signals: diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp index 56c625dd5e..22929f3fee 100644 --- a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp +++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp @@ -335,11 +335,9 @@ bool mtpFileLoader::tryLoadLocal() { if (duplicateInData) { MediaKey mkey = mediaKey(_locationType, dc, id); if (_locationType == DocumentFileLocation) { - data = Local::readStickerImage(mkey); - if (!data.isEmpty()) type = mtpc_storage_filePartial; + _localTaskId = Local::startStickerImageLoad(mkey, this); } else if (_locationType == AudioFileLocation) { - data = Local::readAudio(mkey); - if (!data.isEmpty()) type = mtpc_storage_filePartial; + _localTaskId = Local::startAudioLoad(mkey, this); } } } @@ -382,7 +380,7 @@ void mtpFileLoader::localLoaded(const StorageImageSaved &result, const QByteArra } data = result.data; type = mtpFromStorageType(result.type); - if (_locationType == UnknownFileLocation) { // photo + if (!imagePixmap.isNull()) { _imageFormat = imageFormat; _imagePixmap = imagePixmap; } diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 04d33b7b82..2525ae8d33 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -988,6 +988,8 @@ QPixmap OverviewInner::genPix(PhotoData *photo, int32 size) { } void OverviewInner::paintEvent(QPaintEvent *e) { + if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; + Painter p(this); QRect r(e->rect()); @@ -1014,10 +1016,10 @@ void OverviewInner::paintEvent(QPaintEvent *e) { bool hasSel = !_selected.isEmpty(); if (_type == OverviewPhotos) { - int32 rowFrom = int32(r.top() - _addToY - st::overviewPhotoSkip) / int32(_vsize + st::overviewPhotoSkip); - int32 rowTo = int32(r.bottom() - _addToY - st::overviewPhotoSkip) / int32(_vsize + st::overviewPhotoSkip) + 1; History::MediaOverview &overview(_hist->overview[_type]); int32 count = overview.size(); + int32 rowFrom = floorclamp(r.y() - _addToY - st::overviewPhotoSkip, _vsize + st::overviewPhotoSkip, 0, count); + int32 rowTo = ceilclamp(r.y() + r.height() - _addToY - st::overviewPhotoSkip, _vsize + st::overviewPhotoSkip, 0, count); float64 w = float64(_width - st::overviewPhotoSkip) / _photosInRow; for (int32 row = rowFrom; row < rowTo; ++row) { if (row * _photosInRow >= _photosToAdd + count) break; @@ -1097,10 +1099,10 @@ void OverviewInner::paintEvent(QPaintEvent *e) { } } } else if (_type == OverviewAudioDocuments) { - int32 from = int32(r.top() - _addToY) / int32(_audioHeight); - int32 to = int32(r.bottom() - _addToY) / int32(_audioHeight) + 1; History::MediaOverview &overview(_hist->overview[_type]); int32 count = overview.size(); + int32 from = floorclamp(r.y() - _addToY, _audioHeight, 0, count); + int32 to = ceilclamp(r.y() + r.height() - _addToY, _audioHeight, 0, count); p.translate(_audioLeft, _addToY + from * _audioHeight); for (int32 index = from; index < to; ++index) { if (index >= count) break; @@ -1128,7 +1130,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { for (int32 i = 0, l = _items.size(); i < l; ++i) { if (i + 1 == l || _addToY + _items[i + 1].y > r.top()) { int32 left = st::dlgPhotoSize + st::dlgPhotoPadding, top = st::linksMargin + st::linksBorder, curY = _items[i].y; - if (_addToY + curY >= r.bottom()) break; + if (_addToY + curY >= r.y() + r.height()) break; p.translate(0, curY - y); if (_items[i].msgid) { // draw item @@ -1220,7 +1222,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { --i; if (!i || (_addToY + _height - _items[i - 1].y > r.top())) { int32 curY = _height - _items[i].y; - if (_addToY + curY >= r.bottom()) break; + if (_addToY + curY >= r.y() + r.height()) break; p.translate(0, curY - y); if (_items[i].msgid) { // draw item @@ -2598,7 +2600,9 @@ void OverviewWidget::resizeEvent(QResizeEvent *e) { } void OverviewWidget::paintEvent(QPaintEvent *e) { - QPainter p(this); + if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; + + Painter p(this); if (animating() && _showing) { p.setOpacity(a_bgAlpha.current()); p.drawPixmap(a_bgCoord.current(), 0, _bgAnimCache); diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 320f54157e..5970be2a0a 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -651,6 +651,8 @@ bool ProfileInner::event(QEvent *e) { } void ProfileInner::paintEvent(QPaintEvent *e) { + if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; + Painter p(this); QRect r(e->rect()); @@ -810,7 +812,7 @@ void ProfileInner::paintEvent(QPaintEvent *e) { for (Participants::const_iterator i = _participants.cbegin(), e = _participants.cend(); i != e; ++i, ++cnt) { int32 top = partfrom + cnt * _pHeight; if (top + _pHeight <= r.top()) continue; - if (top > r.bottom()) break; + if (top >= r.y() + r.height()) break; if (_selectedRow == cnt) { p.fillRect(_left - st::profileListPadding.width(), top, _width + 2 * st::profileListPadding.width(), _pHeight, st::profileHoverBG->b); @@ -1553,7 +1555,9 @@ void ProfileWidget::mousePressEvent(QMouseEvent *e) { } void ProfileWidget::paintEvent(QPaintEvent *e) { - QPainter p(this); + if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; + + Painter p(this); if (animating() && _showing) { p.setOpacity(a_bgAlpha.current()); p.drawPixmap(a_bgCoord.current(), 0, _bgAnimCache); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index f7412a36ca..f8e994e3ca 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -208,7 +208,7 @@ void settingsParseArgs(int argc, char *argv[]) { RecentEmojiPack &cGetRecentEmojis() { if (cRecentEmojis().isEmpty()) { RecentEmojiPack r; - if (!cRecentEmojisPreload().isEmpty()) { + if (!cRecentEmojisPreload().isEmpty() && false) { RecentEmojisPreload p(cRecentEmojisPreload()); cSetRecentEmojisPreload(RecentEmojisPreload()); r.reserve(p.size()); diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 0e14a422f2..8f7aed552f 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -205,7 +205,8 @@ RecentStickerPack &cGetRecentStickers(); DeclareSetting(uint64, LastStickersUpdate); static const uint64 DefaultStickerSetId = 0; // for backward compatibility -static const uint64 CustomStickerSetId = 0xFFFFFFFFFFFFFFFFLLU, RecentStickerSetId = 0xFFFFFFFFFFFFFFFELLU; +static const uint64 CustomStickerSetId = 0xFFFFFFFFFFFFFFFFULL, RecentStickerSetId = 0xFFFFFFFFFFFFFFFEULL; +static const uint64 NoneStickerSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel struct StickerSet { StickerSet(uint64 id, uint64 access, const QString &title, const QString &shortName, int32 count, int32 hash, int32 flags) : id(id), access(access), title(title), shortName(shortName), count(count), hash(hash), flags(flags) { } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index e36a1a1c75..6cff40ea64 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -936,6 +936,9 @@ struct DocumentData { if (loader->done()) { location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); data = loader->bytes(); + if (sticker() && !loader->imagePixmap().isNull()) { + sticker()->img = ImagePtr(data, loader->imageFormat(), loader->imagePixmap()); + } } loader->deleteLater(); loader->rpcInvalidate(); diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index a4932d05b3..47e23d5e29 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -65,6 +65,7 @@ TitleWidget::TitleWidget(Window *window) , lastMaximized(!(window->windowState() & Qt::WindowMaximized)) { setGeometry(0, 0, wnd->width(), st::titleHeight); + setAttribute(Qt::WA_OpaquePaintEvent); _lock.hide(); _update.hide(); _cancel.hide(); @@ -363,7 +364,7 @@ void TitleWidget::maximizedChanged(bool maximized, bool force) { HitTestType TitleWidget::hitTest(const QPoint &p) { if (App::wnd() && App::wnd()->layerShown()) return HitTestNone; - int x(p.x()), y(p.y()), w(width()), h(height() - st::titleShadow); + int x(p.x()), y(p.y()), w(width()), h(height()); if (cWideMode() && hider && x >= App::main()->dlgsWidth()) return HitTestNone; if (x >= st::titleIconPos.x() && y >= st::titleIconPos.y() && x < st::titleIconPos.x() + st::titleIconImg.pxWidth() && y < st::titleIconPos.y() + st::titleIconImg.pxHeight()) { diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index af3ff52d55..a7447b9d38 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -417,3 +417,16 @@ private: MimeType mimeTypeForName(const QString &mime); MimeType mimeTypeForFile(const QFileInfo &file); MimeType mimeTypeForData(const QByteArray &data); + +inline int32 floorclamp(int32 value, int32 step, int32 lowest, int32 highest) { + return qMin(qMax(value / step, lowest), highest); +} +inline int32 floorclamp(float64 value, int32 step, int32 lowest, int32 highest) { + return qMin(qMax(qFloor(value / step), lowest), highest); +} +inline int32 ceilclamp(int32 value, int32 step, int32 lowest, int32 highest) { + return qMax(qMin((value / step) + ((value % step) ? 1 : 0), highest), lowest); +} +inline int32 ceilclamp(float64 value, int32 step, int32 lowest, int32 highest) { + return qMax(qMin(qCeil(value / step), highest), lowest); +} diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 139f543404..b5db91a42e 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -865,6 +865,12 @@ void Window::hideMediaview() { } } +bool Window::contentOverlapped(const QRect &globalRect) { + if (main && main->contentOverlapped(globalRect)) return true; + if (layerBG && layerBG->contentOverlapped(globalRect)) return true; + return false; +} + void Window::setInnerFocus() { if (layerBG && layerBG->canSetFocus()) { layerBG->setInnerFocus(); @@ -1177,7 +1183,7 @@ void Window::resizeEvent(QResizeEvent *e) { cSetWideMode(wideMode); updateWideMode(); } - title->setGeometry(QRect(0, 0, width(), st::titleHeight + st::titleShadow)); + title->setGeometry(0, 0, width(), st::titleHeight); if (layerBG) layerBG->resize(width(), height()); if (_connecting) _connecting->setGeometry(0, height() - _connecting->height(), _connecting->width(), _connecting->height()); emit resized(QSize(width(), height() - st::titleHeight)); diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index f81793ef94..be20c0b4b5 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -233,6 +233,14 @@ public: bool isActive(bool cached = true) const; void hideMediaview(); + bool contentOverlapped(const QRect &globalRect); + bool contentOverlapped(QWidget *w, QPaintEvent *e) { + return contentOverlapped(QRect(w->mapToGlobal(e->rect().topLeft()), e->rect().size())); + } + bool contentOverlapped(QWidget *w, const QRegion &r) { + return contentOverlapped(QRect(w->mapToGlobal(r.boundingRect().topLeft()), r.boundingRect().size())); + } + public slots: void updateIsActive(int timeout = 0);