Merge tag 'v6.7.2' into dev

This commit is contained in:
AlexeyZavar
2026-04-04 05:27:01 +03:00
592 changed files with 35740 additions and 5632 deletions
@@ -40,8 +40,8 @@ QString WrapCommandInChat(
? peer->asChat()->botStatus
: peer->isMegagroup()
? peer->asChannel()->mgInfo->botStatus
: -1;
return ((command.indexOf('@') < 2) && (botStatus == 0 || botStatus == 2))
: Data::BotStatus::NoBots;
return ((command.indexOf('@') < 2) && (botStatus != Data::BotStatus::NoBots))
? command + '@' + bot->username()
: command;
}
@@ -15,7 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h"
#include "main/main_session.h"
#include "ui/cached_round_corners.h"
#include "ui/chat/chat_style_radius.h"
#include "ui/painter.h"
#include "ui/round_rect.h"
#include "ui/ui_utility.h"
#include "window/window_session_controller.h"
#include "styles/style_chat.h"
@@ -23,6 +25,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
const auto kBotKeyboardRounding = Ui::BubbleRounding{
Ui::BubbleCornerRounding::Large,
Ui::BubbleCornerRounding::Large,
Ui::BubbleCornerRounding::Large,
Ui::BubbleCornerRounding::Large,
};
class Style : public ReplyKeyboard::Style {
public:
Style(
@@ -95,7 +104,26 @@ Images::CornersMaskRef Style::buttonRounding(
Ui::BubbleRounding outer,
RectParts sides) const {
using namespace Images;
return CornersMaskRef(CornersMask(ImageRoundRadius::Small));
using namespace Ui;
using Radius = CachedCornerRadius;
using Corner = BubbleCornerRounding;
auto result = CornersMaskRef(CachedCornersMasks(Radius::BubbleSmall));
const auto &large = CachedCornersMasks(Radius::BubbleLarge);
const auto round = [&](
RectPart vertSide,
RectPart horizSide,
int index) {
if ((sides & vertSide)
&& (sides & horizSide)
&& (outer[index] == Corner::Large)) {
result.p[index] = &large[index];
}
};
round(RectPart::Top, RectPart::Left, kTopLeft);
round(RectPart::Top, RectPart::Right, kTopRight);
round(RectPart::Bottom, RectPart::Left, kBottomLeft);
round(RectPart::Bottom, RectPart::Right, kBottomRight);
return result;
}
void Style::paintButtonBg(
@@ -106,19 +134,29 @@ void Style::paintButtonBg(
Ui::BubbleRounding rounding,
float64 howMuchOver) const {
using Color = HistoryMessageMarkupButton::Color;
if (color == Color::Normal) {
Ui::FillRoundRect(p, rect, st::botKbBg, Ui::BotKeyboardCorners);
} else {
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush((color == Color::Primary)
? st::botKbPrimaryBg->c
: (color == Color::Danger)
? st::botKbDangerBg->c
: st::botKbSuccessBg->c);
const auto radius = st::roundRadiusSmall;
p.drawRoundedRect(rect, radius, radius);
}
using Corner = Ui::BubbleCornerRounding;
const auto bg = (color == Color::Normal)
? st::botKbBg->c
: (color == Color::Primary)
? st::botKbPrimaryBg->c
: (color == Color::Danger)
? st::botKbDangerBg->c
: st::botKbSuccessBg->c;
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(bg);
const auto large = Ui::BubbleRadiusLarge();
const auto small = Ui::BubbleRadiusSmall();
const auto radius = [&](int index) {
return (rounding[index] == Corner::Large) ? large : small;
};
p.drawPath(
Ui::ComplexRoundedRectPath(
rect,
radius(0),
radius(1),
radius(2),
radius(3)));
}
void Style::paintButtonIcon(
@@ -170,7 +208,7 @@ void BotKeyboard::paintEvent(QPaintEvent *e) {
_impl->paint(
p,
nullptr,
Ui::BubbleRounding(),
kBotKeyboardRounding,
width(),
clip.translated(-x, -st::botKbScroll.deltat),
_controller->isGifPausedAtLeastFor(Window::GifPauseReason::Any));
@@ -279,7 +317,7 @@ void BotKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool activ
void BotKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
if (!_impl) return;
_impl->clickHandlerPressedChanged(p, pressed, Ui::BubbleRounding());
_impl->clickHandlerPressedChanged(p, pressed, kBotKeyboardRounding);
}
bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
@@ -70,9 +70,9 @@ ComposeIcons {
menuSchedule: icon;
menuWhenOnline: icon;
menuSpoiler: icon;
menuSpoilerOff: icon;
menuBelow: icon;
menuAbove: icon;
menuQualityHigh: icon;
menuPrice: icon;
menuEditStars: icon;
@@ -156,6 +156,7 @@ SendButton {
stars: RoundButton;
recordSize: size;
sendDisabledFg: color;
sendIconFg: color;
}
RecordBarLock {
@@ -242,6 +243,7 @@ ComposeControls {
tabbedHeightMax: pixels;
record: RecordBar;
files: ComposeFiles;
photoQualityBadgeOuterSkip: pixels;
premium: PremiumLimits;
boxField: InputField;
restrictionLabel: FlatLabel;
@@ -436,7 +438,6 @@ emojiTabs: defaultTabsSlider;
emojiCategoryIconTop: 6px;
emojiPanAnimation: PanelAnimation(defaultPanelAnimation) {
fadeBg: emojiPanBg;
shadow: boxRoundShadow;
}
emojiPanWidth: 345px;
emojiPanMinHeight: 278px;
@@ -554,6 +555,7 @@ stickerPanRemoveSet: IconButton(hashtagClose) {
stickerIconMove: 400;
stickerPreviewDuration: 150;
stickerPreviewMin: 0.1;
mediaPreviewPhotoSkip: 48px;
emojiPanColorAll: IconButton(stickerPanRemoveSet) {
width: 24px;
@@ -588,7 +590,7 @@ emojiEmpty: icon {{ "emoji_empty", windowSubTextFg }};
editMediaButtonSize: 32px;
editMediaButtonIconFile: icon {{ "send_media/send_media_replace", menuIconFg }};
editMediaButtonIconFile: icon {{ "send_media/send_media_more", menuIconFg }};
editMediaButton: IconButton(defaultIconButton) {
width: editMediaButtonSize;
height: editMediaButtonSize;
@@ -602,10 +604,10 @@ editMediaButton: IconButton(defaultIconButton) {
sendBoxAlbumGroupEditInternalSkip: 8px;
sendBoxAlbumGroupSkipRight: 5px;
sendBoxAlbumGroupSkipTop: 5px;
sendBoxAlbumGroupRadius: 4px;
sendBoxAlbumGroupSize: size(62px, 25px);
sendBoxAlbumGroupSize: size(48px, 26px);
sendBoxAlbumGroupSizeVertical: size(30px, 50px);
sendBoxAlbumSmallGroupSize: size(30px, 25px);
sendBoxAlbumSmallGroupCircleSize: 27px;
sendBoxFileGroupSkipTop: 2px;
sendBoxFileGroupSkipRight: 5px;
@@ -617,11 +619,11 @@ sendBoxAlbumGroupButtonFile: IconButton(editMediaButton) {
}
}
sendBoxAlbumGroupEditButtonIconFile: editMediaButtonIconFile;
sendBoxAlbumGroupDeleteButtonIconFile: icon {{ "send_media/send_media_delete", menuIconFg }};
sendBoxAlbumGroupDeleteButtonIconFile: icon {{ "send_media/send_media_cross", menuIconFg }};
sendBoxAlbumButtonMediaEdit: icon {{ "send_media/send_media_replace", roundedFg }};
sendBoxAlbumGroupButtonMediaEdit: icon {{ "send_media/send_media_replace", roundedFg, point(4px, 1px) }};
sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_delete", roundedFg }};
sendBoxAlbumButtonMediaMore: icon {{ "send_media/send_media_more", roundedFg }};
sendBoxAlbumGroupButtonMediaMore: icon {{ "send_media/send_media_more", roundedFg, point(4px, 1px) }};
sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_cross", roundedFg, point(-2px, 1px) }};
defaultComposeIcons: ComposeIcons {
settings: icon {{ "emoji/emoji_settings", emojiIconFg }};
@@ -655,9 +657,9 @@ defaultComposeIcons: ComposeIcons {
menuSchedule: menuIconSchedule;
menuWhenOnline: menuIconWhenOnline;
menuSpoiler: menuIconSpoiler;
menuSpoilerOff: menuIconSpoilerOff;
menuBelow: menuIconBelow;
menuAbove: menuIconAbove;
menuQualityHigh: menuIconQualityHigh;
menuPrice: menuIconEarn;
stripBubble: icon{
@@ -847,32 +849,31 @@ reactPanelScrollRounded: ScrollArea(emojiScroll) {
deltab: 14px;
}
selfForwardsTaggerStripSkip: 26px;
selfForwardsTaggerIcon: size(32px, 32px);
selfForwardsTaggerIconPadding: margins(14px, 2px, 8px, 2px);
selfForwardsTaggerToast: Toast(defaultToast) {
minWidth: 160px;
maxWidth: 380px;
radius: 9px;
padding: margins(54px, 12px, 19px, 12px);
iconPosition: point(15px, 6px);
padding: margins(19px, 12px, 19px, 12px);
}
joinChatAddToFilterToast: Toast(defaultToast) {
minWidth: 160px;
maxWidth: 380px;
radius: 9px;
padding: margins(54px, 12px, 44px, 12px);
iconPosition: point(15px, 6px);
padding: margins(7px, 12px, 44px, 12px);
}
joinChatAddToFilterToastButton: IconButton(defaultIconButton) {
width: 40px;
height: 40px;
icon: icon{
{ "chat/reactions_round_small", windowBgRipple },
{ "chat/reactions_expand_panel", windowSubTextFg },
{ "chat/reactions_round_small", toastFg },
{ "chat/reactions_expand_panel", toastBg },
};
iconOver: icon{
{ "chat/reactions_round_small", windowBgRipple },
{ "chat/reactions_expand_panel", windowSubTextFg },
{ "chat/reactions_round_small", toastFg },
{ "chat/reactions_expand_panel", toastBg },
};
rippleAreaPosition: point(10px, 10px);
rippleAreaSize: 20px;
@@ -969,6 +970,16 @@ historyEmojiStatusInfoLabel: FlatLabel(historyContactStatusLabel) {
}
historyContactStatusMinSkip: 16px;
historySetBotPhotoIcon: icon {{ "menu/photo_set", windowActiveTextFg }};
historySetBotPhotoIconSize: 49px;
historySetBotPhotoLabel: FlatLabel(defaultFlatLabel) {
style: semiboldTextStyle;
textFg: windowActiveTextFg;
align: align(center);
maxHeight: 30px;
}
historySetBotPhotoLabelMarginRight: 20px;
historyBusinessBotPhoto: UserpicButton(defaultUserpicButton) {
size: size(46px, 46px);
photoSize: 46px;
@@ -1087,6 +1098,10 @@ historyUnreadReactions: TwoIconButton(historyToDown) {
iconAbove: icon {{ "history_unread_reaction", historyToDownFg }};
iconAboveOver: icon {{ "history_unread_reaction", historyToDownFgOver }};
}
historyUnreadPollVotes: TwoIconButton(historyToDown) {
iconAbove: icon {{ "history_unread_poll_vote-40x40", historyToDownFg, point(6px, 6px) }};
iconAboveOver: icon {{ "history_unread_poll_vote-40x40", historyToDownFgOver, point(6px, 6px) }};
}
historyUnreadThingsSkip: 4px;
historyQuoteStyle: QuoteStyle(defaultQuoteStyle) {
@@ -1346,8 +1361,21 @@ historyRecordDelete: IconButton(historyAttach) {
}
historyRecordWaveformRightSkip: 10px;
historyRecordWaveformBgMargins: margins(5px, 8px, 5px, 9px);
historyRecordWaveformBgRadius: 7px;
historyRecordWaveformOutsideAlpha: 0.6;
historyRecordWaveformInactiveAlpha: 0.7;
historyRecordCenterControlHeight: 18px;
historyRecordCenterControlIconScale: 0.6;
historyRecordCenterControlPadding: 4px;
historyRecordCenterControlTextSkip: 2px;
historyRecordCenterControlMinimumProgressPadding: 5px;
historyRecordWaveformBar: 3px;
historyRecordTrimFrameRadius: 5px;
historyRecordTrimFrameBorder: 1px;
historyRecordTrimHandleWidth: 10px;
historyRecordTrimHandleInnerSize: size(2px, 8px);
historyRecordTrimHandleInnerSkip: 2px;
historyRecordLockPosition: point(1px, 22px);
@@ -1405,7 +1433,28 @@ historySend: SendButton {
}
recordSize: size(26px, 26px);
sendDisabledFg: historyComposeIconFg;
sendIconFg: historySendIconFg;
}
historyAiComposeButton: IconButton(historyAttach) {
width: 28px;
height: 28px;
rippleAreaSize: 28px;
rippleAreaPosition: point(0px, 0px);
}
historyAiComposeButtonLetters: icon{{ "chat/ai_letters-20x20", historyComposeIconFg }};
historyAiComposeButtonStar1: icon{{ "chat/ai_star1-20x20", historyComposeIconFg }};
historyAiComposeButtonStar2: icon{{ "chat/ai_star2-20x20", historyComposeIconFg }};
historyAiComposeButtonPosition: point(-8px, -4px);
historyAiComposeTooltipSkip: 8px;
importantTooltipHide: IconButton(defaultIconButton) {
width: 34px;
height: 20px;
iconPosition: point(-1px, -1px);
icon: icon {{ "calls/video_tooltip", importantTooltipFg }};
iconOver: icon {{ "calls/video_tooltip", importantTooltipFg }};
ripple: emptyRippleAnimation;
}
boxAiComposeButtonPosition: point(0px, -4px);
historyRecordFrameIndex: 30;
defaultComposeFilesMenu: IconButton(defaultIconButton) {
@@ -1498,6 +1547,7 @@ defaultComposeControls: ComposeControls {
tabbedHeightMax: emojiPanMaxHeight;
record: defaultRecordBar;
files: defaultComposeFiles;
photoQualityBadgeOuterSkip: 3px;
premium: defaultPremiumLimits;
boxField: defaultInputField;
restrictionLabel: defaultRestrictionLabel;
@@ -1753,3 +1803,41 @@ topPeersSelectorImportantTooltipLabel: FlatLabel(defaultImportantTooltipLabel) {
font: font(semibold 12px);
}
}
aiComposeSendButton: SendButton(historySend) {
sendIconPosition: point(9px, 9px);
sendIconFillPadding: 5px;
inner: IconButton(historyAttach) {
width: 42px;
height: 42px;
icon: icon {{ "chat/input_send_round", windowFgActive }};
iconOver: icon {{ "chat/input_send_round", windowFgActive }};
rippleAreaSize: 40px;
rippleAreaPosition: point(1px, 1px);
}
sendIconFg: windowFgActive;
}
aiComposeBodyLabel: FlatLabel(defaultFlatLabel) {
minWidth: 300px;
align: align(topleft);
style: TextStyle(defaultTextStyle) {
lineHeight: 22px;
blockquote: QuoteStyle(historyQuoteStyle) {
padding: margins(10px, 2px, 20px, 2px);
icon: icon{{ "chat/mini_quote", windowFg }};
iconPosition: point(4px, 4px);
expand: icon{{ "intro_country_dropdown", windowFg }};
expandPosition: point(6px, 4px);
collapse: icon{{ "intro_country_dropdown-flip_vertical", windowFg }};
collapsePosition: point(6px, 4px);
}
pre: QuoteStyle(historyQuoteStyle) {
header: 20px;
headerPosition: point(10px, 2px);
scrollable: true;
icon: icon{{ "chat/mini_copy", windowFg }};
iconPosition: point(4px, 2px);
}
}
}
@@ -69,6 +69,7 @@ constexpr auto kCollapsedRows = 3;
constexpr auto kAppearDuration = 0.3;
constexpr auto kCustomSearchLimit = 256;
constexpr auto kColorPickerDelay = crl::time(500);
constexpr auto kSearchRequestDelay = 400;
using Core::RecentEmojiId;
using Core::RecentEmojiDocument;
@@ -124,6 +125,7 @@ private:
QPoint _innerPosition;
Ui::RoundRect _backgroundRect;
Ui::RoundRect _overBg;
Ui::BoxShadow _shadow;
bool _hiding = false;
QPixmap _cache;
@@ -154,7 +156,8 @@ EmojiColorPicker::EmojiColorPicker(
: RpWidget(parent)
, _st(st)
, _backgroundRect(st::emojiPanRadius, _st.bg)
, _overBg(st::emojiPanRadius, _st.overBg) {
, _overBg(st::emojiPanRadius, _st.overBg)
, _shadow(_st.showAnimation.shadow) {
setMouseTracking(true);
}
@@ -237,7 +240,7 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) {
p.drawPixmap(0, 0, _cache);
return;
}
Ui::Shadow::paint(p, inner, width(), _st.showAnimation.shadow);
_shadow.paint(p, inner, st::emojiPanRadius);
_backgroundRect.paint(p, inner);
const auto skip = topColorAllSkip();
@@ -504,6 +507,7 @@ EmojiListWidget::EmojiListWidget(
&session(),
st::emojiPremiumLock))
, _collapsedBg(st::emojiPanExpand.height / 2, st().headerFg)
, _searchRequestTimer([=] { sendSearchRequest(); })
, _picker(this, st())
, _showPickerTimer([=] { showPicker(); })
, _previewTimer([=] { showPreview(); }) {
@@ -649,6 +653,7 @@ void EmojiListWidget::applyNextSearchQuery() {
if (!searching) {
_searchResults.clear();
_searchCustomIds.clear();
_searchSets.clear();
}
resizeToWidth(width());
_recentShownCount = searching
@@ -661,6 +666,7 @@ void EmojiListWidget::applyNextSearchQuery() {
updateSelected();
};
if (_searchQuery.empty()) {
cancelSearchRequest();
finish(false);
return;
}
@@ -669,11 +675,13 @@ void EmojiListWidget::applyNextSearchQuery() {
if (_searchEmoji == _searchEmojiPrevious) {
return;
}
_searchEmoticon = QString();
for (const auto emoji : plain) {
_searchEmoticon += emoji->text();
}
_searchResults.clear();
_searchCustomIds.clear();
if (_mode != Mode::Full || session().premium()) {
appendPremiumSearchResults();
}
_searchSets.clear();
if (_mode == Mode::Full) {
for (const auto emoji : plain) {
_searchResults.push_back({
@@ -681,6 +689,44 @@ void EmojiListWidget::applyNextSearchQuery() {
});
}
}
if (_mode != Mode::Full || session().premium()) {
appendPremiumSearchResults();
}
if (_mode == Mode::Full) {
appendLocalPackSearchResults();
}
_searchQueryText = ranges::accumulate(
_searchQuery,
QString(),
[](QString a, const QString &b) {
return a.isEmpty() ? b : (a + ' ' + b);
}).trimmed();
if (!_searchQueryText.isEmpty()) {
toggleSearchLoading(false);
if (const auto requestId = base::take(_searchCloudRequestId)) {
_api.request(requestId).cancel();
}
if (const auto requestId = base::take(_searchSetsRequestId)) {
_api.request(requestId).cancel();
}
_searchNextRequestQuery = _searchQueryText;
const auto cloudCached = _searchCloudCache.find(_searchQueryText)
!= _searchCloudCache.cend();
const auto setsCached = _searchSetsCache.find(_searchQueryText)
!= _searchSetsCache.cend();
if (cloudCached || setsCached) {
_searchRequestTimer.cancel();
_searchRequestQuery = _searchQueryText;
fillCloudSearchResults();
fillCloudSearchSets();
if (!cloudCached || !setsCached) {
sendSearchRequest();
}
} else {
_searchRequestTimer.callOnce(kSearchRequestDelay);
}
}
}
void EmojiListWidget::showPreview() {
@@ -778,6 +824,337 @@ void EmojiListWidget::appendPremiumSearchResults() {
}
}
void EmojiListWidget::appendLocalPackSearchResults() {
const auto text = _searchQueryText.toLower();
if (text.isEmpty()) {
return;
}
const auto test = session().isTestMode();
const auto &sets = session().data().stickers().sets();
const auto processSet = [&](uint64 setId) {
const auto it = sets.find(setId);
if (it == sets.end()) {
return;
}
const auto set = it->second.get();
if (!(set->flags & Data::StickersSetFlag::Emoji)) {
return;
}
const auto title = set->title.toLower();
if (!title.startsWith(text)
&& !title.contains(u' ' + text)) {
return;
}
const auto &list = set->stickers.empty()
? set->covers
: set->stickers;
for (const auto document : list) {
if (_searchResults.size() >= kCustomSearchLimit) {
return;
}
const auto sticker = document->sticker();
if (!sticker) {
continue;
}
const auto id = document->id;
if (!_searchCustomIds.emplace(id).second) {
continue;
}
const auto statusId = EmojiStatusId{ id };
_searchResults.push_back({
.custom = resolveCustomEmoji(
statusId,
document,
SearchEmojiSectionSetId()),
.id = { RecentEmojiDocument{ .id = id, .test = test } },
});
}
};
for (const auto setId
: session().data().stickers().emojiSetsOrder()) {
processSet(setId);
}
for (const auto setId
: session().data().stickers().featuredEmojiSetsOrder()) {
processSet(setId);
}
}
void EmojiListWidget::toggleSearchLoading(bool loading) {
if (_search) {
_search->setLoading(loading);
}
if (_searchLoading != loading) {
_searchLoading = loading;
update();
}
}
void EmojiListWidget::sendSearchRequest() {
_searchRequestQuery = _searchQueryText;
if (_searchRequestQuery.isEmpty()) {
return;
}
const auto cloudCached = _searchCloudCache.find(
_searchRequestQuery) != _searchCloudCache.cend();
const auto setsCached = _searchSetsCache.find(
_searchRequestQuery) != _searchSetsCache.cend();
if (cloudCached && setsCached) {
toggleSearchLoading(false);
return;
}
toggleSearchLoading(true);
const auto hash = uint64(0);
if (!cloudCached) {
auto langCodes = QVector<MTPstring>();
const auto method = QGuiApplication::inputMethod();
if (method) {
for (const auto &lang : method->locale().uiLanguages()) {
langCodes.push_back(MTP_string(lang));
}
}
using Flag = MTPmessages_SearchStickers::Flag;
_searchCloudRequestId = _api.request(MTPmessages_SearchStickers(
MTP_flags(Flag::f_emojis),
MTP_string(_searchRequestQuery),
MTP_string(_searchEmoticon),
MTP_vector<MTPstring>(langCodes),
MTP_int(0),
MTP_int(50),
MTP_long(hash)
)).done([=](const MTPmessages_FoundStickers &result) {
searchCloudResultsDone(result);
}).fail([=] {
_searchCloudRequestId = 0;
_searchCloudCache.emplace(
_searchRequestQuery,
std::vector<DocumentId>());
if (!_searchSetsRequestId) {
toggleSearchLoading(false);
showSearchResults();
}
}).handleAllErrors().send();
}
if (!setsCached) {
sendSearchSetsRequest();
}
}
void EmojiListWidget::sendSearchSetsRequest() {
const auto hash = uint64(0);
_searchSetsRequestId = _api.request(
MTPmessages_SearchEmojiStickerSets(
MTP_flags(0),
MTP_string(_searchRequestQuery),
MTP_long(hash))
).done([=](const MTPmessages_FoundStickerSets &result) {
searchSetsResultsDone(result);
}).fail([=] {
_searchSetsRequestId = 0;
if (!_searchCloudRequestId) {
toggleSearchLoading(false);
}
}).handleAllErrors().send();
}
void EmojiListWidget::cancelSearchRequest() {
toggleSearchLoading(false);
if (const auto requestId = base::take(_searchCloudRequestId)) {
_api.request(requestId).cancel();
}
if (const auto requestId = base::take(_searchSetsRequestId)) {
_api.request(requestId).cancel();
}
_searchRequestTimer.cancel();
_searchRequestQuery = QString();
_searchNextRequestQuery = QString();
_searchCloudCache.clear();
_searchSetsCache.clear();
_searchSets.clear();
}
void EmojiListWidget::searchCloudResultsDone(
const MTPmessages_FoundStickers &result) {
_searchCloudRequestId = 0;
result.match([&](const MTPDmessages_foundStickersNotModified &) {
LOG(("API: messages.foundStickersNotModified."));
}, [&](const MTPDmessages_foundStickers &data) {
auto it = _searchCloudCache.find(_searchRequestQuery);
if (it == _searchCloudCache.cend()) {
it = _searchCloudCache.emplace(
_searchRequestQuery,
std::vector<DocumentId>()).first;
}
for (const auto &sticker : data.vstickers().v) {
if (const auto doc = session().data().processDocument(
sticker)) {
it->second.push_back(doc->id);
}
}
if (!_searchSetsRequestId) {
toggleSearchLoading(false);
}
showSearchResults();
});
}
void EmojiListWidget::searchSetsResultsDone(
const MTPmessages_FoundStickerSets &result) {
_searchSetsRequestId = 0;
if (!_searchCloudRequestId) {
toggleSearchLoading(false);
}
result.match([&](const MTPDmessages_foundStickerSetsNotModified &) {
LOG(("API Error: "
"messages.foundStickerSetsNotModified not expected."));
}, [&](const MTPDmessages_foundStickerSets &data) {
auto it = _searchSetsCache.find(_searchRequestQuery);
if (it == _searchSetsCache.cend()) {
it = _searchSetsCache.emplace(
_searchRequestQuery,
std::vector<uint64>()).first;
}
for (const auto &setData : data.vsets().v) {
const auto set
= session().data().stickers().feedSet(setData);
if (set->stickers.empty() && set->covers.empty()) {
continue;
}
it->second.push_back(set->id);
}
showSearchResults();
});
}
void EmojiListWidget::showSearchResults() {
clearSelection();
_searchResults.clear();
_searchCustomIds.clear();
_searchSets.clear();
_searchEmoji.clear();
auto plain = collectPlainSearchResults();
if (_mode == Mode::Full) {
for (const auto emoji : plain) {
_searchResults.push_back({
.id = { emoji },
});
}
}
if (_mode != Mode::Full || session().premium()) {
appendPremiumSearchResults();
}
fillCloudSearchResults();
if (_mode == Mode::Full) {
appendLocalPackSearchResults();
}
fillCloudSearchSets();
resizeToWidth(width());
_recentShownCount = _searchResults.size();
update();
updateSelected();
}
void EmojiListWidget::fillCloudSearchResults() {
const auto it = _searchCloudCache.find(_searchRequestQuery);
if (it == _searchCloudCache.cend() || it->second.empty()) {
return;
}
const auto test = session().isTestMode();
for (const auto id : it->second) {
if (_searchResults.size() >= kCustomSearchLimit) {
break;
}
if (!_searchCustomIds.emplace(id).second) {
continue;
}
const auto document = session().data().document(id);
const auto sticker = document->sticker();
if (!sticker) {
continue;
}
const auto statusId = EmojiStatusId{ id };
_searchResults.push_back({
.custom = resolveCustomEmoji(
statusId,
document,
SearchEmojiSectionSetId()),
.id = { RecentEmojiDocument{ .id = id, .test = test } },
});
}
}
void EmojiListWidget::fillCloudSearchSets() {
const auto it = _searchSetsCache.find(_searchRequestQuery);
if (it == _searchSetsCache.cend() || it->second.empty()) {
return;
}
const auto &sets = session().data().stickers().sets();
for (const auto setId : it->second) {
const auto setIt = sets.find(setId);
if (setIt == sets.end()) {
continue;
}
const auto set = setIt->second.get();
const auto &list = set->stickers.empty()
? set->covers
: set->stickers;
if (list.empty()) {
continue;
}
auto customs = std::vector<CustomOne>();
customs.reserve(list.size());
for (const auto document : list) {
if (const auto sticker = document->sticker()) {
const auto statusId = EmojiStatusId{ document->id };
customs.push_back({
.custom = resolveCustomEmoji(
statusId,
document,
setId),
.document = document,
.emoji = Ui::Emoji::Find(sticker->alt),
});
}
}
if (customs.empty()) {
continue;
}
const auto installed = !!(set->flags
& Data::StickersSetFlag::Installed);
_searchSets.push_back({
.id = setId,
.set = set,
.thumbnailDocument = set->lookupThumbnailDocument(),
.title = set->title,
.list = std::move(customs),
.canRemove = installed,
});
}
}
EmojiListWidget::CustomSet &EmojiListWidget::searchSetBySection(
int section) {
Expects(section > 0 && section <= int(_searchSets.size()));
return _searchSets[section - 1];
}
const EmojiListWidget::CustomSet &EmojiListWidget::searchSetBySection(
int section) const {
Expects(section > 0 && section <= int(_searchSets.size()));
return _searchSets[section - 1];
}
void EmojiListWidget::provideRecent(
const std::vector<EmojiStatusId> &customRecentList) {
clearSelection();
@@ -793,6 +1170,18 @@ void EmojiListWidget::repaintCustom(uint64 setId) {
if (_searchMode) {
if (repaintSearch) {
update();
} else {
enumerateSections([&](const SectionInfo &info) {
if (info.section > 0
&& searchSetBySection(info.section).id == setId) {
update(
0,
info.rowsTop,
width(),
info.rowsBottom - info.rowsTop);
}
return true;
});
}
return;
}
@@ -908,6 +1297,16 @@ void EmojiListWidget::unloadCustomIn(const SectionInfo &info) {
}
}
return;
} else if (_searchMode && info.section > 0) {
auto &custom = searchSetBySection(info.section);
if (!custom.painted) {
return;
}
custom.painted = false;
for (const auto &single : custom.list) {
single.custom->unload();
}
return;
} else if (info.section < _staticCount) {
return;
}
@@ -991,7 +1390,22 @@ bool EmojiListWidget::enumerateSections(Callback callback) const {
if (_searchMode) {
info.section = i;
info.count = _searchResults.size();
return next();
if (!next()) {
return false;
}
++i;
for (auto &section : _searchSets) {
info.section = i++;
info.premiumRequired = section.premiumRequired;
info.count = int(section.list.size());
info.collapsed = !section.expanded
&& (!section.canRemove || section.premiumRequired)
&& (info.count > _columnCount * kCollapsedRows);
if (!next()) {
return false;
}
}
return true;
}
for (; i != _staticCount; ++i) {
info.section = i;
@@ -1043,7 +1457,9 @@ EmojiListWidget::SectionInfo EmojiListWidget::sectionInfoByOffset(
}
int EmojiListWidget::sectionsCount() const {
return _searchMode ? 1 : (_staticCount + int(_custom.size()));
return _searchMode
? (1 + int(_searchSets.size()))
: (_staticCount + int(_custom.size()));
}
void EmojiListWidget::setSingleSize(QSize size) {
@@ -1408,7 +1824,11 @@ void EmojiListWidget::paint(
auto selectedButton = std::get_if<OverButton>(!v::is_null(_pressed)
? &_pressed
: &_selected);
if (_searchResults.empty() && _searchMode) {
if (_searchResults.empty()
&& _searchSets.empty()
&& _searchMode
&& !_searchLoading
&& !_searchRequestTimer.isActive()) {
paintEmptySearchResults(p);
}
enumerateSections([&](const SectionInfo &info) {
@@ -1429,7 +1849,9 @@ void EmojiListWidget::paint(
if (info.section > 0 && clip.top() < info.rowsTop) {
p.setFont(st::emojiPanHeaderFont);
p.setPen(st().headerFg);
auto titleText = (info.section < _staticCount)
auto titleText = (_searchMode && info.section > 0)
? searchSetBySection(info.section).title
: (info.section < _staticCount)
? ChatHelpers::EmojiCategoryTitle(info.section)(tr::now)
: _custom[info.section - _staticCount].title;
auto titleWidth = st::emojiPanHeaderFont->width(titleText);
@@ -1510,8 +1932,15 @@ void EmojiListWidget::paint(
}
_overBg.paint(p, QRect(tl, st::emojiPanArea));
}
if (_searchMode) {
if (_searchMode && info.section == 0) {
drawRecent(p, context, w, _searchResults[index]);
} else if (_searchMode && info.section > 0) {
drawSearchSetCustom(
p,
context,
w,
info.section,
index);
} else if (info.section == int(Section::Recent)) {
drawRecent(p, context, w, _recent[index]);
} else if (info.section < _staticCount) {
@@ -1646,6 +2075,22 @@ void EmojiListWidget::drawCustom(
entry.custom->paint(p, *_emojiPaintContext);
}
void EmojiListWidget::drawSearchSetCustom(
QPainter &p,
const ExpandingContext &context,
QPoint position,
int section,
int index) {
auto &custom = searchSetBySection(section);
custom.painted = true;
auto &entry = custom.list[index];
_emojiPaintContext->scale = context.progress;
_emojiPaintContext->position = position
+ _innerPosition
+ _customPosition;
entry.custom->paint(p, *_emojiPaintContext);
}
bool EmojiListWidget::checkPickerHide() {
if (!_picker->isHidden() && !v::is_null(_pickerSelected)) {
_picker->hideAnimated();
@@ -1666,7 +2111,7 @@ EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
int index,
int section) const {
if (_searchMode) {
if (_searchMode && section == 0) {
if (index < _searchResults.size()) {
const auto document = std::get_if<RecentEmojiDocument>(
&_searchResults[index].id.data);
@@ -1675,6 +2120,13 @@ EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
}
}
return {};
} else if (_searchMode && section > 0) {
const auto &set = searchSetBySection(section);
if (index < int(set.list.size())) {
auto &entry = set.list[index];
return { entry.document, entry.collectible };
}
return {};
} else if (section == int(Section::Recent) && index < _recent.size()) {
const auto &recent = _recent[index];
if (recent.collectible) {
@@ -1700,11 +2152,15 @@ EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const {
const auto section = over ? over->section : -1;
const auto index = over ? over->index : -1;
return _searchMode
return (_searchMode && section == 0)
? ((index < _searchResults.size()
&& v::is<EmojiPtr>(_searchResults[index].id.data))
? v::get<EmojiPtr>(_searchResults[index].id.data)
: nullptr)
: (_searchMode && section > 0)
? ((index < int(searchSetBySection(section).list.size()))
? searchSetBySection(section).list[index].emoji
: nullptr)
: (section == int(Section::Recent)
&& index < _recent.size()
&& v::is<EmojiPtr>(_recent[index].id.data))
@@ -1826,10 +2282,13 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
if (const auto over = std::get_if<OverEmoji>(&_selected)) {
const auto section = over->section;
const auto index = over->index;
if (section >= _staticCount
&& sectionInfo(section).collapsed
if (sectionInfo(section).collapsed
&& index + 1 == _columnCount * kCollapsedRows) {
_custom[section - _staticCount].expanded = true;
if (_searchMode && section > 0) {
searchSetBySection(section).expanded = true;
} else if (section >= _staticCount) {
_custom[section - _staticCount].expanded = true;
}
resizeToWidth(width());
update();
return;
@@ -1842,13 +2301,20 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
selectCustom(lookupChosen(custom, over));
}
} else if (const auto set = std::get_if<OverSet>(&pressed)) {
Assert(set->section >= _staticCount
&& set->section < _staticCount + _custom.size());
displaySet(_custom[set->section - _staticCount].id);
const auto setId = (_searchMode && set->section > 0)
? searchSetBySection(set->section).id
: (set->section >= _staticCount)
? _custom[set->section - _staticCount].id
: uint64(0);
if (setId) {
displaySet(setId);
}
} else if (auto button = std::get_if<OverButton>(&pressed)) {
Assert(hasButton(button->section));
const auto id = hasColorButton(button->section)
? 0
: (_searchMode && button->section > 0)
? searchSetBySection(button->section).id
: _custom[button->section - _staticCount].id;
if (hasColorButton(button->section)) {
_pickerSelected = pressed;
@@ -2023,6 +2489,12 @@ QRect EmojiListWidget::colorButtonRect(const SectionInfo &info) const {
}
bool EmojiListWidget::hasRemoveButton(int index) const {
if (_searchMode) {
if (index > 0 && index <= int(_searchSets.size())) {
return searchSetBySection(index).canRemove;
}
return false;
}
if (index < _staticCount
|| index >= _staticCount + _custom.size()) {
return false;
@@ -2058,6 +2530,13 @@ QRect EmojiListWidget::removeButtonRect(const SectionInfo &info) const {
}
bool EmojiListWidget::hasAddButton(int index) const {
if (_searchMode) {
if (index > 0 && index <= int(_searchSets.size())) {
const auto &set = searchSetBySection(index);
return !set.canRemove && !set.premiumRequired;
}
return false;
}
if (index < _staticCount
|| index >= _staticCount + _custom.size()) {
return false;
@@ -2074,6 +2553,12 @@ QRect EmojiListWidget::addButtonRect(int index) const {
}
bool EmojiListWidget::hasUnlockButton(int index) const {
if (_searchMode) {
if (index > 0 && index <= int(_searchSets.size())) {
return searchSetBySection(index).premiumRequired;
}
return false;
}
if (index < _staticCount
|| index >= _staticCount + _custom.size()) {
return false;
@@ -2083,13 +2568,19 @@ bool EmojiListWidget::hasUnlockButton(int index) const {
}
QRect EmojiListWidget::unlockButtonRect(int index) const {
Expects(index >= _staticCount
&& index < _staticCount + _custom.size());
Expects((_searchMode
&& index > 0
&& index <= int(_searchSets.size()))
|| (index >= _staticCount
&& index < _staticCount + _custom.size()));
return buttonRect(sectionInfo(index), rightButton(index));
}
bool EmojiListWidget::hasButton(int index) const {
if (_searchMode) {
return (index > 0 && index <= int(_searchSets.size()));
}
if (hasColorButton(index)) {
return true;
} else if (index >= _staticCount
@@ -2123,6 +2614,15 @@ QRect EmojiListWidget::buttonRect(
}
auto EmojiListWidget::rightButton(int index) const -> const RightButton & {
if (_searchMode) {
Expects(index > 0 && index <= int(_searchSets.size()));
return hasAddButton(index)
? _add
: searchSetBySection(index).canRemove
? _restore
: _unlock;
}
Expects(index >= _staticCount
&& index < _staticCount + _custom.size());
@@ -2274,6 +2774,7 @@ void EmojiListWidget::processHideFinished() {
_picker->hideFast();
_pickerSelected = v::null;
}
cancelSearchRequest();
unloadAllCustom();
clearSelection();
}
@@ -2652,7 +3153,9 @@ int EmojiListWidget::paintButtonGetWidth(
if (!hasButton(info.section)) {
return 0;
}
auto &ripple = (info.section >= _staticCount)
auto &ripple = (_searchMode && info.section > 0)
? searchSetBySection(info.section).ripple
: (info.section >= _staticCount)
? _custom[info.section - _staticCount].ripple
: _colorAllRipple;
const auto colorAll = hasColorButton(info.section);
@@ -2807,9 +3310,13 @@ void EmojiListWidget::setSelected(OverState newSelected) {
void EmojiListWidget::setPressed(OverState newPressed) {
if (auto button = std::get_if<OverButton>(&_pressed)) {
Assert(hasColorButton(button->section)
|| (_searchMode && button->section > 0
&& button->section <= int(_searchSets.size()))
|| (button->section >= _staticCount
&& button->section < _staticCount + _custom.size()));
auto &ripple = (button->section >= _staticCount)
auto &ripple = (_searchMode && button->section > 0)
? searchSetBySection(button->section).ripple
: (button->section >= _staticCount)
? _custom[button->section - _staticCount].ripple
: _colorAllRipple;
if (ripple) {
@@ -2819,9 +3326,13 @@ void EmojiListWidget::setPressed(OverState newPressed) {
_pressed = newPressed;
if (auto button = std::get_if<OverButton>(&_pressed)) {
Assert(hasColorButton(button->section)
|| (_searchMode && button->section > 0
&& button->section <= int(_searchSets.size()))
|| (button->section >= _staticCount
&& button->section < _staticCount + _custom.size()));
auto &ripple = (button->section >= _staticCount)
auto &ripple = (_searchMode && button->section > 0)
? searchSetBySection(button->section).ripple
: (button->section >= _staticCount)
? _custom[button->section - _staticCount].ripple
: _colorAllRipple;
if (!ripple) {
@@ -2941,8 +3452,10 @@ uint64 EmojiListWidget::sectionSetId(int section) const {
|| section < _staticCount
|| (section - _staticCount) < _custom.size());
return _searchMode
return (_searchMode && section == 0)
? SearchEmojiSectionSetId()
: (_searchMode && section > 0)
? searchSetBySection(section).id
: (section < _staticCount)
? EmojiSectionSetId(static_cast<Section>(section))
: _custom[section - _staticCount].id;
@@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/round_rect.h"
#include "base/timer.h"
#include <map>
class StickerPremiumMark;
namespace style {
@@ -297,6 +299,18 @@ private:
void setupSearch();
[[nodiscard]] std::vector<EmojiPtr> collectPlainSearchResults();
void appendPremiumSearchResults();
void appendLocalPackSearchResults();
void sendSearchRequest();
void sendSearchSetsRequest();
void cancelSearchRequest();
void toggleSearchLoading(bool loading);
void searchCloudResultsDone(const MTPmessages_FoundStickers &result);
void searchSetsResultsDone(const MTPmessages_FoundStickerSets &result);
void showSearchResults();
void fillCloudSearchResults();
void fillCloudSearchSets();
[[nodiscard]] CustomSet &searchSetBySection(int section);
[[nodiscard]] const CustomSet &searchSetBySection(int section) const;
void ensureLoaded(int section);
void updateSelected();
void setSelected(OverState newSelected);
@@ -344,6 +358,12 @@ private:
QPoint position,
int set,
int index);
void drawSearchSetCustom(
QPainter &p,
const ExpandingContext &context,
QPoint position,
int section,
int index);
void validateEmojiPaintContext(const ExpandingContext &context);
[[nodiscard]] bool hasColorButton(int index) const;
[[nodiscard]] QRect colorButtonRect(int index) const;
@@ -438,7 +458,7 @@ private:
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
std::vector<CustomSet> _custom;
base::flat_set<DocumentId> _restrictedCustomList;
base::flat_map<EmojiStatusId, CustomEmojiInstance> _customEmoji;
std::map<EmojiStatusId, CustomEmojiInstance> _customEmoji;
base::flat_map<
DocumentId,
std::unique_ptr<Ui::Text::CustomEmoji>> _customRecent;
@@ -457,11 +477,21 @@ private:
rpl::event_stream<std::vector<QString>> _searchQueries;
std::vector<QString> _nextSearchQuery;
std::vector<QString> _searchQuery;
QString _searchQueryText;
base::flat_set<EmojiPtr> _searchEmoji;
base::flat_set<EmojiPtr> _searchEmojiPrevious;
base::flat_set<DocumentId> _searchCustomIds;
std::vector<RecentOne> _searchResults;
bool _searchMode = false;
std::map<QString, std::vector<DocumentId>> _searchCloudCache;
std::map<QString, std::vector<uint64>> _searchSetsCache;
std::vector<CustomSet> _searchSets;
QString _searchRequestQuery;
QString _searchNextRequestQuery;
QString _searchEmoticon;
mtpRequestId _searchCloudRequestId = 0;
mtpRequestId _searchSetsRequestId = 0;
bool _searchLoading = false;
int _rowsTop = 0;
int _rowsLeft = 0;
@@ -481,6 +511,7 @@ private:
OverState _pickerSelected;
QPoint _lastMousePos;
base::Timer _searchRequestTimer;
object_ptr<EmojiColorPicker> _picker;
base::Timer _showPickerTimer;
base::Timer _previewTimer;
@@ -592,7 +592,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
bots.emplace(_user, &_user->botInfo->commands);
} else if (_channel && _channel->isMegagroup()) {
if (_channel->mgInfo->bots.empty()) {
if (!_channel->mgInfo->botStatus) {
if (_channel->mgInfo->botStatus == Data::BotStatus::Unknown) {
_channel->session().api().chatParticipants().requestBots(
_channel);
}
@@ -622,7 +622,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
};
};
brows.reserve(cnt);
int32 botStatus = _chat ? _chat->botStatus : ((_channel && _channel->isMegagroup()) ? _channel->mgInfo->botStatus : -1);
const auto botStatus = _chat ? _chat->botStatus : ((_channel && _channel->isMegagroup()) ? _channel->mgInfo->botStatus : Data::BotStatus::NoBots);
if (_chat) {
for (const auto &user : _chat->lastAuthors) {
if (!user->isBot()) {
@@ -634,7 +634,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
}
for (const auto &command : *i->second) {
if (!listAllSuggestions) {
auto toFilter = (hasUsername || botStatus == 0 || botStatus == 2)
auto toFilter = (hasUsername || botStatus != Data::BotStatus::NoBots)
? command.command + '@' + PrimaryUsername(user)
: command.command;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) {
@@ -652,8 +652,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
for (const auto &command : *i->second) {
if (!listAllSuggestions) {
const auto toFilter = (hasUsername
|| botStatus == 0
|| botStatus == 2)
|| botStatus != Data::BotStatus::NoBots)
? command.command + '@' + PrimaryUsername(user)
: command.command;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue;
@@ -1138,8 +1137,8 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
}
auto toHighlight = row.command;
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (hasUsername || botStatus == 0 || botStatus == 2) {
const auto botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : Data::BotStatus::NoBots);
if (hasUsername || botStatus != Data::BotStatus::NoBots) {
toHighlight += '@' + PrimaryUsername(user);
}
user->loadUserpic();
@@ -1307,10 +1306,9 @@ bool FieldAutocomplete::Inner::chooseAtIndex(
? _parent->chat()->botStatus
: ((_parent->channel() && _parent->channel()->isMegagroup())
? _parent->channel()->mgInfo->botStatus
: -1);
: Data::BotStatus::NoBots);
const auto insertUsername = (botStatus == 0
|| botStatus == 2
const auto insertUsername = (botStatus != Data::BotStatus::NoBots
|| _parent->filter().indexOf('@') > 0);
const auto commandString = QString("/%1%2").arg(
command,
@@ -158,7 +158,8 @@ void EditLinkBox(
st::markdownLinkFieldPadding);
text->setInstantReplaces(Ui::InstantReplaces::Default());
text->setInstantReplacesEnabled(
Core::App().settings().replaceEmojiValue());
Core::App().settings().replaceEmojiValue(),
Core::App().settings().systemTextReplaceValue());
Ui::Emoji::SuggestionsController::Init(
box->getDelegate()->outerContainer(),
text,
@@ -499,7 +500,8 @@ Fn<void(QString now, Fn<void(QString)> save)> DefaultEditLanguageCallback(
};
}
void InitMessageFieldHandlers(MessageFieldHandlersArgs &&args) {
auto InitMessageFieldHandlers(MessageFieldHandlersArgs &&args)
-> std::shared_ptr<Ui::ChatStyle> {
const auto paused = [passed = args.customEmojiPaused] {
return passed && passed();
};
@@ -516,7 +518,8 @@ void InitMessageFieldHandlers(MessageFieldHandlersArgs &&args) {
});
field->setInstantReplaces(Ui::InstantReplaces::Default());
field->setInstantReplacesEnabled(
Core::App().settings().replaceEmojiValue());
Core::App().settings().replaceEmojiValue(),
Core::App().settings().systemTextReplaceValue());
field->setMarkdownReplacesEnabled(rpl::single(Ui::MarkdownEnabledState{
Ui::MarkdownEnabled{ std::move(args.allowMarkdownTags) }
}));
@@ -526,7 +529,7 @@ void InitMessageFieldHandlers(MessageFieldHandlersArgs &&args) {
field->setEditLanguageCallback(DefaultEditLanguageCallback(show));
InitSpellchecker(show, field, args.fieldStyle != nullptr);
}
const auto style = field->lifetime().make_state<Ui::ChatStyle>(
const auto style = std::make_shared<Ui::ChatStyle>(
session->colorIndicesValue());
field->setPreCache([=] {
return style->messageStyle(false, false).preCache.get();
@@ -535,6 +538,7 @@ void InitMessageFieldHandlers(MessageFieldHandlersArgs &&args) {
const auto colorIndex = session->user()->colorIndex();
return style->coloredQuoteCache(false, colorIndex).get();
});
return style;
}
[[nodiscard]] bool IsGoodFactcheckUrl(QStringView url) {
@@ -606,7 +610,8 @@ Fn<void(not_null<Ui::InputField*>)> FactcheckFieldIniter(
});
field->setInstantReplaces(Ui::InstantReplaces::Default());
field->setInstantReplacesEnabled(
Core::App().settings().replaceEmojiValue());
Core::App().settings().replaceEmojiValue(),
Core::App().settings().systemTextReplaceValue());
field->setMarkdownReplacesEnabled(rpl::single(
Ui::MarkdownEnabledState{
Ui::MarkdownEnabled{
@@ -645,11 +650,11 @@ void InitMessageFieldGeometry(not_null<Ui::InputField*> field) {
field->setAdditionalMargin(style::ConvertScale(4) - 4);
}
void InitMessageField(
std::shared_ptr<Ui::ChatStyle> InitMessageField(
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::InputField*> field,
Fn<bool(not_null<DocumentData*>)> allowPremiumEmoji) {
InitMessageFieldHandlers({
const auto style = InitMessageFieldHandlers({
.session = &show->session(),
.show = show,
.field = field,
@@ -659,9 +664,10 @@ void InitMessageField(
.allowPremiumEmoji = std::move(allowPremiumEmoji),
});
InitMessageFieldGeometry(field);
return style;
}
void InitMessageField(
std::shared_ptr<Ui::ChatStyle> InitMessageField(
not_null<Window::SessionController*> controller,
not_null<Ui::InputField*> field,
Fn<bool(not_null<DocumentData*>)> allowPremiumEmoji) {
@@ -42,6 +42,7 @@ struct WriteRestriction;
} // namespace HistoryView::Controls
namespace Ui {
class ChatStyle;
class GenericBox;
class PopupMenu;
class Show;
@@ -73,18 +74,19 @@ struct MessageFieldHandlersArgs {
const style::InputField *fieldStyle = nullptr;
base::flat_set<QString> allowMarkdownTags;
};
void InitMessageFieldHandlers(MessageFieldHandlersArgs &&args);
auto InitMessageFieldHandlers(MessageFieldHandlersArgs &&args)
-> std::shared_ptr<Ui::ChatStyle>;
void InitMessageFieldHandlers(
not_null<Window::SessionController*> controller,
not_null<Ui::InputField*> field,
ChatHelpers::PauseReason pauseReasonLevel,
Fn<bool(not_null<DocumentData*>)> allowPremiumEmoji = nullptr);
void InitMessageField(
std::shared_ptr<Ui::ChatStyle> InitMessageField(
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::InputField*> field,
Fn<bool(not_null<DocumentData*>)> allowPremiumEmoji);
void InitMessageField(
std::shared_ptr<Ui::ChatStyle> InitMessageField(
not_null<Window::SessionController*> controller,
not_null<Ui::InputField*> field,
Fn<bool(not_null<DocumentData*>)> allowPremiumEmoji);
@@ -72,7 +72,8 @@ TabbedPanel::TabbedPanel(
: _ownedSelector.data())
, _heightRatio(st::emojiPanHeightRatio)
, _minContentHeight(st::emojiPanMinHeight)
, _maxContentHeight(st::emojiPanMaxHeight) {
, _maxContentHeight(st::emojiPanMaxHeight)
, _shadow(_selector->st().showAnimation.shadow) {
Expects(_selector != nullptr);
_selector->setParent(this);
@@ -255,7 +256,7 @@ void TabbedPanel::paintEvent(QPaintEvent *e) {
hideFinished();
} else {
if (!_cache.isNull()) _cache = QPixmap();
Ui::Shadow::paint(p, innerRect(), width(), _selector->st().showAnimation.shadow);
_shadow.paint(p, innerRect(), st::emojiPanRadius);
}
}
@@ -391,7 +392,8 @@ void TabbedPanel::startShowAnimation() {
std::move(image),
QRect(
inner.topLeft() * style::DevicePixelRatio(),
inner.size() * style::DevicePixelRatio()));
inner.size() * style::DevicePixelRatio()),
st::emojiPanRadius);
_showAnimation->setCornerMasks(Images::CornersMask(st::emojiPanRadius));
_showAnimation->start();
}
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/rp_widget.h"
#include "ui/widgets/shadow.h"
#include "base/timer.h"
#include "base/object_ptr.h"
@@ -124,6 +125,7 @@ private:
bool _hiding = false;
bool _hideAfterSlide = false;
Ui::BoxShadow _shadow;
QPixmap _cache;
Ui::Animations::Simple _a_opacity;
base::Timer _hideTimer;