mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Redesign search scope selection.
This commit is contained in:
parent
fe6f65b3ab
commit
72c667b153
22 changed files with 1069 additions and 640 deletions
BIN
Telegram/Resources/icons/menu/chats.png
Normal file
BIN
Telegram/Resources/icons/menu/chats.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 766 B |
BIN
Telegram/Resources/icons/menu/chats@2x.png
Normal file
BIN
Telegram/Resources/icons/menu/chats@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/Resources/icons/menu/chats@3x.png
Normal file
BIN
Telegram/Resources/icons/menu/chats@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
using "ui/basic.style";
|
||||
|
||||
using "ui/layers/layers.style"; // boxRoundShadow
|
||||
using "ui/widgets/widgets.style";
|
||||
|
||||
DialogRow {
|
||||
|
@ -446,10 +447,25 @@ dialogsLoadMoreLoading: InfiniteRadialAnimation(defaultInfiniteRadialAnimation)
|
|||
size: size(12px, 12px);
|
||||
}
|
||||
|
||||
dialogsSearchInHeight: 52px;
|
||||
dialogsSearchInPhotoSize: 36px;
|
||||
dialogsSearchInPhotoPadding: 10px;
|
||||
dialogsSearchInSkip: 7px;
|
||||
dialogsSearchInHeight: 38px;
|
||||
dialogsSearchInPhotoSize: 26px;
|
||||
dialogsSearchInPhotoPadding: 12px;
|
||||
dialogsSearchInSkip: 10px;
|
||||
dialogsSearchInNameTop: 9px;
|
||||
dialogsSearchInDownTop: 15px;
|
||||
dialogsSearchInDown: icon {{ "intro_country_dropdown", windowBoldFg }};
|
||||
dialogsSearchInDownSkip: 4px;
|
||||
dialogsSearchInMenu: PopupMenu(defaultPopupMenu) {
|
||||
shadow: boxRoundShadow;
|
||||
animation: PanelAnimation(defaultPanelAnimation) {
|
||||
shadow: boxRoundShadow;
|
||||
}
|
||||
scrollPadding: margins(0px, 0px, 0px, 0px);
|
||||
radius: 8px;
|
||||
menu: menuWithIcons;
|
||||
}
|
||||
dialogsSearchInCheck: icon {{ "player/player_check", mediaPlayerActiveFg }};
|
||||
dialogsSearchInCheckSkip: 8px;
|
||||
dialogsSearchFromStyle: defaultTextStyle;
|
||||
dialogsSearchFromPalette: TextPalette(defaultTextPalette) {
|
||||
linkFg: dialogsNameFg;
|
||||
|
@ -694,17 +710,17 @@ dialogsStoriesTooltipHide: IconButton(defaultIconButton) {
|
|||
ripple: emptyRippleAnimation;
|
||||
}
|
||||
|
||||
searchedBarHeight: 32px;
|
||||
searchedBarHeight: 28px;
|
||||
searchedBarFont: normalFont;
|
||||
searchedBarPosition: point(17px, 7px);
|
||||
searchedBarPosition: point(14px, 5px);
|
||||
searchedBarLabel: FlatLabel(defaultFlatLabel) {
|
||||
textFg: searchedBarFg;
|
||||
margin: margins(17px, 7px, 17px, 7px);
|
||||
margin: margins(14px, 5px, 14px, 5px);
|
||||
}
|
||||
searchedBarLink: LinkButton(defaultLinkButton) {
|
||||
color: searchedBarFg;
|
||||
overColor: searchedBarFg;
|
||||
padding: margins(17px, 7px, 17px, 7px);
|
||||
padding: margins(14px, 5px, 14px, 5px);
|
||||
}
|
||||
|
||||
dialogsSearchTagSkip: point(8px, 4px);
|
||||
|
|
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "dialogs/dialogs_three_state_icon.h"
|
||||
#include "dialogs/ui/chat_search_empty.h"
|
||||
#include "dialogs/ui/chat_search_tabs.h"
|
||||
#include "dialogs/ui/chat_search_in.h"
|
||||
#include "dialogs/ui/dialogs_layout.h"
|
||||
#include "dialogs/ui/dialogs_stories_content.h"
|
||||
#include "dialogs/ui/dialogs_video_userpic.h"
|
||||
|
@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/dynamic_thumbnails.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "data/data_drafts.h"
|
||||
|
@ -137,9 +138,7 @@ constexpr auto kStartReorderThreshold = 30;
|
|||
if (hashtag) {
|
||||
text.append(tr::lng_search_tab_by_hashtag(tr::now));
|
||||
} else {
|
||||
text.append(
|
||||
tr::lng_dlg_search_for_messages(tr::now)
|
||||
).append('\n').append(Ui::Text::Link(tr::lng_cancel(tr::now)));
|
||||
text.append(tr::lng_dlg_search_for_messages(tr::now));
|
||||
}
|
||||
} else {
|
||||
text.append(tr::lng_search_tab_no_results(
|
||||
|
@ -214,12 +213,9 @@ InnerWidget::InnerWidget(
|
|||
, _narrowWidth(st::defaultDialogRow.padding.left()
|
||||
+ st::defaultDialogRow.photoSize
|
||||
+ st::defaultDialogRow.padding.left())
|
||||
, _cancelSearchFromUser(this, st::dialogsCancelSearchInPeer)
|
||||
, _childListShown(std::move(childListShown)) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, true);
|
||||
|
||||
_cancelSearchFromUser->hide();
|
||||
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
_topicJumpCache = nullptr;
|
||||
|
@ -553,11 +549,7 @@ int InnerWidget::searchTagsOffset() const {
|
|||
}
|
||||
|
||||
int InnerWidget::searchInChatOffset() const {
|
||||
auto result = searchTagsOffset();
|
||||
if (_searchTags) {
|
||||
result += _searchTags->height();
|
||||
}
|
||||
return result;
|
||||
return searchTagsOffset() + (_searchTags ? _searchTags->height() : 0);
|
||||
}
|
||||
|
||||
int InnerWidget::searchedOffset() const {
|
||||
|
@ -567,11 +559,7 @@ int InnerWidget::searchedOffset() const {
|
|||
}
|
||||
|
||||
int InnerWidget::searchInChatSkip() const {
|
||||
auto result = 0;
|
||||
if (_searchFromShown) {
|
||||
result += st::dialogsSearchInHeight;
|
||||
}
|
||||
return result;
|
||||
return _searchIn ? _searchIn->height() : 0;
|
||||
}
|
||||
|
||||
void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
|
||||
|
@ -928,14 +916,14 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
p.translate(0, _searchTags->height());
|
||||
top += _searchTags->height();
|
||||
}
|
||||
if (_searchFromShown) {
|
||||
paintSearchInChat(p, {
|
||||
.st = &st::forumTopicRow,
|
||||
.currentBg = currentBg(),
|
||||
.now = ms,
|
||||
.width = fullWidth,
|
||||
.paused = videoPaused,
|
||||
});
|
||||
if (_searchIn) {
|
||||
//paintSearchInChat(p, {
|
||||
// .st = &st::forumTopicRow,
|
||||
// .currentBg = currentBg(),
|
||||
// .now = ms,
|
||||
// .width = fullWidth,
|
||||
// .paused = videoPaused,
|
||||
//});
|
||||
p.translate(0, searchInChatSkip());
|
||||
top += searchInChatSkip();
|
||||
if (_searchResults.empty()) {
|
||||
|
@ -1211,111 +1199,112 @@ void InnerWidget::paintSearchTags(
|
|||
const auto position = QPoint(_searchTagsLeft, top);
|
||||
_searchTags->paint(p, position, context.now, context.paused);
|
||||
}
|
||||
|
||||
void InnerWidget::paintSearchInChat(
|
||||
Painter &p,
|
||||
const Ui::PaintContext &context) const {
|
||||
auto height = searchInChatSkip();
|
||||
|
||||
auto top = 0;
|
||||
p.setFont(st::searchedBarFont);
|
||||
auto fullRect = QRect(0, top, width(), height - top);
|
||||
p.fillRect(fullRect, currentBg());
|
||||
if (_searchFromShown) {
|
||||
p.setPen(st::dialogsTextFg);
|
||||
p.setTextPalette(st::dialogsSearchFromPalette);
|
||||
paintSearchInPeer(p, _searchFromShown, _searchFromUserUserpic, top, _searchFromUserText);
|
||||
p.restoreTextPalette();
|
||||
}
|
||||
}
|
||||
template <typename PaintUserpic>
|
||||
void InnerWidget::paintSearchInFilter(
|
||||
Painter &p,
|
||||
PaintUserpic paintUserpic,
|
||||
int top,
|
||||
const style::icon *icon,
|
||||
const Ui::Text::String &text) const {
|
||||
const auto savedPen = p.pen();
|
||||
const auto userpicLeft = st::defaultDialogRow.padding.left();
|
||||
const auto userpicTop = top
|
||||
+ (st::dialogsSearchInHeight - st::dialogsSearchInPhotoSize) / 2;
|
||||
paintUserpic(p, userpicLeft, userpicTop, st::dialogsSearchInPhotoSize);
|
||||
|
||||
const auto nameleft = st::defaultDialogRow.padding.left()
|
||||
+ st::dialogsSearchInPhotoSize
|
||||
+ st::dialogsSearchInPhotoPadding;
|
||||
const auto namewidth = width()
|
||||
- nameleft
|
||||
- st::defaultDialogRow.padding.left()
|
||||
- st::defaultDialogRow.padding.right()
|
||||
- st::dialogsCancelSearch.width;
|
||||
auto rectForName = QRect(
|
||||
nameleft,
|
||||
top + (st::dialogsSearchInHeight - st::semiboldFont->height) / 2,
|
||||
namewidth,
|
||||
st::semiboldFont->height);
|
||||
if (icon) {
|
||||
icon->paint(p, rectForName.topLeft(), width());
|
||||
rectForName.setLeft(rectForName.left()
|
||||
+ icon->width()
|
||||
+ st::dialogsChatTypeSkip);
|
||||
}
|
||||
p.setPen(savedPen);
|
||||
text.drawLeftElided(
|
||||
p,
|
||||
rectForName.left(),
|
||||
rectForName.top(),
|
||||
rectForName.width(),
|
||||
width());
|
||||
}
|
||||
|
||||
void InnerWidget::paintSearchInPeer(
|
||||
Painter &p,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::PeerUserpicView &userpic,
|
||||
int top,
|
||||
const Ui::Text::String &text) const {
|
||||
const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
|
||||
peer->paintUserpicLeft(p, userpic, x, y, width(), size);
|
||||
};
|
||||
const auto icon = Ui::ChatTypeIcon(peer);
|
||||
paintSearchInFilter(p, paintUserpic, top, icon, text);
|
||||
}
|
||||
|
||||
void InnerWidget::paintSearchInSaved(
|
||||
Painter &p,
|
||||
int top,
|
||||
const Ui::Text::String &text) const {
|
||||
const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, width(), size);
|
||||
};
|
||||
paintSearchInFilter(p, paintUserpic, top, nullptr, text);
|
||||
}
|
||||
|
||||
void InnerWidget::paintSearchInReplies(
|
||||
Painter &p,
|
||||
int top,
|
||||
const Ui::Text::String &text) const {
|
||||
const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
|
||||
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, width(), size);
|
||||
};
|
||||
paintSearchInFilter(p, paintUserpic, top, nullptr, text);
|
||||
}
|
||||
|
||||
void InnerWidget::paintSearchInTopic(
|
||||
Painter &p,
|
||||
const Ui::PaintContext &context,
|
||||
not_null<Data::ForumTopic*> topic,
|
||||
Ui::PeerUserpicView &userpic,
|
||||
int top,
|
||||
const Ui::Text::String &text) const {
|
||||
const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
|
||||
p.translate(x, y);
|
||||
topic->paintUserpic(p, userpic, context);
|
||||
p.translate(-x, -y);
|
||||
};
|
||||
paintSearchInFilter(p, paintUserpic, top, nullptr, text);
|
||||
}
|
||||
//
|
||||
//void InnerWidget::paintSearchInChat(
|
||||
// Painter &p,
|
||||
// const Ui::PaintContext &context) const {
|
||||
// auto height = searchInChatSkip();
|
||||
//
|
||||
// auto top = 0;
|
||||
// p.setFont(st::searchedBarFont);
|
||||
// auto fullRect = QRect(0, top, width(), height - top);
|
||||
// p.fillRect(fullRect, currentBg());
|
||||
// if (_searchFromShown) {
|
||||
// p.setPen(st::dialogsTextFg);
|
||||
// p.setTextPalette(st::dialogsSearchFromPalette);
|
||||
// paintSearchInPeer(p, _searchFromShown, _searchFromUserUserpic, top, _searchFromUserText);
|
||||
// p.restoreTextPalette();
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//template <typename PaintUserpic>
|
||||
//void InnerWidget::paintSearchInFilter(
|
||||
// Painter &p,
|
||||
// PaintUserpic paintUserpic,
|
||||
// int top,
|
||||
// const style::icon *icon,
|
||||
// const Ui::Text::String &text) const {
|
||||
// const auto savedPen = p.pen();
|
||||
// const auto userpicLeft = st::defaultDialogRow.padding.left();
|
||||
// const auto userpicTop = top
|
||||
// + (st::dialogsSearchInHeight - st::dialogsSearchInPhotoSize) / 2;
|
||||
// paintUserpic(p, userpicLeft, userpicTop, st::dialogsSearchInPhotoSize);
|
||||
//
|
||||
// const auto nameleft = st::defaultDialogRow.padding.left()
|
||||
// + st::dialogsSearchInPhotoSize
|
||||
// + st::dialogsSearchInPhotoPadding;
|
||||
// const auto namewidth = width()
|
||||
// - nameleft
|
||||
// - st::defaultDialogRow.padding.left()
|
||||
// - st::defaultDialogRow.padding.right()
|
||||
// - st::dialogsCancelSearch.width;
|
||||
// auto rectForName = QRect(
|
||||
// nameleft,
|
||||
// top + (st::dialogsSearchInHeight - st::semiboldFont->height) / 2,
|
||||
// namewidth,
|
||||
// st::semiboldFont->height);
|
||||
// if (icon) {
|
||||
// icon->paint(p, rectForName.topLeft(), width());
|
||||
// rectForName.setLeft(rectForName.left()
|
||||
// + icon->width()
|
||||
// + st::dialogsChatTypeSkip);
|
||||
// }
|
||||
// p.setPen(savedPen);
|
||||
// text.drawLeftElided(
|
||||
// p,
|
||||
// rectForName.left(),
|
||||
// rectForName.top(),
|
||||
// rectForName.width(),
|
||||
// width());
|
||||
//}
|
||||
//
|
||||
//void InnerWidget::paintSearchInPeer(
|
||||
// Painter &p,
|
||||
// not_null<PeerData*> peer,
|
||||
// Ui::PeerUserpicView &userpic,
|
||||
// int top,
|
||||
// const Ui::Text::String &text) const {
|
||||
// const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
|
||||
// peer->paintUserpicLeft(p, userpic, x, y, width(), size);
|
||||
// };
|
||||
// const auto icon = Ui::ChatTypeIcon(peer);
|
||||
// paintSearchInFilter(p, paintUserpic, top, icon, text);
|
||||
//}
|
||||
//
|
||||
//void InnerWidget::paintSearchInSaved(
|
||||
// Painter &p,
|
||||
// int top,
|
||||
// const Ui::Text::String &text) const {
|
||||
// const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
|
||||
// Ui::EmptyUserpic::PaintSavedMessages(p, x, y, width(), size);
|
||||
// };
|
||||
// paintSearchInFilter(p, paintUserpic, top, nullptr, text);
|
||||
//}
|
||||
//
|
||||
//void InnerWidget::paintSearchInReplies(
|
||||
// Painter &p,
|
||||
// int top,
|
||||
// const Ui::Text::String &text) const {
|
||||
// const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
|
||||
// Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, width(), size);
|
||||
// };
|
||||
// paintSearchInFilter(p, paintUserpic, top, nullptr, text);
|
||||
//}
|
||||
//
|
||||
//void InnerWidget::paintSearchInTopic(
|
||||
// Painter &p,
|
||||
// const Ui::PaintContext &context,
|
||||
// not_null<Data::ForumTopic*> topic,
|
||||
// Ui::PeerUserpicView &userpic,
|
||||
// int top,
|
||||
// const Ui::Text::String &text) const {
|
||||
// const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
|
||||
// p.translate(x, y);
|
||||
// topic->paintUserpic(p, userpic, context);
|
||||
// p.translate(-x, -y);
|
||||
// };
|
||||
// paintSearchInFilter(p, paintUserpic, top, nullptr, text);
|
||||
//}
|
||||
|
||||
void InnerWidget::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (_chatPreviewTouchGlobal || _touchDragStartGlobal) {
|
||||
|
@ -1989,17 +1978,20 @@ void InnerWidget::resizeEvent(QResizeEvent *e) {
|
|||
_searchTags->resizeToWidth(width() - 2 * _searchTagsLeft);
|
||||
}
|
||||
resizeEmpty();
|
||||
moveCancelSearchButtons();
|
||||
moveSearchIn();
|
||||
}
|
||||
|
||||
void InnerWidget::moveCancelSearchButtons() {
|
||||
const auto widthForCancelButton = qMax(
|
||||
void InnerWidget::moveSearchIn() {
|
||||
if (!_searchIn) {
|
||||
return;
|
||||
}
|
||||
const auto searchInWidth = std::max(
|
||||
width(),
|
||||
st::columnMinimalWidthLeft - _narrowWidth);
|
||||
const auto left = widthForCancelButton - st::dialogsSearchInSkip - _cancelSearchFromUser->width();
|
||||
const auto top = (st::dialogsSearchInHeight - st::dialogsCancelSearchInPeer.height) / 2;
|
||||
const auto skip = (_searchTags ? _searchTags->height() : 0);
|
||||
_cancelSearchFromUser->moveToLeft(left, skip + top);
|
||||
_searchIn->resizeToWidth(searchInWidth);
|
||||
|
||||
const auto top = (_searchTags ? _searchTags->height() : 0);
|
||||
_searchIn->moveToLeft(0, top);
|
||||
}
|
||||
|
||||
void InnerWidget::dialogRowReplaced(
|
||||
|
@ -2656,7 +2648,7 @@ void InnerWidget::applySearchState(SearchState state) {
|
|||
1
|
||||
) | rpl::start_with_next([=] {
|
||||
refresh();
|
||||
moveCancelSearchButtons();
|
||||
moveSearchIn();
|
||||
}, _searchTags->lifetime());
|
||||
} else {
|
||||
_searchTags = nullptr;
|
||||
|
@ -2670,17 +2662,12 @@ void InnerWidget::applySearchState(SearchState state) {
|
|||
if (state.inChat) {
|
||||
onHashtagFilterUpdate(QStringView());
|
||||
}
|
||||
if (_searchFromShown) {
|
||||
_cancelSearchFromUser->show();
|
||||
_searchFromUserUserpic = _searchFromShown->createUserpicView();
|
||||
} else {
|
||||
_cancelSearchFromUser->hide();
|
||||
_searchFromUserUserpic = {};
|
||||
}
|
||||
refreshSearchInChatLabel();
|
||||
moveCancelSearchButtons();
|
||||
|
||||
_searchState = std::move(state);
|
||||
_searchingHashtag = IsHashtagSearchQuery(_searchState.query);
|
||||
|
||||
updateSearchIn();
|
||||
moveSearchIn();
|
||||
|
||||
auto newFilter = _searchState.query;
|
||||
const auto mentionsSearch = (newFilter == u"@"_q);
|
||||
const auto words = mentionsSearch
|
||||
|
@ -2924,12 +2911,20 @@ rpl::producer<> InnerWidget::listBottomReached() const {
|
|||
return _listBottomReached.events();
|
||||
}
|
||||
|
||||
rpl::producer<> InnerWidget::cancelSearchFromUserRequests() const {
|
||||
return _cancelSearchFromUser->clicks() | rpl::to_empty;
|
||||
rpl::producer<ChatSearchTab> InnerWidget::changeSearchTabRequests() const {
|
||||
return _changeSearchTabRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<> InnerWidget::cancelSearchRequests() const {
|
||||
return _cancelSearch.events();
|
||||
return _cancelSearchRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<> InnerWidget::cancelSearchFromRequests() const {
|
||||
return _cancelSearchFromRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<> InnerWidget::changeSearchFromRequests() const {
|
||||
return _changeSearchFromRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> InnerWidget::mustScrollTo() const {
|
||||
|
@ -3200,9 +3195,6 @@ void InnerWidget::refreshEmpty() {
|
|||
} else if (_searchEmptyState != _searchState) {
|
||||
_searchEmptyState = _searchState;
|
||||
_searchEmpty = MakeSearchEmpty(this, _searchState);
|
||||
_searchEmpty->linkClicks() | rpl::start_with_next([=] {
|
||||
_cancelSearch.fire({});
|
||||
}, _searchEmpty->lifetime());
|
||||
if (_controller->session().data().chatsListLoaded()) {
|
||||
_searchEmpty->animate();
|
||||
}
|
||||
|
@ -3342,19 +3334,76 @@ auto InnerWidget::searchTagsChanges() const
|
|||
: rpl::never<std::vector<Data::ReactionId>>();
|
||||
}
|
||||
|
||||
void InnerWidget::refreshSearchInChatLabel() {
|
||||
const auto from = _searchFromShown ? _searchFromShown->name() : u""_q;
|
||||
if (!from.isEmpty()) {
|
||||
const auto fromUserText = tr::lng_dlg_search_from(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Semibold(from),
|
||||
Ui::Text::WithEntities);
|
||||
_searchFromUserText.setMarkedText(
|
||||
st::dialogsSearchFromStyle,
|
||||
fromUserText,
|
||||
Ui::DialogTextOptions());
|
||||
void InnerWidget::updateSearchIn() {
|
||||
if (!_searchState.inChat && !_searchingHashtag) {
|
||||
_searchIn = nullptr;
|
||||
return;
|
||||
} else if (!_searchIn) {
|
||||
_searchIn = std::make_unique<ChatSearchIn>(this);
|
||||
_searchIn->show();
|
||||
_searchIn->changeFromRequests() | rpl::start_to_stream(
|
||||
_changeSearchFromRequests,
|
||||
_searchIn->lifetime());
|
||||
_searchIn->cancelFromRequests() | rpl::start_to_stream(
|
||||
_cancelSearchFromRequests,
|
||||
_searchIn->lifetime());
|
||||
_searchIn->cancelInRequests() | rpl::start_to_stream(
|
||||
_cancelSearchRequests,
|
||||
_searchIn->lifetime());
|
||||
_searchIn->tabChanges() | rpl::start_to_stream(
|
||||
_changeSearchTabRequests,
|
||||
_searchIn->lifetime());
|
||||
}
|
||||
|
||||
const auto sublist = _searchState.inChat.sublist();
|
||||
const auto topic = _searchState.inChat.topic();
|
||||
const auto peer = _searchState.inChat.owningHistory()
|
||||
? _searchState.inChat.owningHistory()->peer.get()
|
||||
: _openedForum
|
||||
? _openedForum->channel().get()
|
||||
: nullptr;
|
||||
const auto topicIcon = !topic
|
||||
? nullptr
|
||||
: topic->iconId()
|
||||
? Ui::MakeEmojiThumbnail(
|
||||
&topic->owner(),
|
||||
Data::SerializeCustomEmojiId(topic->iconId()))
|
||||
: Ui::MakeEmojiThumbnail(
|
||||
&topic->owner(),
|
||||
Data::TopicIconEmojiEntity({
|
||||
.title = (topic->isGeneral()
|
||||
? Data::ForumGeneralIconTitle()
|
||||
: topic->title()),
|
||||
.colorId = (topic->isGeneral()
|
||||
? Data::ForumGeneralIconColor(st::windowSubTextFg->c)
|
||||
: topic->colorId()),
|
||||
}));
|
||||
const auto peerIcon = peer
|
||||
? Ui::MakeUserpicThumbnail(peer)
|
||||
: sublist
|
||||
? Ui::MakeUserpicThumbnail(sublist->peer())
|
||||
: nullptr;
|
||||
const auto myIcon = Ui::MakeIconThumbnail(st::menuIconChats);
|
||||
const auto publicIcon = _searchingHashtag
|
||||
? Ui::MakeIconThumbnail(st::menuIconChannel)
|
||||
: nullptr;
|
||||
const auto peerTabType = (peer && peer->isBroadcast())
|
||||
? ChatSearchPeerTabType::Channel
|
||||
: (peer && (peer->isChat() || peer->isMegagroup()))
|
||||
? ChatSearchPeerTabType::Group
|
||||
: ChatSearchPeerTabType::Chat;
|
||||
const auto fromImage = _searchFromShown
|
||||
? Ui::MakeUserpicThumbnail(_searchFromShown)
|
||||
: nullptr;
|
||||
const auto fromName = _searchFromShown
|
||||
? _searchFromShown->shortName()
|
||||
: QString();
|
||||
_searchIn->apply({
|
||||
{ ChatSearchTab::ThisTopic, topicIcon },
|
||||
{ ChatSearchTab::ThisPeer, peerIcon },
|
||||
{ ChatSearchTab::MyMessages, myIcon },
|
||||
{ ChatSearchTab::PublicPosts, publicIcon },
|
||||
}, _searchState.tab, peerTabType, fromImage, fromName);
|
||||
}
|
||||
|
||||
void InnerWidget::repaintSearchResult(int index) {
|
||||
|
|
|
@ -61,6 +61,7 @@ class FakeRow;
|
|||
class IndexedList;
|
||||
class SearchTags;
|
||||
class SearchEmpty;
|
||||
class ChatSearchIn;
|
||||
|
||||
struct ChosenRow {
|
||||
Key key;
|
||||
|
@ -156,8 +157,11 @@ public:
|
|||
void setLoadMoreCallback(Fn<void()> callback);
|
||||
void setLoadMoreFilteredCallback(Fn<void()> callback);
|
||||
[[nodiscard]] rpl::producer<> listBottomReached() const;
|
||||
[[nodiscard]] rpl::producer<> cancelSearchFromUserRequests() const;
|
||||
[[nodiscard]] auto changeSearchTabRequests() const
|
||||
-> rpl::producer<ChatSearchTab>;
|
||||
[[nodiscard]] rpl::producer<> cancelSearchRequests() const;
|
||||
[[nodiscard]] rpl::producer<> cancelSearchFromRequests() const;
|
||||
[[nodiscard]] rpl::producer<> changeSearchFromRequests() const;
|
||||
[[nodiscard]] rpl::producer<ChosenRow> chosenRow() const;
|
||||
[[nodiscard]] rpl::producer<> updated() const;
|
||||
|
||||
|
@ -358,38 +362,38 @@ private:
|
|||
void paintSearchTags(
|
||||
Painter &p,
|
||||
const Ui::PaintContext &context) const;
|
||||
void paintSearchInChat(
|
||||
Painter &p,
|
||||
const Ui::PaintContext &context) const;
|
||||
void paintSearchInPeer(
|
||||
Painter &p,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::PeerUserpicView &userpic,
|
||||
int top,
|
||||
const Ui::Text::String &text) const;
|
||||
void paintSearchInSaved(
|
||||
Painter &p,
|
||||
int top,
|
||||
const Ui::Text::String &text) const;
|
||||
void paintSearchInReplies(
|
||||
Painter &p,
|
||||
int top,
|
||||
const Ui::Text::String &text) const;
|
||||
void paintSearchInTopic(
|
||||
Painter &p,
|
||||
const Ui::PaintContext &context,
|
||||
not_null<Data::ForumTopic*> topic,
|
||||
Ui::PeerUserpicView &userpic,
|
||||
int top,
|
||||
const Ui::Text::String &text) const;
|
||||
template <typename PaintUserpic>
|
||||
void paintSearchInFilter(
|
||||
Painter &p,
|
||||
PaintUserpic paintUserpic,
|
||||
int top,
|
||||
const style::icon *icon,
|
||||
const Ui::Text::String &text) const;
|
||||
void refreshSearchInChatLabel();
|
||||
//void paintSearchInChat(
|
||||
// Painter &p,
|
||||
// const Ui::PaintContext &context) const;
|
||||
//void paintSearchInPeer(
|
||||
// Painter &p,
|
||||
// not_null<PeerData*> peer,
|
||||
// Ui::PeerUserpicView &userpic,
|
||||
// int top,
|
||||
// const Ui::Text::String &text) const;
|
||||
//void paintSearchInSaved(
|
||||
// Painter &p,
|
||||
// int top,
|
||||
// const Ui::Text::String &text) const;
|
||||
//void paintSearchInReplies(
|
||||
// Painter &p,
|
||||
// int top,
|
||||
// const Ui::Text::String &text) const;
|
||||
//void paintSearchInTopic(
|
||||
// Painter &p,
|
||||
// const Ui::PaintContext &context,
|
||||
// not_null<Data::ForumTopic*> topic,
|
||||
// Ui::PeerUserpicView &userpic,
|
||||
// int top,
|
||||
// const Ui::Text::String &text) const;
|
||||
//template <typename PaintUserpic>
|
||||
//void paintSearchInFilter(
|
||||
// Painter &p,
|
||||
// PaintUserpic paintUserpic,
|
||||
// int top,
|
||||
// const style::icon *icon,
|
||||
// const Ui::Text::String &text) const;
|
||||
void updateSearchIn();
|
||||
void repaintSearchResult(int index);
|
||||
|
||||
Ui::VideoUserpic *validateVideoUserpic(not_null<Row*> row);
|
||||
|
@ -415,7 +419,7 @@ private:
|
|||
void savePinnedOrder();
|
||||
bool pinnedShiftAnimationCallback(crl::time now);
|
||||
void handleChatListEntryRefreshes();
|
||||
void moveCancelSearchButtons();
|
||||
void moveSearchIn();
|
||||
void dragPinnedFromTouch();
|
||||
|
||||
void saveChatsFilterScrollState(FilterId filterId);
|
||||
|
@ -490,19 +494,22 @@ private:
|
|||
|
||||
WidgetState _state = WidgetState::Default;
|
||||
|
||||
std::unique_ptr<ChatSearchIn> _searchIn;
|
||||
rpl::event_stream<ChatSearchTab> _changeSearchTabRequests;
|
||||
rpl::event_stream<> _cancelSearchRequests;
|
||||
rpl::event_stream<> _cancelSearchFromRequests;
|
||||
rpl::event_stream<> _changeSearchFromRequests;
|
||||
object_ptr<Ui::RpWidget> _loadingAnimation = { nullptr };
|
||||
object_ptr<SearchEmpty> _searchEmpty = { nullptr };
|
||||
SearchState _searchEmptyState;
|
||||
object_ptr<Ui::FlatLabel> _empty = { nullptr };
|
||||
object_ptr<Ui::IconButton> _cancelSearchFromUser;
|
||||
rpl::event_stream<> _cancelSearch;
|
||||
|
||||
Ui::DraggingScrollManager _draggingScroll;
|
||||
|
||||
SearchState _searchState;
|
||||
bool _searchingHashtag = false;
|
||||
History *_searchInMigrated = nullptr;
|
||||
PeerData *_searchFromShown = nullptr;
|
||||
mutable Ui::PeerUserpicView _searchFromUserUserpic;
|
||||
Ui::Text::String _searchFromUserText;
|
||||
std::unique_ptr<SearchTags> _searchTags;
|
||||
int _searchTagsLeft = 0;
|
||||
|
|
|
@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "dialogs/ui/chat_search_tabs.h"
|
||||
#include "dialogs/ui/chat_search_in.h"
|
||||
#include "history/history.h"
|
||||
|
||||
namespace Dialogs {
|
||||
|
|
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "base/qt/qt_key_modifiers.h"
|
||||
#include "base/options.h"
|
||||
#include "dialogs/ui/chat_search_tabs.h"
|
||||
#include "dialogs/ui/chat_search_in.h"
|
||||
#include "dialogs/ui/dialogs_stories_content.h"
|
||||
#include "dialogs/ui/dialogs_stories_list.h"
|
||||
#include "dialogs/ui/dialogs_suggestions.h"
|
||||
|
@ -336,7 +336,20 @@ Widget::Widget(
|
|||
) | rpl::start_with_next([=] {
|
||||
searchCursorMoved();
|
||||
}, lifetime());
|
||||
_inner->cancelSearchFromUserRequests(
|
||||
_inner->changeSearchTabRequests(
|
||||
) | rpl::filter([=](ChatSearchTab tab) {
|
||||
return _searchState.tab != tab;
|
||||
}) | rpl::start_with_next([=](ChatSearchTab tab) {
|
||||
auto copy = _searchState;
|
||||
copy.tab = tab;
|
||||
applySearchState(std::move(copy));
|
||||
}, lifetime());
|
||||
_inner->cancelSearchRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
setInnerFocus(true);
|
||||
applySearchState({});
|
||||
}, lifetime());
|
||||
_inner->cancelSearchFromRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto copy = _searchState;
|
||||
copy.fromPeer = nullptr;
|
||||
|
@ -345,10 +358,9 @@ Widget::Widget(
|
|||
}
|
||||
applySearchState(std::move(copy));
|
||||
}, lifetime());
|
||||
_inner->cancelSearchRequests(
|
||||
_inner->changeSearchFromRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
setInnerFocus(true);
|
||||
applySearchState({});
|
||||
showSearchFrom();
|
||||
}, lifetime());
|
||||
_inner->chosenRow(
|
||||
) | rpl::start_with_next([=](const ChosenRow &row) {
|
||||
|
@ -1096,9 +1108,6 @@ void Widget::updateControlsVisibility(bool fast) {
|
|||
updateJumpToDateVisibility(fast);
|
||||
updateSearchFromVisibility(fast);
|
||||
}
|
||||
if (_searchTabs) {
|
||||
_searchTabs->show();
|
||||
}
|
||||
if (_connecting) {
|
||||
_connecting->setForceHidden(false);
|
||||
}
|
||||
|
@ -1242,102 +1251,6 @@ void Widget::updateSuggestions(anim::type animated) {
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::updateSearchTabs() {
|
||||
const auto has = _searchState.inChat || _searchingHashtag;
|
||||
if (!has) {
|
||||
if (_searchTabs) {
|
||||
_searchTabs = nullptr;
|
||||
updateControlsGeometry();
|
||||
}
|
||||
return;
|
||||
} else if (!_searchTabs) {
|
||||
const auto savedSession = &session();
|
||||
const auto markedTextContext = [=](Fn<void()> repaint) {
|
||||
return Core::MarkedTextContext{
|
||||
.session = savedSession,
|
||||
.customEmojiRepaint = std::move(repaint),
|
||||
};
|
||||
};
|
||||
_searchTabs = std::make_unique<ChatSearchTabs>(
|
||||
this,
|
||||
_searchState.tab,
|
||||
std::move(markedTextContext));
|
||||
_searchTabs->setVisible(!_showAnimation);
|
||||
_searchTabs->tabChanges(
|
||||
) | rpl::filter([=](ChatSearchTab tab) {
|
||||
return (_searchState.tab != tab);
|
||||
}) | rpl::start_with_next([=](ChatSearchTab tab) {
|
||||
auto copy = _searchState;
|
||||
copy.tab = tab;
|
||||
applySearchState(std::move(copy));
|
||||
}, _searchTabs->lifetime());
|
||||
}
|
||||
const auto sublist = _searchState.inChat.sublist();
|
||||
const auto topic = _searchState.inChat.topic();
|
||||
const auto peer = _searchState.inChat.owningHistory()
|
||||
? _searchState.inChat.owningHistory()->peer.get()
|
||||
: _openedForum
|
||||
? _openedForum->channel().get()
|
||||
: nullptr;
|
||||
const auto topicShortLabel = !topic
|
||||
? TextWithEntities()
|
||||
: topic->iconId()
|
||||
? Ui::Text::SingleCustomEmoji(
|
||||
Data::SerializeCustomEmojiId(topic->iconId()))
|
||||
: Ui::Text::SingleCustomEmoji(Data::TopicIconEmojiEntity({
|
||||
.title = (topic->isGeneral()
|
||||
? Data::ForumGeneralIconTitle()
|
||||
: topic->title()),
|
||||
.colorId = (topic->isGeneral()
|
||||
? Data::ForumGeneralIconColor(st::windowSubTextFg->c)
|
||||
: topic->colorId()),
|
||||
}));
|
||||
const auto peerShortLabel = peer
|
||||
? Ui::Text::SingleCustomEmoji(
|
||||
session().data().customEmojiManager().peerUserpicEmojiData(
|
||||
peer,
|
||||
{},
|
||||
true))
|
||||
: sublist
|
||||
? Ui::Text::SingleCustomEmoji(
|
||||
session().data().customEmojiManager().peerUserpicEmojiData(
|
||||
sublist->peer(),
|
||||
{},
|
||||
true))
|
||||
: TextWithEntities();
|
||||
const auto myShortLabel = DefaultShortLabel(ChatSearchTab::MyMessages);
|
||||
const auto publicShortLabel = _searchingHashtag
|
||||
? DefaultShortLabel(ChatSearchTab::PublicPosts)
|
||||
: TextWithEntities();
|
||||
if ((_searchState.tab == ChatSearchTab::ThisTopic
|
||||
&& !_searchState.inChat.topic())
|
||||
|| (_searchState.tab == ChatSearchTab::ThisPeer
|
||||
&& !_searchState.inChat
|
||||
&& !_openedForum)
|
||||
|| (_searchState.tab == ChatSearchTab::PublicPosts
|
||||
&& !_searchingHashtag)) {
|
||||
_searchState.tab = _searchState.inChat.topic()
|
||||
? ChatSearchTab::ThisTopic
|
||||
: (_searchState.inChat.owningHistory()
|
||||
|| _searchState.inChat.sublist())
|
||||
? ChatSearchTab::ThisPeer
|
||||
: ChatSearchTab::MyMessages;
|
||||
}
|
||||
const auto peerTabType = (peer && peer->isBroadcast())
|
||||
? ChatSearchPeerTabType::Channel
|
||||
: (peer && (peer->isChat() || peer->isMegagroup()))
|
||||
? ChatSearchPeerTabType::Group
|
||||
: ChatSearchPeerTabType::Chat;
|
||||
_searchTabs->setTabShortLabels({
|
||||
{ ChatSearchTab::ThisTopic, topicShortLabel },
|
||||
{ ChatSearchTab::ThisPeer, peerShortLabel },
|
||||
{ ChatSearchTab::MyMessages, myShortLabel },
|
||||
{ ChatSearchTab::PublicPosts, publicShortLabel },
|
||||
}, _searchState.tab, peerTabType);
|
||||
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
void Widget::changeOpenedSubsection(
|
||||
FnMut<void()> change,
|
||||
bool fromRight,
|
||||
|
@ -2732,9 +2645,8 @@ QString Widget::validateSearchQuery() {
|
|||
setSearchQuery(fixed.text, fixed.cursorPosition);
|
||||
}
|
||||
return fixed.text;
|
||||
} else if (_searchingHashtag != IsHashtagSearchQuery(query)) {
|
||||
_searchingHashtag = !_searchingHashtag;
|
||||
updateSearchTabs();
|
||||
} else {
|
||||
_searchingHashtag = IsHashtagSearchQuery(query);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
@ -2932,7 +2844,7 @@ bool Widget::applySearchState(SearchState state) {
|
|||
state.tab = (_openedForum && !state.inChat)
|
||||
? ChatSearchTab::ThisPeer
|
||||
: ChatSearchTab::MyMessages;
|
||||
} else if (!state.inChat && !_searchTabs) {
|
||||
} else if (!state.inChat && !_searchingHashtag) {
|
||||
state.tab = (forum || _openedForum)
|
||||
? ChatSearchTab::ThisPeer
|
||||
: ChatSearchTab::MyMessages;
|
||||
|
@ -2966,6 +2878,20 @@ bool Widget::applySearchState(SearchState state) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((state.tab == ChatSearchTab::ThisTopic
|
||||
&& !state.inChat.topic())
|
||||
|| (state.tab == ChatSearchTab::ThisPeer
|
||||
&& !state.inChat
|
||||
&& !_openedForum)
|
||||
|| (state.tab == ChatSearchTab::PublicPosts
|
||||
&& !_searchingHashtag)) {
|
||||
state.tab = state.inChat.topic()
|
||||
? ChatSearchTab::ThisTopic
|
||||
: (state.inChat.owningHistory() || state.inChat.sublist())
|
||||
? ChatSearchTab::ThisPeer
|
||||
: ChatSearchTab::MyMessages;
|
||||
}
|
||||
|
||||
const auto migrateFrom = (peer && !topic)
|
||||
? peer->migrateFrom()
|
||||
: nullptr;
|
||||
|
@ -2979,7 +2905,6 @@ bool Widget::applySearchState(SearchState state) {
|
|||
}
|
||||
if (inChatChanged) {
|
||||
controller()->setSearchInChat(_searchState.inChat);
|
||||
updateSearchTabs();
|
||||
}
|
||||
if (queryChanged || inChatChanged) {
|
||||
updateCancelSearch();
|
||||
|
@ -3334,9 +3259,6 @@ void Widget::updateControlsGeometry() {
|
|||
if (_forumRequestsBar) {
|
||||
_forumRequestsBar->resizeToWidth(barw);
|
||||
}
|
||||
if (_searchTabs) {
|
||||
_searchTabs->resizeToWidth(barw);
|
||||
}
|
||||
_updateScrollGeometryCached = [=] {
|
||||
const auto moreChatsBarTop = expandedStoriesTop
|
||||
+ ((!_stories || _stories->isHidden()) ? 0 : _aboveScrollAdded);
|
||||
|
@ -3358,13 +3280,8 @@ void Widget::updateControlsGeometry() {
|
|||
if (_forumReportBar) {
|
||||
_forumReportBar->bar().move(0, forumReportTop);
|
||||
}
|
||||
const auto searchTabsTop = forumReportTop
|
||||
const auto scrollTop = forumReportTop
|
||||
+ (_forumReportBar ? _forumReportBar->bar().height() : 0);
|
||||
if (_searchTabs) {
|
||||
_searchTabs->move(0, searchTabsTop);
|
||||
}
|
||||
const auto scrollTop = searchTabsTop
|
||||
+ (_searchTabs ? _searchTabs->height() : 0);
|
||||
const auto scrollHeight = height() - scrollTop - bottomSkip;
|
||||
const auto wasScrollHeight = _scroll->height();
|
||||
_scroll->setGeometry(0, scrollTop, scrollWidth, scrollHeight);
|
||||
|
|
|
@ -76,7 +76,7 @@ struct ChosenRow;
|
|||
class InnerWidget;
|
||||
enum class SearchRequestType;
|
||||
class Suggestions;
|
||||
class ChatSearchTabs;
|
||||
class ChatSearchIn;
|
||||
enum class ChatSearchTab : uchar;
|
||||
|
||||
class Widget final : public Window::AbstractSectionWidget {
|
||||
|
@ -255,7 +255,6 @@ private:
|
|||
void updateScrollUpPosition();
|
||||
void updateLockUnlockPosition();
|
||||
void updateSuggestions(anim::type animated);
|
||||
void updateSearchTabs();
|
||||
void processSearchFocusChange();
|
||||
|
||||
[[nodiscard]] bool redirectToSearchPossible() const;
|
||||
|
@ -294,7 +293,6 @@ private:
|
|||
QPointer<InnerWidget> _inner;
|
||||
std::unique_ptr<Suggestions> _suggestions;
|
||||
std::vector<std::unique_ptr<Suggestions>> _hidingSuggestions;
|
||||
std::unique_ptr<ChatSearchTabs> _searchTabs;
|
||||
class BottomButton;
|
||||
object_ptr<BottomButton> _updateTelegram = { nullptr };
|
||||
object_ptr<BottomButton> _loadMoreChats = { nullptr };
|
||||
|
|
|
@ -33,12 +33,6 @@ void SearchEmpty::setup(Icon icon, rpl::producer<TextWithEntities> text) {
|
|||
this,
|
||||
std::move(text),
|
||||
st::defaultPeerListAbout);
|
||||
label->setClickHandlerFilter([=](const auto &, Qt::MouseButton button) {
|
||||
if (button == Qt::LeftButton) {
|
||||
_linkClicks.fire({});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const auto size = st::recentPeersEmptySize;
|
||||
const auto animation = [&] {
|
||||
switch (icon) {
|
||||
|
|
|
@ -26,9 +26,6 @@ public:
|
|||
rpl::producer<TextWithEntities> text);
|
||||
|
||||
void setMinimalHeight(int minimalHeight);
|
||||
[[nodiscard]] rpl::producer<> linkClicks() const {
|
||||
return _linkClicks.events();
|
||||
}
|
||||
|
||||
void animate();
|
||||
|
||||
|
@ -36,7 +33,6 @@ private:
|
|||
void setup(Icon icon, rpl::producer<TextWithEntities> text);
|
||||
|
||||
Fn<void()> _animate;
|
||||
rpl::event_stream<> _linkClicks;
|
||||
|
||||
};
|
||||
|
||||
|
|
459
Telegram/SourceFiles/dialogs/ui/chat_search_in.cpp
Normal file
459
Telegram/SourceFiles/dialogs/ui/chat_search_in.cpp
Normal file
|
@ -0,0 +1,459 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "dialogs/ui/chat_search_in.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/menu/menu_item_base.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/painter.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_window.h"
|
||||
|
||||
namespace Dialogs {
|
||||
namespace {
|
||||
|
||||
class Action final : public Ui::Menu::ItemBase {
|
||||
public:
|
||||
Action(
|
||||
not_null<Ui::PopupMenu*> parentMenu,
|
||||
std::shared_ptr<Ui::DynamicImage> icon,
|
||||
const QString &label,
|
||||
bool chosen);
|
||||
|
||||
bool isEnabled() const override;
|
||||
not_null<QAction*> action() const override;
|
||||
|
||||
void handleKeyPress(not_null<QKeyEvent*> e) override;
|
||||
|
||||
protected:
|
||||
QPoint prepareRippleStartPosition() const override;
|
||||
QImage prepareRippleMask() const override;
|
||||
|
||||
int contentHeight() const override;
|
||||
|
||||
private:
|
||||
void paint(Painter &p);
|
||||
|
||||
void resolveMinWidth();
|
||||
void refreshDimensions();
|
||||
|
||||
const not_null<Ui::PopupMenu*> _parentMenu;
|
||||
const not_null<QAction*> _dummyAction;
|
||||
const style::Menu &_st;
|
||||
const int _height = 0;
|
||||
|
||||
std::shared_ptr<Ui::DynamicImage> _icon;
|
||||
Ui::Text::String _text;
|
||||
bool _checked = false;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] QString TabLabel(
|
||||
ChatSearchTab tab,
|
||||
ChatSearchPeerTabType type = {}) {
|
||||
switch (tab) {
|
||||
case ChatSearchTab::MyMessages:
|
||||
return tr::lng_search_tab_my_messages(tr::now);
|
||||
case ChatSearchTab::ThisTopic:
|
||||
return tr::lng_search_tab_this_topic(tr::now);
|
||||
case ChatSearchTab::ThisPeer:
|
||||
switch (type) {
|
||||
case ChatSearchPeerTabType::Chat:
|
||||
return tr::lng_search_tab_this_chat(tr::now);
|
||||
case ChatSearchPeerTabType::Channel:
|
||||
return tr::lng_search_tab_this_channel(tr::now);
|
||||
case ChatSearchPeerTabType::Group:
|
||||
return tr::lng_search_tab_this_group(tr::now);
|
||||
}
|
||||
Unexpected("Type in Dialogs::TabLabel.");
|
||||
case ChatSearchTab::PublicPosts:
|
||||
return tr::lng_search_tab_public_posts(tr::now);
|
||||
}
|
||||
Unexpected("Tab in Dialogs::TabLabel.");
|
||||
}
|
||||
|
||||
Action::Action(
|
||||
not_null<Ui::PopupMenu*> parentMenu,
|
||||
std::shared_ptr<Ui::DynamicImage> icon,
|
||||
const QString &label,
|
||||
bool chosen)
|
||||
: ItemBase(parentMenu->menu(), parentMenu->menu()->st())
|
||||
, _parentMenu(parentMenu)
|
||||
, _dummyAction(CreateChild<QAction>(parentMenu->menu().get()))
|
||||
, _st(parentMenu->menu()->st())
|
||||
, _height(st::dialogsSearchInHeight)
|
||||
, _icon(std::move(icon))
|
||||
, _checked(chosen) {
|
||||
const auto parent = parentMenu->menu();
|
||||
|
||||
_text.setText(st::semiboldTextStyle, label);
|
||||
_icon->subscribeToUpdates([=] { update(); });
|
||||
|
||||
initResizeHook(parent->sizeValue());
|
||||
resolveMinWidth();
|
||||
|
||||
paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
Painter p(this);
|
||||
paint(p);
|
||||
}, lifetime());
|
||||
|
||||
enableMouseSelecting();
|
||||
}
|
||||
|
||||
void Action::resolveMinWidth() {
|
||||
const auto maxWidth = st::dialogsSearchInPhotoPadding
|
||||
+ st::dialogsSearchInPhotoSize
|
||||
+ st::dialogsSearchInSkip
|
||||
+ _text.maxWidth()
|
||||
+ st::dialogsSearchInCheckSkip
|
||||
+ st::dialogsSearchInCheck.width()
|
||||
+ st::dialogsSearchInCheckSkip;
|
||||
setMinWidth(maxWidth);
|
||||
}
|
||||
|
||||
void Action::paint(Painter &p) {
|
||||
const auto enabled = isEnabled();
|
||||
const auto selected = isSelected();
|
||||
if (selected && _st.itemBgOver->c.alpha() < 255) {
|
||||
p.fillRect(0, 0, width(), _height, _st.itemBg);
|
||||
}
|
||||
const auto &bg = selected ? _st.itemBgOver : _st.itemBg;
|
||||
p.fillRect(0, 0, width(), _height, bg);
|
||||
if (enabled) {
|
||||
paintRipple(p, 0, 0);
|
||||
}
|
||||
|
||||
auto x = st::dialogsSearchInPhotoPadding;
|
||||
const auto photos = st::dialogsSearchInPhotoSize;
|
||||
const auto photoy = (height() - photos) / 2;
|
||||
p.drawImage(QRect{ x, photoy, photos, photos }, _icon->image(photos));
|
||||
x += photos + st::dialogsSearchInSkip;
|
||||
const auto available = width()
|
||||
- x
|
||||
- st::dialogsSearchInCheckSkip
|
||||
- st::dialogsSearchInCheck.width()
|
||||
- st::dialogsSearchInCheckSkip;
|
||||
|
||||
p.setPen(!enabled
|
||||
? _st.itemFgDisabled
|
||||
: selected
|
||||
? _st.itemFgOver
|
||||
: _st.itemFg);
|
||||
_text.drawLeftElided(
|
||||
p,
|
||||
x,
|
||||
st::dialogsSearchInNameTop,
|
||||
available,
|
||||
width());
|
||||
x += available;
|
||||
if (_checked) {
|
||||
x += st::dialogsSearchInCheckSkip;
|
||||
const auto &icon = st::dialogsSearchInCheck;
|
||||
const auto icony = (height() - icon.height()) / 2;
|
||||
icon.paint(p, x, icony, width());
|
||||
}
|
||||
}
|
||||
|
||||
bool Action::isEnabled() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
not_null<QAction*> Action::action() const {
|
||||
return _dummyAction;
|
||||
}
|
||||
|
||||
QPoint Action::prepareRippleStartPosition() const {
|
||||
return mapFromGlobal(QCursor::pos());
|
||||
}
|
||||
|
||||
QImage Action::prepareRippleMask() const {
|
||||
return Ui::RippleAnimation::RectMask(size());
|
||||
}
|
||||
|
||||
int Action::contentHeight() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
void Action::handleKeyPress(not_null<QKeyEvent*> e) {
|
||||
if (!isSelected()) {
|
||||
return;
|
||||
}
|
||||
const auto key = e->key();
|
||||
if (key == Qt::Key_Enter || key == Qt::Key_Return) {
|
||||
setClicked(Ui::Menu::TriggeredSource::Keyboard);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FixedHashtagSearchQuery FixHashtagSearchQuery(
|
||||
const QString &query,
|
||||
int cursorPosition) {
|
||||
const auto trimmed = query.trimmed();
|
||||
const auto hash = int(trimmed.isEmpty()
|
||||
? query.size()
|
||||
: query.indexOf(trimmed));
|
||||
const auto start = std::min(cursorPosition, hash);
|
||||
auto result = query.mid(0, start);
|
||||
for (const auto &ch : query.mid(start)) {
|
||||
if (ch.isSpace()) {
|
||||
if (cursorPosition > result.size()) {
|
||||
--cursorPosition;
|
||||
}
|
||||
continue;
|
||||
} else if (result.size() == start) {
|
||||
result += '#';
|
||||
if (ch != '#') {
|
||||
++cursorPosition;
|
||||
}
|
||||
}
|
||||
if (ch != '#') {
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
if (result.size() == start) {
|
||||
result += '#';
|
||||
++cursorPosition;
|
||||
}
|
||||
return { result, cursorPosition };
|
||||
}
|
||||
|
||||
bool IsHashtagSearchQuery(const QString &query) {
|
||||
const auto trimmed = query.trimmed();
|
||||
if (trimmed.isEmpty() || trimmed[0] != '#') {
|
||||
return false;
|
||||
}
|
||||
for (const auto &ch : trimmed) {
|
||||
if (ch.isSpace()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ChatSearchIn::Section::update() {
|
||||
outer->update();
|
||||
}
|
||||
|
||||
ChatSearchIn::ChatSearchIn(QWidget *parent)
|
||||
: RpWidget(parent) {
|
||||
_in.clicks.events() | rpl::start_with_next([=] {
|
||||
showMenu();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
ChatSearchIn::~ChatSearchIn() = default;
|
||||
|
||||
void ChatSearchIn::apply(
|
||||
std::vector<PossibleTab> tabs,
|
||||
ChatSearchTab active,
|
||||
ChatSearchPeerTabType peerTabType,
|
||||
std::shared_ptr<Ui::DynamicImage> fromUserpic,
|
||||
QString fromName) {
|
||||
_tabs = std::move(tabs);
|
||||
_peerTabType = peerTabType;
|
||||
_active = active;
|
||||
const auto i = ranges::find(_tabs, active, &PossibleTab::tab);
|
||||
Assert(i != end(_tabs));
|
||||
Assert(i->icon != nullptr);
|
||||
updateSection(
|
||||
&_in,
|
||||
i->icon->clone(),
|
||||
Ui::Text::Semibold(TabLabel(active, peerTabType)));
|
||||
|
||||
auto text = tr::lng_dlg_search_from(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Semibold(fromName),
|
||||
Ui::Text::WithEntities);
|
||||
updateSection(&_from, std::move(fromUserpic), std::move(text));
|
||||
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
||||
rpl::producer<> ChatSearchIn::cancelInRequests() const {
|
||||
return _in.cancelRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<> ChatSearchIn::cancelFromRequests() const {
|
||||
return _from.cancelRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<> ChatSearchIn::changeFromRequests() const {
|
||||
return _from.clicks.events();
|
||||
}
|
||||
|
||||
rpl::producer<ChatSearchTab> ChatSearchIn::tabChanges() const {
|
||||
return _active.changes();
|
||||
}
|
||||
|
||||
void ChatSearchIn::showMenu() {
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||
this,
|
||||
st::dialogsSearchInMenu);
|
||||
const auto active = _active.current();
|
||||
auto activeIndex = 0;
|
||||
for (const auto &tab : _tabs) {
|
||||
if (!tab.icon) {
|
||||
continue;
|
||||
}
|
||||
const auto value = tab.tab;
|
||||
if (value == active) {
|
||||
activeIndex = _menu->actions().size();
|
||||
}
|
||||
auto action = base::make_unique_q<Action>(
|
||||
_menu.get(),
|
||||
tab.icon,
|
||||
TabLabel(value, _peerTabType),
|
||||
(value == active));
|
||||
action->setClickedCallback([=] {
|
||||
_active = value;
|
||||
});
|
||||
_menu->addAction(std::move(action));
|
||||
}
|
||||
const auto count = int(_menu->actions().size());
|
||||
const auto bottomLeft = (activeIndex * 2 >= count);
|
||||
const auto single = st::dialogsSearchInHeight;
|
||||
const auto in = mapToGlobal(_in.outer->pos()
|
||||
+ QPoint(0, bottomLeft ? count * single : 0));
|
||||
_menu->setForcedOrigin(bottomLeft
|
||||
? Ui::PanelAnimation::Origin::BottomLeft
|
||||
: Ui::PanelAnimation::Origin::TopLeft);
|
||||
if (_menu->prepareGeometryFor(in)) {
|
||||
_menu->move(_menu->pos() - QPoint(_menu->inner().x(), activeIndex * single));
|
||||
_menu->popupPrepared();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatSearchIn::paintEvent(QPaintEvent *e) {
|
||||
auto p = Painter(this);
|
||||
const auto top = QRect(0, 0, width(), st::searchedBarHeight);
|
||||
p.fillRect(top, st::searchedBarBg);
|
||||
p.fillRect(rect().translated(0, st::searchedBarHeight), st::dialogsBg);
|
||||
|
||||
p.setFont(st::searchedBarFont);
|
||||
p.setPen(st::searchedBarFg);
|
||||
p.drawTextLeft(
|
||||
st::searchedBarPosition.x(),
|
||||
st::searchedBarPosition.y(),
|
||||
width(),
|
||||
tr::lng_dlg_search_in(tr::now));
|
||||
}
|
||||
|
||||
int ChatSearchIn::resizeGetHeight(int newWidth) {
|
||||
auto result = st::searchedBarHeight;
|
||||
if (const auto raw = _in.outer.get()) {
|
||||
raw->resizeToWidth(newWidth);
|
||||
raw->move(0, result);
|
||||
result += raw->height();
|
||||
_in.shadow->setGeometry(0, result, newWidth, st::lineWidth);
|
||||
result += st::lineWidth;
|
||||
}
|
||||
if (const auto raw = _from.outer.get()) {
|
||||
raw->resizeToWidth(newWidth);
|
||||
raw->move(0, result);
|
||||
result += raw->height();
|
||||
_from.shadow->setGeometry(0, result, newWidth, st::lineWidth);
|
||||
result += st::lineWidth;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ChatSearchIn::updateSection(
|
||||
not_null<Section*> section,
|
||||
std::shared_ptr<Ui::DynamicImage> image,
|
||||
TextWithEntities text) {
|
||||
if (section->subscribed) {
|
||||
section->image->subscribeToUpdates(nullptr);
|
||||
section->subscribed = false;
|
||||
}
|
||||
if (!image) {
|
||||
if (section->outer) {
|
||||
section->cancel = nullptr;
|
||||
section->shadow = nullptr;
|
||||
section->outer = nullptr;
|
||||
section->subscribed = false;
|
||||
}
|
||||
return;
|
||||
} else if (!section->outer) {
|
||||
auto button = std::make_unique<Ui::AbstractButton>(this);
|
||||
const auto raw = button.get();
|
||||
section->outer = std::move(button);
|
||||
|
||||
raw->resize(
|
||||
st::columnMinimalWidthLeft,
|
||||
st::dialogsSearchInHeight);
|
||||
|
||||
raw->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(raw);
|
||||
if (!section->subscribed) {
|
||||
section->subscribed = true;
|
||||
section->image->subscribeToUpdates([=] {
|
||||
raw->update();
|
||||
});
|
||||
}
|
||||
const auto outer = raw->width();
|
||||
const auto size = st::dialogsSearchInPhotoSize;
|
||||
const auto left = st::dialogsSearchInPhotoPadding;
|
||||
const auto top = (st::dialogsSearchInHeight - size) / 2;
|
||||
p.drawImage(
|
||||
QRect{ left, top, size, size },
|
||||
section->image->image(size));
|
||||
|
||||
const auto x = left + size + st::dialogsSearchInSkip;
|
||||
const auto available = outer
|
||||
- st::dialogsSearchInSkip
|
||||
- section->cancel->width()
|
||||
- 2 * st::dialogsSearchInDownSkip
|
||||
- st::dialogsSearchInDown.width()
|
||||
- x;
|
||||
const auto use = std::min(section->text.maxWidth(), available);
|
||||
const auto iconx = x + use + st::dialogsSearchInDownSkip;
|
||||
const auto icony = st::dialogsSearchInDownTop;
|
||||
st::dialogsSearchInDown.paint(p, iconx, icony, outer);
|
||||
p.setPen(st::windowBoldFg);
|
||||
section->text.draw(p, {
|
||||
.position = QPoint(x, st::dialogsSearchInNameTop),
|
||||
.outerWidth = outer,
|
||||
.availableWidth = available,
|
||||
.elisionLines = 1,
|
||||
});
|
||||
}, raw->lifetime());
|
||||
|
||||
section->shadow = std::make_unique<Ui::PlainShadow>(this);
|
||||
section->shadow->show();
|
||||
|
||||
const auto st = &st::dialogsCancelSearchInPeer;
|
||||
section->cancel = std::make_unique<Ui::IconButton>(raw, *st);
|
||||
section->cancel->show();
|
||||
raw->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||
const auto left = size.width() - section->cancel->width();
|
||||
const auto top = (size.height() - st->height) / 2;
|
||||
section->cancel->moveToLeft(left, top);
|
||||
}, section->cancel->lifetime());
|
||||
section->cancel->clicks() | rpl::to_empty | rpl::start_to_stream(
|
||||
section->cancelRequests,
|
||||
section->cancel->lifetime());
|
||||
|
||||
raw->clicks() | rpl::to_empty | rpl::start_to_stream(
|
||||
section->clicks,
|
||||
raw->lifetime());
|
||||
|
||||
raw->show();
|
||||
}
|
||||
section->image = std::move(image);
|
||||
section->text.setMarkedText(st::dialogsSearchFromStyle, std::move(text));
|
||||
}
|
||||
|
||||
} // namespace Dialogs
|
100
Telegram/SourceFiles/dialogs/ui/chat_search_in.h
Normal file
100
Telegram/SourceFiles/dialogs/ui/chat_search_in.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/unique_qptr.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
class PlainShadow;
|
||||
class DynamicImage;
|
||||
class IconButton;
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Dialogs {
|
||||
|
||||
enum class ChatSearchTab : uchar {
|
||||
MyMessages,
|
||||
ThisTopic,
|
||||
ThisPeer,
|
||||
PublicPosts,
|
||||
};
|
||||
|
||||
enum class ChatSearchPeerTabType : uchar {
|
||||
Chat,
|
||||
Channel,
|
||||
Group,
|
||||
};
|
||||
|
||||
class ChatSearchIn final : public Ui::RpWidget {
|
||||
public:
|
||||
explicit ChatSearchIn(QWidget *parent);
|
||||
~ChatSearchIn();
|
||||
|
||||
struct PossibleTab {
|
||||
ChatSearchTab tab = {};
|
||||
std::shared_ptr<Ui::DynamicImage> icon;
|
||||
};
|
||||
void apply(
|
||||
std::vector<PossibleTab> tabs,
|
||||
ChatSearchTab active,
|
||||
ChatSearchPeerTabType peerTabType,
|
||||
std::shared_ptr<Ui::DynamicImage> fromUserpic,
|
||||
QString fromName);
|
||||
|
||||
[[nodiscard]] rpl::producer<> cancelInRequests() const;
|
||||
[[nodiscard]] rpl::producer<> cancelFromRequests() const;
|
||||
[[nodiscard]] rpl::producer<> changeFromRequests() const;
|
||||
[[nodiscard]] rpl::producer<ChatSearchTab> tabChanges() const;
|
||||
|
||||
private:
|
||||
struct Section {
|
||||
std::unique_ptr<Ui::RpWidget> outer;
|
||||
std::unique_ptr<Ui::IconButton> cancel;
|
||||
std::unique_ptr<Ui::PlainShadow> shadow;
|
||||
std::shared_ptr<Ui::DynamicImage> image;
|
||||
Ui::Text::String text;
|
||||
rpl::event_stream<> clicks;
|
||||
rpl::event_stream<> cancelRequests;
|
||||
bool subscribed = false;
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void showMenu();
|
||||
|
||||
void updateSection(
|
||||
not_null<Section*> section,
|
||||
std::shared_ptr<Ui::DynamicImage> image,
|
||||
TextWithEntities text);
|
||||
|
||||
Section _in;
|
||||
Section _from;
|
||||
rpl::variable<ChatSearchTab> _active;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
|
||||
std::vector<PossibleTab> _tabs;
|
||||
ChatSearchPeerTabType _peerTabType = ChatSearchPeerTabType::Chat;
|
||||
|
||||
};
|
||||
|
||||
struct FixedHashtagSearchQuery {
|
||||
QString text;
|
||||
int cursorPosition = 0;
|
||||
};
|
||||
[[nodiscard]] FixedHashtagSearchQuery FixHashtagSearchQuery(
|
||||
const QString &query,
|
||||
int cursorPosition);
|
||||
|
||||
[[nodiscard]] bool IsHashtagSearchQuery(const QString &query);
|
||||
|
||||
} // namespace Dialogs
|
|
@ -1,201 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "dialogs/ui/chat_search_tabs.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/discrete_sliders.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
|
||||
namespace Dialogs {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString TabLabel(
|
||||
ChatSearchTab tab,
|
||||
ChatSearchPeerTabType type = {}) {
|
||||
switch (tab) {
|
||||
case ChatSearchTab::MyMessages:
|
||||
return tr::lng_search_tab_my_messages(tr::now);
|
||||
case ChatSearchTab::ThisTopic:
|
||||
return tr::lng_search_tab_this_topic(tr::now);
|
||||
case ChatSearchTab::ThisPeer:
|
||||
switch (type) {
|
||||
case ChatSearchPeerTabType::Chat:
|
||||
return tr::lng_search_tab_this_chat(tr::now);
|
||||
case ChatSearchPeerTabType::Channel:
|
||||
return tr::lng_search_tab_this_channel(tr::now);
|
||||
case ChatSearchPeerTabType::Group:
|
||||
return tr::lng_search_tab_this_group(tr::now);
|
||||
}
|
||||
Unexpected("Type in Dialogs::TabLabel.");
|
||||
case ChatSearchTab::PublicPosts:
|
||||
return tr::lng_search_tab_public_posts(tr::now);
|
||||
}
|
||||
Unexpected("Tab in Dialogs::TabLabel.");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TextWithEntities DefaultShortLabel(ChatSearchTab tab) {
|
||||
// Return them in QString::fromUtf8 format.
|
||||
switch (tab) {
|
||||
case ChatSearchTab::MyMessages:
|
||||
return { QString::fromUtf8("\xf0\x9f\x93\xa8") };
|
||||
case ChatSearchTab::PublicPosts:
|
||||
return { QString::fromUtf8("\xf0\x9f\x8c\x8e") };
|
||||
}
|
||||
Unexpected("Tab in Dialogs::DefaultShortLabel.");
|
||||
}
|
||||
|
||||
FixedHashtagSearchQuery FixHashtagSearchQuery(
|
||||
const QString &query,
|
||||
int cursorPosition) {
|
||||
const auto trimmed = query.trimmed();
|
||||
const auto hash = int(trimmed.isEmpty()
|
||||
? query.size()
|
||||
: query.indexOf(trimmed));
|
||||
const auto start = std::min(cursorPosition, hash);
|
||||
auto result = query.mid(0, start);
|
||||
for (const auto &ch : query.mid(start)) {
|
||||
if (ch.isSpace()) {
|
||||
if (cursorPosition > result.size()) {
|
||||
--cursorPosition;
|
||||
}
|
||||
continue;
|
||||
} else if (result.size() == start) {
|
||||
result += '#';
|
||||
if (ch != '#') {
|
||||
++cursorPosition;
|
||||
}
|
||||
}
|
||||
if (ch != '#') {
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
if (result.size() == start) {
|
||||
result += '#';
|
||||
++cursorPosition;
|
||||
}
|
||||
return { result, cursorPosition };
|
||||
}
|
||||
|
||||
bool IsHashtagSearchQuery(const QString &query) {
|
||||
const auto trimmed = query.trimmed();
|
||||
if (trimmed.isEmpty() || trimmed[0] != '#') {
|
||||
return false;
|
||||
}
|
||||
for (const auto &ch : trimmed) {
|
||||
if (ch.isSpace()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ChatSearchTabs::ChatSearchTabs(
|
||||
QWidget *parent,
|
||||
ChatSearchTab active,
|
||||
Fn<std::any(Fn<void()>)> markedTextContext)
|
||||
: RpWidget(parent)
|
||||
, _tabs(std::make_unique<Ui::SettingsSlider>(this, st::dialogsSearchTabs))
|
||||
, _shadow(std::make_unique<Ui::PlainShadow>(this))
|
||||
, _markedTextContext(std::move(markedTextContext))
|
||||
, _active(active) {
|
||||
_tabs->move(st::dialogsSearchTabsPadding, 0);
|
||||
_tabs->sectionActivated(
|
||||
) | rpl::start_with_next([=](int index) {
|
||||
_active = _list[index].value;
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
ChatSearchTabs::~ChatSearchTabs() = default;
|
||||
|
||||
void ChatSearchTabs::setTabShortLabels(
|
||||
std::vector<ShortLabel> labels,
|
||||
ChatSearchTab active,
|
||||
ChatSearchPeerTabType peerTabType) {
|
||||
const auto &st = st::dialogsSearchTabs;
|
||||
const auto &font = st.labelStyle.font;
|
||||
_list.clear();
|
||||
_list.reserve(labels.size());
|
||||
|
||||
auto widthTotal = 0;
|
||||
for (const auto tab : {
|
||||
ChatSearchTab::ThisTopic,
|
||||
ChatSearchTab::ThisPeer,
|
||||
ChatSearchTab::MyMessages,
|
||||
ChatSearchTab::PublicPosts,
|
||||
}) {
|
||||
const auto i = ranges::find(labels, tab, &ShortLabel::tab);
|
||||
if (i != end(labels) && !i->label.empty()) {
|
||||
const auto label = TabLabel(tab, peerTabType);
|
||||
const auto widthFull = font->width(label) + st.strictSkip;
|
||||
_list.push_back({
|
||||
.value = tab,
|
||||
.label = label,
|
||||
.shortLabel = i->label,
|
||||
.widthFull = widthFull,
|
||||
});
|
||||
widthTotal += widthFull;
|
||||
}
|
||||
}
|
||||
const auto widthSingleEmoji = st::emojiSize + st.strictSkip;
|
||||
for (const auto tab : {
|
||||
ChatSearchTab::PublicPosts,
|
||||
ChatSearchTab::ThisTopic,
|
||||
ChatSearchTab::ThisPeer,
|
||||
ChatSearchTab::MyMessages,
|
||||
}) {
|
||||
const auto i = ranges::find(_list, tab, &Tab::value);
|
||||
if (i != end(_list)) {
|
||||
i->widthThresholdForShort = widthTotal;
|
||||
widthTotal -= i->widthFull;
|
||||
widthTotal += widthSingleEmoji;
|
||||
}
|
||||
}
|
||||
refillTabs(active, width());
|
||||
}
|
||||
|
||||
rpl::producer<ChatSearchTab> ChatSearchTabs::tabChanges() const {
|
||||
return _active.changes();
|
||||
}
|
||||
|
||||
void ChatSearchTabs::refillTabs(
|
||||
ChatSearchTab active,
|
||||
int newWidth) {
|
||||
auto labels = std::vector<TextWithEntities>();
|
||||
const auto available = newWidth - 2 * st::dialogsSearchTabsPadding;
|
||||
for (const auto &tab : _list) {
|
||||
auto label = (available < tab.widthThresholdForShort)
|
||||
? tab.shortLabel
|
||||
: TextWithEntities{ tab.label };
|
||||
labels.push_back(std::move(label));
|
||||
}
|
||||
_tabs->setSections(labels, _markedTextContext([=] { update(); }));
|
||||
|
||||
const auto i = ranges::find(_list, active, &Tab::value);
|
||||
Assert(i != end(_list));
|
||||
_tabs->setActiveSectionFast(i - begin(_list));
|
||||
_tabs->resizeToWidth(newWidth);
|
||||
}
|
||||
|
||||
int ChatSearchTabs::resizeGetHeight(int newWidth) {
|
||||
refillTabs(_active.current(), newWidth);
|
||||
_shadow->setGeometry(
|
||||
0,
|
||||
_tabs->y() + _tabs->height() - st::lineWidth,
|
||||
newWidth,
|
||||
st::lineWidth);
|
||||
return _tabs->height();
|
||||
}
|
||||
|
||||
void ChatSearchTabs::paintEvent(QPaintEvent *e) {
|
||||
QPainter(this).fillRect(e->rect(), st::dialogsBg);
|
||||
}
|
||||
|
||||
} // namespace Dialogs
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
class SettingsSlider;
|
||||
class PlainShadow;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Dialogs {
|
||||
|
||||
enum class ChatSearchTab : uchar {
|
||||
MyMessages,
|
||||
ThisTopic,
|
||||
ThisPeer,
|
||||
PublicPosts,
|
||||
};
|
||||
|
||||
enum class ChatSearchPeerTabType : uchar {
|
||||
Chat,
|
||||
Channel,
|
||||
Group,
|
||||
};
|
||||
|
||||
// Available for MyMessages and PublicPosts.
|
||||
[[nodiscard]] TextWithEntities DefaultShortLabel(ChatSearchTab tab);
|
||||
|
||||
class ChatSearchTabs final : public Ui::RpWidget {
|
||||
public:
|
||||
ChatSearchTabs(
|
||||
QWidget *parent,
|
||||
ChatSearchTab active,
|
||||
Fn<std::any(Fn<void()>)> markedTextContext);
|
||||
~ChatSearchTabs();
|
||||
|
||||
// A [custom] emoji to use when there is not enough space for text.
|
||||
// Only tabs with available short labels are shown.
|
||||
struct ShortLabel {
|
||||
ChatSearchTab tab = {};
|
||||
TextWithEntities label;
|
||||
};
|
||||
void setTabShortLabels(
|
||||
std::vector<ShortLabel> labels,
|
||||
ChatSearchTab active,
|
||||
ChatSearchPeerTabType peerTabType);
|
||||
|
||||
[[nodiscard]] rpl::producer<ChatSearchTab> tabChanges() const;
|
||||
|
||||
private:
|
||||
struct Tab {
|
||||
ChatSearchTab value = {};
|
||||
QString label;
|
||||
TextWithEntities shortLabel;
|
||||
int widthFull = 0;
|
||||
int widthThresholdForShort = 0;
|
||||
};
|
||||
|
||||
void refreshTabs(ChatSearchTab active);
|
||||
void refillTabs(ChatSearchTab active, int newWidth);
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
const std::unique_ptr<Ui::SettingsSlider> _tabs;
|
||||
const std::unique_ptr<Ui::PlainShadow> _shadow;
|
||||
const Fn<std::any(Fn<void()>)> _markedTextContext;
|
||||
|
||||
std::vector<Tab> _list;
|
||||
rpl::variable<ChatSearchTab> _active;
|
||||
|
||||
};
|
||||
|
||||
struct FixedHashtagSearchQuery {
|
||||
QString text;
|
||||
int cursorPosition = 0;
|
||||
};
|
||||
[[nodiscard]] FixedHashtagSearchQuery FixHashtagSearchQuery(
|
||||
const QString &query,
|
||||
int cursorPosition);
|
||||
|
||||
[[nodiscard]] bool IsHashtagSearchQuery(const QString &query);
|
||||
|
||||
} // namespace Dialogs
|
|
@ -379,6 +379,10 @@ bool Gif::autoplayEnabled() const {
|
|||
_data);
|
||||
}
|
||||
|
||||
bool Gif::hideMessageText() const {
|
||||
return _data->isVideoMessage();
|
||||
}
|
||||
|
||||
void Gif::draw(Painter &p, const PaintContext &context) const {
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
|
||||
|
|
|
@ -54,9 +54,7 @@ public:
|
|||
bool spoiler);
|
||||
~Gif();
|
||||
|
||||
bool hideMessageText() const override {
|
||||
return false;
|
||||
}
|
||||
bool hideMessageText() const override;
|
||||
|
||||
void draw(Painter &p, const PaintContext &context) const override;
|
||||
TextState textState(QPoint point, StateRequest request) const override;
|
||||
|
|
|
@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_peer.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/data_story.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
|
@ -28,6 +30,8 @@ class PeerUserpic final : public DynamicImage {
|
|||
public:
|
||||
PeerUserpic(not_null<PeerData*> peer, bool forceRound);
|
||||
|
||||
std::shared_ptr<DynamicImage> clone() override;
|
||||
|
||||
QImage image(int size) override;
|
||||
void subscribeToUpdates(Fn<void()> callback) override;
|
||||
|
||||
|
@ -58,7 +62,6 @@ private:
|
|||
class StoryThumbnail : public DynamicImage {
|
||||
public:
|
||||
explicit StoryThumbnail(FullStoryId id);
|
||||
virtual ~StoryThumbnail() = default;
|
||||
|
||||
QImage image(int size) override;
|
||||
void subscribeToUpdates(Fn<void()> callback) override;
|
||||
|
@ -68,6 +71,9 @@ protected:
|
|||
Image *image = nullptr;
|
||||
bool blurred = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] FullStoryId id() const;
|
||||
|
||||
[[nodiscard]] virtual Main::Session &session() = 0;
|
||||
[[nodiscard]] virtual Thumb loaded(FullStoryId id) = 0;
|
||||
virtual void clear() = 0;
|
||||
|
@ -85,6 +91,8 @@ class PhotoThumbnail final : public StoryThumbnail {
|
|||
public:
|
||||
PhotoThumbnail(not_null<PhotoData*> photo, FullStoryId id);
|
||||
|
||||
std::shared_ptr<DynamicImage> clone() override;
|
||||
|
||||
private:
|
||||
Main::Session &session() override;
|
||||
Thumb loaded(FullStoryId id) override;
|
||||
|
@ -99,6 +107,8 @@ class VideoThumbnail final : public StoryThumbnail {
|
|||
public:
|
||||
VideoThumbnail(not_null<DocumentData*> video, FullStoryId id);
|
||||
|
||||
std::shared_ptr<DynamicImage> clone() override;
|
||||
|
||||
private:
|
||||
Main::Session &session() override;
|
||||
Thumb loaded(FullStoryId id) override;
|
||||
|
@ -111,6 +121,8 @@ private:
|
|||
|
||||
class EmptyThumbnail final : public DynamicImage {
|
||||
public:
|
||||
std::shared_ptr<DynamicImage> clone() override;
|
||||
|
||||
QImage image(int size) override;
|
||||
void subscribeToUpdates(Fn<void()> callback) override;
|
||||
|
||||
|
@ -121,6 +133,8 @@ private:
|
|||
|
||||
class SavedMessagesUserpic final : public DynamicImage {
|
||||
public:
|
||||
std::shared_ptr<DynamicImage> clone() override;
|
||||
|
||||
QImage image(int size) override;
|
||||
void subscribeToUpdates(Fn<void()> callback) override;
|
||||
|
||||
|
@ -132,6 +146,8 @@ private:
|
|||
|
||||
class RepliesUserpic final : public DynamicImage {
|
||||
public:
|
||||
std::shared_ptr<DynamicImage> clone() override;
|
||||
|
||||
QImage image(int size) override;
|
||||
void subscribeToUpdates(Fn<void()> callback) override;
|
||||
|
||||
|
@ -141,11 +157,48 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class IconThumbnail final : public DynamicImage {
|
||||
public:
|
||||
explicit IconThumbnail(const style::icon &icon);
|
||||
|
||||
std::shared_ptr<DynamicImage> clone() override;
|
||||
|
||||
QImage image(int size) override;
|
||||
void subscribeToUpdates(Fn<void()> callback) override;
|
||||
|
||||
private:
|
||||
const style::icon &_icon;
|
||||
int _paletteVersion = 0;
|
||||
QImage _frame;
|
||||
|
||||
};
|
||||
|
||||
class EmojiThumbnail final : public DynamicImage {
|
||||
public:
|
||||
EmojiThumbnail(not_null<Data::Session*> owner, const QString &data);
|
||||
|
||||
std::shared_ptr<DynamicImage> clone() override;
|
||||
|
||||
QImage image(int size) override;
|
||||
void subscribeToUpdates(Fn<void()> callback) override;
|
||||
|
||||
private:
|
||||
const not_null<Data::Session*> _owner;
|
||||
const QString _data;
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> _emoji;
|
||||
QImage _frame;
|
||||
|
||||
};
|
||||
|
||||
PeerUserpic::PeerUserpic(not_null<PeerData*> peer, bool forceRound)
|
||||
: _peer(peer)
|
||||
, _forceRound(forceRound) {
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> PeerUserpic::clone() {
|
||||
return std::make_shared<PeerUserpic>(_peer, _forceRound);
|
||||
}
|
||||
|
||||
QImage PeerUserpic::image(int size) {
|
||||
Expects(_subscribed != nullptr);
|
||||
|
||||
|
@ -280,11 +333,19 @@ void StoryThumbnail::subscribeToUpdates(Fn<void()> callback) {
|
|||
}
|
||||
}
|
||||
|
||||
FullStoryId StoryThumbnail::id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
PhotoThumbnail::PhotoThumbnail(not_null<PhotoData*> photo, FullStoryId id)
|
||||
: StoryThumbnail(id)
|
||||
, _photo(photo) {
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> PhotoThumbnail::clone() {
|
||||
return std::make_shared<PhotoThumbnail>(_photo, id());
|
||||
}
|
||||
|
||||
Main::Session &PhotoThumbnail::session() {
|
||||
return _photo->session();
|
||||
}
|
||||
|
@ -311,6 +372,10 @@ VideoThumbnail::VideoThumbnail(
|
|||
, _video(video) {
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> VideoThumbnail::clone() {
|
||||
return std::make_shared<VideoThumbnail>(_video, id());
|
||||
}
|
||||
|
||||
Main::Session &VideoThumbnail::session() {
|
||||
return _video->session();
|
||||
}
|
||||
|
@ -330,6 +395,10 @@ void VideoThumbnail::clear() {
|
|||
_media = nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> EmptyThumbnail::clone() {
|
||||
return std::make_shared<EmptyThumbnail>();
|
||||
}
|
||||
|
||||
QImage EmptyThumbnail::image(int size) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
if (_cached.width() != size * ratio) {
|
||||
|
@ -345,6 +414,10 @@ QImage EmptyThumbnail::image(int size) {
|
|||
void EmptyThumbnail::subscribeToUpdates(Fn<void()> callback) {
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> SavedMessagesUserpic::clone() {
|
||||
return std::make_shared<SavedMessagesUserpic>();
|
||||
}
|
||||
|
||||
QImage SavedMessagesUserpic::image(int size) {
|
||||
const auto good = (_frame.width() == size * _frame.devicePixelRatio());
|
||||
const auto paletteVersion = style::PaletteVersion();
|
||||
|
@ -367,6 +440,13 @@ QImage SavedMessagesUserpic::image(int size) {
|
|||
}
|
||||
|
||||
void SavedMessagesUserpic::subscribeToUpdates(Fn<void()> callback) {
|
||||
if (!callback) {
|
||||
_frame = {};
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> RepliesUserpic::clone() {
|
||||
return std::make_shared<RepliesUserpic>();
|
||||
}
|
||||
|
||||
QImage RepliesUserpic::image(int size) {
|
||||
|
@ -391,6 +471,90 @@ QImage RepliesUserpic::image(int size) {
|
|||
}
|
||||
|
||||
void RepliesUserpic::subscribeToUpdates(Fn<void()> callback) {
|
||||
if (!callback) {
|
||||
_frame = {};
|
||||
}
|
||||
}
|
||||
|
||||
IconThumbnail::IconThumbnail(const style::icon &icon) : _icon(icon) {
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> IconThumbnail::clone() {
|
||||
return std::make_shared<IconThumbnail>(_icon);
|
||||
}
|
||||
|
||||
QImage IconThumbnail::image(int size) {
|
||||
const auto good = (_frame.width() == size * _frame.devicePixelRatio());
|
||||
const auto paletteVersion = style::PaletteVersion();
|
||||
if (!good || _paletteVersion != paletteVersion) {
|
||||
_paletteVersion = paletteVersion;
|
||||
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
if (!good) {
|
||||
_frame = QImage(
|
||||
QSize(size, size) * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_frame.setDevicePixelRatio(ratio);
|
||||
}
|
||||
_frame.fill(Qt::transparent);
|
||||
|
||||
auto p = Painter(&_frame);
|
||||
_icon.paintInCenter(p, QRect(0, 0, size, size));
|
||||
}
|
||||
return _frame;
|
||||
}
|
||||
|
||||
void IconThumbnail::subscribeToUpdates(Fn<void()> callback) {
|
||||
if (!callback) {
|
||||
_frame = {};
|
||||
}
|
||||
}
|
||||
|
||||
EmojiThumbnail::EmojiThumbnail(
|
||||
not_null<Data::Session*> owner,
|
||||
const QString &data)
|
||||
: _owner(owner)
|
||||
, _data(data) {
|
||||
}
|
||||
|
||||
void EmojiThumbnail::subscribeToUpdates(Fn<void()> callback) {
|
||||
if (!callback) {
|
||||
_emoji = nullptr;
|
||||
return;
|
||||
}
|
||||
_emoji = _owner->customEmojiManager().create(
|
||||
_data,
|
||||
std::move(callback),
|
||||
Data::CustomEmojiSizeTag::Large);
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> EmojiThumbnail::clone() {
|
||||
return std::make_shared<EmojiThumbnail>(_owner, _data);
|
||||
}
|
||||
|
||||
QImage EmojiThumbnail::image(int size) {
|
||||
Expects(_emoji != nullptr);
|
||||
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto good = (_frame.width() == size * _frame.devicePixelRatio());
|
||||
if (!good) {
|
||||
_frame = QImage(
|
||||
QSize(size, size) * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_frame.setDevicePixelRatio(ratio);
|
||||
}
|
||||
_frame.fill(Qt::transparent);
|
||||
|
||||
auto p = Painter(&_frame);
|
||||
_emoji->paint(p, {
|
||||
.textColor = st::windowBoldFg->c,
|
||||
.now = crl::now(),
|
||||
.position = QPoint(0, 0),
|
||||
.paused = false,
|
||||
});
|
||||
p.end();
|
||||
|
||||
return _frame;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -422,4 +586,14 @@ std::shared_ptr<DynamicImage> MakeStoryThumbnail(
|
|||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> MakeIconThumbnail(const style::icon &icon) {
|
||||
return std::make_shared<IconThumbnail>(icon);
|
||||
}
|
||||
|
||||
std::shared_ptr<DynamicImage> MakeEmojiThumbnail(
|
||||
not_null<Data::Session*> owner,
|
||||
const QString &data) {
|
||||
return std::make_shared<EmojiThumbnail>(owner, data);
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -11,6 +11,7 @@ class PeerData;
|
|||
|
||||
namespace Data {
|
||||
class Story;
|
||||
class Session;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
|
@ -24,5 +25,10 @@ class DynamicImage;
|
|||
[[nodiscard]] std::shared_ptr<DynamicImage> MakeRepliesThumbnail();
|
||||
[[nodiscard]] std::shared_ptr<DynamicImage> MakeStoryThumbnail(
|
||||
not_null<Data::Story*> story);
|
||||
[[nodiscard]] std::shared_ptr<DynamicImage> MakeIconThumbnail(
|
||||
const style::icon &icon);
|
||||
[[nodiscard]] std::shared_ptr<DynamicImage> MakeEmojiThumbnail(
|
||||
not_null<Data::Session*> owner,
|
||||
const QString &data);
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -136,6 +136,7 @@ menuIconGroupCreate: icon {{ "menu/groups_create", menuIconColor }};
|
|||
menuIconSigned: icon {{ "menu/signed", menuIconColor }};
|
||||
menuIconAntispam: icon {{ "menu/antispam", menuIconColor }};
|
||||
menuIconChatDiscuss: icon {{ "menu/chat_discuss", menuIconColor }};
|
||||
menuIconChats: icon {{ "menu/chats", menuIconColor }};
|
||||
menuIconBotCommands: icon {{ "menu/bot_commands", menuIconColor }};
|
||||
menuIconPremium: icon {{ "menu/premium", menuIconColor }};
|
||||
menuIconShop: icon {{ "menu/shop", menuIconColor }};
|
||||
|
|
|
@ -89,8 +89,8 @@ PRIVATE
|
|||
dialogs/dialogs_three_state_icon.h
|
||||
dialogs/ui/chat_search_empty.cpp
|
||||
dialogs/ui/chat_search_empty.h
|
||||
dialogs/ui/chat_search_tabs.cpp
|
||||
dialogs/ui/chat_search_tabs.h
|
||||
dialogs/ui/chat_search_in.cpp
|
||||
dialogs/ui/chat_search_in.h
|
||||
dialogs/ui/dialogs_stories_list.cpp
|
||||
dialogs/ui/dialogs_stories_list.h
|
||||
dialogs/ui/top_peers_strip.cpp
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d0a7ff7734b6f887dc6367742c40c50729c032e3
|
||||
Subproject commit 7b48c6a3618c7953046914ca4c6fe739684644b8
|
Loading…
Add table
Reference in a new issue