diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 0042cc86e..385b58a41 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1802,6 +1802,7 @@ endif()
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
include(GNUInstallDirs)
+ configure_file("../lib/xdg/org.telegram.desktop.service" "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.service" @ONLY)
configure_file("../lib/xdg/org.ayugram.desktop.metainfo.xml" "${CMAKE_CURRENT_BINARY_DIR}/org.ayugram.desktop.metainfo.xml" @ONLY)
generate_appdata_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/org.ayugram.desktop.metainfo.xml")
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
@@ -1813,6 +1814,6 @@ if (LINUX AND DESKTOP_APP_USE_PACKAGED)
install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png")
install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png")
install(FILES "../lib/xdg/org.ayugram.desktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
- install(FILES "../lib/xdg/org.ayugram.desktop.service" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/dbus-1/services")
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.ayugram.desktop.service" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/dbus-1/services")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.ayugram.desktop.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo")
endif()
diff --git a/Telegram/Resources/day-blue.tdesktop-theme b/Telegram/Resources/day-blue.tdesktop-theme
index 04b07154d..54d4dfa29 100644
Binary files a/Telegram/Resources/day-blue.tdesktop-theme and b/Telegram/Resources/day-blue.tdesktop-theme differ
diff --git a/Telegram/Resources/day-custom-base.tdesktop-theme b/Telegram/Resources/day-custom-base.tdesktop-theme
index 36f91ef02..d10f68c75 100644
Binary files a/Telegram/Resources/day-custom-base.tdesktop-theme and b/Telegram/Resources/day-custom-base.tdesktop-theme differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 5a0e5e847..b0260f9dc 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -433,7 +433,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channel_usernames_deactivate_description" = "Do you want to hide this link from the channel info page?";
"lng_channel_usernames_description" = "Drag and drop links to change the order in which they will be displayed on the channel info page.";
-"lng_bot_username_title" = "Username";
+"lng_bot_username_title" = "Public Link";
"lng_bot_username_description1" = "This link cannot be edited. You can acquire additional usernames on {link}.";
"lng_bot_username_description1_link" = "Fragment";
"lng_bot_usernames_activate_description" = "Do you want to show this link on the bot info page?";
@@ -1143,6 +1143,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_shared_links#other" = "{count} shared links";
"lng_profile_copy_phone" = "Copy Phone Number";
"lng_profile_copy_fullname" = "Copy Name";
+"lng_profile_photo_by_you" = "photo set by you";
+"lng_profile_public_photo" = "public photo";
"lng_via_link_group_one" = "**{user}** restricts adding them to groups.\nYou can send them an invite link as message instead.";
"lng_via_link_group_many#one" = "**{count} user** restricts adding them to groups.\nYou can send them an invite link as message instead.";
@@ -1710,6 +1712,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_forwarded" = "Forwarded from {user}";
"lng_forwarded_story" = "Story from {user}";
+"lng_forwarded_story_expired" = "This story has expired.";
"lng_forwarded_date" = "Original: {date}";
"lng_forwarded_channel" = "Forwarded from {channel}";
"lng_forwarded_psa_default" = "Forwarded from {channel}";
@@ -1923,6 +1926,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_switch_gifs" = "GIFs";
"lng_switch_masks" = "Masks";
"lng_stickers_featured_add" = "Add";
+"lng_stickers_featured_installed" = "Added";
"lng_emoji_featured_unlock" = "Unlock";
"lng_emoji_premium_restore" = "Restore";
"lng_gifs_search" = "Search GIFs";
@@ -3583,6 +3587,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_filters_restore" = "Undo";
"lng_filters_new" = "New Folder";
"lng_filters_edit" = "Edit Folder";
+"lng_filters_setup_menu" = "Edit Folders";
"lng_filters_new_name" = "Folder name";
"lng_filters_add_chats" = "Add chats";
"lng_filters_remove_chats" = "Remove chats";
@@ -3832,6 +3837,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_no_views" = "No views";
"lng_stories_unsupported" = "This story is not supported\nby your version of Telegram.";
"lng_stories_cant_reply" = "You can't reply to this story.";
+"lng_stories_about_silent" = "This video has no sound.";
+"lng_stories_about_close_friends" = "You're seeing this story because {user} added you to their list of **Close Friends**.";
+"lng_stories_about_contacts" = "Only {user}'s contacts can view this story.";
+"lng_stories_about_selected_contacts" = "Only some contacts {user} selected can view this story.";
+"lng_stories_about_close_friends_my" = "Only your list of **Close Friends** can view this story.";
+"lng_stories_about_contacts_my" = "Only your contacts can view this story.";
+"lng_stories_about_selected_contacts_my" = "Only some contacts you selected can view this story.";
+"lng_stories_click_to_view_mine" = "Click here to view your story.";
+"lng_stories_click_to_view" = "Click here to view updates from {users}.";
+"lng_stories_click_to_view_and_one" = "{accumulated}, {user}";
+"lng_stories_click_to_view_and_last" = "{accumulated} and {user}";
+"lng_stories_show_more" = "Show more";
"lng_stories_my_title" = "Saved Stories";
"lng_stories_archive_button" = "Stories Archive";
diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index 898e980a2..9e1d43480 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -10,7 +10,7 @@
+ Version="4.8.10.0" />
Telegram Desktop
Telegram Messenger LLP
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index 68328ffe8..e1fcc6baf 100644
--- a/Telegram/Resources/winrc/Telegram.rc
+++ b/Telegram/Resources/winrc/Telegram.rc
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,8,7,0
- PRODUCTVERSION 4,8,7,0
+ FILEVERSION 4,8,10,0
+ PRODUCTVERSION 4,8,10,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop"
- VALUE "FileVersion", "4.8.7.0"
+ VALUE "FileVersion", "4.8.10.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop"
- VALUE "ProductVersion", "4.8.7.0"
+ VALUE "ProductVersion", "4.8.10.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc
index 86b7ee28f..1cc8fed3c 100644
--- a/Telegram/Resources/winrc/Updater.rc
+++ b/Telegram/Resources/winrc/Updater.rc
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,8,7,0
- PRODUCTVERSION 4,8,7,0
+ FILEVERSION 4,8,10,0
+ PRODUCTVERSION 4,8,10,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater"
- VALUE "FileVersion", "4.8.7.0"
+ VALUE "FileVersion", "4.8.10.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop"
- VALUE "ProductVersion", "4.8.7.0"
+ VALUE "ProductVersion", "4.8.10.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
index 199ee4cad..6720339c2 100644
--- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp
+++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
@@ -156,14 +156,15 @@ void GiftBox(
// List.
const auto group = std::make_shared();
- group->setChangedCallback([=](int value) {
+ const auto groupValueChangedCallback = [=](int value) {
Expects(value < options.size() && value >= 0);
auto text = tr::lng_premium_gift_button(
tr::now,
lt_cost,
options[value].costTotal);
state->buttonText.fire(std::move(text));
- });
+ };
+ group->setChangedCallback(groupValueChangedCallback);
Ui::Premium::AddGiftOptions(
buttonsParent,
group,
@@ -215,7 +216,7 @@ void GiftBox(
});
box->addButton(std::move(button));
- group->setValue(0);
+ groupValueChangedCallback(0);
Data::PeerPremiumValue(
user
diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
index 5831ec0b5..335531fc9 100644
--- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
+++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
@@ -59,7 +59,20 @@ constexpr auto kSearchPerPage = 50;
object_ptr PrepareContactsBox(
not_null sessionController) {
using Mode = ContactsBoxController::SortMode;
- auto controller = std::make_unique(
+ class Controller final : public ContactsBoxController {
+ public:
+ using ContactsBoxController::ContactsBoxController;
+
+ protected:
+ std::unique_ptr createRow(
+ not_null user) override {
+ return !user->isSelf()
+ ? ContactsBoxController::createRow(user)
+ : nullptr;
+ }
+
+ };
+ auto controller = std::make_unique(
&sessionController->session());
controller->setStyleOverrides(&st::contactsWithStories);
controller->setStoriesShown(true);
diff --git a/Telegram/SourceFiles/boxes/peers/peer_short_info_box.cpp b/Telegram/SourceFiles/boxes/peers/peer_short_info_box.cpp
index 1bf236db1..bc07210f6 100644
--- a/Telegram/SourceFiles/boxes/peers/peer_short_info_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/peer_short_info_box.cpp
@@ -105,6 +105,7 @@ PeerShortInfoCover::PeerShortInfoCover(
userpic
) | rpl::start_with_next([=](PeerShortInfoUserpic &&value) {
applyUserpic(std::move(value));
+ applyAdditionalStatus(value.additionalStatus);
}, lifetime());
style::PaletteChanged(
@@ -136,16 +137,7 @@ PeerShortInfoCover::PeerShortInfoCover(
return base::EventFilterResult::Cancel;
});
- _name->moveToLeft(
- _st.namePosition.x(),
- _st.size - _st.namePosition.y() - _name->height(),
- _st.size);
- _status->moveToLeft(
- _st.statusPosition.x(),
- (_st.size
- - _st.statusPosition.y()
- - _status->height()),
- _st.size);
+ refreshLabelsGeometry();
_roundedTopImage = QImage(
QSize(_st.size, _st.radius) * style::DevicePixelRatio(),
@@ -415,6 +407,23 @@ QImage PeerShortInfoCover::currentVideoFrame() const {
: QImage();
}
+void PeerShortInfoCover::applyAdditionalStatus(const QString &status) {
+ if (status.isEmpty()) {
+ if (_additionalStatus) {
+ _additionalStatus.destroy();
+ refreshLabelsGeometry();
+ }
+ return;
+ }
+ if (_additionalStatus) {
+ _additionalStatus->setText(status);
+ } else {
+ _additionalStatus.create(_widget.get(), status, _statusStyle->st);
+ _additionalStatus->show();
+ refreshLabelsGeometry();
+ }
+}
+
void PeerShortInfoCover::applyUserpic(PeerShortInfoUserpic &&value) {
if (_index != value.index) {
_index = value.index;
@@ -593,6 +602,28 @@ void PeerShortInfoCover::refreshBarImages() {
_barLarge = makeBar(_largeWidth);
}
+void PeerShortInfoCover::refreshLabelsGeometry() {
+ const auto statusTop = _st.size
+ - _st.statusPosition.y()
+ - _status->height();
+ const auto diff = _st.namePosition.y()
+ - _name->height()
+ - _st.statusPosition.y();
+ if (_additionalStatus) {
+ _additionalStatus->moveToLeft(
+ _status->x(),
+ statusTop - diff - _additionalStatus->height());
+ }
+ _name->moveToLeft(
+ _st.namePosition.x(),
+ _st.size
+ - _st.namePosition.y()
+ - _name->height()
+ - (_additionalStatus ? (diff + _additionalStatus->height()) : 0),
+ _st.size);
+ _status->moveToLeft(_st.statusPosition.x(), statusTop, _st.size);
+}
+
QRect PeerShortInfoCover::radialRect() const {
const auto cover = _widget->rect();
const auto size = st::boxLoadingSize;
@@ -654,12 +685,16 @@ rpl::producer PeerShortInfoBox::moveRequests() const {
void PeerShortInfoBox::prepare() {
addButton(tr::lng_close(), [=] { closeBox(); });
- // Perhaps a new lang key should be added for opening a group.
- addLeftButton((_type == PeerShortInfoType::User)
- ? tr::lng_profile_send_message()
- : (_type == PeerShortInfoType::Group)
- ? tr::lng_view_button_group()
- : tr::lng_profile_view_channel(), [=] { _openRequests.fire({}); });
+ if (_type != PeerShortInfoType::Self) {
+ // Perhaps a new lang key should be added for opening a group.
+ addLeftButton(
+ (_type == PeerShortInfoType::User)
+ ? tr::lng_profile_send_message()
+ : (_type == PeerShortInfoType::Group)
+ ? tr::lng_view_button_group()
+ : tr::lng_profile_view_channel(),
+ [=] { _openRequests.fire({}); });
+ }
prepareRows();
diff --git a/Telegram/SourceFiles/boxes/peers/peer_short_info_box.h b/Telegram/SourceFiles/boxes/peers/peer_short_info_box.h
index 07e60132a..bf59ff7aa 100644
--- a/Telegram/SourceFiles/boxes/peers/peer_short_info_box.h
+++ b/Telegram/SourceFiles/boxes/peers/peer_short_info_box.h
@@ -28,6 +28,7 @@ class RpWidget;
} // namespace Ui
enum class PeerShortInfoType {
+ Self,
User,
Group,
Channel,
@@ -50,6 +51,7 @@ struct PeerShortInfoUserpic {
float64 photoLoadingProgress = 0.;
std::shared_ptr videoDocument;
crl::time videoStartPosition = 0;
+ QString additionalStatus;
};
class PeerShortInfoCover final {
@@ -87,6 +89,7 @@ private:
[[nodiscard]] QImage currentVideoFrame() const;
void applyUserpic(PeerShortInfoUserpic &&value);
+ void applyAdditionalStatus(const QString &status);
[[nodiscard]] QRect radialRect() const;
void videoWaiting();
@@ -99,6 +102,7 @@ private:
void updateRadialState();
void refreshCoverCursor();
void refreshBarImages();
+ void refreshLabelsGeometry();
const style::ShortInfoCover &_st;
@@ -108,6 +112,7 @@ private:
object_ptr _name;
std::unique_ptr _statusStyle;
object_ptr _status;
+ object_ptr _additionalStatus = { nullptr };
std::array _roundMask;
QImage _userpicImage;
diff --git a/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp b/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp
index 8baf378b5..d71645316 100644
--- a/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp
@@ -335,6 +335,15 @@ bool ProcessCurrent(
: state->photoView
? state->photoView->owner().get()
: nullptr;
+ state->current.additionalStatus = (!peer->isUser())
+ ? QString()
+ : ((state->photoId == userpicPhotoId)
+ && peer->asUser()->hasPersonalPhoto())
+ ? tr::lng_profile_photo_by_you(tr::now)
+ : ((state->current.index == (state->current.count - 1))
+ && SyncUserFallbackPhotoViewer(peer->asUser()))
+ ? tr::lng_profile_public_photo(tr::now)
+ : QString();
state->waitingLoad = false;
if (!changedPhotoId
&& (state->current.index > 0 || !changedUserpic)
@@ -422,7 +431,9 @@ object_ptr PrepareShortInfoBox(
Fn open,
Fn videoPaused,
const style::ShortInfoBox *stOverride) {
- const auto type = peer->isUser()
+ const auto type = peer->isSelf()
+ ? PeerShortInfoType::Self
+ : peer->isUser()
? PeerShortInfoType::User
: peer->isBroadcast()
? PeerShortInfoType::Channel
diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp
index 29a16c78e..32295cf08 100644
--- a/Telegram/SourceFiles/boxes/stickers_box.cpp
+++ b/Telegram/SourceFiles/boxes/stickers_box.cpp
@@ -211,7 +211,7 @@ private:
void setActionDown(int newActionDown);
void setPressed(SelectedRow pressed);
void setup();
- QRect relativeButtonRect(bool removeButton) const;
+ QRect relativeButtonRect(bool removeButton, bool installedSet) const;
void ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton);
bool shiftingAnimationCallback(crl::time now);
@@ -234,7 +234,7 @@ private:
void readVisibleSets();
void updateControlsGeometry();
- void rebuildAppendSet(not_null set, int maxNameWidth);
+ void rebuildAppendSet(not_null set);
void fillSetCover(not_null set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
int fillSetCount(not_null set) const;
[[nodiscard]] QString fillSetTitle(
@@ -247,7 +247,7 @@ private:
void handleMegagroupSetAddressChange();
void setMegagroupSelectedSet(const StickerSetIdentifier &set);
- int countMaxNameWidth() const;
+ int countMaxNameWidth(bool installedSet) const;
[[nodiscard]] bool skipPremium() const;
const std::shared_ptr _show;
@@ -255,9 +255,9 @@ private:
MTP::Sender _api;
const Section _section;
- const bool _isInstalled;
+ const bool _isInstalledTab;
- Ui::RoundRect _buttonBgOver, _buttonBg;
+ Ui::RoundRect _buttonBgOver, _buttonBg, _inactiveButtonBg;
int32 _rowHeight = 0;
@@ -282,6 +282,8 @@ private:
int _addWidth = 0;
QString _undoText;
int _undoWidth = 0;
+ QString _installedText;
+ int _installedWidth = 0;
QPoint _mouse;
bool _inDragArea = false;
@@ -678,19 +680,19 @@ void StickersBox::refreshTabs() {
_tabIndices.clear();
auto sections = std::vector();
if (_installed.widget()) {
- sections.push_back(tr::lng_stickers_installed_tab(tr::now).toUpper());
+ sections.push_back(tr::lng_stickers_installed_tab(tr::now));
_tabIndices.push_back(Section::Installed);
}
if (_masks.widget()) {
- sections.push_back(tr::lng_stickers_masks_tab(tr::now).toUpper());
+ sections.push_back(tr::lng_stickers_masks_tab(tr::now));
_tabIndices.push_back(Section::Masks);
}
if (!stickers.featuredSetsOrder().isEmpty() && _featured.widget()) {
- sections.push_back(tr::lng_stickers_featured_tab(tr::now).toUpper());
+ sections.push_back(tr::lng_stickers_featured_tab(tr::now));
_tabIndices.push_back(Section::Featured);
}
if (!archivedSetsOrder().isEmpty() && _archived.widget()) {
- sections.push_back(tr::lng_stickers_archived_tab(tr::now).toUpper());
+ sections.push_back(tr::lng_stickers_archived_tab(tr::now));
_tabIndices.push_back(Section::Archived);
}
_tabs->setSections(sections);
@@ -771,7 +773,7 @@ void StickersBox::updateTabsGeometry() {
auto featuredLeft = width() / maxTabs;
auto featuredRight = 2 * width() / maxTabs;
- auto featuredTextWidth = st::stickersTabs.labelStyle.font->width(tr::lng_stickers_featured_tab(tr::now).toUpper());
+ auto featuredTextWidth = st::stickersTabs.labelStyle.font->width(tr::lng_stickers_featured_tab(tr::now));
auto featuredTextRight = featuredLeft + (featuredRight - featuredLeft - featuredTextWidth) / 2 + featuredTextWidth;
auto unreadBadgeLeft = featuredTextRight - st::stickersFeaturedBadgeSkip;
auto unreadBadgeTop = st::stickersFeaturedBadgeTop;
@@ -1133,26 +1135,32 @@ StickersBox::Inner::Inner(
, _session(&_show->session())
, _api(&_session->mtp())
, _section(section)
-, _isInstalled(_section == Section::Installed || _section == Section::Masks)
+, _isInstalledTab(_section == Section::Installed
+ || _section == Section::Masks)
, _buttonBgOver(
- ImageRoundRadius::Small,
- (_isInstalled
+ ImageRoundRadius::Large,
+ (_isInstalledTab
? st::stickersUndoRemove
: st::stickersTrendingAdd).textBgOver)
, _buttonBg(
- ImageRoundRadius::Small,
- (_isInstalled
+ ImageRoundRadius::Large,
+ (_isInstalledTab
? st::stickersUndoRemove
: st::stickersTrendingAdd).textBg)
+, _inactiveButtonBg(
+ ImageRoundRadius::Large,
+ st::stickersTrendingInstalled.textBg)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _shiftingAnimation([=](crl::time now) {
return shiftingAnimationCallback(now);
})
, _itemsTop(st::membersMarginTop)
-, _addText(tr::lng_stickers_featured_add(tr::now).toUpper())
+, _addText(tr::lng_stickers_featured_add(tr::now))
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
-, _undoText(tr::lng_stickers_return(tr::now).toUpper())
-, _undoWidth(st::stickersUndoRemove.font->width(_undoText)) {
+, _undoText(tr::lng_stickers_return(tr::now))
+, _undoWidth(st::stickersUndoRemove.font->width(_undoText))
+, _installedText(tr::lng_stickers_featured_installed(tr::now))
+, _installedWidth(st::stickersTrendingInstalled.font->width(_installedText)) {
setup();
}
@@ -1165,17 +1173,21 @@ StickersBox::Inner::Inner(
, _session(&_show->session())
, _api(&_session->mtp())
, _section(StickersBox::Section::Installed)
-, _isInstalled(_section == Section::Installed || _section == Section::Masks)
+, _isInstalledTab(_section == Section::Installed
+ || _section == Section::Masks)
, _buttonBgOver(
- ImageRoundRadius::Small,
- (_isInstalled
+ ImageRoundRadius::Large,
+ (_isInstalledTab
? st::stickersUndoRemove
: st::stickersTrendingAdd).textBgOver)
, _buttonBg(
- ImageRoundRadius::Small,
- (_isInstalled
+ ImageRoundRadius::Large,
+ (_isInstalledTab
? st::stickersUndoRemove
: st::stickersTrendingAdd).textBg)
+, _inactiveButtonBg(
+ ImageRoundRadius::Large,
+ st::stickersTrendingInstalled.textBg)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _shiftingAnimation([=](crl::time now) {
return shiftingAnimationCallback(now);
@@ -1295,15 +1307,23 @@ void StickersBox::Inner::updateControlsGeometry() {
}
}
-QRect StickersBox::Inner::relativeButtonRect(bool removeButton) const {
+QRect StickersBox::Inner::relativeButtonRect(
+ bool removeButton,
+ bool installedSet) const {
auto buttonw = st::stickersRemove.width;
auto buttonh = st::stickersRemove.height;
auto buttonshift = st::stickersRemoveSkip;
if (!removeButton) {
- const auto &st = _isInstalled
+ const auto &st = installedSet
+ ? st::stickersTrendingInstalled
+ : _isInstalledTab
? st::stickersUndoRemove
: st::stickersTrendingAdd;
- const auto textWidth = _isInstalled ? _undoWidth : _addWidth;
+ const auto textWidth = installedSet
+ ? _installedWidth
+ : _isInstalledTab
+ ? _undoWidth
+ : _addWidth;
buttonw = textWidth - st.width;
buttonh = st.height;
buttonshift = 0;
@@ -1332,7 +1352,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null row, int index) {
}
}
- if (_isInstalled) {
+ if (_isInstalledTab) {
if (index >= 0 && index == _above) {
auto current = _aboveShadowFadeOpacity.current();
if (_started >= 0) {
@@ -1359,13 +1379,13 @@ void StickersBox::Inner::paintRow(Painter &p, not_null row, int index) {
paintFakeButton(p, row, index);
}
- if (row->removed && _isInstalled) {
+ if (row->removed && _isInstalledTab) {
p.setOpacity(st::stickersRowDisabledOpacity);
}
auto stickerx = st::contactsPadding.left();
- if (!_megagroupSet && _isInstalled) {
+ if (!_megagroupSet && _isInstalledTab) {
stickerx += st::stickersReorderIcon.width() + st::stickersReorderSkip;
if (!row->isRecentSet()) {
st::stickersReorderIcon.paint(p, st::contactsPadding.left(), (_rowHeight - st::stickersReorderIcon.height()) / 2, width());
@@ -1553,7 +1573,7 @@ void StickersBox::Inner::updateRowThumbnail(not_null row) {
Unexpected("StickersBox::Inner::updateRowThumbnail: row not found");
}();
const auto left = st::contactsPadding.left()
- + ((!_megagroupSet && _isInstalled)
+ + ((!_megagroupSet && _isInstalledTab)
? st::stickersReorderIcon.width() + st::stickersReorderSkip
: 0);
update(
@@ -1564,14 +1584,19 @@ void StickersBox::Inner::updateRowThumbnail(not_null row) {
}
void StickersBox::Inner::paintFakeButton(Painter &p, not_null row, int index) {
- auto removeButton = (_isInstalled && !row->removed);
- auto rect = relativeButtonRect(removeButton);
- if (!_isInstalled && row->isInstalled() && !row->isArchived() && !row->removed) {
- // Checkbox after installed from Trending or Archived.
- int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (rect.width() + st::stickersFeaturedInstalled.width()) / 2);
- int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2;
- st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width());
+ const auto removeButton = (_isInstalledTab && !row->removed);
+ if (!_isInstalledTab && row->isInstalled() && !row->isArchived() && !row->removed) {
+ // Round button "Added" after installed from Trending or Archived.
+ const auto rect = relativeButtonRect(removeButton, true);
+ const auto &st = st::stickersTrendingInstalled;
+ const auto textWidth = _installedWidth;
+ const auto &text = _installedText;
+ _inactiveButtonBg.paint(p, myrtlrect(rect));
+ p.setFont(st.font);
+ p.setPen(st.textFg);
+ p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
} else {
+ const auto rect = relativeButtonRect(removeButton, false);
auto selected = (index == _actionSel && _actionDown < 0) || (index == _actionDown);
if (removeButton) {
// Trash icon button when not disabled in Installed.
@@ -1589,11 +1614,11 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null row, int ind
} else {
// Round button ADD when not installed from Trending or Archived.
// Or round button UNDO after disabled from Installed.
- const auto &st = _isInstalled
+ const auto &st = _isInstalledTab
? st::stickersUndoRemove
: st::stickersTrendingAdd;
- const auto textWidth = _isInstalled ? _undoWidth : _addWidth;
- const auto &text = _isInstalled ? _undoText : _addText;
+ const auto textWidth = _isInstalledTab ? _undoWidth : _addWidth;
+ const auto &text = _isInstalledTab ? _undoText : _addText;
(selected ? _buttonBgOver : _buttonBg).paint(p, myrtlrect(rect));
if (row->ripple) {
row->ripple->paint(p, rect.x(), rect.y(), width());
@@ -1618,7 +1643,7 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) {
setActionDown(_actionSel);
update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
} else if (auto selectedIndex = std::get_if(&_selected)) {
- if (_isInstalled && !_rows[*selectedIndex]->isRecentSet() && _inDragArea) {
+ if (_isInstalledTab && !_rows[*selectedIndex]->isRecentSet() && _inDragArea) {
_above = _dragging = _started = *selectedIndex;
_dragStart = mapFromGlobal(_mouse);
}
@@ -1640,12 +1665,12 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
if (_actionDown >= 0 && _actionDown < _rows.size()) {
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
const auto row = _rows[_actionDown].get();
- auto removeButton = (_isInstalled && !row->removed);
+ auto removeButton = (_isInstalledTab && !row->removed);
if (!row->ripple) {
- if (_isInstalled) {
+ if (_isInstalledTab) {
if (row->removed) {
auto rippleSize = QSize(_undoWidth - st::stickersUndoRemove.width, st::stickersUndoRemove.height);
- auto rippleMask = Ui::RippleAnimation::RoundRectMask(rippleSize, st::roundRadiusSmall);
+ auto rippleMask = Ui::RippleAnimation::RoundRectMask(rippleSize, st::roundRadiusLarge);
ensureRipple(st::stickersUndoRemove.ripple, std::move(rippleMask), removeButton);
} else {
auto rippleSize = st::stickersRemove.rippleAreaSize;
@@ -1654,12 +1679,12 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
}
} else if (!row->isInstalled() || row->isArchived() || row->removed) {
auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
- auto rippleMask = Ui::RippleAnimation::RoundRectMask(rippleSize, st::roundRadiusSmall);
+ auto rippleMask = Ui::RippleAnimation::RoundRectMask(rippleSize, st::roundRadiusLarge);
ensureRipple(st::stickersTrendingAdd.ripple, std::move(rippleMask), removeButton);
}
}
if (row->ripple) {
- auto rect = relativeButtonRect(removeButton);
+ auto rect = relativeButtonRect(removeButton, false);
row->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(myrtlrect(rect).x(), _itemsTop + _actionDown * _rowHeight + rect.y()));
}
}
@@ -1722,7 +1747,7 @@ void StickersBox::Inner::setPressed(SelectedRow pressed) {
void StickersBox::Inner::ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton) {
_rows[_actionDown]->ripple = std::make_unique(st, std::move(mask), [this, index = _actionDown, removeButton] {
- update(myrtlrect(relativeButtonRect(removeButton).translated(0, _itemsTop + index * _rowHeight)));
+ update(myrtlrect(relativeButtonRect(removeButton, false).translated(0, _itemsTop + index * _rowHeight)));
});
}
@@ -1785,14 +1810,14 @@ void StickersBox::Inner::updateSelected() {
selected = selectedIndex;
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
const auto row = _rows[selectedIndex].get();
- if (!_megagroupSet && (_isInstalled || !row->isInstalled() || row->isArchived() || row->removed)) {
- auto removeButton = (_isInstalled && !row->removed);
- auto rect = myrtlrect(relativeButtonRect(removeButton));
+ if (!_megagroupSet && (_isInstalledTab || !row->isInstalled() || row->isArchived() || row->removed)) {
+ auto removeButton = (_isInstalledTab && !row->removed);
+ auto rect = myrtlrect(relativeButtonRect(removeButton, false));
actionSel = rect.contains(local) ? selectedIndex : -1;
} else {
actionSel = -1;
}
- if (!_megagroupSet && _isInstalled && !row->isRecentSet()) {
+ if (!_megagroupSet && _isInstalledTab && !row->isRecentSet()) {
auto dragAreaWidth = st::contactsPadding.left() + st::stickersReorderIcon.width() + st::stickersReorderSkip;
auto dragArea = myrtlrect(0, 0, dragAreaWidth, _rowHeight);
inDragArea = dragArea.contains(local);
@@ -1816,7 +1841,7 @@ void StickersBox::Inner::updateSelected() {
void StickersBox::Inner::updateCursor() {
setCursor(_inDragArea
? style::cur_sizeall
- : (!_megagroupSet && _isInstalled)
+ : (!_megagroupSet && _isInstalledTab)
? ((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel))
? style::cur_pointer
: style::cur_default)
@@ -1841,7 +1866,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
_mouse = e->globalPos();
updateSelected();
if (_actionDown == _actionSel && _actionSel >= 0) {
- if (_isInstalled) {
+ if (_isInstalledTab) {
setRowRemoved(_actionDown, !_rows[_actionDown]->removed);
} else if (_installSetCallback) {
_installSetCallback(_rows[_actionDown]->set->id);
@@ -2079,15 +2104,15 @@ void StickersBox::Inner::rebuildMegagroupSet() {
}
const auto set = it->second.get();
- auto maxNameWidth = countMaxNameWidth();
- auto titleWidth = 0;
- auto title = fillSetTitle(set, maxNameWidth, &titleWidth);
auto count = fillSetCount(set);
auto sticker = (DocumentData*)nullptr;
auto pixw = 0, pixh = 0;
fillSetCover(set, &sticker, &pixw, &pixh);
auto flagsOverride = SetFlag::Installed;
auto removed = false;
+ auto maxNameWidth = countMaxNameWidth(!_isInstalledTab);
+ auto titleWidth = 0;
+ auto title = fillSetTitle(set, maxNameWidth, &titleWidth);
if (!_megagroupSelectedSet
|| _megagroupSelectedSet->set->id != set->id) {
_megagroupSetField->setText(set->shortName);
@@ -2125,8 +2150,6 @@ void StickersBox::Inner::rebuild(bool masks) {
rebuildMegagroupSet();
}
- auto maxNameWidth = countMaxNameWidth();
-
_oldRows = std::move(_rows);
clear();
const auto &order = ([&]() -> const StickersSetsOrder & {
@@ -2155,12 +2178,12 @@ void StickersBox::Inner::rebuild(bool masks) {
? tr::lng_stickers_group_from_featured(tr::now)
: tr::lng_stickers_group_from_your(tr::now));
updateControlsGeometry();
- } else if (_isInstalled) {
+ } else if (_isInstalledTab) {
const auto cloudIt = sets.find((_section == Section::Masks)
? Data::Stickers::CloudRecentAttachedSetId
: Data::Stickers::CloudRecentSetId); // Section::Installed.
if (cloudIt != sets.cend() && !cloudIt->second->stickers.isEmpty()) {
- rebuildAppendSet(cloudIt->second.get(), maxNameWidth);
+ rebuildAppendSet(cloudIt->second.get());
}
}
for (const auto setId : order) {
@@ -2170,7 +2193,7 @@ void StickersBox::Inner::rebuild(bool masks) {
}
const auto set = it->second.get();
- rebuildAppendSet(set, maxNameWidth);
+ rebuildAppendSet(set);
if (set->stickers.isEmpty()
|| (set->flags & SetFlag::NotLoaded)) {
@@ -2205,7 +2228,8 @@ void StickersBox::Inner::updateSize(int newWidth) {
}
void StickersBox::Inner::updateRows() {
- int maxNameWidth = countMaxNameWidth();
+ const auto maxNameWidth = countMaxNameWidth(false);
+ const auto maxNameWidthInstalled = countMaxNameWidth(true);
const auto &sets = session().data().stickers().sets();
for (const auto &row : _rows) {
const auto it = sets.find(row->set->id);
@@ -2231,7 +2255,7 @@ void StickersBox::Inner::updateRows() {
auto wasInstalled = row->isInstalled();
auto wasArchived = row->isArchived();
row->flagsOverride = fillSetFlags(set);
- if (_isInstalled) {
+ if (_isInstalledTab) {
row->flagsOverride &= ~SetFlag::Archived;
}
if (row->isInstalled() != wasInstalled
@@ -2239,7 +2263,14 @@ void StickersBox::Inner::updateRows() {
row->ripple.reset();
}
}
- row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth);
+ const auto installedSet = (!_isInstalledTab
+ && row->isInstalled()
+ && !row->isArchived()
+ && !row->removed);
+ row->title = fillSetTitle(
+ set,
+ installedSet ? maxNameWidthInstalled : maxNameWidth,
+ &row->titleWidth);
row->count = fillSetCount(set);
}
update();
@@ -2251,7 +2282,7 @@ bool StickersBox::Inner::appendSet(not_null set) {
return false;
}
}
- rebuildAppendSet(set, countMaxNameWidth());
+ rebuildAppendSet(set);
return true;
}
@@ -2259,18 +2290,20 @@ bool StickersBox::Inner::skipPremium() const {
return !_session->premiumPossible();
}
-int StickersBox::Inner::countMaxNameWidth() const {
+int StickersBox::Inner::countMaxNameWidth(bool installedSet) const {
int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left();
- if (!_megagroupSet && _isInstalled) {
+ if (!_megagroupSet && _isInstalledTab) {
namex += st::stickersReorderIcon.width() + st::stickersReorderSkip;
}
int namew = st::boxWideWidth - namex - st::contactsPadding.right() - st::contactsCheckPosition.x();
- if (_isInstalled) {
+ if (_isInstalledTab) {
if (!_megagroupSet) {
namew -= _undoWidth - st::stickersUndoRemove.width;
}
} else {
- namew -= _addWidth - st::stickersTrendingAdd.width;
+ namew -= installedSet
+ ? (_installedWidth - st::stickersTrendingInstalled.width)
+ : (_addWidth - st::stickersTrendingAdd.width);
if (_section == Section::Featured) {
namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip;
}
@@ -2278,14 +2311,12 @@ int StickersBox::Inner::countMaxNameWidth() const {
return namew;
}
-void StickersBox::Inner::rebuildAppendSet(
- not_null set,
- int maxNameWidth) {
+void StickersBox::Inner::rebuildAppendSet(not_null set) {
auto flagsOverride = (set->id != Data::Stickers::CloudRecentSetId)
? fillSetFlags(set)
: SetFlag::Installed;
auto removed = false;
- if (_isInstalled && (flagsOverride & SetFlag::Archived)) {
+ if (_isInstalledTab && (flagsOverride & SetFlag::Archived)) {
return;
}
@@ -2293,6 +2324,10 @@ void StickersBox::Inner::rebuildAppendSet(
int pixw = 0, pixh = 0;
fillSetCover(set, &sticker, &pixw, &pixh);
+ const auto maxNameWidth = countMaxNameWidth(!_isInstalledTab
+ && (flagsOverride & SetFlag::Installed)
+ && !(flagsOverride & SetFlag::Archived)
+ && !removed);
int titleWidth = 0;
QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
int count = fillSetCount(set);
diff --git a/Telegram/SourceFiles/boxes/username_box.cpp b/Telegram/SourceFiles/boxes/username_box.cpp
index 8e7ccd244..ded98183a 100644
--- a/Telegram/SourceFiles/boxes/username_box.cpp
+++ b/Telegram/SourceFiles/boxes/username_box.cpp
@@ -393,8 +393,12 @@ void UsernamesBox(
editor->submitted(
) | rpl::start_with_next(finish, editor->lifetime());
- box->addButton(tr::lng_settings_save(), finish);
- box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
+ if (isBot) {
+ box->addButton(tr::lng_close(), [=] { box->closeBox(); });
+ } else {
+ box->addButton(tr::lng_settings_save(), finish);
+ box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
+ }
}
void AddUsernameCheckLabel(
diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style
index 28b5f90a6..2791724dd 100644
--- a/Telegram/SourceFiles/calls/calls.style
+++ b/Telegram/SourceFiles/calls/calls.style
@@ -744,6 +744,11 @@ groupCallShareBoxComment: InputField(groupCallField) {
}
groupCallShareBoxList: PeerList(groupCallMembersList) {
item: PeerListItem(groupCallMembersListItem) {
+ nameStyle: TextStyle(defaultTextStyle) {
+ font: font(11px);
+ linkFont: font(11px);
+ linkFontOver: font(11px);
+ }
checkbox: RoundImageCheckbox(groupCallMembersListCheckbox) {
imageRadius: 28px;
imageSmallRadius: 24px;
@@ -1333,13 +1338,7 @@ groupCallNiceTooltip: ImportantTooltip(defaultImportantTooltip) {
radius: 4px;
arrow: 4px;
}
-groupCallNiceTooltipLabel: FlatLabel(defaultImportantTooltipLabel) {
- style: TextStyle(defaultTextStyle) {
- font: font(11px);
- linkFont: font(11px);
- linkFontOver: font(11px underline);
- }
-}
+groupCallNiceTooltipLabel: defaultImportantTooltipLabel;
groupCallStickedTooltip: ImportantTooltip(groupCallNiceTooltip) {
padding: margins(10px, 1px, 6px, 3px);
}
diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp
index 8af7d3f0d..a240ce118 100644
--- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp
+++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp
@@ -2060,6 +2060,7 @@ void Panel::showNiceTooltip(
(normal ? widget().get() : container),
std::move(text),
st::groupCallNiceTooltipLabel);
+ label->resizeToNaturalWidth(label->naturalWidth());
if (normal) {
return label;
}
diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
index 5c72090e0..e791a7a35 100644
--- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style
+++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
@@ -105,7 +105,6 @@ EmojiPan {
trendingHeaderFg: color;
trendingSubheaderFg: color;
trendingUnreadFg: color;
- trendingInstalled: icon;
overBg: color;
pathBg: color;
pathFg: color;
@@ -204,6 +203,7 @@ ComposeControls {
record: RecordBar;
files: ComposeFiles;
premium: PremiumLimits;
+ boxField: InputField;
}
ReportBox {
@@ -287,6 +287,12 @@ stickersTrendingAdd: RoundButton(defaultActiveButton) {
height: 26px;
textTop: 4px;
}
+stickersTrendingInstalled: RoundButton(stickersTrendingAdd) {
+ textFg: activeButtonBg;
+ textFgOver: activeButtonBgOver;
+ textBg: activeButtonSecondaryFg;
+ textBgOver: activeButtonSecondaryFgOver;
+}
stickersRemove: IconButton(defaultIconButton) {
width: 40px;
height: 40px;
@@ -323,7 +329,6 @@ stickersFeaturedUnreadBg: msgFileInBg;
stickersFeaturedUnreadSize: 5px;
stickersFeaturedUnreadSkip: 5px;
stickersFeaturedUnreadTop: 7px;
-stickersFeaturedInstalled: icon {{ "chat/input_save", lightButtonFg }};
stickersMaxHeight: 320px;
stickersPadding: margins(19px, 13px, 19px, 13px);
@@ -590,7 +595,6 @@ defaultEmojiPan: EmojiPan {
trendingHeaderFg: stickersTrendingHeaderFg;
trendingSubheaderFg: stickersTrendingSubheaderFg;
trendingUnreadFg: stickersFeaturedUnreadBg;
- trendingInstalled: stickersFeaturedInstalled;
overBg: emojiPanHover;
pathBg: windowBgRipple;
pathFg: windowBgOver;
@@ -1148,6 +1152,7 @@ defaultComposeControls: ComposeControls {
record: defaultRecordBar;
files: defaultComposeFiles;
premium: defaultPremiumLimits;
+ boxField: defaultInputField;
}
moreChatsBarHeight: 48px;
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
index c1ffda95c..5c5d6c58e 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
@@ -188,7 +188,7 @@ StickersListWidget::StickersListWidget(
, _mode(descriptor.mode)
, _show(std::move(descriptor.show))
, _features(descriptor.features)
-, _overBg(st::roundRadiusSmall, st().overBg)
+, _overBg(st::roundRadiusLarge, st().overBg)
, _api(&session().mtp())
, _localSetsManager(std::make_unique(&session()))
, _section(Section::Stickers)
@@ -196,22 +196,27 @@ StickersListWidget::StickersListWidget(
, _updateItemsTimer([=] { updateItems(); })
, _updateSetsTimer([=] { updateSets(); })
, _trendingAddBgOver(
- ImageRoundRadius::Small,
+ ImageRoundRadius::Large,
st::stickersTrendingAdd.textBgOver)
-, _trendingAddBg(ImageRoundRadius::Small, st::stickersTrendingAdd.textBg)
+, _trendingAddBg(ImageRoundRadius::Large, st::stickersTrendingAdd.textBg)
+, _inactiveButtonBg(
+ ImageRoundRadius::Large,
+ st::stickersTrendingInstalled.textBg)
, _groupCategoryAddBgOver(
- ImageRoundRadius::Small,
+ ImageRoundRadius::Large,
st::stickerGroupCategoryAdd.textBgOver)
, _groupCategoryAddBg(
- ImageRoundRadius::Small,
+ ImageRoundRadius::Large,
st::stickerGroupCategoryAdd.textBg)
, _pathGradient(std::make_unique(
st().pathBg,
st().pathFg,
[=] { update(); }))
, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st().headerLeft)
-, _addText(tr::lng_stickers_featured_add(tr::now).toUpper())
+, _addText(tr::lng_stickers_featured_add(tr::now))
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
+, _installedText(tr::lng_stickers_featured_installed(tr::now))
+, _installedWidth(st::stickersTrendingInstalled.font->width(_installedText))
, _settings(this, tr::lng_stickers_you_have(tr::now))
, _previewTimer([=] { showPreview(); })
, _premiumMark(std::make_unique(&session()))
@@ -898,26 +903,40 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
: loadedCount;
auto widthForTitle = stickersRight() - (st().headerLeft - st().margin.left());
- if (featuredHasAddButton(info.section)) {
- auto add = featuredAddRect(info);
- auto selected = selectedButton ? (selectedButton->section == info.section) : false;
- (selected ? _trendingAddBgOver : _trendingAddBg).paint(p, myrtlrect(add));
+ {
+ const auto installedSet = !featuredHasAddButton(info.section);
+ const auto add = featuredAddRect(info, installedSet);
+ const auto selected = selectedButton
+ ? (selectedButton->section == info.section)
+ : false;
+ (installedSet
+ ? _inactiveButtonBg
+ : selected
+ ? _trendingAddBgOver
+ : _trendingAddBg).paint(p, myrtlrect(add));
if (set.ripple) {
set.ripple->paint(p, add.x(), add.y(), width());
if (set.ripple->empty()) {
set.ripple.reset();
}
}
- p.setFont(st::stickersTrendingAdd.font);
- p.setPen(selected ? st::stickersTrendingAdd.textFgOver : st::stickersTrendingAdd.textFg);
- p.drawTextLeft(add.x() - (st::stickersTrendingAdd.width / 2), add.y() + st::stickersTrendingAdd.textTop, width(), _addText, _addWidth);
+ const auto &text = installedSet ? _installedText : _addText;
+ const auto textWidth = installedSet
+ ? _installedWidth
+ : _addWidth;
+ const auto &st = installedSet
+ ? st::stickersTrendingInstalled
+ : st::stickersTrendingAdd;
+ p.setFont(st.font);
+ p.setPen(selected ? st.textFgOver : st.textFg);
+ p.drawTextLeft(
+ add.x() - (st.width / 2),
+ add.y() + st.textTop,
+ width(),
+ text,
+ textWidth);
- widthForTitle -= add.width() - (st::stickersTrendingAdd.width / 2);
- } else {
- auto add = featuredAddRect(info);
- int checkx = add.left() + (add.width() - st::stickersFeaturedInstalled.width()) / 2;
- int checky = add.top() + (add.height() - st::stickersFeaturedInstalled.height()) / 2;
- st().trendingInstalled.paint(p, QPoint(checkx, checky), width());
+ widthForTitle -= add.width() - (st.width / 2);
}
if (set.flags & SetFlag::Unread) {
widthForTitle -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip;
@@ -1455,14 +1474,17 @@ bool StickersListWidget::featuredHasAddButton(int index) const {
}
QRect StickersListWidget::featuredAddRect(int index) const {
- return featuredAddRect(sectionInfo(index));
+ return featuredAddRect(sectionInfo(index), false);
}
-QRect StickersListWidget::featuredAddRect(const SectionInfo &info) const {
- auto addw = _addWidth - st::stickersTrendingAdd.width;
- auto addh = st::stickersTrendingAdd.height;
- auto addx = stickersRight() - addw;
- auto addy = info.top + st::stickersTrendingAddTop;
+QRect StickersListWidget::featuredAddRect(
+ const SectionInfo &info,
+ bool installedSet) const {
+ const auto addw = (installedSet ? _installedWidth : _addWidth)
+ - st::stickersTrendingAdd.width;
+ const auto addh = st::stickersTrendingAdd.height;
+ const auto addx = stickersRight() - addw;
+ const auto addy = info.top + st::stickersTrendingAddTop;
return QRect(addx, addy, addw, addh);
}
@@ -1538,7 +1560,7 @@ void StickersListWidget::setPressed(OverState newPressed) {
} else if (std::get_if(&_pressed)) {
if (!_megagroupSetButtonRipple) {
auto maskSize = _megagroupSetButtonRect.size();
- auto mask = Ui::RippleAnimation::RoundRectMask(maskSize, st::roundRadiusSmall);
+ auto mask = Ui::RippleAnimation::RoundRectMask(maskSize, st::roundRadiusLarge);
_megagroupSetButtonRipple = std::make_unique(st::stickerGroupCategoryAdd.ripple, std::move(mask), [this] {
rtlupdate(megagroupSetButtonRectFinal());
});
@@ -1566,7 +1588,7 @@ std::unique_ptr StickersListWidget::createButtonRipple(int
if (shownSets()[section].externalLayout) {
auto maskSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
- auto mask = Ui::RippleAnimation::RoundRectMask(maskSize, st::roundRadiusSmall);
+ auto mask = Ui::RippleAnimation::RoundRectMask(maskSize, st::roundRadiusLarge);
return std::make_unique(
st::stickersTrendingAdd.ripple,
std::move(mask),
@@ -1768,6 +1790,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
Assert(button->section >= 0 && button->section < sets.size());
if (sets[button->section].externalLayout) {
_localSetsManager->install(sets[button->section].id);
+ update();
} else {
removeSet(sets[button->section].id);
}
@@ -2384,7 +2407,7 @@ void StickersListWidget::updateSelected() {
if (p.y() >= info.top && p.y() < info.rowsTop) {
if (hasRemoveButton(section) && myrtlrect(removeButtonRect(info)).contains(p.x(), p.y())) {
newSelected = OverButton{ section };
- } else if (featuredHasAddButton(section) && myrtlrect(featuredAddRect(info)).contains(p.x(), p.y())) {
+ } else if (featuredHasAddButton(section) && myrtlrect(featuredAddRect(info, false)).contains(p.x(), p.y())) {
newSelected = OverButton{ section };
} else if (_features.openStickerSets
&& !(sets[section].flags & SetFlag::Special)) {
@@ -2587,7 +2610,7 @@ void StickersListWidget::showMegagroupSet(ChannelData *megagroup) {
_megagroupSetAbout.setText(
st::stickerGroupCategoryAbout,
tr::lng_group_stickers_description(tr::now));
- _megagroupSetButtonText = tr::lng_group_stickers_add(tr::now).toUpper();
+ _megagroupSetButtonText = tr::lng_group_stickers_add(tr::now);
refreshMegagroupSetGeometry();
}
_megagroupSetButtonRipple.reset();
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
index 6e2c2fa04..2e1ef9c1c 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
@@ -302,7 +302,9 @@ private:
[[nodiscard]] int stickersRight() const;
[[nodiscard]] bool featuredHasAddButton(int index) const;
[[nodiscard]] QRect featuredAddRect(int index) const;
- [[nodiscard]] QRect featuredAddRect(const SectionInfo &info) const;
+ [[nodiscard]] QRect featuredAddRect(
+ const SectionInfo &info,
+ bool installedSet) const;
[[nodiscard]] bool hasRemoveButton(int index) const;
[[nodiscard]] QRect removeButtonRect(int index) const;
[[nodiscard]] QRect removeButtonRect(const SectionInfo &info) const;
@@ -392,7 +394,7 @@ private:
OverState _pressed;
QPoint _lastMousePosition;
- Ui::RoundRect _trendingAddBgOver, _trendingAddBg;
+ Ui::RoundRect _trendingAddBgOver, _trendingAddBg, _inactiveButtonBg;
Ui::RoundRect _groupCategoryAddBgOver, _groupCategoryAddBg;
const std::unique_ptr _pathGradient;
@@ -405,6 +407,8 @@ private:
QString _addText;
int _addWidth;
+ QString _installedText;
+ int _installedWidth;
object_ptr _settings;
diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp
index 02472e437..e3bbba7d1 100644
--- a/Telegram/SourceFiles/core/click_handler_types.cpp
+++ b/Telegram/SourceFiles/core/click_handler_types.cpp
@@ -147,6 +147,7 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) {
my.show->showBox(std::move(box));
} else if (use) {
use->show(std::move(box));
+ use->activate();
}
} else {
open();
diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp
index 1b953ef84..8d620a0b6 100644
--- a/Telegram/SourceFiles/core/core_settings.cpp
+++ b/Telegram/SourceFiles/core/core_settings.cpp
@@ -202,7 +202,8 @@ QByteArray Settings::serialize() const {
+ sizeof(qint32) * 3
+ Serialize::bytearraySize(mediaViewPosition)
+ sizeof(qint32)
- + sizeof(quint64);
+ + sizeof(quint64)
+ + sizeof(qint32);
auto result = QByteArray();
result.reserve(size);
@@ -336,7 +337,8 @@ QByteArray Settings::serialize() const {
<< qint32(_windowTitleContent.current().hideTotalUnread ? 1 : 0)
<< mediaViewPosition
<< qint32(_ignoreBatterySaving.current() ? 1 : 0)
- << quint64(_macRoundIconDigest.value_or(0));
+ << quint64(_macRoundIconDigest.value_or(0))
+ << qint32(_storiesClickTooltipHidden.current() ? 1 : 0);
}
return result;
}
@@ -444,6 +446,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
QByteArray mediaViewPosition;
qint32 ignoreBatterySaving = _ignoreBatterySaving.current() ? 1 : 0;
quint64 macRoundIconDigest = _macRoundIconDigest.value_or(0);
+ qint32 storiesClickTooltipHidden = _storiesClickTooltipHidden.current() ? 1 : 0;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -678,6 +681,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> macRoundIconDigest;
}
+ if (!stream.atEnd()) {
+ stream >> storiesClickTooltipHidden;
+ }
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -869,6 +875,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
}
_ignoreBatterySaving = (ignoreBatterySaving == 1);
_macRoundIconDigest = macRoundIconDigest ? macRoundIconDigest : std::optional();
+ _storiesClickTooltipHidden = (storiesClickTooltipHidden == 1);
}
QString Settings::getSoundPath(const QString &key) const {
@@ -1159,6 +1166,7 @@ void Settings::resetOnLastLogout() {
_tabbedReplacedWithInfo = false; // per-window
_systemDarkModeEnabled = false;
_hiddenGroupCallTooltips = 0;
+ _storiesClickTooltipHidden = 0;
_recentEmojiPreload.clear();
_recentEmoji.clear();
diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h
index 111bf6c80..72468e755 100644
--- a/Telegram/SourceFiles/core/core_settings.h
+++ b/Telegram/SourceFiles/core/core_settings.h
@@ -802,6 +802,15 @@ public:
[[nodiscard]] std::optional macRoundIconDigest() const {
return _macRoundIconDigest;
}
+ [[nodiscard]] bool storiesClickTooltipHidden() const {
+ return _storiesClickTooltipHidden.current();
+ }
+ [[nodiscard]] rpl::producer storiesClickTooltipHiddenValue() const {
+ return _storiesClickTooltipHidden.value();
+ }
+ void setStoriesClickTooltipHidden(bool value) {
+ _storiesClickTooltipHidden = value;
+ }
[[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
@@ -923,6 +932,7 @@ private:
WindowPosition _mediaViewPosition = { .maximized = 2 };
rpl::variable _ignoreBatterySaving = false;
std::optional _macRoundIconDigest;
+ rpl::variable _storiesClickTooltipHidden = false;
bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream _tabbedReplacedWithInfoValue; // per-window
diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp
index 0bbe30a97..6c14ccd0e 100644
--- a/Telegram/SourceFiles/core/launcher.cpp
+++ b/Telegram/SourceFiles/core/launcher.cpp
@@ -394,6 +394,18 @@ int Launcher::exec() {
return result;
}
+bool Launcher::validateCustomWorkingDir() {
+ if (customWorkingDir()) {
+ if (_customWorkingDir == cWorkingDir()) {
+ _customWorkingDir = QString();
+ return false;
+ }
+ cForceWorkingDir(_customWorkingDir);
+ return true;
+ }
+ return false;
+}
+
void Launcher::workingFolderReady() {
srand((unsigned int)time(nullptr));
@@ -435,7 +447,7 @@ const QStringList &Launcher::arguments() const {
}
bool Launcher::customWorkingDir() const {
- return _customWorkingDir;
+ return !_customWorkingDir.isEmpty();
}
void Launcher::prepareSettings() {
@@ -534,9 +546,9 @@ void Launcher::processArguments() {
gStartInTray = parseResult.contains("-startintray");
gQuit = parseResult.contains("-quit");
gSendPaths = parseResult.value("-sendpath", {});
- cForceWorkingDir(parseResult.value("-workdir", {}).join(QString()));
- if (!gWorkingDir.isEmpty()) {
- _customWorkingDir = true;
+ _customWorkingDir = parseResult.value("-workdir", {}).join(QString());
+ if (!_customWorkingDir.isEmpty()) {
+ _customWorkingDir = QDir(_customWorkingDir).absolutePath() + '/';
}
gStartUrl = parseResult.value("--", {}).join(QString());
diff --git a/Telegram/SourceFiles/core/launcher.h b/Telegram/SourceFiles/core/launcher.h
index 264085f20..a5e5117a5 100644
--- a/Telegram/SourceFiles/core/launcher.h
+++ b/Telegram/SourceFiles/core/launcher.h
@@ -34,6 +34,7 @@ public:
uint64 installationTag() const;
bool checkPortableVersionFolder();
+ bool validateCustomWorkingDir();
void workingFolderReady();
void writeDebugModeSetting();
void writeInstallBetaVersionsSetting();
@@ -83,7 +84,7 @@ private:
QStringList _arguments;
BaseIntegration _baseIntegration;
- bool _customWorkingDir = false;
+ QString _customWorkingDir;
};
diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h
index 16c30b177..072d67dba 100644
--- a/Telegram/SourceFiles/core/version.h
+++ b/Telegram/SourceFiles/core/version.h
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
constexpr auto AppName = "AyuGram Desktop"_cs;
constexpr auto AppFile = "AyuGram"_cs;
-constexpr auto AppVersion = 4008007;
-constexpr auto AppVersionStr = "4.8.7";
+constexpr auto AppVersion = 4008010;
+constexpr auto AppVersionStr = "4.8.10";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp
index c9e66759b..5852a9606 100644
--- a/Telegram/SourceFiles/data/data_document.cpp
+++ b/Telegram/SourceFiles/data/data_document.cpp
@@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kDefaultCoverThumbnailSize = 100;
+constexpr auto kMaxAllowedPreloadPrefix = 6 * 1024 * 1024;
const auto kLottieStickerDimensions = QSize(
kStickerSideSize,
@@ -393,7 +394,7 @@ void DocumentData::setattributes(
if (data.is_round_message()) {
_additional = std::make_unique();
} else if (const auto size = data.vpreload_prefix_size()) {
- if (size->v > 0) {
+ if (size->v > 0 && size->v < kMaxAllowedPreloadPrefix) {
_videoPreloadPrefix = size->v;
}
}
diff --git a/Telegram/SourceFiles/data/data_shared_media.cpp b/Telegram/SourceFiles/data/data_shared_media.cpp
index e2a69482e..8033fba9c 100644
--- a/Telegram/SourceFiles/data/data_shared_media.cpp
+++ b/Telegram/SourceFiles/data/data_shared_media.cpp
@@ -185,7 +185,8 @@ rpl::producer SharedScheduledMediaViewer(
SharedMediaMergedKey key,
int limitBefore,
int limitAfter) {
- Expects(!IsServerMsgId(key.mergedKey.universalId));
+ Expects(!key.mergedKey.universalId
+ || Data::IsScheduledMsgId(key.mergedKey.universalId));
Expects((key.mergedKey.universalId != 0)
|| (limitBefore == 0 && limitAfter == 0));
diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h
index c4d55a129..48ea79688 100644
--- a/Telegram/SourceFiles/data/data_stories.h
+++ b/Telegram/SourceFiles/data/data_stories.h
@@ -217,7 +217,7 @@ public:
void registerPolling(not_null story, Polling polling);
void unregisterPolling(not_null story, Polling polling);
- bool registerPolling(FullStoryId id, Polling polling);
+ [[nodiscard]] bool registerPolling(FullStoryId id, Polling polling);
void unregisterPolling(FullStoryId id, Polling polling);
void requestUserStories(
not_null user,
diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp
index 2a139de87..fbc678581 100644
--- a/Telegram/SourceFiles/data/data_story.cpp
+++ b/Telegram/SourceFiles/data/data_story.cpp
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "media/streaming/media_streaming_reader.h"
#include "storage/download_manager_mtproto.h"
+#include "storage/file_download.h" // kMaxFileInMemory
#include "ui/text/text_utilities.h"
namespace Data {
@@ -574,6 +575,7 @@ void StoryPreload::load() {
}
_task = std::make_unique(id(), video, [=](QByteArray data) {
if (!data.isEmpty()) {
+ Assert(data.size() < Storage::kMaxFileInMemory);
_story->owner().cacheBigFile().putIfEmpty(
key,
Storage::Cache::Database::TaggedValue(std::move(data), 0));
diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp
index ffedf1f50..b9f34f030 100644
--- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp
+++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lottie/lottie_frame_generator.h"
#include "ffmpeg/ffmpeg_frame_generator.h"
#include "chat_helpers/stickers_lottie.h"
+#include "storage/file_download.h" // kMaxFileInMemory
#include "ui/widgets/input_fields.h"
#include "ui/text/text_custom_emoji.h"
#include "ui/text/text_utilities.h"
@@ -345,7 +346,7 @@ void CustomEmojiLoader::check() {
};
auto put = [=, key = cacheKey(document)](QByteArray value) {
const auto size = value.size();
- if (size <= Storage::Cache::Database::Settings().maxDataSize) {
+ if (size <= Storage::kMaxFileInMemory) {
document->owner().cacheBigFile().put(key, std::move(value));
} else {
LOG(("Data Error: Cached emoji size too big: %1.").arg(size));
diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style
index c824b8219..622016bb8 100644
--- a/Telegram/SourceFiles/dialogs/dialogs.style
+++ b/Telegram/SourceFiles/dialogs/dialogs.style
@@ -562,3 +562,16 @@ dialogsStoriesListInfo: DialogsStoriesList(dialogsStoriesList) {
dialogsStoriesListMine: DialogsStoriesList(dialogsStoriesListInfo) {
readOpacity: 1.;
}
+dialogsStoriesTooltip: ImportantTooltip(defaultImportantTooltip) {
+ padding: margins(0px, 0px, 0px, 0px);
+}
+dialogsStoriesTooltipLabel: defaultImportantTooltipLabel;
+dialogsStoriesTooltipMaxWidth: 200px;
+dialogsStoriesTooltipHide: IconButton(defaultIconButton) {
+ width: 34px;
+ height: 20px;
+ iconPosition: point(-1px, -1px);
+ icon: icon {{ "calls/video_tooltip", importantTooltipFg }};
+ iconOver: icon {{ "calls/video_tooltip", importantTooltipFg }};
+ ripple: emptyRippleAnimation;
+}
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
index 1b696ae8d..11e7b2f1b 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
@@ -798,6 +798,22 @@ void Widget::setupStories() {
_scroll->viewportEvent(e);
}, _stories->lifetime());
+ if (!Core::App().settings().storiesClickTooltipHidden()) {
+ // Don't create tooltip
+ // until storiesClickTooltipHidden can be returned to false.
+ const auto hideTooltip = [=] {
+ Core::App().settings().setStoriesClickTooltipHidden(true);
+ Core::App().saveSettingsDelayed();
+ };
+ _stories->setShowTooltip(
+ parentWidget(),
+ rpl::combine(
+ Core::App().settings().storiesClickTooltipHiddenValue(),
+ shownValue(),
+ !rpl::mappers::_1 && rpl::mappers::_2),
+ hideTooltip);
+ }
+
_storiesContents.fire(Stories::ContentForSession(
&controller()->session(),
Data::StorySourcesList::NotHidden));
@@ -1370,6 +1386,15 @@ void Widget::jumpToTop(bool belowPinned) {
}
}
+void Widget::raiseWithTooltip() {
+ raise();
+ if (_stories) {
+ Ui::PostponeCall(this, [=] {
+ _stories->raiseTooltip();
+ });
+ }
+}
+
void Widget::scrollToDefault(bool verytop) {
if (verytop) {
//_scroll->verticalScrollBar()->setMinimum(0);
@@ -1447,6 +1472,7 @@ void Widget::stopWidthAnimation() {
}
void Widget::updateStoriesVisibility() {
+ updateLockUnlockVisibility();
if (!_stories) {
return;
}
@@ -1476,7 +1502,6 @@ void Widget::updateStoriesVisibility() {
if (_aboveScrollAdded > 0 && _updateScrollGeometryCached) {
_updateScrollGeometryCached();
}
- updateLockUnlockVisibility();
updateLockUnlockPosition();
}
}
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h
index 9473ac348..e1d1a64ef 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.h
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h
@@ -98,6 +98,7 @@ public:
void setInnerFocus();
void jumpToTop(bool belowPinned = false);
+ void raiseWithTooltip();
void startWidthAnimation();
void stopWidthAnimation();
diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp
index 1a1a395d3..82854b90b 100644
--- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp
+++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp
@@ -7,14 +7,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "dialogs/ui/dialogs_stories_list.h"
+#include "base/event_filter.h"
+#include "base/qt_signal_producer.h"
#include "lang/lang_keys.h"
#include "ui/effects/outline_segments.h"
+#include "ui/text/text_utilities.h"
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
+#include "ui/widgets/buttons.h"
+#include "ui/widgets/labels.h"
#include "ui/widgets/popup_menu.h"
+#include "ui/widgets/tooltip.h"
#include "ui/painter.h"
#include "styles/style_dialogs.h"
#include
+#include
+#include
#include "base/debug_log.h"
@@ -27,6 +35,8 @@ constexpr auto kExpandAfterRatio = 0.72;
constexpr auto kCollapseAfterRatio = 0.68;
constexpr auto kFrictionRatio = 0.15;
constexpr auto kExpandCatchUpDuration = crl::time(200);
+constexpr auto kMaxTooltipNames = 3;
+constexpr auto kStoriesTooltipHideBgOpacity = 0.2;
[[nodiscard]] int AvailableNameWidth(const style::DialogsStoriesList &st) {
const auto &full = st.full;
@@ -35,6 +45,33 @@ constexpr auto kExpandCatchUpDuration = crl::time(200);
return full.photoLeft * 2 + full.photo - 2 * skip;
}
+[[nodiscard]] object_ptr MakeTooltipContent(
+ not_null parent,
+ rpl::producer text,
+ Fn hide) {
+ const auto size = st::dialogsStoriesTooltipHide.width;
+ const auto skip = st::defaultImportantTooltip.padding.right();
+ auto result = object_ptr>(
+ parent,
+ Ui::MakeNiceTooltipLabel(
+ parent,
+ std::move(text),
+ st::dialogsStoriesTooltipMaxWidth,
+ st::dialogsStoriesTooltipLabel),
+ (st::defaultImportantTooltip.padding
+ + QMargins(0, 0, skip + size, 0)));
+ const auto button = Ui::CreateChild(
+ result.data(),
+ st::dialogsStoriesTooltipHide);
+ result->sizeValue(
+ ) | rpl::start_with_next([=](QSize size) {
+ button->resize(button->width(), size.height());
+ button->moveToRight(0, 0, size.width());
+ }, button->lifetime());
+ button->setClickedCallback(std::move(hide));
+ return result;
+}
+
} // namespace
struct List::Layout {
@@ -112,6 +149,7 @@ void List::showContent(Content &&content) {
_data.items.emplace_back(Item{ .element = element });
}
}
+ _lastCollapsedGeometry = {};
if (int(_data.items.size()) != wasCount) {
updateGeometry();
}
@@ -120,6 +158,8 @@ void List::showContent(Content &&content) {
if (!wasCount) {
_empty = false;
}
+ _tooltipText = computeTooltipText();
+ updateTooltipGeometry();
}
void List::updateScrollMax() {
@@ -162,10 +202,16 @@ void List::requestExpanded(bool expanded) {
const auto from = _expanded ? 0. : 1.;
const auto till = _expanded ? 2. : 0.;
const auto duration = (_expanded ? 2 : 1) * st::slideWrapDuration;
+ if (!isHidden() && _expanded) {
+ toggleTooltip(false);
+ }
_expandedAnimation.start([=] {
checkForFullState();
update();
_collapsedGeometryChanged.fire({});
+ if (!isHidden() && !_expandedAnimation.animating()) {
+ toggleTooltip(false);
+ }
}, from, till, duration, anim::sineInOut);
}
_toggleExpandedRequests.fire_copy(_expanded);
@@ -179,6 +225,12 @@ void List::resizeEvent(QResizeEvent *e) {
updateScrollMax();
}
+void List::updateExpanding() {
+ updateExpanding(
+ _lastExpandedHeight * _expandCatchUpAnimation.value(1.),
+ _st.full.height);
+}
+
void List::updateExpanding(int expandingHeight, int expandedHeight) {
Expects(!expandingHeight || expandedHeight > 0);
@@ -196,12 +248,10 @@ void List::updateExpanding(int expandingHeight, int expandedHeight) {
if (change) {
requestExpanded(!_expanded);
}
+ updateTooltipGeometry();
}
List::Layout List::computeLayout() {
- updateExpanding(
- _lastExpandedHeight * _expandCatchUpAnimation.value(1.),
- _st.full.height);
return computeLayout(_expandedAnimation.value(_expanded ? 2. : 0.));
}
@@ -332,11 +382,7 @@ void List::paint(
bool layered) {
const auto &st = _st.small;
const auto &full = _st.full;
- const auto ratio = layout.ratio;
const auto expandRatio = layout.expandRatio;
- const auto lerp = [&](float64 a, float64 b) {
- return a + (b - a) * ratio;
- };
const auto elerp = [&](float64 a, float64 b) {
return a + (b - a) * expandRatio;
};
@@ -682,6 +728,9 @@ void List::mousePressEvent(QMouseEvent *e) {
return;
} else if (_state == State::Small) {
requestExpanded(true);
+ if (const auto onstack = _tooltipHide) {
+ onstack();
+ }
return;
} else if (_state != State::Full) {
return;
@@ -761,6 +810,7 @@ void List::setExpandedHeight(int height, bool momentum) {
} else if (!momentum && _expandIgnored && height > 0) {
_expandIgnored = false;
_expandCatchUpAnimation.start([=] {
+ updateExpanding();
update();
checkForFullState();
}, 0., 1., kExpandCatchUpDuration);
@@ -768,6 +818,7 @@ void List::setExpandedHeight(int height, bool momentum) {
_expandCatchUpAnimation.stop();
}
_lastExpandedHeight = height;
+ updateExpanding();
if (!checkForFullState()) {
setState(!height ? State::Small : State::Changing);
}
@@ -788,17 +839,189 @@ void List::setLayoutConstraints(
QPoint positionSmall,
style::align alignSmall,
QRect geometryFull) {
+ if (_positionSmall == positionSmall
+ && _alignSmall == alignSmall
+ && _geometryFull == geometryFull) {
+ return;
+ }
_positionSmall = positionSmall;
_alignSmall = alignSmall;
_geometryFull = geometryFull;
+ _lastCollapsedGeometry = {};
updateGeometry();
update();
}
+TextWithEntities List::computeTooltipText() const {
+ const auto &list = _data.items;
+ if (list.empty()) {
+ return {};
+ } else if (list.size() == 1 && list.front().element.skipSmall) {
+ return { tr::lng_stories_click_to_view_mine(tr::now) };
+ }
+ auto names = QStringList();
+ for (const auto &item : list) {
+ if (item.element.skipSmall) {
+ continue;
+ }
+ names.append(item.element.name);
+ if (names.size() >= kMaxTooltipNames) {
+ break;
+ }
+ }
+ auto sequence = Ui::Text::Bold(names.front());
+ if (names.size() > 1) {
+ for (auto i = 1; i + 1 != names.size(); ++i) {
+ sequence = tr::lng_stories_click_to_view_and_one(
+ tr::now,
+ lt_accumulated,
+ sequence,
+ lt_user,
+ Ui::Text::Bold(names[i]),
+ Ui::Text::WithEntities);
+ }
+ sequence = tr::lng_stories_click_to_view_and_last(
+ tr::now,
+ lt_accumulated,
+ sequence,
+ lt_user,
+ Ui::Text::Bold(names.back()),
+ Ui::Text::WithEntities);
+ }
+ return tr::lng_stories_click_to_view(
+ tr::now,
+ lt_users,
+ sequence,
+ Ui::Text::WithEntities);
+}
+
+void List::setShowTooltip(
+ not_null tooltipParent,
+ rpl::producer shown,
+ Fn hide) {
+ _tooltip = nullptr;
+ _tooltipHide = std::move(hide);
+ _tooltipNotHidden = std::move(shown);
+ _tooltipText = computeTooltipText();
+ const auto notEmpty = [](const TextWithEntities &text) {
+ return !text.empty();
+ };
+ _tooltip = std::make_unique(
+ tooltipParent,
+ MakeTooltipContent(
+ tooltipParent,
+ _tooltipText.value() | rpl::filter(notEmpty),
+ _tooltipHide),
+ st::dialogsStoriesTooltip);
+ const auto tooltip = _tooltip.get();
+ const auto weak = QPointer(tooltip);
+ tooltip->toggleFast(false);
+ updateTooltipGeometry();
+
+ const auto handle = tooltipParent->window()->windowHandle();
+ auto windowActive = rpl::single(
+ handle->isActive()
+ ) | rpl::then(base::qt_signal_producer(
+ handle,
+ &QWindow::activeChanged
+ ) | rpl::map([=] {
+ return handle->isActive();
+ })) | rpl::distinct_until_changed();
+
+ {
+ const auto recompute = [=] {
+ updateTooltipGeometry();
+ tooltip->raise();
+ };
+ using namespace base;
+ using Event = not_null;
+ install_event_filter(tooltip, tooltipParent, [=](Event e) {
+ if (e->type() == QEvent::ChildAdded) {
+ recompute();
+ }
+ return EventFilterResult::Continue;
+ });
+ }
+
+ rpl::combine(
+ _tooltipNotHidden.value(),
+ _tooltipText.value() | rpl::map(
+ notEmpty
+ ) | rpl::distinct_until_changed(),
+ std::move(windowActive)
+ ) | rpl::start_with_next([=](bool, bool, bool active) {
+ _tooltipWindowActive = active;
+ if (!isHidden()) {
+ toggleTooltip(false);
+ }
+ }, tooltip->lifetime());
+
+ shownValue(
+ ) | rpl::skip(1) | rpl::start_with_next([=](bool shown) {
+ toggleTooltip(true);
+ }, tooltip->lifetime());
+}
+
+void List::raiseTooltip() {
+ if (_tooltip) {
+ _tooltip->raise();
+ }
+}
+
+void List::toggleTooltip(bool fast) {
+ const auto shown = !_expanded
+ && !_expandedAnimation.animating()
+ && !isHidden()
+ && _tooltipNotHidden.current()
+ && !_tooltipText.current().empty()
+ && window()->windowHandle()->isActive();
+ if (_tooltip) {
+ if (fast) {
+ _tooltip->toggleFast(shown);
+ } else {
+ _tooltip->toggleAnimated(shown);
+ }
+ }
+ if (shown) {
+ updateTooltipGeometry();
+ }
+}
+
+void List::updateTooltipGeometry() {
+ if (!_tooltip || _expanded || _expandedAnimation.animating()) {
+ return;
+ }
+ const auto collapsed = collapsedGeometryCurrent();
+ const auto geometry = Ui::MapFrom(
+ _tooltip->parentWidget(),
+ parentWidget(),
+ QRect(
+ collapsed.geometry.x(),
+ collapsed.geometry.y(),
+ int(std::ceil(collapsed.singleWidth)),
+ collapsed.geometry.height()));
+ const auto weak = QPointer(_tooltip.get());
+ const auto countPosition = [=](QSize size) {
+ const auto left = geometry.x()
+ + (geometry.width() - size.width()) / 2;
+ const auto right = _tooltip->parentWidget()->width()
+ - st::dialogsStoriesTooltip.padding.right();
+ return QPoint(
+ std::max(std::min(left, right - size.width()), 0),
+ geometry.y() + geometry.height());
+ };
+ _tooltip->pointAt(geometry, RectPart::Bottom, countPosition);
+}
+
List::CollapsedGeometry List::collapsedGeometryCurrent() const {
const auto expanded = _expandedAnimation.value(_expanded ? 2. : 0.);
if (expanded >= 1.) {
- return { QRect(), 1. };
+ const auto single = 2 * _st.full.photoLeft + _st.full.photo;
+ return { QRect(), 1., float64(single) };
+ } else if (_lastCollapsedRatio == _lastRatio
+ && _lastCollapsedGeometry.expanded == expanded
+ && !_lastCollapsedGeometry.geometry.isEmpty()) {
+ return _lastCollapsedGeometry;
}
const auto layout = computeLayout(0.);
const auto small = countSmallGeometry();
@@ -807,10 +1030,21 @@ List::CollapsedGeometry List::collapsedGeometryCurrent() const {
const auto left = int(base::SafeRound(
shift + layout.left + layout.single * index));
const auto width = small.x() + small.width() - left;
- return {
- QRect(left, small.y(), width, small.height()),
+ const auto photoTopSmall = _st.small.photoTop;
+ const auto photoTop = photoTopSmall
+ + (_st.full.photoTop - photoTopSmall) * layout.expandedRatio;
+ const auto ySmall = photoTopSmall
+ + ((photoTop - photoTopSmall) * kSmallThumbsShown / 0.5);
+ const auto photo = _st.small.photo
+ + (_st.full.photo - _st.small.photo) * layout.ratio;
+ const auto top = y() + layout.geometryShift.y();
+ _lastCollapsedRatio = _lastRatio;
+ _lastCollapsedGeometry = {
+ QRect(left, top, width, ySmall + photo + _st.full.photoTop),
expanded,
+ layout.photoLeft * 2 + photo,
};
+ return _lastCollapsedGeometry;
}
rpl::producer<> List::collapsedGeometryChanged() const {
@@ -826,6 +1060,7 @@ void List::updateGeometry() {
} break;
case State::Full: setGeometry(_geometryFull);
}
+ updateTooltipGeometry();
update();
}
diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h
index 3182d7229..24468850a 100644
--- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h
+++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h
@@ -23,6 +23,7 @@ struct DialogsStoriesList;
namespace Ui {
class PopupMenu;
struct OutlineSegment;
+class ImportantTooltip;
} // namespace Ui
namespace Dialogs::Stories {
@@ -72,9 +73,16 @@ public:
QPoint positionSmall,
style::align alignSmall,
QRect geometryFull = QRect());
+ void setShowTooltip(
+ not_null tooltipParent,
+ rpl::producer shown,
+ Fn hide);
+ void raiseTooltip();
+
struct CollapsedGeometry {
QRect geometry;
float64 expanded = 0.;
+ float64 singleWidth = 0.;
};
[[nodiscard]] CollapsedGeometry collapsedGeometryCurrent() const;
[[nodiscard]] rpl::producer<> collapsedGeometryChanged() const;
@@ -142,10 +150,15 @@ private:
void checkLoadMore();
void requestExpanded(bool expanded);
+ void updateTooltipGeometry();
+ [[nodiscard]] TextWithEntities computeTooltipText() const;
+ void toggleTooltip(bool fast);
+
bool checkForFullState();
void setState(State state);
void updateGeometry();
[[nodiscard]] QRect countSmallGeometry() const;
+ void updateExpanding();
void updateExpanding(int expandingHeight, int expandedHeight);
void validateSegments(
not_null- item,
@@ -189,11 +202,20 @@ private:
bool _expandIgnored : 1 = false;
bool _expanded : 1 = false;
+ mutable CollapsedGeometry _lastCollapsedGeometry;
+ mutable float64 _lastCollapsedRatio = 0.;
+
int _selected = -1;
int _pressed = -1;
rpl::event_stream> _verticalScrollEvents;
+ rpl::variable _tooltipText;
+ rpl::variable _tooltipNotHidden;
+ Fn _tooltipHide;
+ std::unique_ptr _tooltip;
+ bool _tooltipWindowActive = false;
+
base::unique_qptr _menu;
base::has_weak_ptr _menuGuard;
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index 07dcb96ef..60469fa16 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -1055,6 +1055,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
_translateTracker->startBunch();
auto readTill = (HistoryItem*)nullptr;
auto readContents = base::flat_set>();
+ const auto markingAsViewed = _widget->markingContentsRead();
const auto guard = gsl::finally([&] {
if (_pinnedItem) {
_translateTracker->add(_pinnedItem);
@@ -1063,7 +1064,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
if (readTill && _widget->markingMessagesRead()) {
session().data().histories().readInboxTill(readTill);
}
- if (!readContents.empty() && _widget->markingContentsRead()) {
+ if (markingAsViewed && !readContents.empty()) {
session().api().markContentsRead(readContents);
}
_userpicsCache.clear();
@@ -1096,7 +1097,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
} else if (isUnread) {
readTill = item;
}
- if (item->hasViews()) {
+ if (markingAsViewed && item->hasViews()) {
session().api().views().scheduleIncrement(item);
}
if (withReaction) {
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 0cf17c93c..dab734fa3 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -2851,14 +2851,35 @@ void HistoryWidget::updateControlsVisibility() {
if (_botMenuButton) {
_botMenuButton->show();
}
- if (_silent) {
- _silent->setVisible(!_editMsgId);
- }
- if (_scheduled) {
- _scheduled->show();
- }
- if (_ttlInfo) {
- _ttlInfo->show();
+ {
+ auto rightButtonsChanged = false;
+ if (_silent) {
+ const auto was = _silent->isVisible();
+ const auto now = (!_editMsgId);
+ if (was != now) {
+ _silent->setVisible(now);
+ rightButtonsChanged = true;
+ }
+ }
+ if (_scheduled) {
+ const auto was = _scheduled->isVisible();
+ const auto now = (!_editMsgId);
+ if (was != now) {
+ _scheduled->setVisible(now);
+ rightButtonsChanged = true;
+ }
+ }
+ if (_ttlInfo) {
+ const auto was = _ttlInfo->isVisible();
+ const auto now = (!_editMsgId);
+ if (was != now) {
+ _ttlInfo->setVisible(now);
+ rightButtonsChanged = true;
+ }
+ }
+ if (rightButtonsChanged) {
+ updateFieldSize();
+ }
}
if (_sendAs) {
_sendAs->show();
@@ -5028,19 +5049,33 @@ void HistoryWidget::moveFieldControls() {
}
void HistoryWidget::updateFieldSize() {
- auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup();
+ const auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup();
auto fieldWidth = width()
- _attachToggle->width()
- st::historySendRight
- _send->width()
- _tabbedSelectorToggle->width();
- if (_botMenuButton) fieldWidth -= st::historyBotMenuSkip + _botMenuButton->width();
- if (_sendAs) fieldWidth -= _sendAs->width();
- if (kbShowShown) fieldWidth -= _botKeyboardShow->width();
- if (_cmdStartShown) fieldWidth -= _botCommandStart->width();
- if (_silent && !_silent->isHidden()) fieldWidth -= _silent->width();
- if (_scheduled) fieldWidth -= _scheduled->width();
- if (_ttlInfo) fieldWidth -= _ttlInfo->width();
+ if (_botMenuButton) {
+ fieldWidth -= st::historyBotMenuSkip + _botMenuButton->width();
+ }
+ if (_sendAs) {
+ fieldWidth -= _sendAs->width();
+ }
+ if (kbShowShown) {
+ fieldWidth -= _botKeyboardShow->width();
+ }
+ if (_cmdStartShown) {
+ fieldWidth -= _botCommandStart->width();
+ }
+ if (_silent && _silent->isVisible()) {
+ fieldWidth -= _silent->width();
+ }
+ if (_scheduled && _scheduled->isVisible()) {
+ fieldWidth -= _scheduled->width();
+ }
+ if (_ttlInfo && _ttlInfo->isVisible()) {
+ fieldWidth -= _ttlInfo->width();
+ }
if (_fieldDisabled) {
_fieldDisabled->resize(fieldWidth, fieldHeight());
@@ -6547,6 +6582,8 @@ void HistoryWidget::checkPinnedBarState() {
_list->setShownPinned(
session().data().message(
_pinnedTracker->currentMessageId().message));
+ } else {
+ _list->setShownPinned(nullptr);
}
return std::move(content);
}));
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
index 86d412b2a..4901c3bf9 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
@@ -347,7 +347,7 @@ public:
void setHistory(const SetHistoryArgs &args);
void init();
- void editMessage(FullMsgId id);
+ void editMessage(FullMsgId id, bool photoEditAllowed = false);
void replyToMessage(FullMsgId id);
void updateForwarding(
Data::Thread *thread,
@@ -363,8 +363,10 @@ public:
[[nodiscard]] bool readyToForward() const;
[[nodiscard]] const HistoryItemsList &forwardItems() const;
[[nodiscard]] FullMsgId replyingToMessage() const;
- [[nodiscard]] rpl::producer editMsgId() const;
+ [[nodiscard]] FullMsgId editMsgId() const;
+ [[nodiscard]] rpl::producer editMsgIdValue() const;
[[nodiscard]] rpl::producer scrollToItemRequests() const;
+ [[nodiscard]] rpl::producer<> editPhotoRequests() const;
[[nodiscard]] MessageToEdit queryToEdit();
[[nodiscard]] WebPageId webPageId() const;
@@ -425,16 +427,24 @@ private:
HistoryItem *_shownMessage = nullptr;
Ui::Text::String _shownMessageName;
Ui::Text::String _shownMessageText;
+ std::unique_ptr _shownPreviewSpoiler;
+ Ui::Animations::Simple _inPhotoEditOver;
int _shownMessageNameVersion = -1;
- bool _repaintScheduled = false;
+ bool _shownMessageHasPreview : 1 = false;
+ bool _inPhotoEdit : 1 = false;
+ bool _photoEditAllowed : 1 = false;
+ bool _repaintScheduled : 1 = false;
+ bool _inClickable : 1 = false;
const not_null _data;
const not_null _cancel;
QRect _clickableRect;
+ QRect _shownMessagePreviewRect;
rpl::event_stream _visibleChanged;
rpl::event_stream _scrollToItemRequests;
+ rpl::event_stream<> _editPhotoRequests;
};
@@ -554,26 +564,45 @@ void FieldHeader::init() {
}, lifetime());
setMouseTracking(true);
- const auto inClickable = lifetime().make_state(false);
events(
) | rpl::filter([=](not_null event) {
- return ranges::contains(kMouseEvents, event->type())
+ const auto type = event->type();
+ const auto leaving = (type == QEvent::Leave);
+ return (ranges::contains(kMouseEvents, type) || leaving)
&& (isEditingMessage()
|| readyToForward()
|| replyingToMessage());
}) | rpl::start_with_next([=](not_null event) {
- const auto type = event->type();
- const auto e = static_cast(event.get());
- const auto pos = e ? e->pos() : mapFromGlobal(QCursor::pos());
- const auto inPreviewRect = _clickableRect.contains(pos);
-
- if (type == QEvent::MouseMove) {
- if (inPreviewRect != *inClickable) {
- *inClickable = inPreviewRect;
- setCursor(*inClickable
+ const auto updateOver = [&](bool inClickable, bool inPhotoEdit) {
+ if (_inClickable != inClickable) {
+ _inClickable = inClickable;
+ setCursor(_inClickable
? style::cur_pointer
: style::cur_default);
}
+ if (_inPhotoEdit != inPhotoEdit) {
+ _inPhotoEdit = inPhotoEdit;
+ _inPhotoEditOver.start(
+ [=] { update(); },
+ _inPhotoEdit ? 0. : 1.,
+ _inPhotoEdit ? 1. : 0.,
+ st::defaultMessageBar.duration);
+ }
+ };
+ const auto type = event->type();
+ if (type == QEvent::Leave) {
+ updateOver(false, false);
+ return;
+ }
+ const auto e = static_cast(event.get());
+ const auto pos = e ? e->pos() : mapFromGlobal(QCursor::pos());
+ const auto inPreviewRect = _clickableRect.contains(pos);
+ const auto inPhotoEdit = _shownMessageHasPreview
+ && _photoEditAllowed
+ && _shownMessagePreviewRect.contains(pos);
+
+ if (type == QEvent::MouseMove) {
+ updateOver(inPreviewRect, inPhotoEdit);
return;
}
const auto isLeftIcon = (pos.x() < st::historyReplySkip);
@@ -582,6 +611,8 @@ void FieldHeader::init() {
if (isLeftButton && isLeftIcon) {
*leftIconPressed = true;
update();
+ } else if (isLeftButton && inPhotoEdit) {
+ _editPhotoRequests.fire({});
} else if (isLeftButton && inPreviewRect) {
if (!isEditingMessage() && readyToForward()) {
_forwardPanel->editOptions(_show);
@@ -794,20 +825,74 @@ void FieldHeader::paintEditOrReplyToMessage(Painter &p) {
}
}
+ const auto media = _shownMessage->media();
+ _shownMessageHasPreview = media && media->hasReplyPreview();
+ const auto preview = _shownMessageHasPreview
+ ? media->replyPreview()
+ : nullptr;
+ const auto spoilered = preview && media->hasSpoiler();
+ if (!spoilered) {
+ _shownPreviewSpoiler = nullptr;
+ } else if (!_shownPreviewSpoiler) {
+ _shownPreviewSpoiler = std::make_unique([=] {
+ update();
+ });
+ }
+ const auto previewSkipValue = st::msgReplyBarSize.height()
+ + st::msgReplyBarSkip
+ - st::msgReplyBarSize.width()
+ - st::msgReplyBarPos.x();
+ const auto previewSkip = _shownMessageHasPreview ? previewSkipValue : 0;
+ const auto textLeft = replySkip + previewSkip;
+ const auto textAvailableWidth = availableWidth - previewSkip;
+ if (preview) {
+ const auto overEdit = _photoEditAllowed
+ ? _inPhotoEditOver.value(_inPhotoEdit ? 1. : 0.)
+ : 0.;
+ const auto to = QRect(
+ replySkip,
+ st::msgReplyPadding.top(),
+ st::msgReplyBarSize.height(),
+ st::msgReplyBarSize.height());
+ p.drawPixmap(to.x(), to.y(), preview->pixSingle(
+ preview->size() / style::DevicePixelRatio(),
+ {
+ .options = Images::Option::RoundSmall,
+ .outer = to.size(),
+ }));
+ if (_shownPreviewSpoiler) {
+ if (overEdit > 0.) {
+ p.setOpacity(1. - overEdit);
+ }
+ Ui::FillSpoilerRect(
+ p,
+ to,
+ Ui::DefaultImageSpoiler().frame(
+ _shownPreviewSpoiler->index(crl::now(), p.inactive())));
+ }
+ if (overEdit > 0.) {
+ p.setOpacity(overEdit);
+ p.fillRect(to, st::historyEditMediaBg);
+ st::historyEditMedia.paintInCenter(p, to);
+ p.setOpacity(1.);
+ }
+
+ }
+
p.setPen(st::historyReplyNameFg);
p.setFont(st::msgServiceNameFont);
_shownMessageName.drawElided(
p,
- replySkip,
+ textLeft,
st::msgReplyPadding.top(),
- availableWidth);
+ textAvailableWidth);
p.setPen(st::historyComposeAreaFg);
_shownMessageText.draw(p, {
.position = QPoint(
- replySkip,
+ textLeft,
st::msgReplyPadding.top() + st::msgServiceNameFont->height),
- .availableWidth = availableWidth,
+ .availableWidth = textAvailableWidth,
.palette = &st::historyComposeAreaPalette,
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = crl::now(),
@@ -848,6 +933,10 @@ bool FieldHeader::isEditingMessage() const {
return !!_editMsgId.current();
}
+FullMsgId FieldHeader::editMsgId() const {
+ return _editMsgId.current();
+}
+
bool FieldHeader::readyToForward() const {
return !_forwardPanel->empty();
}
@@ -879,10 +968,21 @@ void FieldHeader::updateControlsGeometry(QSize size) {
0,
width() - st::historyReplySkip - _cancel->width(),
height());
+ _shownMessagePreviewRect = QRect(
+ st::historyReplySkip,
+ st::msgReplyPadding.top(),
+ st::msgReplyBarSize.height(),
+ st::msgReplyBarSize.height());
}
-void FieldHeader::editMessage(FullMsgId id) {
+void FieldHeader::editMessage(FullMsgId id, bool photoEditAllowed) {
+ _photoEditAllowed = photoEditAllowed;
_editMsgId = id;
+ if (!photoEditAllowed) {
+ _inPhotoEdit = false;
+ _inPhotoEditOver.stop();
+ }
+ update();
}
void FieldHeader::replyToMessage(FullMsgId id) {
@@ -898,7 +998,7 @@ void FieldHeader::updateForwarding(
}
}
-rpl::producer FieldHeader::editMsgId() const {
+rpl::producer FieldHeader::editMsgIdValue() const {
return _editMsgId.value();
}
@@ -906,6 +1006,10 @@ rpl::producer FieldHeader::scrollToItemRequests() const {
return _scrollToItemRequests.events();
}
+rpl::producer<> FieldHeader::editPhotoRequests() const {
+ return _editPhotoRequests.events();
+}
+
MessageToEdit FieldHeader::queryToEdit() {
const auto item = _data->message(_editMsgId.current());
if (!isEditingMessage() || !item) {
@@ -1470,7 +1574,7 @@ void ComposeControls::init() {
paintBackground(clip);
}, _wrap->lifetime());
- _header->editMsgId(
+ _header->editMsgIdValue(
) | rpl::start_with_next([=](const auto &id) {
unregisterDraftSources();
updateSendButtonType();
@@ -1482,6 +1586,16 @@ void ComposeControls::init() {
registerDraftSource();
}, _wrap->lifetime());
+ _header->editPhotoRequests(
+ ) | rpl::start_with_next([=] {
+ EditCaptionBox::StartPhotoEdit(
+ _regularWindow,
+ _photoEditMedia,
+ _editingId,
+ _field->getTextWithTags(),
+ crl::guard(_wrap.get(), [=] { cancelEditMessage(); }));
+ }, _wrap->lifetime());
+
_header->previewCancelled(
) | rpl::start_with_next([=] {
if (_preview) {
@@ -1521,7 +1635,7 @@ void ComposeControls::init() {
_voiceRecordBar->requestToSendWithOptions(options);
}, _wrap->lifetime());
- _header->editMsgId(
+ _header->editMsgIdValue(
) | rpl::start_with_next([=](const auto &id) {
_editingId = id;
}, _wrap->lifetime());
@@ -1663,6 +1777,8 @@ void ComposeControls::initField() {
}
return false;
});
+ _field->setEditLinkCallback(
+ DefaultEditLinkCallback(_show, _field, &_st.boxField));
initAutocomplete();
const auto allow = [=](const auto &) {
return _history && Data::AllowEmojiWithoutPremium(_history->peer);
@@ -2049,7 +2165,43 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
}
if (draft == editDraft) {
- _header->editMessage(editingId);
+ const auto resolve = [=] {
+ if (const auto item = _history->owner().message(editingId)) {
+ const auto media = item->media();
+ _canReplaceMedia = media && media->allowsEditMedia();
+ _photoEditMedia = (_canReplaceMedia
+ && _regularWindow
+ && media->photo()
+ && !media->photo()->isNull())
+ ? media->photo()->createMediaView()
+ : nullptr;
+ if (_photoEditMedia) {
+ _photoEditMedia->wanted(
+ Data::PhotoSize::Large,
+ item->fullId());
+ }
+ _header->editMessage(editingId, _photoEditMedia != nullptr);
+ return true;
+ }
+ _canReplaceMedia = false;
+ _photoEditMedia = nullptr;
+ _header->editMessage(editingId, false);
+ return false;
+ };
+ if (!resolve()) {
+ const auto callback = crl::guard(_header.get(), [=] {
+ if (_header->editMsgId() == editingId
+ && resolve()
+ && updateReplaceMediaButton()) {
+ updateControlsVisibility();
+ updateControlsGeometry(_wrap->size());
+ }
+ });
+ _history->session().api().requestMessageData(
+ _history->peer,
+ editingId.msg,
+ callback);
+ }
_header->replyToMessage({});
} else {
_canReplaceMedia = false;
@@ -2708,17 +2860,6 @@ void ComposeControls::editMessage(not_null item) {
cursor,
previewState));
applyDraft();
-
- const auto media = item->media();
- _canReplaceMedia = media && media->allowsEditMedia();
- _photoEditMedia = (_canReplaceMedia
- && media->photo()
- && !media->photo()->isNull())
- ? media->photo()->createMediaView()
- : nullptr;
- if (_photoEditMedia) {
- _photoEditMedia->wanted(Data::PhotoSize::Large, item->fullId());
- }
if (updateReplaceMediaButton()) {
updateControlsVisibility();
updateControlsGeometry(_wrap->size());
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp
index 945253ec6..f7e15f5e5 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp
@@ -50,6 +50,14 @@ void TTLButton::hide() {
_button.hide();
}
+void TTLButton::setVisible(bool visible) {
+ _button.setVisible(visible);
+}
+
+bool TTLButton::isVisible() const {
+ return _button.isVisible();
+}
+
void TTLButton::move(int x, int y) {
_button.move(x, y);
}
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h
index dbdc50a9f..16098aa6e 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h
+++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h
@@ -28,6 +28,8 @@ public:
void show();
void hide();
+ void setVisible(bool visible);
+ [[nodiscard]] bool isVisible() const;
void move(int x, int y);
[[nodiscard]] int width() const;
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index 6873a4a88..348ac64a9 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -846,8 +846,8 @@ void Element::validateText() {
_media = nullptr;
if (!storyMention) {
if (_text.isEmpty()) {
- setTextWithLinks(
- Ui::Text::Italic(u"This story has expired"_q));
+ setTextWithLinks(Ui::Text::Italic(
+ tr::lng_forwarded_story_expired(tr::now)));
}
return;
}
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
index 9ca933844..1e2648125 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
@@ -933,7 +933,7 @@ Element *ListWidget::viewByPosition(Data::MessagePosition position) const {
const auto result = (index < 0) ? nullptr : _items[index].get();
return (position == Data::MinMessagePosition
|| position == Data::MaxMessagePosition
- || result->data()->position() == position)
+ || (result && result->data()->position() == position))
? result
: nullptr;
}
@@ -2059,12 +2059,13 @@ void ListWidget::paintEvent(QPaintEvent *e) {
}
auto readTill = (HistoryItem*)nullptr;
auto readContents = base::flat_set>();
+ const auto markingAsViewed = markingMessagesRead();
const auto guard = gsl::finally([&] {
if (_translateTracker) {
_delegate->listAddTranslatedItems(_translateTracker.get());
_translateTracker->finishBunch();
}
- if (readTill && markingMessagesRead()) {
+ if (markingAsViewed && readTill) {
_delegate->listMarkReadTill(readTill);
}
if (!readContents.empty() && markingContentsRead()) {
@@ -2136,7 +2137,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
} else if (isUnread) {
readTill = item;
}
- if (item->hasViews()) {
+ if (markingAsViewed && item->hasViews()) {
session->api().views().scheduleIncrement(item);
}
if (withReaction) {
diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp
index d3040ce07..0bee2fbf0 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp
@@ -88,14 +88,13 @@ Gif::Gif(
bool spoiler)
: File(parent, realParent)
, _data(document)
-, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
+, _storyId(realParent->media()
+ ? realParent->media()->storyId()
+ : FullStoryId())
+, _caption(
+ st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
, _spoiler(spoiler ? std::make_unique() : nullptr)
, _downloadSize(Ui::FormatSizeText(_data->size)) {
- if (const auto media = realParent->media()) {
- if (media->storyId()) {
- _story = true;
- }
- }
setDocumentLinks(_data, realParent, [=] {
if (!_data->createMediaView()->canBePlayed(realParent)
|| !_data->isAnimation()
@@ -1438,15 +1437,14 @@ void Gif::dataMediaCreated() const {
}
void Gif::togglePollingStory(bool enabled) const {
- if (!_story || _pollingStory == enabled) {
+ if (!_storyId || _pollingStory == enabled) {
return;
}
const auto polling = Data::Stories::Polling::Chat;
- const auto media = _parent->data()->media();
- const auto id = media ? media->storyId() : FullStoryId();
if (!enabled) {
- _data->owner().stories().unregisterPolling(id, polling);
- } else if (!_data->owner().stories().registerPolling(id, polling)) {
+ _data->owner().stories().unregisterPolling(_storyId, polling);
+ } else if (
+ !_data->owner().stories().registerPolling(_storyId, polling)) {
return;
}
_pollingStory = enabled;
@@ -1464,7 +1462,7 @@ void Gif::hideSpoilers() {
}
bool Gif::needsBubble() const {
- if (_story) {
+ if (_storyId) {
return true;
} else if (_data->isVideoMessage()) {
return false;
diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h
index 50c5583ac..c737d2391 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_gif.h
+++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h
@@ -211,6 +211,7 @@ private:
void togglePollingStory(bool enabled) const;
const not_null _data;
+ const FullStoryId _storyId;
Ui::Text::String _caption;
std::unique_ptr _streamed;
const std::unique_ptr _spoiler;
@@ -223,7 +224,6 @@ private:
mutable std::optional _thumbCacheRounding;
mutable bool _thumbCacheBlurred : 1 = false;
mutable bool _thumbIsEllipse : 1 = false;
- mutable bool _story : 1 = false;
mutable bool _pollingStory : 1 = false;
};
diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
index ed5c49ac9..a5cf0f41c 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
@@ -69,13 +69,11 @@ Photo::Photo(
bool spoiler)
: File(parent, realParent)
, _data(photo)
+, _storyId(realParent->media()
+ ? realParent->media()->storyId()
+ : FullStoryId())
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
, _spoiler(spoiler ? std::make_unique() : nullptr) {
- if (const auto media = realParent->media()) {
- if (media->storyId()) {
- _story = 1;
- }
- }
_caption = createCaption(realParent);
create(realParent->fullId());
}
@@ -168,15 +166,14 @@ void Photo::unloadHeavyPart() {
void Photo::togglePollingStory(bool enabled) const {
const auto pollingStory = (enabled ? 1 : 0);
- if (!_story || _pollingStory == pollingStory) {
+ if (!_storyId || _pollingStory == pollingStory) {
return;
}
const auto polling = Data::Stories::Polling::Chat;
- const auto media = _parent->data()->media();
- const auto id = media ? media->storyId() : FullStoryId();
if (!enabled) {
- _data->owner().stories().unregisterPolling(id, polling);
- } else if (!_data->owner().stories().registerPolling(id, polling)) {
+ _data->owner().stories().unregisterPolling(_storyId, polling);
+ } else if (
+ !_data->owner().stories().registerPolling(_storyId, polling)) {
return;
}
_pollingStory = pollingStory;
@@ -285,7 +282,7 @@ int Photo::adjustHeightForLessCrop(QSize dimensions, QSize current) const {
void Photo::draw(Painter &p, const PaintContext &context) const {
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return;
- } else if (_story && _data->isNull()) {
+ } else if (_storyId && _data->isNull()) {
return;
}
@@ -623,7 +620,7 @@ void Photo::paintUserpicFrame(
}
QSize Photo::photoSize() const {
- if (_story) {
+ if (_storyId) {
return { kStoryWidth, kStoryHeight };
}
return QSize(_data->width(), _data->height());
@@ -634,7 +631,7 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
- } else if (_story && _data->isNull()) {
+ } else if (_storyId && _data->isNull()) {
return result;
}
auto paintx = 0, painty = 0, paintw = width(), painth = height();
@@ -1054,7 +1051,7 @@ void Photo::hideSpoilers() {
}
bool Photo::needsBubble() const {
- if (_story || !_caption.isEmpty()) {
+ if (_storyId || !_caption.isEmpty()) {
return true;
}
const auto item = _parent->data();
diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.h b/Telegram/SourceFiles/history/view/media/history_view_photo.h
index 94404ddbe..cfe9e9627 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_photo.h
+++ b/Telegram/SourceFiles/history/view/media/history_view_photo.h
@@ -163,6 +163,7 @@ private:
void togglePollingStory(bool enabled) const;
const not_null _data;
+ const FullStoryId _storyId;
Ui::Text::String _caption;
mutable std::shared_ptr _dataMedia;
mutable std::unique_ptr _streamed;
@@ -172,7 +173,6 @@ private:
uint32 _serviceWidth : 28 = 0;
mutable uint32 _imageCacheForum : 1 = 0;
mutable uint32 _imageCacheBlurred : 1 = 0;
- mutable uint32 _story : 1 = 0;
mutable uint32 _pollingStory : 1 = 0;
};
diff --git a/Telegram/SourceFiles/info/stories/info_stories_provider.cpp b/Telegram/SourceFiles/info/stories/info_stories_provider.cpp
index e582f9ce7..82f0ca5f2 100644
--- a/Telegram/SourceFiles/info/stories/info_stories_provider.cpp
+++ b/Telegram/SourceFiles/info/stories/info_stories_provider.cpp
@@ -283,9 +283,10 @@ BaseLayout *Provider::getLayout(
if (auto layout = createLayout(id, delegate)) {
layout->initDimensions();
it = _layouts.emplace(id, std::move(layout)).first;
- _peer->owner().stories().registerPolling(
+ const auto ok = _peer->owner().stories().registerPolling(
{ _peer->id, id },
Data::Stories::Polling::Chat);
+ Assert(ok);
} else {
return nullptr;
}
diff --git a/Telegram/SourceFiles/logs.cpp b/Telegram/SourceFiles/logs.cpp
index 03cfe4c49..b8bd88d6c 100644
--- a/Telegram/SourceFiles/logs.cpp
+++ b/Telegram/SourceFiles/logs.cpp
@@ -346,7 +346,8 @@ bool WritingEntry() {
void start() {
Assert(LogsData == nullptr);
- if (!Core::Launcher::Instance().checkPortableVersionFolder()) {
+ auto &launcher = Core::Launcher::Instance();
+ if (!launcher.checkPortableVersionFolder()) {
return;
}
@@ -362,7 +363,6 @@ void start() {
if (!cWorkingDir().isEmpty()) {
// This value must come from TelegramForcePortable
- // or from the "-workdir" command line argument.
cForceWorkingDir(cWorkingDir());
workingDirChosen = true;
} else {
@@ -390,7 +390,6 @@ void start() {
if (!cWorkingDir().isEmpty()) {
// This value must come from TelegramForcePortable
- // or from the "-workdir" command line argument.
cForceWorkingDir(cWorkingDir());
workingDirChosen = true;
}
@@ -407,6 +406,11 @@ void start() {
}
}
+ if (launcher.validateCustomWorkingDir()) {
+ delete LogsData;
+ LogsData = new LogsDataFields();
+ }
+
// WinRT build requires the working dir to stay the same for plugin loading.
#ifndef Q_OS_WINRT
QDir().setCurrent(cWorkingDir());
@@ -414,7 +418,7 @@ void start() {
QDir().mkpath(cWorkingDir() + u"tdata"_q);
- Core::Launcher::Instance().workingFolderReady();
+ launcher.workingFolderReady();
CrashReports::StartCatching();
if (!LogsData->openMain()) {
@@ -430,7 +434,7 @@ void start() {
LOG(("Executable dir: %1, name: %2").arg(cExeDir(), cExeName()));
LOG(("Initial working dir: %1").arg(initialWorkingDir));
LOG(("Working dir: %1").arg(cWorkingDir()));
- LOG(("Command line: %1").arg(Core::Launcher::Instance().arguments().join(' ')));
+ LOG(("Command line: %1").arg(launcher.arguments().join(' ')));
if (!LogsData) {
LOG(("FATAL: Could not open '%1' for writing log!"
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 4b46807d0..8abf9f2d2 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -1926,7 +1926,7 @@ void MainWidget::showBackFromStack(
void MainWidget::orderWidgets() {
if (_dialogs) {
- _dialogs->raise();
+ _dialogs->raiseWithTooltip();
}
if (_player) {
_player->raise();
diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style
index 841cc3e5c..5e5a3ec8b 100644
--- a/Telegram/SourceFiles/media/player/media_player.style
+++ b/Telegram/SourceFiles/media/player/media_player.style
@@ -106,7 +106,7 @@ mediaPlayerRepeatButton: IconButton {
icon: icon {
{ "player/player_repeat", mediaPlayerActiveFg }
};
- iconPosition: point(3px, 6px);
+ iconPosition: point(2px, 5px);
rippleAreaPosition: point(2px, 6px);
rippleAreaSize: 24px;
@@ -135,6 +135,10 @@ mediaPlayerReverseDisabledIconOver: icon {
mediaPlayerShuffleIcon: icon {
{ "player/player_shuffle", mediaPlayerActiveFg }
};
+mediaPlayerOrderButton: IconButton(mediaPlayerRepeatButton) {
+ iconPosition: point(2px, 6px);
+ rippleAreaPosition: point(2px, 6px);
+}
mediaPlayerRepeatDisabledRippleBg: windowBgOver;
mediaPlayerPlayButton: IconButton(mediaPlayerRepeatButton) {
@@ -233,7 +237,7 @@ mediaPlayerVolumeToggle: IconButton(mediaPlayerRepeatButton) {
{ "player/player_mini_full", mediaPlayerActiveFg },
};
iconPosition: point(5px, 6px);
- rippleAreaPosition: point(4px, 5px);
+ rippleAreaPosition: point(5px, 6px);
}
mediaPlayerVolumeMargin: 10px;
mediaPlayerVolumeSize: size(27px, 100px);
@@ -267,9 +271,9 @@ mediaPlayerClose: IconButton(mediaPlayerRepeatButton) {
width: 39px;
icon: icon {{ "player/panel_close", menuIconFg }};
iconOver: icon {{ "player/panel_close", menuIconFgOver }};
- iconPosition: point(5px, 6px);
+ iconPosition: point(4px, 6px);
- rippleAreaPosition: point(4px, 5px);
+ rippleAreaPosition: point(4px, 6px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp
index d9be06b6d..abe1c738c 100644
--- a/Telegram/SourceFiles/media/player/media_player_widget.cpp
+++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp
@@ -55,7 +55,7 @@ Widget::Widget(
, _playPause(this, st::mediaPlayerPlayButton)
, _volumeToggle(rightControls(), st::mediaPlayerVolumeToggle)
, _repeatToggle(rightControls(), st::mediaPlayerRepeatButton)
-, _orderToggle(rightControls(), st::mediaPlayerRepeatButton)
+, _orderToggle(rightControls(), st::mediaPlayerOrderButton)
, _speedToggle(rightControls(), st::mediaPlayerSpeedButton)
, _close(this, st::mediaPlayerClose)
, _shadow(this)
diff --git a/Telegram/SourceFiles/media/stories/media_stories_caption_full_view.cpp b/Telegram/SourceFiles/media/stories/media_stories_caption_full_view.cpp
index 6506dd62c..25687468f 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_caption_full_view.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_caption_full_view.cpp
@@ -7,79 +7,168 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/stories/media_stories_caption_full_view.h"
+#include "base/event_filter.h"
#include "core/ui_integration.h"
-#include "ui/widgets/scroll_area.h"
+#include "chat_helpers/compose/compose_show.h"
+#include "media/stories/media_stories_controller.h"
+#include "media/stories/media_stories_view.h"
+#include "ui/widgets/elastic_scroll.h"
#include "ui/widgets/labels.h"
+#include "ui/click_handler.h"
#include "styles/style_media_view.h"
namespace Media::Stories {
-CaptionFullView::CaptionFullView(
- not_null parent,
- not_null session,
- const TextWithEntities &text,
- Fn close)
-: RpWidget(parent)
-, _scroll(std::make_unique((RpWidget*)this))
-, _text(_scroll->setOwnedWidget(
+CaptionFullView::CaptionFullView(not_null controller)
+: _controller(controller)
+, _scroll(std::make_unique(controller->wrap()))
+, _wrap(_scroll->setOwnedWidget(
object_ptr>(
_scroll.get(),
object_ptr(_scroll.get(), st::storiesCaptionFull),
- st::mediaviewCaptionPadding))->entity())
-, _close(std::move(close))
-, _background(st::storiesRadius, st::mediaviewCaptionBg) {
- _text->setMarkedText(text, Core::MarkedTextContext{
- .session = session,
+ st::mediaviewCaptionPadding)))
+, _text(_wrap->entity()) {
+ _text->setMarkedText(controller->captionText(), Core::MarkedTextContext{
+ .session = &controller->uiShow()->session(),
.customEmojiRepaint = [=] { _text->update(); },
});
- parent->sizeValue() | rpl::start_with_next([=](QSize size) {
- setGeometry(QRect(QPoint(), size));
- }, lifetime());
+ startAnimation();
+ _controller->layoutValue(
+ ) | rpl::start_with_next([=](const Layout &layout) {
+ if (_outer != layout.content) {
+ const auto skip = layout.header.y()
+ + layout.header.height()
+ - layout.content.y();
+ _outer = layout.content.marginsRemoved({ 0, skip, 0, 0 });
+ updateGeometry();
+ }
+ }, _scroll->lifetime());
- show();
- setFocus();
+ const auto filter = [=](not_null e) {
+ const auto mouse = [&] {
+ return static_cast(e.get());
+ };
+ const auto type = e->type();
+ if (type == QEvent::MouseButtonPress
+ && mouse()->button() == Qt::LeftButton
+ && !ClickHandler::getActive()) {
+ _down = true;
+ } else if (type == QEvent::MouseButtonRelease && _down) {
+ _down = false;
+ if (!ClickHandler::getPressed()) {
+ close();
+ }
+ } else if (type == QEvent::KeyPress
+ && static_cast(e.get())->key() == Qt::Key_Escape) {
+ close();
+ return base::EventFilterResult::Cancel;
+ }
+ return base::EventFilterResult::Continue;
+ };
+ base::install_event_filter(_text.get(), filter);
+ base::install_event_filter(_wrap.get(), filter);
+
+ using Type = Ui::ElasticScroll::OverscrollType;
+
+ rpl::combine(
+ _scroll->positionValue(),
+ _scroll->movementValue()
+ ) | rpl::filter([=] {
+ return !_closing;
+ }) | rpl::start_with_next([=](
+ Ui::ElasticScrollPosition position,
+ Ui::ElasticScrollMovement movement) {
+ const auto overscrollTop = std::max(-position.overscroll, 0);
+ using Phase = Ui::ElasticScrollMovement;
+ if (movement == Phase::Progress) {
+ if (overscrollTop > 0) {
+ _pulling = true;
+ } else {
+ _pulling = false;
+ }
+ } else if (_pulling
+ && (movement == Phase::Momentum
+ || movement == Phase::Returning)) {
+ _pulling = false;
+ if (overscrollTop > st::storiesCaptionPullThreshold) {
+ _closingTopAdded = overscrollTop;
+ _scroll->setOverscrollTypes(Type::None, Type::Real);
+ close();
+ updateGeometry();
+ }
+ }
+ }, _scroll->lifetime());
+
+ _scroll->show();
+ _scroll->setOverscrollBg(QColor(0, 0, 0, 0));
+ _scroll->setOverscrollTypes(Type::Real, Type::Real);
+ _text->show();
+ _text->setFocus();
}
CaptionFullView::~CaptionFullView() = default;
-void CaptionFullView::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- _background.paint(p, _scroll->geometry());
- _background.paint(p, _scroll->geometry());
+bool CaptionFullView::closing() const {
+ return _closing;
}
-void CaptionFullView::resizeEvent(QResizeEvent *e) {
- const auto wanted = _text->naturalWidth();
+bool CaptionFullView::focused() const {
+ return Ui::InFocusChain(_scroll.get());
+}
+
+void CaptionFullView::close() {
+ if (_closing) {
+ return;
+ }
+ _closing = true;
+ _controller->captionClosing();
+ startAnimation();
+}
+
+void CaptionFullView::updateGeometry() {
+ if (_outer.isEmpty()) {
+ return;
+ }
+ const auto lineHeight = st::mediaviewCaptionStyle.font->height;
const auto padding = st::mediaviewCaptionPadding;
- const auto margin = st::mediaviewCaptionMargin * 2;
- const auto available = (rect() - padding).width()
- - (margin.width() * 2);
- const auto use = std::min(wanted, available);
- _text->resizeToWidth(use);
- const auto fullw = use + padding.left() + padding.right();
- const auto fullh = std::min(
- _text->height() + padding.top() + padding.bottom(),
- height() - (margin.height() * 2));
- const auto left = (width() - fullw) / 2;
- const auto top = (height() - fullh) / 2;
- _scroll->setGeometry(left, top, fullw, fullh);
-}
-
-void CaptionFullView::keyPressEvent(QKeyEvent *e) {
- if (e->key() == Qt::Key_Escape) {
- if (const auto onstack = _close) {
- onstack();
- }
+ _text->resizeToWidth(_outer.width() - padding.left() - padding.right());
+ const auto add = padding.top() + padding.bottom();
+ const auto maxShownHeight = lineHeight * kMaxShownCaptionLines;
+ const auto shownHeight = (_text->height() > maxShownHeight)
+ ? (lineHeight * kCollapsedCaptionLines)
+ : _text->height();
+ const auto collapsedHeight = shownHeight + add;
+ const auto addedToBottom = lineHeight;
+ const auto expandedHeight = _text->height() + add + addedToBottom;
+ const auto fullHeight = std::min(expandedHeight, _outer.height());
+ const auto shown = _animation.value(_closing ? 0. : 1.);
+ const auto height = (_closing || _animation.animating())
+ ? anim::interpolate(collapsedHeight, fullHeight, shown)
+ : _outer.height();
+ const auto added = anim::interpolate(0, _closingTopAdded, shown);
+ const auto bottomPadding = anim::interpolate(0, addedToBottom, shown);
+ const auto use = padding + ((_closing || _animation.animating())
+ ? QMargins(0, 0, 0, bottomPadding)
+ : QMargins(0, height - fullHeight, 0, bottomPadding));
+ _wrap->setPadding(use);
+ _scroll->setGeometry(
+ _outer.x(),
+ added + _outer.y() + _outer.height() - height,
+ _outer.width(),
+ std::max(height - added, 0));
+ if (_closing && !_animation.animating()) {
+ _controller->captionClosed();
}
}
-void CaptionFullView::mousePressEvent(QMouseEvent *e) {
- if (e->button() == Qt::LeftButton) {
- if (const auto onstack = _close) {
- onstack();
- }
- }
+void CaptionFullView::startAnimation() {
+ _animation.start(
+ [=] { updateGeometry(); },
+ _closing ? 1. : 0.,
+ _closing ? 0. : 1.,
+ st::fadeWrapDuration,
+ anim::sineInOut);
}
} // namespace Media::Stories
diff --git a/Telegram/SourceFiles/media/stories/media_stories_caption_full_view.h b/Telegram/SourceFiles/media/stories/media_stories_caption_full_view.h
index 94b6e2cff..b51ccb0d9 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_caption_full_view.h
+++ b/Telegram/SourceFiles/media/stories/media_stories_caption_full_view.h
@@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
-#include "ui/rp_widget.h"
-#include "ui/round_rect.h"
+#include "ui/effects/animations.h"
namespace Main {
class Session;
@@ -16,30 +15,38 @@ class Session;
namespace Ui {
class FlatLabel;
-class ScrollArea;
+class ElasticScroll;
+template
+class PaddingWrap;
} // namespace Ui
namespace Media::Stories {
-class CaptionFullView final : private Ui::RpWidget {
+class Controller;
+
+class CaptionFullView final {
public:
- CaptionFullView(
- not_null parent,
- not_null session,
- const TextWithEntities &text,
- Fn close);
+ explicit CaptionFullView(not_null controller);
~CaptionFullView();
-private:
- void paintEvent(QPaintEvent *e) override;
- void resizeEvent(QResizeEvent *e) override;
- void keyPressEvent(QKeyEvent *e) override;
- void mousePressEvent(QMouseEvent *e) override;
+ void close();
+ [[nodiscard]] bool closing() const;
+ [[nodiscard]] bool focused() const;
- std::unique_ptr _scroll;
+private:
+ void updateGeometry();
+ void startAnimation();
+
+ const not_null _controller;
+ const std::unique_ptr _scroll;
+ const not_null*> _wrap;
const not_null _text;
- Fn _close;
- Ui::RoundRect _background;
+ Ui::Animations::Simple _animation;
+ QRect _outer;
+ int _closingTopAdded = 0;
+ bool _pulling = false;
+ bool _closing = false;
+ bool _down = false;
};
diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp
index 2c2efc7d6..237401606 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp
@@ -64,7 +64,7 @@ namespace {
constexpr auto kPhotoProgressInterval = crl::time(100);
constexpr auto kPhotoDuration = 5 * crl::time(1000);
-constexpr auto kFullContentFade = 0.35;
+constexpr auto kFullContentFade = 0.6;
constexpr auto kSiblingMultiplierDefault = 0.448;
constexpr auto kSiblingMultiplierMax = 0.72;
constexpr auto kSiblingOutsidePart = 0.24;
@@ -280,9 +280,6 @@ Controller::Controller(not_null delegate)
_1 || _2
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool active) {
- if (active) {
- _captionFullView = nullptr;
- }
_replyActive = active;
updateContentFaded();
}, _lifetime);
@@ -322,8 +319,18 @@ Controller::Controller(not_null delegate)
_delegate->storiesLayerShown(
) | rpl::start_with_next([=](bool shown) {
- _layerShown = shown;
- updatePlayingAllowed();
+ if (_layerShown != shown) {
+ _layerShown = shown;
+ updatePlayingAllowed();
+ }
+ }, _lifetime);
+
+ _header->tooltipShownValue(
+ ) | rpl::start_with_next([=](bool shown) {
+ if (_tooltipShown != shown) {
+ _tooltipShown = shown;
+ updatePlayingAllowed();
+ }
}, _lifetime);
const auto window = _wrap->window()->windowHandle();
@@ -345,7 +352,8 @@ Controller::~Controller() {
}
void Controller::updateContentFaded() {
- const auto faded = _replyActive || _captionFullView || _captionExpanded;
+ const auto faded = _replyActive
+ || (_captionFullView && !_captionFullView->closing());
if (_contentFaded == faded) {
return;
}
@@ -378,6 +386,8 @@ void Controller::initLayout() {
_layout = _wrap->sizeValue(
) | rpl::map([=](QSize size) {
+ const auto topNotchSkip = _delegate->storiesTopNotchSkip();
+
size = QSize(
std::max(size.width(), st::mediaviewMinWidth),
std::max(size.height(), st::mediaviewMinHeight));
@@ -387,7 +397,8 @@ void Controller::initLayout() {
? HeaderLayout::Outside
: HeaderLayout::Normal;
- const auto topSkip = st::storiesFieldMargin.bottom()
+ const auto topSkip = topNotchSkip
+ + st::storiesFieldMargin.bottom()
+ (layout.headerLayout == HeaderLayout::Outside
? outsideHeaderHeight
: 0);
@@ -571,26 +582,31 @@ TextWithEntities Controller::captionText() const {
return _captionText;
}
-void Controller::setCaptionExpanded(bool expanded) {
- if (_captionExpanded == expanded) {
- return;
- }
- _captionExpanded = expanded;
- updateContentFaded();
+bool Controller::skipCaption() const {
+ return _captionFullView != nullptr;
}
void Controller::showFullCaption() {
if (_captionText.empty()) {
return;
}
- _captionFullView = std::make_unique(
- wrap(),
- &_delegate->storiesShow()->session(),
- _captionText,
- [=] { _captionFullView = nullptr; updateContentFaded(); });
+ _captionFullView = std::make_unique(this);
updateContentFaded();
}
+void Controller::captionClosing() {
+ updateContentFaded();
+}
+
+void Controller::captionClosed() {
+ if (!_captionFullView) {
+ return;
+ } else if (_captionFullView->focused()) {
+ _wrap->setFocus();
+ }
+ _captionFullView = nullptr;
+}
+
std::shared_ptr Controller::uiShow() const {
return _delegate->storiesShow();
}
@@ -807,9 +823,8 @@ void Controller::show(
_slider->raise();
}
+ captionClosed();
_captionText = story->caption();
- _captionFullView = nullptr;
- _captionExpanded = false;
_contentFaded = false;
_contentFadeAnimation.stop();
const auto document = story->document();
@@ -823,6 +838,7 @@ void Controller::show(
.video = (document != nullptr),
.silent = (document && document->isSilentVideo()),
});
+ uiShow()->hideLayer(anim::type::instant);
if (!changeShown(story)) {
return;
}
@@ -856,9 +872,10 @@ bool Controller::changeShown(Data::Story *story) {
if (_shown == id && !sessionChanged) {
return false;
}
- if (const auto now = this->story()) {
- now->owner().stories().unregisterPolling(
- now,
+ if (_shown) {
+ Assert(_session != nullptr);
+ _session->data().stories().unregisterPolling(
+ _shown,
Data::Stories::Polling::Viewer);
}
if (sessionChanged) {
@@ -958,16 +975,13 @@ void Controller::updatePlayingAllowed() {
&& _windowActive
&& !_paused
&& !_replyActive
- && !_captionFullView
- && !_captionExpanded
+ && (!_captionFullView || _captionFullView->closing())
&& !_layerShown
- && !_menuShown);
+ && !_menuShown
+ && !_tooltipShown);
}
void Controller::setPlayingAllowed(bool allowed) {
- if (allowed) {
- _captionFullView = nullptr;
- }
if (_photoPlayback) {
_photoPlayback->togglePaused(!allowed);
} else {
@@ -1177,6 +1191,9 @@ void Controller::togglePaused(bool paused) {
void Controller::contentPressed(bool pressed) {
togglePaused(pressed);
+ if (_captionFullView) {
+ _captionFullView->close();
+ }
if (pressed) {
_reactions->collapse();
}
diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h
index 760835a81..35d82c9b0 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_controller.h
+++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h
@@ -122,8 +122,10 @@ public:
[[nodiscard]] bool closeByClickAt(QPoint position) const;
[[nodiscard]] Data::FileOrigin fileOrigin() const;
[[nodiscard]] TextWithEntities captionText() const;
- void setCaptionExpanded(bool expanded);
+ [[nodiscard]] bool skipCaption() const;
void showFullCaption();
+ void captionClosing();
+ void captionClosed();
[[nodiscard]] std::shared_ptr uiShow() const;
[[nodiscard]] auto stickerOrEmojiChosen() const
@@ -250,13 +252,13 @@ private:
Ui::Animations::Simple _contentFadeAnimation;
bool _contentFaded = false;
- bool _captionExpanded = false;
bool _windowActive = false;
bool _replyFocused = false;
bool _replyActive = false;
bool _hasSendText = false;
bool _layerShown = false;
bool _menuShown = false;
+ bool _tooltipShown = false;
bool _paused = false;
FullStoryId _shown;
diff --git a/Telegram/SourceFiles/media/stories/media_stories_delegate.h b/Telegram/SourceFiles/media/stories/media_stories_delegate.h
index ca663e69c..d1a06a52e 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_delegate.h
+++ b/Telegram/SourceFiles/media/stories/media_stories_delegate.h
@@ -64,6 +64,7 @@ public:
virtual void storiesVolumeToggle() = 0;
virtual void storiesVolumeChanged(float64 volume) = 0;
virtual void storiesVolumeChangeFinished() = 0;
+ [[nodiscard]] virtual int storiesTopNotchSkip() = 0;
};
} // namespace Media::Stories
diff --git a/Telegram/SourceFiles/media/stories/media_stories_header.cpp b/Telegram/SourceFiles/media/stories/media_stories_header.cpp
index 5dc7f6b15..ac85729ee 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_header.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_header.cpp
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/labels.h"
+#include "ui/widgets/tooltip.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/painter.h"
#include "ui/rp_widget.h"
@@ -50,10 +51,9 @@ struct PrivacyBadge {
class UserpicBadge final : public Ui::RpWidget {
public:
- UserpicBadge(
- not_null userpic,
- PrivacyBadge badge,
- Fn clicked);
+ UserpicBadge(not_null userpic, PrivacyBadge badge);
+
+ [[nodiscard]] QRect badgeGeometry() const;
private:
bool eventFilter(QObject *o, QEvent *e) override;
@@ -63,7 +63,6 @@ private:
const not_null _userpic;
const PrivacyBadge _badgeData;
- const std::unique_ptr _clickable;
QRect _badge;
QImage _layer;
bool _grabbing = false;
@@ -95,15 +94,10 @@ private:
return {};
}
-UserpicBadge::UserpicBadge(
- not_null userpic,
- PrivacyBadge badge,
- Fn clicked)
+UserpicBadge::UserpicBadge(not_null userpic, PrivacyBadge badge)
: RpWidget(userpic->parentWidget())
, _userpic(userpic)
-, _badgeData(badge)
-, _clickable(std::make_unique(parentWidget())) {
- _clickable->setClickedCallback(std::move(clicked));
+, _badgeData(badge) {
userpic->installEventFilter(this);
updateGeometry();
setAttribute(Qt::WA_TransparentForMouseEvents);
@@ -113,6 +107,10 @@ UserpicBadge::UserpicBadge(
show();
}
+QRect UserpicBadge::badgeGeometry() const {
+ return _badge;
+}
+
bool UserpicBadge::eventFilter(QObject *o, QEvent *e) {
if (o != _userpic) {
return false;
@@ -173,22 +171,27 @@ void UserpicBadge::updateGeometry() {
_badge = QRect(
QPoint(width - badge.width(), height - badge.height()),
badge);
- _clickable->setGeometry(_badge.translated(pos()));
update();
}
-[[nodiscard]] std::unique_ptr MakePrivacyBadge(
+struct MadePrivacyBadge {
+ std::unique_ptr widget;
+ QRect geometry;
+};
+
+[[nodiscard]] MadePrivacyBadge MakePrivacyBadge(
not_null userpic,
- Data::StoryPrivacy privacy,
- Fn clicked) {
+ Data::StoryPrivacy privacy) {
const auto badge = LookupPrivacyBadge(privacy);
if (!badge.icon) {
- return nullptr;
+ return {};
}
- return std::make_unique(
- userpic,
- badge,
- std::move(clicked));
+ auto widget = std::make_unique(userpic, badge);
+ const auto geometry = widget->badgeGeometry();
+ return {
+ .widget = std::move(widget),
+ .geometry = geometry,
+ };
}
[[nodiscard]] Timestamp ComposeTimestamp(TimeId when, TimeId now) {
@@ -277,6 +280,8 @@ void Header::show(HeaderData data) {
_info->setGeometry({ 0, 0, r, _widget->height() });
}
};
+ _tooltip = nullptr;
+ _tooltipShown = false;
if (userChanged) {
_volume = nullptr;
_date = nullptr;
@@ -310,7 +315,7 @@ void Header::show(HeaderData data) {
raw,
rpl::single(data.user->isSelf()
? tr::lng_stories_my_name(tr::now)
- : data.user->shortName()),
+ : data.user->name()),
st::storiesHeaderName);
_name->setAttribute(Qt::WA_TransparentForMouseEvents);
_name->setOpacity(kNameOpacity);
@@ -328,6 +333,8 @@ void Header::show(HeaderData data) {
_controller->layoutValue(
) | rpl::start_with_next([=](const Layout &layout) {
raw->setGeometry(layout.header);
+ _contentGeometry = layout.content;
+ updateTooltipGeometry();
}, raw->lifetime());
}
auto timestamp = ComposeDetails(data, base::unixtime::now());
@@ -357,8 +364,29 @@ void Header::show(HeaderData data) {
_counter = nullptr;
}
- _privacy = MakePrivacyBadge(_userpic.get(), data.privacy, [=] {
- });
+ auto made = MakePrivacyBadge(_userpic.get(), data.privacy);
+ _privacy = std::move(made.widget);
+ _privacyBadgeOver = false;
+ _privacyBadgeGeometry = _privacy
+ ? Ui::MapFrom(_info.get(), _privacy.get(), made.geometry)
+ : QRect();
+ if (_privacy) {
+ _info->setMouseTracking(true);
+ _info->events(
+ ) | rpl::filter([=](not_null e) {
+ const auto type = e->type();
+ if (type != QEvent::Leave && type != QEvent::MouseMove) {
+ return false;
+ }
+ const auto over = (type == QEvent::MouseMove)
+ && _privacyBadgeGeometry.contains(
+ static_cast(e.get())->pos());
+ return (_privacyBadgeOver != over);
+ }) | rpl::start_with_next([=] {
+ _privacyBadgeOver = !_privacyBadgeOver;
+ toggleTooltip(Tooltip::Privacy, _privacyBadgeOver);
+ }, _privacy->lifetime());
+ }
if (data.video) {
createPlayPause();
@@ -369,6 +397,7 @@ void Header::show(HeaderData data) {
_playPause->moveToRight(playPause.x(), playPause.y(), width);
const auto volume = st::storiesVolumeButtonPosition;
_volumeToggle->moveToRight(volume.x(), volume.y(), width);
+ updateTooltipGeometry();
}, _playPause->lifetime());
_pauseState = _controller->pauseState();
@@ -496,15 +525,14 @@ void Header::createVolumeToggle() {
_volumeToggle->events(
) | rpl::start_with_next([=](not_null e) {
- if (state->silent) {
- return;
- }
const auto type = e->type();
if (type == QEvent::Enter || type == QEvent::Leave) {
const auto over = (e->type() == QEvent::Enter);
if (state->over != over) {
state->over = over;
- if (over) {
+ if (state->silent) {
+ toggleTooltip(Tooltip::SilentVideo, over);
+ } else if (over) {
state->hideTimer.cancel();
_volume->toggle(true, anim::type::normal);
} else if (!state->dropdownOver) {
@@ -565,6 +593,123 @@ void Header::createVolumeToggle() {
}
}
+void Header::toggleTooltip(Tooltip type, bool show) {
+ const auto guard = gsl::finally([&] {
+ _tooltipShown = (_tooltip != nullptr);
+ });
+ if (const auto was = _tooltip.release()) {
+ was->toggleAnimated(false);
+ }
+ if (!show) {
+ return;
+ }
+ const auto text = [&]() -> TextWithEntities {
+ using Privacy = Data::StoryPrivacy;
+ const auto boldName = Ui::Text::Bold(_data->user->shortName());
+ const auto self = _data->user->isSelf();
+ switch (type) {
+ case Tooltip::SilentVideo:
+ return { tr::lng_stories_about_silent(tr::now) };
+ case Tooltip::Privacy: switch (_data->privacy) {
+ case Privacy::CloseFriends:
+ return self
+ ? tr::lng_stories_about_close_friends_my(
+ tr::now,
+ Ui::Text::RichLangValue)
+ : tr::lng_stories_about_close_friends(
+ tr::now,
+ lt_user,
+ boldName,
+ Ui::Text::RichLangValue);
+ case Privacy::Contacts:
+ return self
+ ? tr::lng_stories_about_contacts_my(
+ tr::now,
+ Ui::Text::RichLangValue)
+ : tr::lng_stories_about_contacts(
+ tr::now,
+ lt_user,
+ boldName,
+ Ui::Text::RichLangValue);
+ case Privacy::SelectedContacts:
+ return self
+ ? tr::lng_stories_about_selected_contacts_my(
+ tr::now,
+ Ui::Text::RichLangValue)
+ : tr::lng_stories_about_selected_contacts(
+ tr::now,
+ lt_user,
+ boldName,
+ Ui::Text::RichLangValue);
+ }
+ }
+ return {};
+ }();
+ if (text.empty()) {
+ return;
+ }
+ _tooltipType = type;
+ _tooltip = std::make_unique(
+ _widget->parentWidget(),
+ Ui::MakeNiceTooltipLabel(
+ _widget.get(),
+ rpl::single(text),
+ st::storiesInfoTooltipMaxWidth,
+ st::storiesInfoTooltipLabel),
+ st::storiesInfoTooltip);
+ const auto tooltip = _tooltip.get();
+ const auto weak = QPointer(tooltip);
+ const auto destroy = [=] {
+ delete weak.data();
+ };
+ tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
+ tooltip->setHiddenCallback(destroy);
+ updateTooltipGeometry();
+ tooltip->toggleAnimated(true);
+}
+
+void Header::updateTooltipGeometry() {
+ if (!_tooltip) {
+ return;
+ }
+ const auto geometry = [&] {
+ switch (_tooltipType) {
+ case Tooltip::SilentVideo:
+ return Ui::MapFrom(
+ _widget->parentWidget(),
+ _volumeToggle.get(),
+ _volumeToggle->rect());
+ case Tooltip::Privacy:
+ return Ui::MapFrom(
+ _widget->parentWidget(),
+ _info.get(),
+ _privacyBadgeGeometry.marginsAdded(
+ st::storiesInfoTooltip.padding));
+ }
+ return QRect();
+ }();
+ if (geometry.isEmpty()) {
+ toggleTooltip(Tooltip::None, false);
+ return;
+ }
+ const auto weak = QPointer(_tooltip.get());
+ const auto countPosition = [=](QSize size) {
+ const auto result = geometry.bottomLeft()
+ - QPoint(size.width() / 2, 0);
+ const auto inner = _contentGeometry.marginsRemoved(
+ st::storiesInfoTooltip.padding);
+ if (size.width() > inner.width()) {
+ return QPoint(
+ inner.x() + (inner.width() - size.width()) / 2,
+ result.y());
+ } else if (result.x() < inner.x()) {
+ return QPoint(inner.x(), result.y());
+ }
+ return result;
+ };
+ _tooltip->pointAt(geometry, RectPart::Bottom, countPosition);
+}
+
void Header::rebuildVolumeControls(
not_null dropdown,
bool horizontal) {
@@ -682,11 +827,14 @@ void Header::raise() {
}
}
-
bool Header::ignoreWindowMove(QPoint position) const {
return _ignoreWindowMove;
}
+rpl::producer Header::tooltipShownValue() const {
+ return _tooltipShown.value();
+}
+
void Header::updateDateText() {
if (!_date || !_data || !_data->date) {
return;
diff --git a/Telegram/SourceFiles/media/stories/media_stories_header.h b/Telegram/SourceFiles/media/stories/media_stories_header.h
index 12a66b1a1..c10df3ec2 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_header.h
+++ b/Telegram/SourceFiles/media/stories/media_stories_header.h
@@ -20,6 +20,7 @@ class FlatLabel;
class IconButton;
class AbstractButton;
class UserpicButton;
+class ImportantTooltip;
template
class FadeWrap;
} // namespace Ui
@@ -55,8 +56,15 @@ public:
void raise();
[[nodiscard]] bool ignoreWindowMove(QPoint position) const;
+ [[nodiscard]] rpl::producer tooltipShownValue() const;
private:
+ enum class Tooltip {
+ None,
+ SilentVideo,
+ Privacy,
+ };
+
void updateDateText();
void applyPauseState();
void createPlayPause();
@@ -64,6 +72,8 @@ private:
void rebuildVolumeControls(
not_null dropdown,
bool horizontal);
+ void toggleTooltip(Tooltip type, bool show);
+ void updateTooltipGeometry();
const not_null _controller;
@@ -81,9 +91,15 @@ private:
std::unique_ptr> _volume;
rpl::variable _volumeIcon;
std::unique_ptr _privacy;
+ QRect _privacyBadgeGeometry;
std::optional _data;
+ std::unique_ptr _tooltip;
+ rpl::variable _tooltipShown = false;
+ QRect _contentGeometry;
+ Tooltip _tooltipType = {};
base::Timer _dateUpdateTimer;
bool _ignoreWindowMove = false;
+ bool _privacyBadgeOver = false;
};
diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp
index 9df6d290f..7ea8ce8b7 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp
@@ -186,8 +186,8 @@ void ReplyArea::send(
session().api().sendMessage(std::move(message));
- _controls->clear();
finishSending(skipToast);
+ _controls->clear();
}
void ReplyArea::sendVoice(VoiceToSend &&data) {
@@ -276,8 +276,6 @@ void ReplyArea::sendInlineResult(
action.generateLocal = true;
session().api().sendInlineResult(bot, result, action, localMessageId);
- _controls->clear();
-
auto &bots = cRefRecentInlineBots();
const auto index = bots.indexOf(bot);
if (index) {
@@ -290,11 +288,12 @@ void ReplyArea::sendInlineResult(
bot->session().local().writeRecentHashtagsAndBots();
}
finishSending();
+ _controls->clear();
}
void ReplyArea::finishSending(bool skipToast) {
_controls->hidePanelsAnimated();
- _controller->wrap()->setFocus();
+ _controller->unfocusReply();
if (!skipToast) {
_controller->uiShow()->showToast(
tr::lng_stories_reply_sent(tr::now));
diff --git a/Telegram/SourceFiles/media/stories/media_stories_sibling.cpp b/Telegram/SourceFiles/media/stories/media_stories_sibling.cpp
index 1024edadb..791c8235f 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_sibling.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_sibling.cpp
@@ -32,6 +32,7 @@ constexpr auto kSiblingFade = 0.5;
constexpr auto kSiblingFadeOver = 0.4;
constexpr auto kSiblingNameOpacity = 0.8;
constexpr auto kSiblingNameOpacityOver = 1.;
+constexpr auto kSiblingScaleOver = 0.05;
[[nodiscard]] StoryId LookupShownId(
const Data::StoriesSource &source,
@@ -325,6 +326,7 @@ SiblingView Sibling::view(const SiblingLayout &layout, float64 over) {
.namePosition = namePosition(layout, name),
.nameOpacity = (kSiblingNameOpacity * (1 - over)
+ kSiblingNameOpacityOver * over),
+ .scale = 1. + (over * kSiblingScaleOver),
};
}
diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.cpp b/Telegram/SourceFiles/media/stories/media_stories_view.cpp
index 159999281..dfbab352b 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_view.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_view.cpp
@@ -123,8 +123,8 @@ TextWithEntities View::captionText() const {
return _controller->captionText();
}
-void View::setCaptionExpanded(bool expanded) {
- _controller->setCaptionExpanded(expanded);
+bool View::skipCaption() const {
+ return _controller->skipCaption();
}
void View::showFullCaption() {
diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.h b/Telegram/SourceFiles/media/stories/media_stories_view.h
index 57e38e70b..4d6cdac1e 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_view.h
+++ b/Telegram/SourceFiles/media/stories/media_stories_view.h
@@ -25,6 +25,7 @@ class Controller;
struct ContentLayout {
QRect geometry;
float64 fade = 0.;
+ float64 scale = 1.;
int radius = 0;
bool headerOutside = false;
};
@@ -39,6 +40,7 @@ struct SiblingView {
QImage name;
QPoint namePosition;
float64 nameOpacity = 0.;
+ float64 scale = 1.;
[[nodiscard]] bool valid() const {
return !image.isNull();
@@ -48,6 +50,9 @@ struct SiblingView {
}
};
+inline constexpr auto kCollapsedCaptionLines = 2;
+inline constexpr auto kMaxShownCaptionLines = 4;
+
class View final {
public:
explicit View(not_null delegate);
@@ -64,7 +69,7 @@ public:
[[nodiscard]] SiblingView sibling(SiblingType type) const;
[[nodiscard]] Data::FileOrigin fileOrigin() const;
[[nodiscard]] TextWithEntities captionText() const;
- void setCaptionExpanded(bool expanded);
+ [[nodiscard]] bool skipCaption() const;
void showFullCaption();
void updatePlayback(const Player::TrackState &state);
diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style
index d463e8adc..adf770688 100644
--- a/Telegram/SourceFiles/media/view/media_view.style
+++ b/Telegram/SourceFiles/media/view/media_view.style
@@ -445,7 +445,8 @@ storiesSideSkip: 145px;
storiesCaptionFull: FlatLabel(defaultFlatLabel) {
style: mediaviewCaptionStyle;
textFg: mediaviewCaptionFg;
- minWidth: 360px;
+ palette: mediaviewTextPalette;
+ minWidth: 36px;
}
storiesComposeBg: groupCallMembersBg;
storiesComposeBgOver: groupCallMembersBgOver;
@@ -570,7 +571,6 @@ storiesEmojiPan: EmojiPan(defaultEmojiPan) {
trendingHeaderFg: storiesComposeWhiteText;
trendingSubheaderFg: storiesComposeGrayText;
trendingUnreadFg: storiesComposeBlue;
- trendingInstalled: icon {{ "chat/input_save", storiesComposeBlue }};
overBg: storiesComposeBgOver;
pathBg: storiesComposeBgRipple;
pathFg: storiesComposeBgOver;
@@ -752,6 +752,20 @@ storiesComposeControls: ComposeControls(defaultComposeControls) {
statusFg: storiesComposeGrayText;
}
premium: storiesComposePremium;
+ boxField: InputField(defaultInputField) {
+ textBg: transparent;
+ textFg: groupCallMembersFg;
+
+ placeholderFg: groupCallMemberNotJoinedStatus;
+ placeholderFgActive: groupCallMemberNotJoinedStatus;
+ placeholderFgError: groupCallMemberNotJoinedStatus;
+
+ borderFg: inputBorderFg;
+ borderFgActive: groupCallMemberInactiveStatus;
+ borderFgError: activeLineFgError;
+
+ menu: storiesPopupMenu;
+ }
}
storiesViewsMenu: PopupMenu(storiesPopupMenuWithIcons) {
scrollPadding: margins(0px, 6px, 0px, 4px);
@@ -891,3 +905,9 @@ storiesVolumeSlider: MediaSlider {
seekSize: size(12px, 12px);
duration: mediaviewOverDuration;
}
+storiesInfoTooltipLabel: defaultImportantTooltipLabel;
+storiesInfoTooltip: defaultImportantTooltip;
+storiesInfoTooltipMaxWidth: 360px;
+storiesCaptionPullThreshold: 50px;
+storiesShowMorePadding: margins(6px, 4px, 6px, 4px);
+storiesShowMoreFont: semiboldFont;
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp
index 7174aef59..7851ed7f9 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp
@@ -21,7 +21,8 @@ namespace {
using namespace Ui::GL;
-constexpr auto kRadialLoadingOffset = 4;
+constexpr auto kNotchOffset = 4;
+constexpr auto kRadialLoadingOffset = kNotchOffset + 4;
constexpr auto kThemePreviewOffset = kRadialLoadingOffset + 4;
constexpr auto kDocumentBubbleOffset = kThemePreviewOffset + 4;
constexpr auto kSaveMsgOffset = kDocumentBubbleOffset + 4;
@@ -129,7 +130,7 @@ OverlayWidget::RendererGL::RendererGL(not_null owner)
void OverlayWidget::RendererGL::init(
not_null widget,
QOpenGLFunctions &f) {
- constexpr auto kQuads = 8;
+ constexpr auto kQuads = 9;
constexpr auto kQuadVertices = kQuads * 4;
constexpr auto kQuadValues = kQuadVertices * 4;
constexpr auto kControlsValues = kControlsCount * kControlValues;
@@ -291,6 +292,28 @@ bool OverlayWidget::RendererGL::handleHideWorkaround(QOpenGLFunctions &f) {
void OverlayWidget::RendererGL::paintBackground() {
_contentBuffer->bind();
+ if (const auto notch = _owner->topNotchSkip()) {
+ const auto top = transformRect(QRect(0, 0, _owner->width(), notch));
+ const GLfloat coords[] = {
+ top.left(), top.top(),
+ top.right(), top.top(),
+ top.right(), top.bottom(),
+ top.left(), top.bottom(),
+ };
+ const auto offset = kNotchOffset;
+ _contentBuffer->write(
+ offset * 4 * sizeof(GLfloat),
+ coords,
+ sizeof(coords));
+
+ _fillProgram->bind();
+ _fillProgram->setUniformValue("viewport", _uniformViewport);
+ FillRectangle(
+ *_f,
+ &*_fillProgram,
+ offset,
+ QColor(0, 0, 0));
+ }
}
void OverlayWidget::RendererGL::paintTransformedVideoFrame(
@@ -465,7 +488,9 @@ void OverlayWidget::RendererGL::paintTransformedContent(
not_null program,
ContentGeometry geometry,
bool fillTransparentBackground) {
- const auto rect = transformRect(geometry.rect);
+ const auto rect = scaleRect(
+ transformRect(geometry.rect),
+ geometry.scale);
const auto centerx = rect.x() + rect.width() / 2;
const auto centery = rect.y() + rect.height() / 2;
const auto rsin = float(std::sin(geometry.rotation * M_PI / 180.));
@@ -1043,4 +1068,17 @@ Rect OverlayWidget::RendererGL::transformRect(const QRect &raster) const {
return TransformRect(Rect(raster), _viewport, _factor);
}
+Rect OverlayWidget::RendererGL::scaleRect(
+ const Rect &unscaled,
+ float64 scale) const {
+ const auto added = scale - 1.;
+ const auto addw = unscaled.width() * added;
+ const auto addh = unscaled.height() * added;
+ return Rect(
+ unscaled.x() - addw / 2,
+ unscaled.y() - addh / 2,
+ unscaled.width() + addw,
+ unscaled.height() + addh);
+}
+
} // namespace Media::View
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h
index 4f769ce7b..a0342518a 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h
@@ -97,6 +97,9 @@ private:
[[nodiscard]] Ui::GL::Rect transformRect(const QRectF &raster) const;
[[nodiscard]] Ui::GL::Rect transformRect(
const Ui::GL::Rect &raster) const;
+ [[nodiscard]] Ui::GL::Rect scaleRect(
+ const Ui::GL::Rect &unscaled,
+ float64 scale) const;
void uploadTexture(
GLint internalformat,
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp
index 277d39d96..4ca702d39 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp
@@ -45,6 +45,12 @@ void OverlayWidget::RendererSW::paintBackground() {
for (const auto &rect : region) {
_p->fillRect(rect, bg);
}
+ if (const auto notch = _owner->topNotchSkip()) {
+ const auto top = QRect(0, 0, _owner->width(), notch);
+ if (const auto black = top.intersected(_clipOuter); !black.isEmpty()) {
+ _p->fillRect(black, Qt::black);
+ }
+ }
_p->setCompositionMode(m);
}
@@ -101,8 +107,8 @@ void OverlayWidget::RendererSW::paintTransformedStaticContent(
}
void OverlayWidget::RendererSW::paintControlsFade(
- QRect content,
- const ContentGeometry &geometry) {
+ QRect content,
+ const ContentGeometry &geometry) {
auto opacity = geometry.controlsOpacity;
if (geometry.fade > 0.) {
_p->setOpacity(geometry.fade);
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
index 0a8be2dc6..04d814478 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
@@ -587,6 +587,16 @@ OverlayWidget::OverlayWidget()
update();
}, lifetime());
+ _helper->topNotchSkipValue(
+ ) | rpl::start_with_next([=](int notch) {
+ if (_topNotchSize != notch) {
+ _topNotchSize = notch;
+ if (_fullscreen) {
+ updateControlsGeometry();
+ }
+ }
+ }, lifetime());
+
_window->setTitle(tr::lng_mediaview_title(tr::now));
_window->setTitleStyle(st::mediaviewTitle);
@@ -673,7 +683,11 @@ void OverlayWidget::showSaveMsgToastWith(
const auto h = st::mediaviewSaveMsgStyle.font->height
+ st::mediaviewSaveMsgPadding.top()
+ st::mediaviewSaveMsgPadding.bottom();
- _saveMsg = QRect((width() - w) / 2, (height() - h) / 2, w, h);
+ _saveMsg = QRect(
+ (width() - w) / 2,
+ _minUsedTop + (_maxUsedHeight - h) / 2,
+ w,
+ h);
const auto callback = [=](float64 value) {
updateSaveMsg();
if (!_saveMsgAnimation.animating()) {
@@ -703,7 +717,7 @@ void OverlayWidget::setupWindow() {
&& _streamed->controls
&& _streamed->controls->dragging())) {
return Flag::None | Flag(0);
- } else if ((_w > _widget->width() || _h > _widget->height())
+ } else if ((_w > _widget->width() || _h > _maxUsedHeight)
&& (widgetPoint.y() > st::mediaviewHeaderTop)
&& QRect(_x, _y, _w, _h).contains(widgetPoint)) {
return Flag::None | Flag(0);
@@ -940,8 +954,14 @@ void OverlayWidget::updateGeometryToScreen(bool inMove) {
void OverlayWidget::updateControlsGeometry() {
updateNavigationControlsGeometry();
- _saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
- _photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize);
+ _saveMsg.moveTo(
+ (width() - _saveMsg.width()) / 2,
+ _minUsedTop + (_maxUsedHeight - _saveMsg.height()) / 2);
+ _photoRadialRect = QRect(
+ QPoint(
+ (width() - st::radialSize.width()) / 2,
+ _minUsedTop + (_maxUsedHeight - st::radialSize.height()) / 2),
+ st::radialSize);
const auto bottom = st::mediaviewShadowBottom.height();
const auto top = st::mediaviewShadowTop.size();
@@ -960,6 +980,9 @@ void OverlayWidget::updateControlsGeometry() {
}
void OverlayWidget::updateNavigationControlsGeometry() {
+ _minUsedTop = topNotchSkip();
+ _maxUsedHeight = height() - _minUsedTop;
+
const auto overRect = QRect(
QPoint(),
QSize(st::mediaviewIconOver, st::mediaviewIconOver));
@@ -969,14 +992,22 @@ void OverlayWidget::updateNavigationControlsGeometry() {
const auto navSkip = st::mediaviewHeaderTop;
const auto xLeft = _stories ? (_x - navSize) : 0;
const auto xRight = _stories ? (_x + _w) : (width() - navSize);
- _leftNav = QRect(xLeft, navSkip, navSize, height() - 2 * navSkip);
+ _leftNav = QRect(
+ xLeft,
+ _minUsedTop + navSkip,
+ navSize,
+ _maxUsedHeight - 2 * navSkip);
_leftNavOver = _stories
? QRect()
: style::centerrect(_leftNav, overRect);
_leftNavIcon = style::centerrect(
_leftNav,
_stories ? st::storiesLeft : st::mediaviewLeft);
- _rightNav = QRect(xRight, navSkip, navSize, height() - 2 * navSkip);
+ _rightNav = QRect(
+ xRight,
+ _minUsedTop + navSkip,
+ navSize,
+ _maxUsedHeight - 2 * navSkip);
_rightNavOver = _stories
? QRect()
: style::centerrect(_rightNav, overRect);
@@ -1213,7 +1244,7 @@ void OverlayWidget::updateControls() {
if (_document && documentBubbleShown()) {
_docRect = QRect(
(width() - st::mediaviewFileSize.width()) / 2,
- (height() - st::mediaviewFileSize.height()) / 2,
+ _minUsedTop + (_maxUsedHeight - st::mediaviewFileSize.height()) / 2,
st::mediaviewFileSize.width(),
st::mediaviewFileSize.height());
_docIconRect = QRect(
@@ -1244,7 +1275,7 @@ void OverlayWidget::updateControls() {
} else {
_docIconRect = QRect(
(width() - st::mediaviewFileIconSize) / 2,
- (height() - st::mediaviewFileIconSize) / 2,
+ _minUsedTop + (_maxUsedHeight - st::mediaviewFileIconSize) / 2,
st::mediaviewFileIconSize,
st::mediaviewFileIconSize);
_docDownload->hide();
@@ -1263,7 +1294,8 @@ void OverlayWidget::updateControls() {
_shareVisible = story && story->canShare();
_rotateVisible = !_themePreviewShown && !story;
const auto navRect = [&](int i) {
- return QRect(width() - st::mediaviewIconSize.width() * i,
+ return QRect(
+ width() - st::mediaviewIconSize.width() * i,
height() - st::mediaviewIconSize.height(),
st::mediaviewIconSize.width(),
st::mediaviewIconSize.height());
@@ -1302,12 +1334,27 @@ void OverlayWidget::updateControls() {
}();
_dateText = d.isValid() ? Ui::FormatDateTime(d) : QString();
if (!_fromName.isEmpty()) {
- _fromNameLabel.setText(st::mediaviewTextStyle, _fromName, Ui::NameTextOptions());
- _nameNav = QRect(st::mediaviewTextLeft, height() - st::mediaviewTextTop, qMin(_fromNameLabel.maxWidth(), width() / 3), st::mediaviewFont->height);
- _dateNav = QRect(st::mediaviewTextLeft + _nameNav.width() + st::mediaviewTextSkip, height() - st::mediaviewTextTop, st::mediaviewFont->width(_dateText), st::mediaviewFont->height);
+ _fromNameLabel.setText(
+ st::mediaviewTextStyle,
+ _fromName,
+ Ui::NameTextOptions());
+ _nameNav = QRect(
+ st::mediaviewTextLeft,
+ height() - st::mediaviewTextTop,
+ qMin(_fromNameLabel.maxWidth(), width() / 3),
+ st::mediaviewFont->height);
+ _dateNav = QRect(
+ st::mediaviewTextLeft + _nameNav.width() + st::mediaviewTextSkip,
+ height() - st::mediaviewTextTop,
+ st::mediaviewFont->width(_dateText),
+ st::mediaviewFont->height);
} else {
_nameNav = QRect();
- _dateNav = QRect(st::mediaviewTextLeft, height() - st::mediaviewTextTop, st::mediaviewFont->width(_dateText), st::mediaviewFont->height);
+ _dateNav = QRect(
+ st::mediaviewTextLeft,
+ height() - st::mediaviewTextTop,
+ st::mediaviewFont->width(_dateText),
+ st::mediaviewFont->height);
}
updateHeader();
refreshNavVisibility();
@@ -1336,6 +1383,10 @@ void OverlayWidget::resizeCenteredControls() {
}
void OverlayWidget::refreshCaptionGeometry() {
+ _caption.updateSkipBlock(0, 0);
+ _captionShowMoreWidth = 0;
+ _captionSkipBlockWidth = 0;
+
if (_caption.isEmpty()) {
_captionRect = QRect();
return;
@@ -1361,32 +1412,28 @@ void OverlayWidget::refreshCaptionGeometry() {
- st::mediaviewCaptionPadding.left()
- st::mediaviewCaptionPadding.right()),
_caption.maxWidth());
- const auto maxExpandedOuterHeight = (_stories
- ? (_h - st::storiesShadowTop.height())
- : height());
- const auto maxCollapsedOuterHeight = !_stories
- ? (height() / 4)
- : (_h / 3);
- const auto maxExpandedHeight = maxExpandedOuterHeight
- - st::mediaviewCaptionPadding.top()
- - st::mediaviewCaptionPadding.bottom();
- const auto maxCollapsedHeight = maxCollapsedOuterHeight
- - st::mediaviewCaptionPadding.top()
- - st::mediaviewCaptionPadding.bottom();
const auto lineHeight = st::mediaviewCaptionStyle.font->height;
const auto wantedHeight = _caption.countHeight(captionWidth);
- const auto maxHeight = _captionExpanded
- ? maxExpandedHeight
- : maxCollapsedHeight;
+ const auto maxHeight = !_stories
+ ? (_maxUsedHeight / 4)
+ : (wantedHeight > lineHeight * Stories::kMaxShownCaptionLines)
+ ? (lineHeight * Stories::kCollapsedCaptionLines)
+ : wantedHeight;
const auto captionHeight = std::min(
wantedHeight,
(maxHeight / lineHeight) * lineHeight);
- _captionFitsIfExpanded = _stories
- && (wantedHeight <= maxExpandedHeight);
- _captionShownFull = (wantedHeight <= maxCollapsedHeight);
- if (_captionShownFull && _captionExpanded && _stories) {
- _captionExpanded = false;
- _stories->setCaptionExpanded(false);
+ if (_stories && captionHeight < wantedHeight) {
+ const auto padding = st::storiesShowMorePadding;
+ _captionShowMoreWidth = st::storiesShowMoreFont->width(
+ tr::lng_stories_show_more(tr::now));
+ _captionSkipBlockWidth = _captionShowMoreWidth
+ + padding.left()
+ + padding.right()
+ - st::mediaviewCaptionPadding.right();
+ const auto skiph = st::storiesShowMoreFont->height
+ + padding.bottom()
+ - st::mediaviewCaptionPadding.bottom();
+ _caption.updateSkipBlock(_captionSkipBlockWidth, skiph);
}
_captionRect = QRect(
(width() - captionWidth) / 2,
@@ -1748,11 +1795,13 @@ OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
}
OverlayWidget::ContentGeometry OverlayWidget::storiesContentGeometry(
- const Stories::ContentLayout &layout) const {
+ const Stories::ContentLayout &layout,
+ float64 scale) const {
return {
.rect = QRectF(layout.geometry),
.controlsOpacity = kStoriesControlsOpacity,
.fade = layout.fade,
+ .scale = scale,
.roundRadius = layout.radius,
.topShadowShown = !layout.headerOutside,
};
@@ -1777,7 +1826,7 @@ void OverlayWidget::recountSkipTop() {
? height()
: (_streamed->controls->y() - st::mediaviewCaptionPadding.bottom());
const auto skipHeightBottom = (height() - bottom);
- _skipTop = std::min(
+ _skipTop = _minUsedTop + std::min(
std::max(
st::mediaviewCaptionMargin.height(),
height() - _height - skipHeightBottom),
@@ -1785,7 +1834,7 @@ void OverlayWidget::recountSkipTop() {
_availableHeight = height() - skipHeightBottom - _skipTop;
if (_fullScreenVideo && skipHeightBottom > 0 && _width > 0) {
const auto h = width() * _height / _width;
- const auto topAllFit = height() - skipHeightBottom - h;
+ const auto topAllFit = _maxUsedHeight - skipHeightBottom - h;
if (_skipTop > topAllFit) {
_skipTop = std::max(topAllFit, 0);
}
@@ -1818,12 +1867,12 @@ void OverlayWidget::resizeContentByScreenSize() {
};
if (_width > 0 && _height > 0) {
_zoomToDefault = countZoomFor(availableWidth, _availableHeight);
- _zoomToScreen = countZoomFor(width(), height());
+ _zoomToScreen = countZoomFor(width(), _maxUsedHeight);
} else {
_zoomToDefault = _zoomToScreen = 0;
}
const auto usew = _fullScreenVideo ? width() : availableWidth;
- const auto useh = _fullScreenVideo ? height() : _availableHeight;
+ const auto useh = _fullScreenVideo ? _maxUsedHeight : _availableHeight;
if ((_width > usew) || (_height > useh) || _fullScreenVideo) {
const auto use = _fullScreenVideo ? _zoomToScreen : _zoomToDefault;
_zoom = kZoomToScreenLevel;
@@ -3156,6 +3205,10 @@ void OverlayWidget::show(OpenRequest request) {
}
}
}
+ if (isHidden() || isMinimized()) {
+ // Count top notch on macOS before counting geometry.
+ _helper->beforeShow(_fullscreen);
+ }
if (photo) {
if (contextItem && contextPeer) {
return;
@@ -3444,7 +3497,6 @@ void OverlayWidget::updateThemePreviewGeometry() {
}
void OverlayWidget::displayFinished(anim::activation activation) {
- _captionExpanded = _captionFitsIfExpanded = _captionShownFull = false;
updateControls();
if (isHidden()) {
_helper->beforeShow(_fullscreen);
@@ -3646,6 +3698,7 @@ bool OverlayWidget::createStreamingObjects() {
_streamed->instance.setPriority(kOverlayLoaderPriority);
_streamed->instance.lockPlayer();
_streamed->withSound = _document
+ && !_document->isSilentVideo()
&& (_document->isAudioFile()
|| _document->isVideoFile()
|| _document->isVoiceMessage()
@@ -3986,12 +4039,14 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) {
};
if (!_streamed->withSound) {
options.mode = Streaming::Mode::Video;
- options.loop = true;
+ options.loop = !_stories;
} else {
Assert(_document != nullptr);
const auto messageId = _message ? _message->fullId() : FullMsgId();
options.audioId = AudioMsgId(_document, messageId);
- options.speed = Core::App().settings().videoPlaybackSpeed();
+ options.speed = _stories
+ ? Core::App().settings().videoPlaybackSpeed()
+ : 1.;
if (_pip) {
_pip = nullptr;
}
@@ -4059,7 +4114,7 @@ void OverlayWidget::playbackControlsSpeedChanged(float64 speed) {
Core::App().settings().setVideoPlaybackSpeed(speed);
Core::App().saveSettingsDelayed();
}
- if (_streamed && _streamed->controls) {
+ if (_streamed && _streamed->controls && !_stories) {
DEBUG_LOG(("Media playback speed: %1 to _streamed.").arg(speed));
_streamed->instance.setSpeed(speed);
}
@@ -4240,6 +4295,14 @@ void OverlayWidget::storiesVolumeChangeFinished() {
playbackControlsVolumeChangeFinished();
}
+int OverlayWidget::topNotchSkip() const {
+ return _fullscreen ? _topNotchSize : 0;
+}
+
+int OverlayWidget::storiesTopNotchSkip() {
+ return topNotchSkip();
+}
+
void OverlayWidget::playbackToggleFullScreen() {
Expects(_streamed != nullptr);
@@ -4400,7 +4463,7 @@ void OverlayWidget::paint(not_null renderer) {
const auto paint = [&](const SiblingView &view, int index) {
renderer->paintTransformedStaticContent(
view.image,
- storiesContentGeometry(view.layout),
+ storiesContentGeometry(view.layout, view.scale),
false, // semi-transparent
false, // fill transparent background
index);
@@ -4443,7 +4506,8 @@ void OverlayWidget::paint(not_null renderer) {
if (!_stories) {
renderer->paintFooter(footerGeometry(), opacity);
}
- if (!_caption.isEmpty()) {
+ if (!_caption.isEmpty()
+ && (!_stories || !_stories->skipCaption())) {
renderer->paintCaption(captionGeometry(), opacity);
}
if (_groupThumbs) {
@@ -4853,6 +4917,7 @@ void OverlayWidget::paintCaptionContent(
}
if (inner.intersects(clip)) {
p.setPen(st::mediaviewCaptionFg);
+ const auto lineHeight = st::mediaviewCaptionStyle.font->height;
_caption.draw(p, {
.position = inner.topLeft(),
.availableWidth = inner.width(),
@@ -4860,8 +4925,31 @@ void OverlayWidget::paintCaptionContent(
.spoiler = Ui::Text::DefaultSpoilerCache(),
.pausedEmoji = On(PowerSaving::kEmojiChat),
.pausedSpoiler = On(PowerSaving::kChatSpoiler),
- .elisionLines = inner.height() / st::mediaviewCaptionStyle.font->height,
+ .elisionLines = inner.height() / lineHeight,
+ .elisionRemoveFromEnd = _captionSkipBlockWidth,
});
+
+ if (_captionShowMoreWidth > 0) {
+ const auto padding = st::storiesShowMorePadding;
+ const auto showMoreLeft = outer.x()
+ + outer.width()
+ - padding.right()
+ - _captionShowMoreWidth;
+ const auto showMoreTop = outer.y()
+ + outer.height()
+ - padding.bottom()
+ - st::storiesShowMoreFont->height;
+ const auto underline = _captionExpandLink
+ && ClickHandler::showAsActive(_captionExpandLink);
+ p.setFont(underline
+ ? st::storiesShowMoreFont->underline()
+ : st::storiesShowMoreFont);
+ p.drawTextLeft(
+ showMoreLeft,
+ showMoreTop,
+ width(),
+ tr::lng_stories_show_more(tr::now));
+ }
}
}
@@ -4960,7 +5048,9 @@ void OverlayWidget::handleKeyPress(not_null e) {
if (_controlsHideTimer.isActive()) {
activateControls();
}
- moveToNext(1);
+ if (!moveToNext(1) && _stories) {
+ storiesClose();
+ }
} else if (ctrl) {
if (key == Qt::Key_Plus
|| key == Qt::Key_Equal
@@ -4978,20 +5068,23 @@ void OverlayWidget::handleKeyPress(not_null e) {
void OverlayWidget::handleWheelEvent(not_null e) {
constexpr auto step = int(QWheelEvent::DefaultDeltasPerStep);
+ const auto acceptForJump = !_stories
+ && ((e->source() == Qt::MouseEventNotSynthesized)
+ || (e->source() == Qt::MouseEventSynthesizedBySystem));
_verticalWheelDelta += e->angleDelta().y();
while (qAbs(_verticalWheelDelta) >= step) {
if (_verticalWheelDelta < 0) {
_verticalWheelDelta += step;
if (e->modifiers().testFlag(Qt::ControlModifier)) {
zoomOut();
- } else if (e->source() == Qt::MouseEventNotSynthesized) {
+ } else if (acceptForJump) {
moveToNext(1);
}
} else {
_verticalWheelDelta -= step;
if (e->modifiers().testFlag(Qt::ControlModifier)) {
zoomIn();
- } else if (e->source() == Qt::MouseEventNotSynthesized) {
+ } else if (acceptForJump) {
moveToNext(-1);
}
}
@@ -5366,7 +5459,7 @@ bool OverlayWidget::handleDoubleClick(
void OverlayWidget::snapXY() {
auto xmin = width() - _w, xmax = 0;
- auto ymin = height() - _h, ymax = 0;
+ auto ymin = height() - _h, ymax = _minUsedTop;
accumulate_min(xmin, (width() - _w) / 2);
accumulate_max(xmax, (width() - _w) / 2);
accumulate_min(ymin, _skipTop + (_availableHeight - _h) / 2);
@@ -5390,7 +5483,7 @@ void OverlayWidget::handleMouseMove(QPoint position) {
>= QApplication::startDragDistance())) {
_dragging = QRect(_x, _y, _w, _h).contains(_mStart) ? 1 : -1;
if (_dragging > 0) {
- if (_w > width() || _h > height()) {
+ if (_w > width() || _h > _maxUsedHeight) {
setCursor(style::cur_sizeall);
} else {
setCursor(style::cur_default);
@@ -5486,9 +5579,13 @@ void OverlayWidget::updateOver(QPoint pos) {
lnk = textState.link;
lnkhost = this;
} else if (_captionRect.contains(pos)) {
- auto textState = _caption.getState(pos - _captionRect.topLeft(), _captionRect.width());
+ auto request = Ui::Text::StateRequestElided();
+ const auto lineHeight = st::mediaviewCaptionStyle.font->height;
+ request.lines = _captionRect.height() / lineHeight;
+ request.removeFromEnd = _captionSkipBlockWidth;
+ auto textState = _caption.getStateElided(pos - _captionRect.topLeft(), _captionRect.width(), request);
lnk = textState.link;
- if (_stories && !_captionShownFull && !lnk) {
+ if (_stories && !lnk) {
lnk = ensureCaptionExpandLink();
}
lnkhost = this;
@@ -5567,19 +5664,7 @@ void OverlayWidget::updateOver(QPoint pos) {
ClickHandlerPtr OverlayWidget::ensureCaptionExpandLink() {
if (!_captionExpandLink) {
const auto toggle = crl::guard(_widget, [=] {
- if (!_stories) {
- return;
- } else if (_captionExpanded) {
- _captionExpanded = false;
- _stories->setCaptionExpanded(false);
- refreshCaptionGeometry();
- update();
- } else if (_captionFitsIfExpanded) {
- _captionExpanded = true;
- _stories->setCaptionExpanded(true);
- refreshCaptionGeometry();
- update();
- } else {
+ if (_stories) {
_stories->showFullCaption();
}
});
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h
index b0feb4f1d..06d0c0468 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h
@@ -180,6 +180,7 @@ private:
// Stories.
qreal fade = 0.;
+ qreal scale = 1.;
int bottomShadowSkip = 0;
int roundRadius = 0;
bool topShadowShown = false;
@@ -243,6 +244,7 @@ private:
void playbackResumeOnCall();
void playbackPauseMusic();
void switchToPip();
+ [[nodiscard]] int topNotchSkip() const;
not_null storiesWrap() override;
std::shared_ptr storiesShow() override;
@@ -264,6 +266,7 @@ private:
void storiesVolumeToggle() override;
void storiesVolumeChanged(float64 volume) override;
void storiesVolumeChangeFinished() override;
+ int storiesTopNotchSkip() override;
void hideControls(bool force = false);
void subscribeToScreenGeometry();
@@ -419,7 +422,8 @@ private:
[[nodiscard]] QRect finalContentRect() const;
[[nodiscard]] ContentGeometry contentGeometry() const;
[[nodiscard]] ContentGeometry storiesContentGeometry(
- const Stories::ContentLayout &layout) const;
+ const Stories::ContentLayout &layout,
+ float64 scale = 1.) const;
void updateContentRect();
void contentSizeChanged();
@@ -585,14 +589,16 @@ private:
Ui::Text::String _caption;
QRect _captionRect;
ClickHandlerPtr _captionExpandLink;
- bool _captionShownFull = false;
- bool _captionFitsIfExpanded = false;
- bool _captionExpanded = false;
+ int _captionShowMoreWidth = 0;
+ int _captionSkipBlockWidth = 0;
+ int _topNotchSize = 0;
int _width = 0;
int _height = 0;
int _skipTop = 0;
int _availableHeight = 0;
+ int _minUsedTop = 0; // Geometry without top notch on macOS.
+ int _maxUsedHeight = 0;
int _x = 0, _y = 0, _w = 0, _h = 0;
int _xStart = 0, _yStart = 0;
int _zoom = 0; // < 0 - out, 0 - none, > 0 - in
diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp
index 2f8609ace..9ac811f19 100644
--- a/Telegram/SourceFiles/overview/overview_layout.cpp
+++ b/Telegram/SourceFiles/overview/overview_layout.cpp
@@ -700,7 +700,7 @@ Voice::Voice(
updateName();
const auto dateText = Ui::Text::Link(
- langDateTime(base::unixtime::parse(_data->date))); // Link 1.
+ langDateTime(base::unixtime::parse(parent->date()))); // Link 1.
_details.setMarkedText(
st::defaultTextStyle,
tr::lng_date_and_duration(
@@ -1020,7 +1020,7 @@ Document::Document(
, _forceFileLayout(fields.forceFileLayout)
, _date(langDateTime(base::unixtime::parse(fields.dateOverride
? fields.dateOverride
- : _data->date)))
+ : parent->date())))
, _ext(_generic.ext)
, _datew(st::normalFont->width(_date)) {
_name.setMarkedText(
diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
index e605ea05c..93bd9420b 100644
--- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "base/platform/base_platform_info.h"
#include "base/event_filter.h"
+#include "ui/platform/ui_platform_window_title.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/input_fields.h"
#include "ui/ui_utility.h"
diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
index 1867823e6..0237215bc 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
@@ -309,6 +309,10 @@ bool GenerateDesktopFile(
hashMd5Hex(d.constData(), d.size(), md5Hash);
if (!Core::Launcher::Instance().customWorkingDir()) {
+ QFile::remove(u"%1org.telegram.desktop._%2.desktop"_q.arg(
+ targetPath,
+ md5Hash));
+
const auto exePath = QFile::encodeName(
cExeDir() + cExeName());
hashMd5Hex(exePath.constData(), exePath.size(), md5Hash);
@@ -335,7 +339,7 @@ bool GenerateServiceFile(bool silent = false) {
+ QGuiApplication::desktopFileName()
+ u".service"_q;
- DEBUG_LOG(("App Info: placing .service file to %1").arg(targetPath));
+ DEBUG_LOG(("App Info: placing D-Bus service file to %1").arg(targetPath));
if (!QDir(targetPath).exists()) QDir().mkpath(targetPath);
const auto target = Glib::KeyFile::create();
@@ -366,6 +370,18 @@ bool GenerateServiceFile(bool silent = false) {
return false;
}
+ if (!Core::UpdaterDisabled() && !Core::Launcher::Instance().customWorkingDir()) {
+ DEBUG_LOG(("App Info: removing old D-Bus service files"));
+
+ char md5Hash[33] = { 0 };
+ const auto d = QFile::encodeName(QDir(cWorkingDir()).absolutePath());
+ hashMd5Hex(d.constData(), d.size(), md5Hash);
+
+ QFile::remove(u"%1org.telegram.desktop._%2.service"_q.arg(
+ targetPath,
+ md5Hash));
+ }
+
QProcess::execute(u"systemctl"_q, {
u"--user"_q,
u"reload"_q,
diff --git a/Telegram/SourceFiles/platform/mac/overlay_widget_mac.h b/Telegram/SourceFiles/platform/mac/overlay_widget_mac.h
index 1e292891f..2c83b5612 100644
--- a/Telegram/SourceFiles/platform/mac/overlay_widget_mac.h
+++ b/Telegram/SourceFiles/platform/mac/overlay_widget_mac.h
@@ -36,6 +36,7 @@ public:
void clearState() override;
void setControlsOpacity(float64 opacity) override;
rpl::producer controlsSideRightValue() override;
+ rpl::producer topNotchSkipValue() override;
private:
using Control = Ui::Platform::TitleControl;
diff --git a/Telegram/SourceFiles/platform/mac/overlay_widget_mac.mm b/Telegram/SourceFiles/platform/mac/overlay_widget_mac.mm
index 4f9160e7a..ef544803e 100644
--- a/Telegram/SourceFiles/platform/mac/overlay_widget_mac.mm
+++ b/Telegram/SourceFiles/platform/mac/overlay_widget_mac.mm
@@ -35,6 +35,7 @@ struct MacOverlayWidgetHelper::Data {
rpl::event_stream<> clearStateRequests;
bool anyOver = false;
NSWindow * __weak native = nil;
+ rpl::variable topNotchSkip;
};
MacOverlayWidgetHelper::MacOverlayWidgetHelper(
@@ -96,6 +97,9 @@ void MacOverlayWidgetHelper::updateStyles(bool fullscreen) {
[window setTitleVisibility:NSWindowTitleHidden];
[window setTitlebarAppearsTransparent:YES];
[window setStyleMask:[window styleMask] | NSWindowStyleMaskFullSizeContentView];
+ if (@available(macOS 12.0, *)) {
+ _data->topNotchSkip = [[window screen] safeAreaInsets].top;
+ }
}
void MacOverlayWidgetHelper::refreshButtons(bool fullscreen) {
@@ -153,6 +157,10 @@ rpl::producer MacOverlayWidgetHelper::controlsSideRightValue() {
return rpl::single(false);
}
+rpl::producer MacOverlayWidgetHelper::topNotchSkipValue() {
+ return _data->topNotchSkip.value();
+}
+
object_ptr MacOverlayWidgetHelper::create(
not_null parent,
Control control) {
diff --git a/Telegram/SourceFiles/platform/platform_overlay_widget.h b/Telegram/SourceFiles/platform/platform_overlay_widget.h
index 97b6ef7ef..2de3687d0 100644
--- a/Telegram/SourceFiles/platform/platform_overlay_widget.h
+++ b/Telegram/SourceFiles/platform/platform_overlay_widget.h
@@ -58,6 +58,9 @@ public:
-> rpl::producer> {
return rpl::never>();
}
+ [[nodiscard]] virtual rpl::producer topNotchSkipValue() {
+ return rpl::single(0);
+ }
};
[[nodiscard]] std::unique_ptr CreateOverlayWidgetHelper(
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index 82d4e4801..2b359b984 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -1653,7 +1653,7 @@ QPointer Premium::createPinnedToBottom(
} else {
#endif
{
- _radioGroup->setChangedCallback([=](int value) {
+ const auto callback = [=](int value) {
const auto options =
_controller->session().api().premium().subscriptionOptions();
if (options.empty()) {
@@ -1665,8 +1665,9 @@ QPointer Premium::createPinnedToBottom(
lt_cost,
options[value].costPerMonth);
_buttonText = std::move(text);
- });
- _radioGroup->setValue(0);
+ };
+ _radioGroup->setChangedCallback(callback);
+ callback(0);
}
_showFinished.events(
diff --git a/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp b/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp
index ae9acc05d..4a7dcb337 100644
--- a/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp
+++ b/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp
@@ -165,8 +165,9 @@ Action::Action(
+ _st.itemStyle.font->height
+ st::defaultWhoRead.itemPadding.bottom()) {
const auto parent = parentMenu->menu();
- const auto checkAppeared = [=, now = crl::now()] {
- _appeared = (crl::now() - now) >= parentMenu->st().duration;
+ const auto delay = anim::Disabled() ? 0 : parentMenu->st().duration;
+ const auto checkAppeared = [=, now = crl::now()](bool force = false) {
+ _appeared = force || ((crl::now() - now) >= delay);
};
setAcceptBoth(true);
@@ -224,8 +225,10 @@ Action::Action(
enableMouseSelecting();
base::call_delayed(parentMenu->st().duration, this, [=] {
- checkAppeared();
- updateUserpicsFromContent();
+ if (!_appeared) {
+ checkAppeared(true);
+ updateUserpicsFromContent();
+ }
});
}
diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
index 2e9972257..a7ce3f922 100644
--- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
+++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
@@ -1138,6 +1138,10 @@ void AddGiftOptions(
stCheckbox,
std::move(radioView));
radio->setAttribute(Qt::WA_TransparentForMouseEvents);
+ { // Paint the last frame instantly for the layer animation.
+ group->setValue(0);
+ radio->finishAnimating();
+ }
row->sizeValue(
) | rpl::start_with_next([=, margins = stCheckbox.margin](
diff --git a/Telegram/SourceFiles/window/window_filters_menu.cpp b/Telegram/SourceFiles/window/window_filters_menu.cpp
index 657e0165d..c6c34a0d9 100644
--- a/Telegram/SourceFiles/window/window_filters_menu.cpp
+++ b/Telegram/SourceFiles/window/window_filters_menu.cpp
@@ -40,6 +40,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Window {
namespace {
+[[nodiscard]] Dialogs::UnreadState MainListMapUnreadState(
+ not_null session,
+ const Dialogs::UnreadState &state) {
+ const auto folderId = Data::Folder::kId;
+ if (const auto folder = session->data().folderLoaded(folderId)) {
+ return state - folder->chatsList()->unreadState();
+ }
+ return state;
+}
+
[[nodiscard]] rpl::producer MainListUnreadState(
not_null list) {
return rpl::single(rpl::empty) | rpl::then(
@@ -59,11 +69,7 @@ namespace {
return MainListUnreadState(
session->data().chatsList()
) | rpl::map([=](const Dialogs::UnreadState &state) {
- const auto folderId = Data::Folder::kId;
- if (const auto folder = session->data().folderLoaded(folderId)) {
- return state - folder->chatsList()->unreadState();
- }
- return state;
+ return MainListMapUnreadState(session, state);
});
}
@@ -314,24 +320,14 @@ base::unique_qptr FiltersMenu::prepareButton(
} else if (id >= 0) {
_session->setActiveChatsFilter(id);
} else {
- const auto filters = &_session->session().data().chatsFilters();
- if (filters->suggestedLoaded()) {
- _session->showSettings(Settings::Folders::Id());
- } else if (!_waitingSuggested) {
- _waitingSuggested = true;
- filters->requestSuggested();
- filters->suggestedUpdated(
- ) | rpl::take(1) | rpl::start_with_next([=] {
- _session->showSettings(Settings::Folders::Id());
- }, _outer.lifetime());
- }
+ openFiltersSettings();
}
});
if (id >= 0) {
raw->setAcceptDrops(true);
raw->events(
) | rpl::filter([=](not_null e) {
- return ((e->type() == QEvent::ContextMenu) && (id > 0))
+ return ((e->type() == QEvent::ContextMenu) && (id >= 0))
|| e->type() == QEvent::DragEnter
|| e->type() == QEvent::DragMove
|| e->type() == QEvent::DragLeave;
@@ -362,13 +358,27 @@ base::unique_qptr FiltersMenu::prepareButton(
return button;
}
+void FiltersMenu::openFiltersSettings() {
+ const auto filters = &_session->session().data().chatsFilters();
+ if (filters->suggestedLoaded()) {
+ _session->showSettings(Settings::Folders::Id());
+ } else if (!_waitingSuggested) {
+ _waitingSuggested = true;
+ filters->requestSuggested();
+ filters->suggestedUpdated(
+ ) | rpl::take(1) | rpl::start_with_next([=] {
+ _session->showSettings(Settings::Folders::Id());
+ }, _outer.lifetime());
+ }
+}
+
void FiltersMenu::showMenu(QPoint position, FilterId id) {
if (_popupMenu) {
_popupMenu = nullptr;
return;
}
const auto i = _filters.find(id);
- if (i == end(_filters)) {
+ if ((i == end(_filters)) && id) {
return;
}
_popupMenu = base::make_unique_q(
@@ -382,23 +392,46 @@ void FiltersMenu::showMenu(QPoint position, FilterId id) {
args.icon);
});
- addAction(
- tr::lng_filters_context_edit(tr::now),
- [=] { showEditBox(id); },
- &st::menuIconEdit);
+ if (id) {
+ addAction(
+ tr::lng_filters_context_edit(tr::now),
+ [=] { showEditBox(id); },
+ &st::menuIconEdit);
- auto filteredChats = [=] {
- return _session->session().data().chatsFilters().chatsList(id);
- };
- Window::MenuAddMarkAsReadChatListAction(
- _session,
- std::move(filteredChats),
- addAction);
+ auto filteredChats = [=] {
+ return _session->session().data().chatsFilters().chatsList(id);
+ };
+ Window::MenuAddMarkAsReadChatListAction(
+ _session,
+ std::move(filteredChats),
+ addAction);
- addAction(
- tr::lng_filters_context_remove(tr::now),
- [=] { showRemoveBox(id); },
- &st::menuIconDelete);
+ addAction(
+ tr::lng_filters_context_remove(tr::now),
+ [=] { showRemoveBox(id); },
+ &st::menuIconDelete);
+ } else {
+ auto customUnreadState = [=] {
+ const auto session = &_session->session();
+ return MainListMapUnreadState(
+ session,
+ session->data().chatsList()->unreadState());
+ };
+ Window::MenuAddMarkAsReadChatListAction(
+ _session,
+ [=] { return _session->session().data().chatsList(); },
+ addAction,
+ std::move(customUnreadState));
+
+ addAction(
+ tr::lng_filters_setup_menu(tr::now),
+ [=] { openFiltersSettings(); },
+ &st::menuIconEdit);
+ }
+ if (_popupMenu->empty()) {
+ _popupMenu = nullptr;
+ return;
+ }
_popupMenu->popup(position);
}
diff --git a/Telegram/SourceFiles/window/window_filters_menu.h b/Telegram/SourceFiles/window/window_filters_menu.h
index a4084164b..ee71510ea 100644
--- a/Telegram/SourceFiles/window/window_filters_menu.h
+++ b/Telegram/SourceFiles/window/window_filters_menu.h
@@ -52,6 +52,7 @@ private:
void showRemoveBox(FilterId id);
void remove(FilterId id, std::vector> leave = {});
void scrollToButton(not_null widget);
+ void openFiltersSettings();
const not_null _session;
const not_null _parent;
diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp
index 0789deb58..66ae90ba6 100644
--- a/Telegram/SourceFiles/window/window_peer_menu.cpp
+++ b/Telegram/SourceFiles/window/window_peer_menu.cpp
@@ -2303,9 +2303,12 @@ void MenuAddMarkAsReadAllChatsAction(
void MenuAddMarkAsReadChatListAction(
not_null controller,
Fn()> &&list,
- const PeerMenuCallback &addAction) {
+ const PeerMenuCallback &addAction,
+ Fn customUnreadState) {
// There is no async to make weak from controller.
- const auto unreadState = list()->unreadState();
+ const auto unreadState = customUnreadState
+ ? customUnreadState()
+ : list()->unreadState();
if (!unreadState.messages && !unreadState.marks && !unreadState.chats) {
return;
}
diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h
index 67b6e18aa..34dc02a89 100644
--- a/Telegram/SourceFiles/window/window_peer_menu.h
+++ b/Telegram/SourceFiles/window/window_peer_menu.h
@@ -32,6 +32,7 @@ class Thread;
namespace Dialogs {
class MainList;
struct EntryState;
+struct UnreadState;
} // namespace Dialogs
namespace ChatHelpers {
@@ -69,7 +70,8 @@ void MenuAddMarkAsReadAllChatsAction(
void MenuAddMarkAsReadChatListAction(
not_null controller,
Fn()> &&list,
- const PeerMenuCallback &addAction);
+ const PeerMenuCallback &addAction,
+ Fn customUnreadState = nullptr);
void PeerMenuExportChat(not_null peer);
void PeerMenuDeleteContact(
diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index 8b8bfd1d3..a8f611a8c 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -520,7 +520,7 @@ mac:
""")
stage('mozjpeg', """
- git clone -b v4.0.3 https://github.com/mozilla/mozjpeg.git
+ git clone -b v4.1.3 https://github.com/mozilla/mozjpeg.git
cd mozjpeg
win:
cmake . ^
@@ -675,13 +675,31 @@ stage('dav1d', """
win:
git clone -b 1.2.1 --depth 1 https://code.videolan.org/videolan/dav1d.git
cd dav1d
+
+ if "%X8664%" equ "x64" (
+ SET "TARGET=x86_64"
+ ) else (
+ SET "TARGET=x86"
+ )
+ set FILE=cross-file.txt
+ echo [binaries] > %FILE%
+ echo c = 'cl' >> %FILE%
+ echo cpp = 'cl' >> %FILE%
+ echo ar = 'lib' >> %FILE%
+ echo windres = 'rc' >> %FILE%
+ echo [host_machine] >> %FILE%
+ echo system = 'windows' >> %FILE%
+ echo cpu_family = '%TARGET%' >> %FILE%
+ echo cpu = '%TARGET%' >> %FILE%
+ echo endian = 'little'>> %FILE%
+
depends:python/Scripts/activate.bat
%THIRDPARTY_DIR%\\python\\Scripts\\activate.bat
- meson setup --prefix %LIBS_DIR%/local --default-library=static --buildtype=debug -Denable_tools=false -Denable_tests=false -Db_vscrt=mtd builddir-debug
+ meson setup --cross-file %FILE% --prefix %LIBS_DIR%/local --default-library=static --buildtype=debug -Denable_tools=false -Denable_tests=false -Db_vscrt=mtd builddir-debug
meson compile -C builddir-debug
meson install -C builddir-debug
release:
- meson setup --prefix %LIBS_DIR%/local --default-library=static --buildtype=release -Denable_tools=false -Denable_tests=false -Db_vscrt=mt builddir-release
+ meson setup --cross-file %FILE% --prefix %LIBS_DIR%/local --default-library=static --buildtype=release -Denable_tools=false -Denable_tests=false -Db_vscrt=mt builddir-release
meson compile -C builddir-release
meson install -C builddir-release
win:
diff --git a/Telegram/build/version b/Telegram/build/version
index 1322ed7d7..f7062fb28 100644
--- a/Telegram/build/version
+++ b/Telegram/build/version
@@ -1,7 +1,7 @@
-AppVersion 4008007
+AppVersion 4008010
AppVersionStrMajor 4.8
-AppVersionStrSmall 4.8.7
-AppVersionStr 4.8.7
+AppVersionStrSmall 4.8.10
+AppVersionStr 4.8.10
BetaChannel 0
AlphaVersion 0
-AppVersionOriginal 4.8.7
+AppVersionOriginal 4.8.10
diff --git a/Telegram/lib_base b/Telegram/lib_base
index 2669a0457..efd052594 160000
--- a/Telegram/lib_base
+++ b/Telegram/lib_base
@@ -1 +1 @@
-Subproject commit 2669a04579069942b6208a18abe93c26adfddf2a
+Subproject commit efd052594db9f3d85a2fbcba76db6fdecf226597
diff --git a/changelog.txt b/changelog.txt
index 314f815c6..28384f4b3 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,16 @@
+4.8.10 (28.07.23)
+
+- Send story sharing comments as separate messages.
+- Fix stories explanation tooltip ordering.
+
+4.8.9 (26.07.23)
+
+- Bug fixes and other minor improvements.
+
+4.8.8 (25.07.23)
+
+- Several crash fixes and story viewer improvements.
+
4.8.7 (21.07.23)
- Several crash fixes and small stories improvements.
diff --git a/lib/xdg/org.telegram.desktop.service b/lib/xdg/org.telegram.desktop.service
new file mode 100644
index 000000000..e69de29bb