diff --git a/Telegram/Resources/lang.txt b/Telegram/Resources/lang.txt index 3a4d9273f..3855a3231 100644 --- a/Telegram/Resources/lang.txt +++ b/Telegram/Resources/lang.txt @@ -382,6 +382,10 @@ This software is licensed under [a href=\"https://github.com/telegramdesktop/tde source code is available on [a href=\"https://github.com/telegramdesktop/tdesktop\"]GitHub[/a]."; lng_about_done: "Done"; +lng_search_no_results: "No messages found"; +lng_search_one_result: "Found {count} message"; +lng_search_n_results: "Found {count} messages"; + // Mac specific lng_mac_choose_app: "Choose Application"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index f95169460..b37b5be5b 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -958,6 +958,12 @@ unreadBarBG: #fcfbfa; unreadBarBorder: titleShadowColor; unreadBarColor: #538bb4; +searchedBarHeight: unreadBarHeight; +searchedBarFont: unreadBarFont; +searchedBarBG: #f1f1f1; +searchedBarBorder: unreadBarBorder; +searchedBarColor: #aaa; + layerSlideDuration: 200; layerHideDuration: 200; layerPadding: margins(10px, 10px, 10px, 10px); diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 517a53bf1..2ce103048 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -133,7 +133,7 @@ namespace App { } void logOut() { - MTP::send(MTPauth_LogOut(), rpcDone(&loggedOut), rpcFail(&loggedOut)); + MTP::logoutKeys(rpcDone(&loggedOut), rpcFail(&loggedOut)); } PeerId peerFromMTP(const MTPPeer &peer_id) { diff --git a/Telegram/SourceFiles/art/icon.png b/Telegram/SourceFiles/art/icon.png deleted file mode 100644 index 4e53389ae..000000000 Binary files a/Telegram/SourceFiles/art/icon.png and /dev/null differ diff --git a/Telegram/SourceFiles/art/iconf.png b/Telegram/SourceFiles/art/iconf.png deleted file mode 100644 index fa9c541a6..000000000 Binary files a/Telegram/SourceFiles/art/iconf.png and /dev/null differ diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 52ebb0aae..99d1b64ad 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com */ #pragma once -static const int32 AppVersion = 5006; -static const wchar_t *AppVersionStr = L"0.5.6"; +static const int32 AppVersion = 5007; +static const wchar_t *AppVersionStr = L"0.5.7"; #ifdef Q_OS_WIN static const wchar_t *AppName = L"Telegram Win (Unofficial)"; #else @@ -60,7 +60,7 @@ enum { SaveRecentEmojisTimeout = 3000, // 3 secs - AutoSearchTimeout = 1500, // 1.5 secs + AutoSearchTimeout = 900, // 0.9 secs SearchPerPage = 50, }; @@ -116,10 +116,20 @@ static const char *ApiHash = "344583e45741c457fe1862106095a5eb"; #endif inline const char *cApiDeviceModel() { +#ifdef Q_OS_WIN return "x86 desktop"; +#else + return "x64 desktop"; +#endif } inline const char *cApiSystemVersion() { +#ifdef Q_OS_WIN return "windows"; +#elif defined Q_OS_MAC + return "os x"; +#elif defined Q_OS_LINUX + return "linux"; +#endif } inline QString cApiAppVersion() { return QString::number(AppVersion); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 13802c6c6..30a3d7854 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -26,16 +26,13 @@ Copyright (c) 2014 John Preston, https://tdesktop.com #include "boxes/newgroupbox.h" DialogsListWidget::DialogsListWidget(QWidget *parent, MainWidget *main) : QWidget(parent), -dialogs(false), contactsNoDialogs(true), contacts(true), sel(0), contactSel(false), selByMouse(false), filteredSel(-1), searchedSel(-1), _state(DefaultState) { + dialogs(false), contactsNoDialogs(true), contacts(true), sel(0), contactSel(false), selByMouse(false), filteredSel(-1), searchedCount(0), searchedSel(-1), _state(DefaultState) { connect(main, SIGNAL(dialogToTop(const History::DialogLinks &)), this, SLOT(onDialogToTop(const History::DialogLinks &))); connect(main, SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(onPeerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &))); connect(main, SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(onPeerPhotoChanged(PeerData *))); connect(main, SIGNAL(dialogRowReplaced(DialogRow *, DialogRow *)), this, SLOT(onDialogRowReplaced(DialogRow *, DialogRow *))); connect(main, SIGNAL(historyItemReplaced(HistoryItem *, HistoryItem *)), this, SLOT(onItemReplaced(HistoryItem *, HistoryItem *))); connect(main, SIGNAL(historyItemDeleted(HistoryItem *)), this, SLOT(onItemRemoved(HistoryItem *))); - - _updateSearchTimer.setSingleShot(true); - connect(&_updateSearchTimer, SIGNAL(timeout()), this, SIGNAL(searchMessages())); } void DialogsListWidget::paintEvent(QPaintEvent *e) { @@ -58,33 +55,42 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) { } else if (!otherStart) { // .. paint no dialogs found } - } else if (_state == FilteredState) { + } else if (_state == FilteredState || _state == SearchedState) { if (filterResults.isEmpty()) { // .. paint no dialogs } else { int32 from = r.top() / int32(st::dlgHeight); - if (from < 0) from = 0; + if (from < 0) { + from = 0; + } else if (from > filterResults.size()) { + from = filterResults.size(); + } + p.translate(0, from * st::dlgHeight); if (from < filterResults.size()) { int32 to = (r.bottom() / int32(st::dlgHeight)) + 1, w = width(); if (to > filterResults.size()) to = filterResults.size(); - - p.translate(0, from * st::dlgHeight); for (; from < to; ++from) { - bool active = (filterResults[from]->history->peer == App::main()->activePeer()); + bool active = (filterResults[from]->history->peer == App::main()->activePeer() && !App::main()->activeMsgId()); bool selected = (from == filteredSel); filterResults[from]->paint(p, w, active, selected); p.translate(0, st::dlgHeight); } } } - } else if (_state == SearchedState) { - if (searchResults.isEmpty()) { - // .. paint no dialogs - } else { - int32 from = r.top() / int32(st::dlgHeight); + + if (_state == SearchedState || !searchResults.isEmpty()) { + QString text = searchResults.isEmpty() ? lang(lng_search_no_results) : lang(searchedCount > 1 ? lng_search_n_results : lng_search_one_result).replace(qsl("{count}"), QString::number(searchedCount)); + p.fillRect(0, 0, width(), st::searchedBarHeight, st::searchedBarBG->b); + p.setFont(st::searchedBarFont->f); + p.setPen(st::searchedBarColor->p); + p.drawText(QRect(0, 0, width(), st::searchedBarHeight), text, style::al_center); + p.translate(0, st::searchedBarHeight); + + int32 skip = filterResults.size() * st::dlgHeight + st::searchedBarHeight; + int32 from = (r.top() - skip) / int32(st::dlgHeight); if (from < 0) from = 0; if (from < searchResults.size()) { - int32 to = (r.bottom() / int32(st::dlgHeight)) + 1, w = width(); + int32 to = ((r.bottom() - skip) / int32(st::dlgHeight)) + 1, w = width(); if (to > searchResults.size()) to = searchResults.size(); p.translate(0, from * st::dlgHeight); @@ -100,11 +106,7 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) { } void DialogsListWidget::activate() { - if ( - (_state == DefaultState && !sel) || - (_state == FilteredState && (filteredSel < 0 || filteredSel >= filterResults.size())) || - (_state == SearchedState && (searchedSel < 0 || searchedSel >= searchResults.size())) - ) { + if (_state == DefaultState && !sel) { selectSkip(1); } } @@ -135,7 +137,7 @@ void DialogsListWidget::onUpdateSelected(bool force) { setCursor(sel ? style::cur_pointer : style::cur_default); parentWidget()->update(); } - } else if (_state == FilteredState) { + } else if (_state == FilteredState || _state == SearchedState) { if (!filterResults.isEmpty()) { int32 newFilteredSel = mouseY / int32(st::dlgHeight); if (newFilteredSel < 0 || newFilteredSel >= filterResults.size()) { @@ -147,9 +149,9 @@ void DialogsListWidget::onUpdateSelected(bool force) { parentWidget()->update(); } } - } else if (_state == SearchedState) { - if (!searchResults.isEmpty()) { - int32 newSearchedSel = mouseY / int32(st::dlgHeight); + if (_state == SearchedState && !searchResults.isEmpty()) { + mouseY -= filterResults.size() * st::dlgHeight + st::searchedBarHeight; + int32 newSearchedSel = (mouseY >= 0) ? mouseY / int32(st::dlgHeight) : -1; if (newSearchedSel < 0 || newSearchedSel >= searchResults.size()) { newSearchedSel = -1; } @@ -172,7 +174,7 @@ void DialogsListWidget::mousePressEvent(QMouseEvent *e) { } void DialogsListWidget::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) { - if (_state == FilteredState) { + if (_state == FilteredState || _state == SearchedState) { for (FilteredDialogs::iterator i = filterResults.begin(); i != filterResults.end();) { if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts! if (newRow) { @@ -243,7 +245,7 @@ void DialogsListWidget::removeContact(UserData *user) { void DialogsListWidget::dlgUpdated(DialogRow *row) { if (_state == DefaultState) { update(0, row->pos * st::dlgHeight, width(), st::dlgHeight); - } else if (_state == FilteredState) { + } else if (_state == FilteredState || _state == SearchedState) { int32 cnt = 0; for (FilteredDialogs::const_iterator i = filterResults.cbegin(), e = filterResults.cend(); i != e; ++i) { if ((*i)->history == row->history) { @@ -267,7 +269,7 @@ void DialogsListWidget::dlgUpdated(History *history) { update(0, (dialogs.list.count + i.value()->pos) * st::dlgHeight, width(), st::dlgHeight); } } - } else if (_state == FilteredState) { + } else if (_state == FilteredState || _state == SearchedState) { int32 cnt = 0; for (FilteredDialogs::const_iterator i = filterResults.cbegin(), e = filterResults.cend(); i != e; ++i) { if ((*i)->history == history) { @@ -276,14 +278,15 @@ void DialogsListWidget::dlgUpdated(History *history) { } ++cnt; } - } else if (_state == SearchedState) { - int32 cnt = 0; - for (SearchResults::const_iterator i = searchResults.cbegin(), e = searchResults.cend(); i != e; ++i) { - if ((*i)->_item->history() == history) { - update(0, cnt * st::dlgHeight, width(), st::dlgHeight); - break; + if (!searchResults.isEmpty()) { + int32 cnt = 0, add = filterResults.size() * st::dlgHeight + st::searchedBarHeight; + for (SearchResults::const_iterator i = searchResults.cbegin(), e = searchResults.cend(); i != e; ++i) { + if ((*i)->_item->history() == history) { + update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight); + break; + } + ++cnt; } - ++cnt; } } } @@ -331,11 +334,6 @@ void DialogsListWidget::onPeerPhotoChanged(PeerData *peer) { } void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) { - if (_state == SearchedState && !newFilter.trimmed().isEmpty()) { - _updateSearchTimer.start(AutoSearchTimeout); - return; - } - newFilter = textAccentFold(newFilter.trimmed().toLower()); if (newFilter != filter || force) { QStringList f; @@ -356,6 +354,7 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) { if (filter.isEmpty()) { _state = DefaultState; filterResults.clear(); + searchResults.clear(); } else { QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; @@ -434,6 +433,9 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) { refresh(true); setMouseSel(false, true); } + if (_state != DefaultState) { + emit searchMessages(); + } } DialogsListWidget::~DialogsListWidget() { @@ -485,7 +487,7 @@ void DialogsListWidget::dialogsReceived(const QVector &added) { refresh(); } -void DialogsListWidget::searchReceived(const QVector &messages, bool fromStart) { +void DialogsListWidget::searchReceived(const QVector &messages, bool fromStart, int32 fullCount) { if (fromStart) { clearSearchResults(); } @@ -493,6 +495,10 @@ void DialogsListWidget::searchReceived(const QVector &messages, bool HistoryItem *item = App::histories().addToBack(*i, -1); searchResults.push_back(new FakeDialogRow(item)); } + searchedCount = fullCount; + if (_state == FilteredState) { + _state = SearchedState; + } refresh(); } @@ -531,15 +537,15 @@ int32 DialogsListWidget::addNewContact(int32 uid, bool select) { } void DialogsListWidget::refresh(bool toTop) { - int32 cnt = 0; + int32 h = 0; if (_state == DefaultState) { - cnt = dialogs.list.count + contactsNoDialogs.list.count; + h = (dialogs.list.count + contactsNoDialogs.list.count) * st::dlgHeight; } else if (_state == FilteredState) { - cnt = filterResults.count(); + h = (filterResults.count() + searchResults.count()) * st::dlgHeight + (searchResults.isEmpty() ? 0 : st::searchedBarHeight); } else if (_state == SearchedState) { - cnt = searchResults.count(); + h = (filterResults.count() + searchResults.count()) * st::dlgHeight + st::searchedBarHeight; } - resize(width(), cnt * st::dlgHeight); + resize(width(), h); if (toTop) { emit mustScrollTo(0, 0); loadPeerPhotos(0); @@ -553,19 +559,18 @@ void DialogsListWidget::setMouseSel(bool msel, bool toTop) { if (_state == DefaultState) { sel = (dialogs.list.count ? dialogs.list.begin : (contactsNoDialogs.list.count ? contactsNoDialogs.list.begin : 0)); contactSel = !dialogs.list.count && contactsNoDialogs.list.count; - } else if (_state == FilteredState) { - filteredSel = 0; - } else if (_state == SearchedState) { - searchedSel = -1; // don't select first elem in search + } else if (_state == FilteredState || _state == SearchedState) { // don't select first elem in search + filteredSel = -1; + searchedSel = -1; } } } void DialogsListWidget::setState(State newState) { _state = newState; - if (_state == DefaultState || _state == FilteredState) { + if (_state == DefaultState) { clearSearchResults(); - searchedSel = -1; + searchedSel = filteredSel = -1; } else if (_state == DefaultState || _state == SearchedState) { filterResults.clear(); filteredSel = -1; @@ -579,8 +584,10 @@ DialogsListWidget::State DialogsListWidget::state() const { } void DialogsListWidget::clearFilter() { - if (_state == FilteredState) { + if (_state == FilteredState || _state == SearchedState) { _state = DefaultState; + filterResults.clear(); + searchResults.clear(); filter = QString(); refresh(true); } @@ -596,6 +603,7 @@ void DialogsListWidget::addDialog(const MTPDdialog &dialog) { } void DialogsListWidget::selectSkip(int32 direction) { + int32 skipMore = 0; if (_state == DefaultState) { if (!sel) { if (dialogs.list.count && direction > 0) { @@ -622,22 +630,56 @@ void DialogsListWidget::selectSkip(int32 direction) { } int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight; emit mustScrollTo(fromY, fromY + st::dlgHeight); - } else if (_state == FilteredState) { - if (filterResults.isEmpty()) return; - int32 newSel = snap(filteredSel + direction, 0, filterResults.size() - 1); - if (newSel != filteredSel) { - filteredSel = newSel; + } else if (_state == FilteredState || _state == SearchedState) { + if (filterResults.isEmpty() && searchResults.isEmpty()) return; + if (filteredSel < 0 || filteredSel >= filterResults.size()) { + if (searchedSel < 0 || searchedSel >= searchResults.size()) { + if (filterResults.isEmpty()) { + searchedSel = 0; + } else { + filteredSel = 0; + } + } else if (direction < 0 && !searchedSel && !filterResults.isEmpty()) { + searchedSel = -1; + filteredSel = filterResults.size() + direction; + if (filteredSel < 0) filteredSel = 0; + } else { + if (direction < -1 && searchedSel + direction < 0) { + skipMore = direction + searchedSel; + if (skipMore == direction) { + skipMore = 0; + } else { + direction -= skipMore; + } + } + searchedSel = snap(searchedSel + direction, 0, searchResults.size() - 1); + } + } else if (direction > 0 && filteredSel == filterResults.size() - 1 && !searchResults.isEmpty()) { + filteredSel = -1; + searchedSel = direction - 1; + if (searchedSel > searchResults.size() - 1) searchedSel = searchResults.size() - 1; + } else { + if (direction > 1 && filteredSel + direction > filterResults.size() - 1) { + skipMore = direction - (filterResults.size() - 1 - filteredSel); + if (skipMore == direction) { + skipMore = 0; + } else { + direction -= skipMore; + } + } + filteredSel = snap(filteredSel + direction, 0, filterResults.size() - 1); } - emit mustScrollTo(filteredSel * st::dlgHeight, (filteredSel + 1) * st::dlgHeight); - } else if (_state == SearchedState) { - if (searchResults.isEmpty()) return; - int32 newSel = snap(searchedSel + direction, 0, searchResults.size() - 1); - if (newSel != searchedSel) { - searchedSel = newSel; + if (filteredSel >= 0 && filteredSel < filterResults.size()) { + emit mustScrollTo(filteredSel * st::dlgHeight, (filteredSel + 1) * st::dlgHeight); + } else { + emit mustScrollTo((searchedSel + filterResults.size()) * st::dlgHeight + (searchedSel ? st::searchedBarHeight : 0), (searchedSel + filterResults.size() + 1) * st::dlgHeight + st::searchedBarHeight); } - emit mustScrollTo(searchedSel * st::dlgHeight, (searchedSel + 1) * st::dlgHeight); } - parentWidget()->update(); + if (skipMore) { + return selectSkip(skipMore); + } else { + parentWidget()->update(); + } } void DialogsListWidget::scrollToPeer(const PeerId &peer) { @@ -729,7 +771,7 @@ void DialogsListWidget::loadPeerPhotos(int32 yFrom) { row->history->peer->photo->load(); } } - } else if (_state == FilteredState) { + } else if (_state == FilteredState || _state == SearchedState) { int32 from = yFrom / st::dlgHeight; if (from < 0) from = 0; if (from < filterResults.size()) { @@ -740,11 +782,11 @@ void DialogsListWidget::loadPeerPhotos(int32 yFrom) { filterResults[from]->history->peer->photo->load(); } } - } else if (_state == SearchedState) { - int32 from = yFrom / st::dlgHeight; + + from = (yFrom > st::searchedBarHeight ? ((yFrom - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size(); if (from < 0) from = 0; if (from < searchResults.size()) { - int32 to = (yTo / int32(st::dlgHeight)) + 1, w = width(); + int32 to = (yTo > st::searchedBarHeight ? ((yTo - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() + 1, w = width(); if (to > searchResults.size()) to = searchResults.size(); for (; from < to; ++from) { @@ -759,10 +801,10 @@ bool DialogsListWidget::choosePeer() { MsgId msgId = 0; if (_state == DefaultState) { if (sel) history = sel->history; - } else if (_state == FilteredState) { - if (filteredSel >= 0 && filteredSel < filterResults.size()) history = filterResults[filteredSel]->history; - } else if (_state == SearchedState) { - if (searchedSel >= 0 && searchedSel < searchResults.size()) { + } else if (_state == FilteredState || _state == SearchedState) { + if (filteredSel >= 0 && filteredSel < filterResults.size()) { + history = filterResults[filteredSel]->history; + } else if (searchedSel >= 0 && searchedSel < searchResults.size()) { history = searchResults[searchedSel]->_item->history(); msgId = searchResults[searchedSel]->_item->id; } @@ -808,7 +850,7 @@ PeerData *DialogsListWidget::peerBefore(const PeerData *peer) const { if (i.value()->prev) { return i.value()->prev->history->peer; } - } else if (_state == FilteredState) { + } else if (_state == FilteredState || _state == SearchedState) { if (filterResults.isEmpty() || filterResults.at(0)->history->peer == peer) return 0; for (FilteredDialogs::const_iterator b = filterResults.cbegin(), i = b + 1, e = filterResults.cend(); i != e; ++i) { @@ -840,7 +882,7 @@ PeerData *DialogsListWidget::peerAfter(const PeerData *peer) const { } else if (contactsNoDialogs.list.count) { return contactsNoDialogs.list.begin->history->peer; } - } else if (_state == FilteredState) { + } else if (_state == FilteredState || _state == SearchedState) { for (FilteredDialogs::const_iterator i = filterResults.cbegin(), e = filterResults.cend(); i != e; ++i) { if ((*i)->history->peer == peer) { ++i; @@ -871,7 +913,6 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent) , dlgPreloading(0) , contactsRequest(0) , _filter(this, st::dlgFilter, lang(lng_dlg_filter)) -, _stateSwitcher(this, st::dlgState) , _newGroup(this, st::btnNewGroup) , _addContact(this, st::btnAddContact) , _cancelSearch(this, st::btnCancelSearch) @@ -883,9 +924,8 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent) scroll.setFocusPolicy(Qt::NoFocus); connect(&list, SIGNAL(mustScrollTo(int, int)), &scroll, SLOT(scrollToY(int, int))); connect(&list, SIGNAL(dialogToTopFrom(int)), this, SLOT(onDialogToTopFrom(int))); -// connect(&list, SIGNAL(peerChosen(const PeerId &, MsgId)), this, SLOT(onCancel())); connect(&list, SIGNAL(peerChosen(const PeerId &, MsgId)), this, SIGNAL(peerChosen(const PeerId &, MsgId))); - connect(&list, SIGNAL(searchMessages()), this, SLOT(onSearchMessages())); + connect(&list, SIGNAL(searchMessages()), this, SLOT(onNeedSearchMessages())); connect(&scroll, SIGNAL(geometryChanged()), &list, SLOT(onParentGeometryChanged())); connect(&scroll, SIGNAL(scrolled()), &list, SLOT(onUpdateSelected())); connect(&scroll, SIGNAL(scrolled()), this, SLOT(onListScroll())); @@ -896,16 +936,12 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent) connect(&_newGroup, SIGNAL(clicked()), this, SLOT(onNewGroup())); connect(&_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); - _stateSwitcher.addButton(lang(lng_dlg_conversations)); - _stateSwitcher.addButton(lang(lng_dlg_messages)); - _stateSwitcher.hide(); - - connect(&_stateSwitcher, SIGNAL(changed()), this, SLOT(onStateChange())); + _searchTimer.setSingleShot(true); + connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchMessages())); scroll.show(); _filter.show(); _filter.move(st::dlgPaddingHor, st::dlgFilterPadding); - _stateSwitcher.move(st::dlgPaddingHor, st::dlgFilterPadding * 2 + _filter.height()); _filter.setFocusPolicy(Qt::StrongFocus); _filter.customUpDown(true); _addContact.hide(); @@ -981,9 +1017,7 @@ void DialogsWidget::onCancel() { } void DialogsWidget::clearFiltered() { - if (list.state() != DialogsListWidget::SearchedState) { - onCancel(); - } + onCancel(); } void DialogsWidget::unreadCountsReceived(const QVector &dialogs) { @@ -1051,29 +1085,45 @@ bool DialogsWidget::dialogsFailed(const RPCError &e) { return true; } -void DialogsWidget::onSearchMessages(bool force) { +bool DialogsWidget::onSearchMessages(bool searchCache) { QString q = _filter.text().trimmed(); if (q.isEmpty()) { if (_searchRequest) { - MTP::cancel(_searchRequest); _searchRequest = 0; } - if (force) { - list.setState(DialogsListWidget::DefaultState); - } - return; + return true; } - if (force || _searchQuery != q) { - if (_searchRequest) MTP::cancel(_searchRequest); + if (searchCache) { + SearchCache::const_iterator i = _searchCache.constFind(q); + if (i != _searchCache.cend()) { + _searchQuery = q; + _searchFull = false; + _searchRequest = 0; + searchReceived(true, i.value(), 0); + return true; + } + } else if (_searchQuery != q) { _searchQuery = q; _searchFull = false; _searchRequest = MTP::send(MTPmessages_Search(MTP_inputPeerEmpty(), MTP_string(_searchQuery), MTP_inputMessagesFilterEmpty(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(SearchPerPage)), rpcDone(&DialogsWidget::searchReceived, true), rpcFail(&DialogsWidget::searchFailed)); + _searchQueries.insert(_searchRequest, _searchQuery); + } + return false; +} + + +void DialogsWidget::onNeedSearchMessages() { + if (!onSearchMessages(true)) { + _searchTimer.start(AutoSearchTimeout); } } void DialogsWidget::onSearchMore(MsgId minMsgId) { if (!_searchRequest && !_searchFull) { _searchRequest = MTP::send(MTPmessages_Search(MTP_inputPeerEmpty(), MTP_string(_searchQuery), MTP_inputMessagesFilterEmpty(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(minMsgId), MTP_int(SearchPerPage)), rpcDone(&DialogsWidget::searchReceived, !minMsgId), rpcFail(&DialogsWidget::searchFailed)); + if (!minMsgId) { + _searchQueries.insert(_searchRequest, _searchQuery); + } } } @@ -1108,13 +1158,21 @@ bool DialogsWidget::contactsFailed() { } void DialogsWidget::searchReceived(bool fromStart, const MTPmessages_Messages &result, mtpRequestId req) { + if (fromStart && (list.state() == DialogsListWidget::FilteredState || list.state() == DialogsListWidget::SearchedState)) { + SearchQueries::iterator i = _searchQueries.find(req); + if (i != _searchQueries.cend()) { + _searchCache[i.value()] = result; + _searchQueries.erase(i); + } + } + if (_searchRequest == req) { switch (result.type()) { case mtpc_messages_messages: { App::feedUsers(result.c_messages_messages().vusers); App::feedChats(result.c_messages_messages().vchats); const QVector &msgs(result.c_messages_messages().vmessages.c_vector().v); - list.searchReceived(msgs, fromStart); + list.searchReceived(msgs, fromStart, msgs.size()); if (msgs.isEmpty()) { _searchFull = true; } @@ -1124,12 +1182,13 @@ void DialogsWidget::searchReceived(bool fromStart, const MTPmessages_Messages &r App::feedUsers(result.c_messages_messagesSlice().vusers); App::feedChats(result.c_messages_messagesSlice().vchats); const QVector &msgs(result.c_messages_messagesSlice().vmessages.c_vector().v); - list.searchReceived(msgs, fromStart); + list.searchReceived(msgs, fromStart, result.c_messages_messagesSlice().vcount.v); if (msgs.isEmpty()) { _searchFull = true; } } break; } + _searchRequest = 0; } } @@ -1167,21 +1226,15 @@ void DialogsWidget::onListScroll() { void DialogsWidget::onFilterUpdate() { QString filterText = _filter.text(); list.onFilterUpdate(filterText); - DialogsListWidget::State s = list.state(); - bool switcherVisible = (s != DialogsListWidget::DefaultState); - if ((switcherVisible && _stateSwitcher.isHidden()) || (!switcherVisible && !_stateSwitcher.isHidden())) { - if (switcherVisible) { - _stateSwitcher.show(); - } else { - _stateSwitcher.hide(); - _stateSwitcher.setSelected(0); + if (filterText.isEmpty()) { + _searchCache.clear(); + _searchQueries.clear(); + _searchQuery = QString(); + if (!_cancelSearch.isHidden()) { + _cancelSearch.hide(); + _newGroup.show(); } - resizeEvent(0); - } - if (filterText.isEmpty() && !_cancelSearch.isHidden()) { - _cancelSearch.hide(); - _newGroup.show(); - } else if (!filterText.isEmpty() && _cancelSearch.isHidden()) { + } else if (_cancelSearch.isHidden()) { _cancelSearch.show(); _newGroup.hide(); } @@ -1190,17 +1243,11 @@ void DialogsWidget::onFilterUpdate() { void DialogsWidget::resizeEvent(QResizeEvent *e) { int32 w = width() - st::dlgShadow; _filter.setGeometry(st::dlgPaddingHor, st::dlgFilterPadding, w - 2 * st::dlgPaddingHor, _filter.height()); - _stateSwitcher.setGeometry(st::dlgPaddingHor, st::dlgFilterPadding * 2 + _filter.height(), _filter.width(), _filter.height()); _newGroup.move(w - _newGroup.width() - st::dlgPaddingHor, _filter.y()); _addContact.move(w - _addContact.width() - st::dlgPaddingHor, _filter.y()); _cancelSearch.move(w - _cancelSearch.width() - st::dlgPaddingHor, _filter.y()); - if (_stateSwitcher.isHidden()) { - scroll.move(0, _filter.height() + 2 * st::dlgFilterPadding); - scroll.resize(w, height() - _filter.y() - _filter.height() - st::dlgFilterPadding - st::dlgPaddingVer); - } else { - scroll.move(0, _filter.height() + _stateSwitcher.height() + 3 * st::dlgFilterPadding); - scroll.resize(w, height() - _stateSwitcher.y() - _stateSwitcher.height() - st::dlgFilterPadding - st::dlgPaddingVer); - } + scroll.move(0, _filter.height() + 2 * st::dlgFilterPadding); + scroll.resize(w, height() - _filter.y() - _filter.height() - st::dlgFilterPadding - st::dlgPaddingVer); list.resize(w, list.height()); onListScroll(); } @@ -1209,7 +1256,7 @@ void DialogsWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { e->ignore(); } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - if (!list.choosePeer() && list.state() == DialogsListWidget::SearchedState) { + if (!list.choosePeer() && (list.state() == DialogsListWidget::SearchedState || list.state() == DialogsListWidget::FilteredState)) { onSearchMessages(); } } else if (e->key() == Qt::Key_Down) { @@ -1285,25 +1332,6 @@ void DialogsWidget::onCancelSearch() { onFilterUpdate(); } -void DialogsWidget::onStateChange() { - if (!_stateSwitcher.isHidden()) { - if (_stateSwitcher.selected() == 0) { - list.setState(DialogsListWidget::FilteredState); - _searchQuery = QString(); - if (_searchRequest) { - MTP::cancel(_searchRequest); - _searchRequest = 0; - } - } else { - list.setState(DialogsListWidget::SearchedState); - } - list.onFilterUpdate(_filter.text()); - if (list.state() == DialogsListWidget::SearchedState) { - onSearchMessages(true); - } - } -} - void DialogsWidget::onDialogToTopFrom(int movedFrom) { if (scroll.scrollTop() > 0) { if (movedFrom > scroll.scrollTop()) { diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 27c471a7c..2e78c200c 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -27,7 +27,7 @@ public: DialogsListWidget(QWidget *parent, MainWidget *main); void dialogsReceived(const QVector &dialogs); - void searchReceived(const QVector &messages, bool fromStart); + void searchReceived(const QVector &messages, bool fromStart, int32 fullCount); void showMore(int32 pixels); void activate(); @@ -119,13 +119,10 @@ private: int32 filteredSel; SearchResults searchResults; - int32 searchedSel; + int32 searchedCount, searchedSel; State _state; - QTimer _updateSearchTimer; - QString _searchQuery; - QPoint lastMousePos; void paintDialog(QPainter &p, DialogRow *dialog); @@ -191,10 +188,9 @@ public slots: void onNewGroup(); void onCancelSearch(); - void onStateChange(); - void onDialogToTopFrom(int movedFrom); - void onSearchMessages(bool force = false); + bool onSearchMessages(bool searchCache = false); + void onNeedSearchMessages(); private: @@ -213,13 +209,19 @@ private: mtpRequestId contactsRequest; FlatInput _filter; - Switcher _stateSwitcher; IconedButton _newGroup, _addContact, _cancelSearch; ScrollArea scroll; DialogsListWidget list; + QTimer _searchTimer; QString _searchQuery; bool _searchFull; mtpRequestId _searchRequest; + typedef QMap SearchCache; + SearchCache _searchCache; + + typedef QMap SearchQueries; + SearchQueries _searchQueries; + }; diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index f7522142b..5eb225d9f 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -112,7 +112,7 @@ namespace { const QRegularExpression reDomain(QString::fromUtf8("(? validProtocols, validTopDomains; void initLinkSets(); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index c33747f83..2ca012a1e 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1397,6 +1397,18 @@ void History::loadAround(MsgId msgId) { } } +bool History::canShowAround(MsgId msgId) const { + if (activeMsgId != msgId) { + if (msgId) { + HistoryItem *item = App::histItemById(msgId); + return item && item->block(); + } else { + return loadedAtBottom(); + } + } + return true; +} + MsgId History::minMsgId() const { for (const_iterator i = cbegin(), e = cend(); i != e; ++i) { for (HistoryBlock::const_iterator j = (*i)->cbegin(), en = (*i)->cend(); j != en; ++j) { @@ -3461,11 +3473,11 @@ void HistoryUnreadBar::setCount(int32 count) { } void HistoryUnreadBar::draw(QPainter &p, uint32 selection) const { - p.fillRect(0, 1, _history->width, st::unreadBarHeight - 2, st::unreadBarBG->b); + p.fillRect(0, st::lineWidth, _history->width, st::unreadBarHeight - 2 * st::lineWidth, st::unreadBarBG->b); p.fillRect(0, st::unreadBarHeight - st::lineWidth, _history->width, st::lineWidth, st::unreadBarBorder->b); p.setFont(st::unreadBarFont->f); p.setPen(st::unreadBarColor->p); - p.drawText(QRect(0, 0, _history->width, st::unreadBarHeight - 1), text, style::al_center); + p.drawText(QRect(0, 0, _history->width, st::unreadBarHeight - st::lineWidth), text, style::al_center); } int32 HistoryUnreadBar::resize(int32 width) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index b641d783e..86fa8f59d 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -610,6 +610,7 @@ struct History : public QList { void fixLastMessage(bool wasAtBottom); void loadAround(MsgId msgId); + bool canShowAround(MsgId msgId) const; MsgId minMsgId() const; MsgId maxMsgId() const; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index ebc311ec0..05c70738b 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1455,6 +1455,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent) , _scroll(this, st::historyScroll, false) , _list(0) , hist(0) + , _loadingAroundId(-1) + , _loadingAroundRequest(0) , _histInited(false) , _toHistoryEnd(this, st::historyToEnd) , _send(this, lang(lng_send_button), st::btnSend) @@ -1583,29 +1585,49 @@ void HistoryWidget::chatLoaded(const MTPmessages_ChatFull &res) { peerUpdated(App::chat(peerId)); } +void HistoryWidget::clearLoadingAround() { + _loadingAroundId = -1; + if (_loadingAroundRequest) { + MTP::cancel(_loadingAroundRequest); + _loadingAroundRequest = 0; + } +} + void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool leaveActive) { if (App::main()->selectingPeer() && !force) { hiderOffered = true; App::main()->offerPeer(peer); return; } - if (peer) { + if (peer && !msgId) { App::main()->dialogsClear(); } if (hist) { if (histPeer->id == peer) { - if (hist->unreadBar) hist->unreadBar->destroy(); if (msgId != hist->activeMsgId) { + if (!force && !hist->canShowAround(msgId)) { + if (_loadingAroundId != msgId) { + clearLoadingAround(); + _loadingAroundId = msgId; + loadMessagesAround(); + } + return; + } hist->loadAround(msgId); if (histPreloading) MTP::cancel(histPreloading); if (histPreloadingDown) MTP::cancel(histPreloadingDown); histPreloading = histPreloadingDown = 0; } + if (hist->unreadBar) hist->unreadBar->destroy(); checkUnreadLoaded(); + + clearLoadingAround(); return activate(); } updateTyping(false); } + clearLoadingAround(); + if (_list) { if (!histPreload.isEmpty()) { _list->messagesReceived(histPreload); @@ -1852,13 +1874,15 @@ bool HistoryWidget::messagesFailed(const RPCError &e, mtpRequestId requestId) { histPreloading = 0; } else if (histPreloadingDown == requestId) { histPreloadingDown = 0; + } else if (_loadingAroundRequest == requestId) { + _loadingAroundRequest = 0; } return true; } void HistoryWidget::messagesReceived(const MTPmessages_Messages &messages, mtpRequestId requestId) { if (!hist) { - histPreloading = histPreloadingDown = 0; + histPreloading = histPreloadingDown = _loadingAroundRequest = 0; return; } @@ -1908,6 +1932,17 @@ void HistoryWidget::messagesReceived(const MTPmessages_Messages &messages, mtpRe histPreloadingDown = 0; down = true; } else { + if (_loadingAroundRequest == requestId) { + _loadingAroundRequest = 0; + hist->loadAround(_loadingAroundId); + if (hist->isEmpty()) { + addMessagesToFront(*histList); + } + if (histPreloading) MTP::cancel(histPreloading); + if (histPreloadingDown) MTP::cancel(histPreloadingDown); + histPreloading = histPreloadingDown = 0; + showPeer(hist->peer->id, _loadingAroundId, true); + } return; } @@ -2044,6 +2079,16 @@ void HistoryWidget::loadMessagesDown() { } } +void HistoryWidget::loadMessagesAround() { + if (!hist || _loadingAroundRequest || _loadingAroundId < 0) return; + + int32 offset = 0, loadCount = MessagesPerPage; + if (_loadingAroundId) { + offset = -loadCount / 2; + } + _loadingAroundRequest = MTP::send(MTPmessages_GetHistory(histInputPeer, MTP_int(offset), MTP_int(_loadingAroundId), MTP_int(loadCount)), rpcDone(&HistoryWidget::messagesReceived), rpcFail(&HistoryWidget::messagesFailed)); +} + void HistoryWidget::onListScroll() { App::checkImageCacheSize(); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 5e88b8b4a..95ddf393e 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -272,6 +272,7 @@ public: void loadMessages(); void loadMessagesDown(); + void loadMessagesAround(); void peerMessagesUpdated(PeerId peer); void peerMessagesUpdated(); @@ -352,6 +353,7 @@ public slots: void onPhotoReady(); void onPhotoFailed(quint64 id); void showPeer(const PeerId &peer, MsgId msgId = 0, bool force = false, bool leaveActive = false); + void clearLoadingAround(); void activate(); void onTextChange(); @@ -394,6 +396,9 @@ private: mtpRequestId histPreloading, histPreloadingDown; QVector histPreload, histPreloadDown; + int32 _loadingAroundId; + mtpRequestId _loadingAroundRequest; + ScrollArea _scroll; HistoryList *_list; History *hist; diff --git a/Telegram/SourceFiles/mtproto/mtp.cpp b/Telegram/SourceFiles/mtproto/mtp.cpp index f3d5f0052..7eaa43d5f 100644 --- a/Telegram/SourceFiles/mtproto/mtp.cpp +++ b/Telegram/SourceFiles/mtproto/mtp.cpp @@ -566,6 +566,11 @@ namespace MTP { return mtpAuthed(); } + void logoutKeys(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) { + mtpRequestId req = MTP::send(MTPauth_LogOut(), onDone, onFail); + mtpLogoutOtherDCs(); + } + void setGlobalDoneHandler(RPCDoneHandlerPtr handler) { globalHandler.onDone = handler; } diff --git a/Telegram/SourceFiles/mtproto/mtp.h b/Telegram/SourceFiles/mtproto/mtp.h index d755cab71..d29114f3e 100644 --- a/Telegram/SourceFiles/mtproto/mtp.h +++ b/Telegram/SourceFiles/mtproto/mtp.h @@ -93,6 +93,7 @@ namespace MTP { void authed(int32 uid); int32 authedId(); + void logoutKeys(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail); void setGlobalDoneHandler(RPCDoneHandlerPtr handler); void setGlobalFailHandler(RPCFailHandlerPtr handler); diff --git a/Telegram/SourceFiles/mtproto/mtpDC.cpp b/Telegram/SourceFiles/mtproto/mtpDC.cpp index 699f74cd5..8319c27e9 100644 --- a/Telegram/SourceFiles/mtproto/mtpDC.cpp +++ b/Telegram/SourceFiles/mtproto/mtpDC.cpp @@ -323,6 +323,19 @@ int32 mtpMainDC() { return mainDC; } +void mtpLogoutOtherDCs() { + QList dcs; + { + QMutexLocker lock(&_keysMapForWriteMutex); + dcs = _keysMapForWrite.keys(); + } + for (int32 i = 0, cnt = dcs.size(); i != cnt; ++i) { + if (dcs[i] != MTP::maindc()) { + MTP::send(MTPauth_LogOut(), RPCResponseHandler(), dcs[i]); + } + } +} + void mtpSetDC(int32 dc) { if (dc != mainDC) { mainDC = dc; diff --git a/Telegram/SourceFiles/mtproto/mtpDC.h b/Telegram/SourceFiles/mtproto/mtpDC.h index 37b953514..2f937d1e1 100644 --- a/Telegram/SourceFiles/mtproto/mtpDC.h +++ b/Telegram/SourceFiles/mtproto/mtpDC.h @@ -97,6 +97,7 @@ const mtpDcOptions &mtpDCOptions(); MTProtoDCMap &mtpDCMap(); bool mtpNeedConfig(); int32 mtpMainDC(); +void mtpLogoutOtherDCs(); void mtpSetDC(int32 dc); uint32 mtpMaxChatSize(); diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 58da9772b..7b732cb62 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -1528,29 +1528,7 @@ - - true - true - true - - - true - true - true - - - true - true - true - - - - - true - true - true - diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index 3ac3f30a8..474a8dde1 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -908,22 +908,8 @@ - - Resource Files - - - Resource Files - - - Resource Files - - - - Resource Files - -