mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Merge tag 'v6.7.2' into dev
This commit is contained in:
@@ -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 §ion : _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;
|
||||
|
||||
Reference in New Issue
Block a user