mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Merge remote-tracking branch 'tdesktop-ustream/dev' into dev
# Conflicts: # Telegram/CMakeLists.txt # Telegram/Resources/winrc/Telegram.rc # Telegram/Resources/winrc/Updater.rc # Telegram/SourceFiles/core/version.h # Telegram/lib_ui # lib/xdg/org.telegram.desktop.service
This commit is contained in:
commit
1f6c440341
86 changed files with 1767 additions and 535 deletions
|
@ -1802,6 +1802,7 @@ endif()
|
||||||
|
|
||||||
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||||
include(GNUInstallDirs)
|
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)
|
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")
|
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}")
|
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/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 "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.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")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.ayugram.desktop.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo")
|
||||||
endif()
|
endif()
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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_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_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" = "This link cannot be edited. You can acquire additional usernames on {link}.";
|
||||||
"lng_bot_username_description1_link" = "Fragment";
|
"lng_bot_username_description1_link" = "Fragment";
|
||||||
"lng_bot_usernames_activate_description" = "Do you want to show this link on the bot info page?";
|
"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_shared_links#other" = "{count} shared links";
|
||||||
"lng_profile_copy_phone" = "Copy Phone Number";
|
"lng_profile_copy_phone" = "Copy Phone Number";
|
||||||
"lng_profile_copy_fullname" = "Copy Name";
|
"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_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.";
|
"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" = "Forwarded from {user}";
|
||||||
"lng_forwarded_story" = "Story from {user}";
|
"lng_forwarded_story" = "Story from {user}";
|
||||||
|
"lng_forwarded_story_expired" = "This story has expired.";
|
||||||
"lng_forwarded_date" = "Original: {date}";
|
"lng_forwarded_date" = "Original: {date}";
|
||||||
"lng_forwarded_channel" = "Forwarded from {channel}";
|
"lng_forwarded_channel" = "Forwarded from {channel}";
|
||||||
"lng_forwarded_psa_default" = "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_gifs" = "GIFs";
|
||||||
"lng_switch_masks" = "Masks";
|
"lng_switch_masks" = "Masks";
|
||||||
"lng_stickers_featured_add" = "Add";
|
"lng_stickers_featured_add" = "Add";
|
||||||
|
"lng_stickers_featured_installed" = "Added";
|
||||||
"lng_emoji_featured_unlock" = "Unlock";
|
"lng_emoji_featured_unlock" = "Unlock";
|
||||||
"lng_emoji_premium_restore" = "Restore";
|
"lng_emoji_premium_restore" = "Restore";
|
||||||
"lng_gifs_search" = "Search GIFs";
|
"lng_gifs_search" = "Search GIFs";
|
||||||
|
@ -3583,6 +3587,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_filters_restore" = "Undo";
|
"lng_filters_restore" = "Undo";
|
||||||
"lng_filters_new" = "New Folder";
|
"lng_filters_new" = "New Folder";
|
||||||
"lng_filters_edit" = "Edit Folder";
|
"lng_filters_edit" = "Edit Folder";
|
||||||
|
"lng_filters_setup_menu" = "Edit Folders";
|
||||||
"lng_filters_new_name" = "Folder name";
|
"lng_filters_new_name" = "Folder name";
|
||||||
"lng_filters_add_chats" = "Add chats";
|
"lng_filters_add_chats" = "Add chats";
|
||||||
"lng_filters_remove_chats" = "Remove 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_no_views" = "No views";
|
||||||
"lng_stories_unsupported" = "This story is not supported\nby your version of Telegram.";
|
"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_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_my_title" = "Saved Stories";
|
||||||
"lng_stories_archive_button" = "Stories Archive";
|
"lng_stories_archive_button" = "Stories Archive";
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||||
ProcessorArchitecture="ARCHITECTURE"
|
ProcessorArchitecture="ARCHITECTURE"
|
||||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||||
Version="4.8.7.0" />
|
Version="4.8.10.0" />
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Telegram Desktop</DisplayName>
|
<DisplayName>Telegram Desktop</DisplayName>
|
||||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||||
|
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 4,8,7,0
|
FILEVERSION 4,8,10,0
|
||||||
PRODUCTVERSION 4,8,7,0
|
PRODUCTVERSION 4,8,10,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -62,10 +62,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop"
|
VALUE "FileDescription", "AyuGram Desktop"
|
||||||
VALUE "FileVersion", "4.8.7.0"
|
VALUE "FileVersion", "4.8.10.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "4.8.7.0"
|
VALUE "ProductVersion", "4.8.10.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 4,8,7,0
|
FILEVERSION 4,8,10,0
|
||||||
PRODUCTVERSION 4,8,7,0
|
PRODUCTVERSION 4,8,10,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -53,10 +53,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||||
VALUE "FileVersion", "4.8.7.0"
|
VALUE "FileVersion", "4.8.10.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "4.8.7.0"
|
VALUE "ProductVersion", "4.8.10.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -156,14 +156,15 @@ void GiftBox(
|
||||||
|
|
||||||
// List.
|
// List.
|
||||||
const auto group = std::make_shared<Ui::RadiobuttonGroup>();
|
const auto group = std::make_shared<Ui::RadiobuttonGroup>();
|
||||||
group->setChangedCallback([=](int value) {
|
const auto groupValueChangedCallback = [=](int value) {
|
||||||
Expects(value < options.size() && value >= 0);
|
Expects(value < options.size() && value >= 0);
|
||||||
auto text = tr::lng_premium_gift_button(
|
auto text = tr::lng_premium_gift_button(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_cost,
|
lt_cost,
|
||||||
options[value].costTotal);
|
options[value].costTotal);
|
||||||
state->buttonText.fire(std::move(text));
|
state->buttonText.fire(std::move(text));
|
||||||
});
|
};
|
||||||
|
group->setChangedCallback(groupValueChangedCallback);
|
||||||
Ui::Premium::AddGiftOptions(
|
Ui::Premium::AddGiftOptions(
|
||||||
buttonsParent,
|
buttonsParent,
|
||||||
group,
|
group,
|
||||||
|
@ -215,7 +216,7 @@ void GiftBox(
|
||||||
});
|
});
|
||||||
box->addButton(std::move(button));
|
box->addButton(std::move(button));
|
||||||
|
|
||||||
group->setValue(0);
|
groupValueChangedCallback(0);
|
||||||
|
|
||||||
Data::PeerPremiumValue(
|
Data::PeerPremiumValue(
|
||||||
user
|
user
|
||||||
|
|
|
@ -59,7 +59,20 @@ constexpr auto kSearchPerPage = 50;
|
||||||
object_ptr<Ui::BoxContent> PrepareContactsBox(
|
object_ptr<Ui::BoxContent> PrepareContactsBox(
|
||||||
not_null<Window::SessionController*> sessionController) {
|
not_null<Window::SessionController*> sessionController) {
|
||||||
using Mode = ContactsBoxController::SortMode;
|
using Mode = ContactsBoxController::SortMode;
|
||||||
auto controller = std::make_unique<ContactsBoxController>(
|
class Controller final : public ContactsBoxController {
|
||||||
|
public:
|
||||||
|
using ContactsBoxController::ContactsBoxController;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<PeerListRow> createRow(
|
||||||
|
not_null<UserData*> user) override {
|
||||||
|
return !user->isSelf()
|
||||||
|
? ContactsBoxController::createRow(user)
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
auto controller = std::make_unique<Controller>(
|
||||||
&sessionController->session());
|
&sessionController->session());
|
||||||
controller->setStyleOverrides(&st::contactsWithStories);
|
controller->setStyleOverrides(&st::contactsWithStories);
|
||||||
controller->setStoriesShown(true);
|
controller->setStoriesShown(true);
|
||||||
|
|
|
@ -105,6 +105,7 @@ PeerShortInfoCover::PeerShortInfoCover(
|
||||||
userpic
|
userpic
|
||||||
) | rpl::start_with_next([=](PeerShortInfoUserpic &&value) {
|
) | rpl::start_with_next([=](PeerShortInfoUserpic &&value) {
|
||||||
applyUserpic(std::move(value));
|
applyUserpic(std::move(value));
|
||||||
|
applyAdditionalStatus(value.additionalStatus);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
style::PaletteChanged(
|
style::PaletteChanged(
|
||||||
|
@ -136,16 +137,7 @@ PeerShortInfoCover::PeerShortInfoCover(
|
||||||
return base::EventFilterResult::Cancel;
|
return base::EventFilterResult::Cancel;
|
||||||
});
|
});
|
||||||
|
|
||||||
_name->moveToLeft(
|
refreshLabelsGeometry();
|
||||||
_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);
|
|
||||||
|
|
||||||
_roundedTopImage = QImage(
|
_roundedTopImage = QImage(
|
||||||
QSize(_st.size, _st.radius) * style::DevicePixelRatio(),
|
QSize(_st.size, _st.radius) * style::DevicePixelRatio(),
|
||||||
|
@ -415,6 +407,23 @@ QImage PeerShortInfoCover::currentVideoFrame() const {
|
||||||
: QImage();
|
: 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) {
|
void PeerShortInfoCover::applyUserpic(PeerShortInfoUserpic &&value) {
|
||||||
if (_index != value.index) {
|
if (_index != value.index) {
|
||||||
_index = value.index;
|
_index = value.index;
|
||||||
|
@ -593,6 +602,28 @@ void PeerShortInfoCover::refreshBarImages() {
|
||||||
_barLarge = makeBar(_largeWidth);
|
_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 {
|
QRect PeerShortInfoCover::radialRect() const {
|
||||||
const auto cover = _widget->rect();
|
const auto cover = _widget->rect();
|
||||||
const auto size = st::boxLoadingSize;
|
const auto size = st::boxLoadingSize;
|
||||||
|
@ -654,12 +685,16 @@ rpl::producer<int> PeerShortInfoBox::moveRequests() const {
|
||||||
void PeerShortInfoBox::prepare() {
|
void PeerShortInfoBox::prepare() {
|
||||||
addButton(tr::lng_close(), [=] { closeBox(); });
|
addButton(tr::lng_close(), [=] { closeBox(); });
|
||||||
|
|
||||||
// Perhaps a new lang key should be added for opening a group.
|
if (_type != PeerShortInfoType::Self) {
|
||||||
addLeftButton((_type == PeerShortInfoType::User)
|
// Perhaps a new lang key should be added for opening a group.
|
||||||
? tr::lng_profile_send_message()
|
addLeftButton(
|
||||||
: (_type == PeerShortInfoType::Group)
|
(_type == PeerShortInfoType::User)
|
||||||
? tr::lng_view_button_group()
|
? tr::lng_profile_send_message()
|
||||||
: tr::lng_profile_view_channel(), [=] { _openRequests.fire({}); });
|
: (_type == PeerShortInfoType::Group)
|
||||||
|
? tr::lng_view_button_group()
|
||||||
|
: tr::lng_profile_view_channel(),
|
||||||
|
[=] { _openRequests.fire({}); });
|
||||||
|
}
|
||||||
|
|
||||||
prepareRows();
|
prepareRows();
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ class RpWidget;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
enum class PeerShortInfoType {
|
enum class PeerShortInfoType {
|
||||||
|
Self,
|
||||||
User,
|
User,
|
||||||
Group,
|
Group,
|
||||||
Channel,
|
Channel,
|
||||||
|
@ -50,6 +51,7 @@ struct PeerShortInfoUserpic {
|
||||||
float64 photoLoadingProgress = 0.;
|
float64 photoLoadingProgress = 0.;
|
||||||
std::shared_ptr<Media::Streaming::Document> videoDocument;
|
std::shared_ptr<Media::Streaming::Document> videoDocument;
|
||||||
crl::time videoStartPosition = 0;
|
crl::time videoStartPosition = 0;
|
||||||
|
QString additionalStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PeerShortInfoCover final {
|
class PeerShortInfoCover final {
|
||||||
|
@ -87,6 +89,7 @@ private:
|
||||||
[[nodiscard]] QImage currentVideoFrame() const;
|
[[nodiscard]] QImage currentVideoFrame() const;
|
||||||
|
|
||||||
void applyUserpic(PeerShortInfoUserpic &&value);
|
void applyUserpic(PeerShortInfoUserpic &&value);
|
||||||
|
void applyAdditionalStatus(const QString &status);
|
||||||
[[nodiscard]] QRect radialRect() const;
|
[[nodiscard]] QRect radialRect() const;
|
||||||
|
|
||||||
void videoWaiting();
|
void videoWaiting();
|
||||||
|
@ -99,6 +102,7 @@ private:
|
||||||
void updateRadialState();
|
void updateRadialState();
|
||||||
void refreshCoverCursor();
|
void refreshCoverCursor();
|
||||||
void refreshBarImages();
|
void refreshBarImages();
|
||||||
|
void refreshLabelsGeometry();
|
||||||
|
|
||||||
const style::ShortInfoCover &_st;
|
const style::ShortInfoCover &_st;
|
||||||
|
|
||||||
|
@ -108,6 +112,7 @@ private:
|
||||||
object_ptr<Ui::FlatLabel> _name;
|
object_ptr<Ui::FlatLabel> _name;
|
||||||
std::unique_ptr<CustomLabelStyle> _statusStyle;
|
std::unique_ptr<CustomLabelStyle> _statusStyle;
|
||||||
object_ptr<Ui::FlatLabel> _status;
|
object_ptr<Ui::FlatLabel> _status;
|
||||||
|
object_ptr<Ui::FlatLabel> _additionalStatus = { nullptr };
|
||||||
|
|
||||||
std::array<QImage, 4> _roundMask;
|
std::array<QImage, 4> _roundMask;
|
||||||
QImage _userpicImage;
|
QImage _userpicImage;
|
||||||
|
|
|
@ -335,6 +335,15 @@ bool ProcessCurrent(
|
||||||
: state->photoView
|
: state->photoView
|
||||||
? state->photoView->owner().get()
|
? state->photoView->owner().get()
|
||||||
: nullptr;
|
: 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;
|
state->waitingLoad = false;
|
||||||
if (!changedPhotoId
|
if (!changedPhotoId
|
||||||
&& (state->current.index > 0 || !changedUserpic)
|
&& (state->current.index > 0 || !changedUserpic)
|
||||||
|
@ -422,7 +431,9 @@ object_ptr<Ui::BoxContent> PrepareShortInfoBox(
|
||||||
Fn<void()> open,
|
Fn<void()> open,
|
||||||
Fn<bool()> videoPaused,
|
Fn<bool()> videoPaused,
|
||||||
const style::ShortInfoBox *stOverride) {
|
const style::ShortInfoBox *stOverride) {
|
||||||
const auto type = peer->isUser()
|
const auto type = peer->isSelf()
|
||||||
|
? PeerShortInfoType::Self
|
||||||
|
: peer->isUser()
|
||||||
? PeerShortInfoType::User
|
? PeerShortInfoType::User
|
||||||
: peer->isBroadcast()
|
: peer->isBroadcast()
|
||||||
? PeerShortInfoType::Channel
|
? PeerShortInfoType::Channel
|
||||||
|
|
|
@ -211,7 +211,7 @@ private:
|
||||||
void setActionDown(int newActionDown);
|
void setActionDown(int newActionDown);
|
||||||
void setPressed(SelectedRow pressed);
|
void setPressed(SelectedRow pressed);
|
||||||
void setup();
|
void setup();
|
||||||
QRect relativeButtonRect(bool removeButton) const;
|
QRect relativeButtonRect(bool removeButton, bool installedSet) const;
|
||||||
void ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton);
|
void ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton);
|
||||||
|
|
||||||
bool shiftingAnimationCallback(crl::time now);
|
bool shiftingAnimationCallback(crl::time now);
|
||||||
|
@ -234,7 +234,7 @@ private:
|
||||||
void readVisibleSets();
|
void readVisibleSets();
|
||||||
|
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
void rebuildAppendSet(not_null<StickersSet*> set, int maxNameWidth);
|
void rebuildAppendSet(not_null<StickersSet*> set);
|
||||||
void fillSetCover(not_null<StickersSet*> set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
|
void fillSetCover(not_null<StickersSet*> set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
|
||||||
int fillSetCount(not_null<StickersSet*> set) const;
|
int fillSetCount(not_null<StickersSet*> set) const;
|
||||||
[[nodiscard]] QString fillSetTitle(
|
[[nodiscard]] QString fillSetTitle(
|
||||||
|
@ -247,7 +247,7 @@ private:
|
||||||
void handleMegagroupSetAddressChange();
|
void handleMegagroupSetAddressChange();
|
||||||
void setMegagroupSelectedSet(const StickerSetIdentifier &set);
|
void setMegagroupSelectedSet(const StickerSetIdentifier &set);
|
||||||
|
|
||||||
int countMaxNameWidth() const;
|
int countMaxNameWidth(bool installedSet) const;
|
||||||
[[nodiscard]] bool skipPremium() const;
|
[[nodiscard]] bool skipPremium() const;
|
||||||
|
|
||||||
const std::shared_ptr<ChatHelpers::Show> _show;
|
const std::shared_ptr<ChatHelpers::Show> _show;
|
||||||
|
@ -255,9 +255,9 @@ private:
|
||||||
MTP::Sender _api;
|
MTP::Sender _api;
|
||||||
|
|
||||||
const Section _section;
|
const Section _section;
|
||||||
const bool _isInstalled;
|
const bool _isInstalledTab;
|
||||||
|
|
||||||
Ui::RoundRect _buttonBgOver, _buttonBg;
|
Ui::RoundRect _buttonBgOver, _buttonBg, _inactiveButtonBg;
|
||||||
|
|
||||||
int32 _rowHeight = 0;
|
int32 _rowHeight = 0;
|
||||||
|
|
||||||
|
@ -282,6 +282,8 @@ private:
|
||||||
int _addWidth = 0;
|
int _addWidth = 0;
|
||||||
QString _undoText;
|
QString _undoText;
|
||||||
int _undoWidth = 0;
|
int _undoWidth = 0;
|
||||||
|
QString _installedText;
|
||||||
|
int _installedWidth = 0;
|
||||||
|
|
||||||
QPoint _mouse;
|
QPoint _mouse;
|
||||||
bool _inDragArea = false;
|
bool _inDragArea = false;
|
||||||
|
@ -678,19 +680,19 @@ void StickersBox::refreshTabs() {
|
||||||
_tabIndices.clear();
|
_tabIndices.clear();
|
||||||
auto sections = std::vector<QString>();
|
auto sections = std::vector<QString>();
|
||||||
if (_installed.widget()) {
|
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);
|
_tabIndices.push_back(Section::Installed);
|
||||||
}
|
}
|
||||||
if (_masks.widget()) {
|
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);
|
_tabIndices.push_back(Section::Masks);
|
||||||
}
|
}
|
||||||
if (!stickers.featuredSetsOrder().isEmpty() && _featured.widget()) {
|
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);
|
_tabIndices.push_back(Section::Featured);
|
||||||
}
|
}
|
||||||
if (!archivedSetsOrder().isEmpty() && _archived.widget()) {
|
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);
|
_tabIndices.push_back(Section::Archived);
|
||||||
}
|
}
|
||||||
_tabs->setSections(sections);
|
_tabs->setSections(sections);
|
||||||
|
@ -771,7 +773,7 @@ void StickersBox::updateTabsGeometry() {
|
||||||
|
|
||||||
auto featuredLeft = width() / maxTabs;
|
auto featuredLeft = width() / maxTabs;
|
||||||
auto featuredRight = 2 * 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 featuredTextRight = featuredLeft + (featuredRight - featuredLeft - featuredTextWidth) / 2 + featuredTextWidth;
|
||||||
auto unreadBadgeLeft = featuredTextRight - st::stickersFeaturedBadgeSkip;
|
auto unreadBadgeLeft = featuredTextRight - st::stickersFeaturedBadgeSkip;
|
||||||
auto unreadBadgeTop = st::stickersFeaturedBadgeTop;
|
auto unreadBadgeTop = st::stickersFeaturedBadgeTop;
|
||||||
|
@ -1133,26 +1135,32 @@ StickersBox::Inner::Inner(
|
||||||
, _session(&_show->session())
|
, _session(&_show->session())
|
||||||
, _api(&_session->mtp())
|
, _api(&_session->mtp())
|
||||||
, _section(section)
|
, _section(section)
|
||||||
, _isInstalled(_section == Section::Installed || _section == Section::Masks)
|
, _isInstalledTab(_section == Section::Installed
|
||||||
|
|| _section == Section::Masks)
|
||||||
, _buttonBgOver(
|
, _buttonBgOver(
|
||||||
ImageRoundRadius::Small,
|
ImageRoundRadius::Large,
|
||||||
(_isInstalled
|
(_isInstalledTab
|
||||||
? st::stickersUndoRemove
|
? st::stickersUndoRemove
|
||||||
: st::stickersTrendingAdd).textBgOver)
|
: st::stickersTrendingAdd).textBgOver)
|
||||||
, _buttonBg(
|
, _buttonBg(
|
||||||
ImageRoundRadius::Small,
|
ImageRoundRadius::Large,
|
||||||
(_isInstalled
|
(_isInstalledTab
|
||||||
? st::stickersUndoRemove
|
? st::stickersUndoRemove
|
||||||
: st::stickersTrendingAdd).textBg)
|
: st::stickersTrendingAdd).textBg)
|
||||||
|
, _inactiveButtonBg(
|
||||||
|
ImageRoundRadius::Large,
|
||||||
|
st::stickersTrendingInstalled.textBg)
|
||||||
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
|
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
|
||||||
, _shiftingAnimation([=](crl::time now) {
|
, _shiftingAnimation([=](crl::time now) {
|
||||||
return shiftingAnimationCallback(now);
|
return shiftingAnimationCallback(now);
|
||||||
})
|
})
|
||||||
, _itemsTop(st::membersMarginTop)
|
, _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))
|
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
|
||||||
, _undoText(tr::lng_stickers_return(tr::now).toUpper())
|
, _undoText(tr::lng_stickers_return(tr::now))
|
||||||
, _undoWidth(st::stickersUndoRemove.font->width(_undoText)) {
|
, _undoWidth(st::stickersUndoRemove.font->width(_undoText))
|
||||||
|
, _installedText(tr::lng_stickers_featured_installed(tr::now))
|
||||||
|
, _installedWidth(st::stickersTrendingInstalled.font->width(_installedText)) {
|
||||||
setup();
|
setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1165,17 +1173,21 @@ StickersBox::Inner::Inner(
|
||||||
, _session(&_show->session())
|
, _session(&_show->session())
|
||||||
, _api(&_session->mtp())
|
, _api(&_session->mtp())
|
||||||
, _section(StickersBox::Section::Installed)
|
, _section(StickersBox::Section::Installed)
|
||||||
, _isInstalled(_section == Section::Installed || _section == Section::Masks)
|
, _isInstalledTab(_section == Section::Installed
|
||||||
|
|| _section == Section::Masks)
|
||||||
, _buttonBgOver(
|
, _buttonBgOver(
|
||||||
ImageRoundRadius::Small,
|
ImageRoundRadius::Large,
|
||||||
(_isInstalled
|
(_isInstalledTab
|
||||||
? st::stickersUndoRemove
|
? st::stickersUndoRemove
|
||||||
: st::stickersTrendingAdd).textBgOver)
|
: st::stickersTrendingAdd).textBgOver)
|
||||||
, _buttonBg(
|
, _buttonBg(
|
||||||
ImageRoundRadius::Small,
|
ImageRoundRadius::Large,
|
||||||
(_isInstalled
|
(_isInstalledTab
|
||||||
? st::stickersUndoRemove
|
? st::stickersUndoRemove
|
||||||
: st::stickersTrendingAdd).textBg)
|
: st::stickersTrendingAdd).textBg)
|
||||||
|
, _inactiveButtonBg(
|
||||||
|
ImageRoundRadius::Large,
|
||||||
|
st::stickersTrendingInstalled.textBg)
|
||||||
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
|
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
|
||||||
, _shiftingAnimation([=](crl::time now) {
|
, _shiftingAnimation([=](crl::time now) {
|
||||||
return shiftingAnimationCallback(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 buttonw = st::stickersRemove.width;
|
||||||
auto buttonh = st::stickersRemove.height;
|
auto buttonh = st::stickersRemove.height;
|
||||||
auto buttonshift = st::stickersRemoveSkip;
|
auto buttonshift = st::stickersRemoveSkip;
|
||||||
if (!removeButton) {
|
if (!removeButton) {
|
||||||
const auto &st = _isInstalled
|
const auto &st = installedSet
|
||||||
|
? st::stickersTrendingInstalled
|
||||||
|
: _isInstalledTab
|
||||||
? st::stickersUndoRemove
|
? st::stickersUndoRemove
|
||||||
: st::stickersTrendingAdd;
|
: st::stickersTrendingAdd;
|
||||||
const auto textWidth = _isInstalled ? _undoWidth : _addWidth;
|
const auto textWidth = installedSet
|
||||||
|
? _installedWidth
|
||||||
|
: _isInstalledTab
|
||||||
|
? _undoWidth
|
||||||
|
: _addWidth;
|
||||||
buttonw = textWidth - st.width;
|
buttonw = textWidth - st.width;
|
||||||
buttonh = st.height;
|
buttonh = st.height;
|
||||||
buttonshift = 0;
|
buttonshift = 0;
|
||||||
|
@ -1332,7 +1352,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_isInstalled) {
|
if (_isInstalledTab) {
|
||||||
if (index >= 0 && index == _above) {
|
if (index >= 0 && index == _above) {
|
||||||
auto current = _aboveShadowFadeOpacity.current();
|
auto current = _aboveShadowFadeOpacity.current();
|
||||||
if (_started >= 0) {
|
if (_started >= 0) {
|
||||||
|
@ -1359,13 +1379,13 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
|
||||||
paintFakeButton(p, row, index);
|
paintFakeButton(p, row, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row->removed && _isInstalled) {
|
if (row->removed && _isInstalledTab) {
|
||||||
p.setOpacity(st::stickersRowDisabledOpacity);
|
p.setOpacity(st::stickersRowDisabledOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stickerx = st::contactsPadding.left();
|
auto stickerx = st::contactsPadding.left();
|
||||||
|
|
||||||
if (!_megagroupSet && _isInstalled) {
|
if (!_megagroupSet && _isInstalledTab) {
|
||||||
stickerx += st::stickersReorderIcon.width() + st::stickersReorderSkip;
|
stickerx += st::stickersReorderIcon.width() + st::stickersReorderSkip;
|
||||||
if (!row->isRecentSet()) {
|
if (!row->isRecentSet()) {
|
||||||
st::stickersReorderIcon.paint(p, st::contactsPadding.left(), (_rowHeight - st::stickersReorderIcon.height()) / 2, width());
|
st::stickersReorderIcon.paint(p, st::contactsPadding.left(), (_rowHeight - st::stickersReorderIcon.height()) / 2, width());
|
||||||
|
@ -1553,7 +1573,7 @@ void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
|
||||||
Unexpected("StickersBox::Inner::updateRowThumbnail: row not found");
|
Unexpected("StickersBox::Inner::updateRowThumbnail: row not found");
|
||||||
}();
|
}();
|
||||||
const auto left = st::contactsPadding.left()
|
const auto left = st::contactsPadding.left()
|
||||||
+ ((!_megagroupSet && _isInstalled)
|
+ ((!_megagroupSet && _isInstalledTab)
|
||||||
? st::stickersReorderIcon.width() + st::stickersReorderSkip
|
? st::stickersReorderIcon.width() + st::stickersReorderSkip
|
||||||
: 0);
|
: 0);
|
||||||
update(
|
update(
|
||||||
|
@ -1564,14 +1584,19 @@ void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int index) {
|
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int index) {
|
||||||
auto removeButton = (_isInstalled && !row->removed);
|
const auto removeButton = (_isInstalledTab && !row->removed);
|
||||||
auto rect = relativeButtonRect(removeButton);
|
if (!_isInstalledTab && row->isInstalled() && !row->isArchived() && !row->removed) {
|
||||||
if (!_isInstalled && row->isInstalled() && !row->isArchived() && !row->removed) {
|
// Round button "Added" after installed from Trending or Archived.
|
||||||
// Checkbox after installed from Trending or Archived.
|
const auto rect = relativeButtonRect(removeButton, true);
|
||||||
int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (rect.width() + st::stickersFeaturedInstalled.width()) / 2);
|
const auto &st = st::stickersTrendingInstalled;
|
||||||
int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2;
|
const auto textWidth = _installedWidth;
|
||||||
st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width());
|
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 {
|
} else {
|
||||||
|
const auto rect = relativeButtonRect(removeButton, false);
|
||||||
auto selected = (index == _actionSel && _actionDown < 0) || (index == _actionDown);
|
auto selected = (index == _actionSel && _actionDown < 0) || (index == _actionDown);
|
||||||
if (removeButton) {
|
if (removeButton) {
|
||||||
// Trash icon button when not disabled in Installed.
|
// Trash icon button when not disabled in Installed.
|
||||||
|
@ -1589,11 +1614,11 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int ind
|
||||||
} else {
|
} else {
|
||||||
// Round button ADD when not installed from Trending or Archived.
|
// Round button ADD when not installed from Trending or Archived.
|
||||||
// Or round button UNDO after disabled from Installed.
|
// Or round button UNDO after disabled from Installed.
|
||||||
const auto &st = _isInstalled
|
const auto &st = _isInstalledTab
|
||||||
? st::stickersUndoRemove
|
? st::stickersUndoRemove
|
||||||
: st::stickersTrendingAdd;
|
: st::stickersTrendingAdd;
|
||||||
const auto textWidth = _isInstalled ? _undoWidth : _addWidth;
|
const auto textWidth = _isInstalledTab ? _undoWidth : _addWidth;
|
||||||
const auto &text = _isInstalled ? _undoText : _addText;
|
const auto &text = _isInstalledTab ? _undoText : _addText;
|
||||||
(selected ? _buttonBgOver : _buttonBg).paint(p, myrtlrect(rect));
|
(selected ? _buttonBgOver : _buttonBg).paint(p, myrtlrect(rect));
|
||||||
if (row->ripple) {
|
if (row->ripple) {
|
||||||
row->ripple->paint(p, rect.x(), rect.y(), width());
|
row->ripple->paint(p, rect.x(), rect.y(), width());
|
||||||
|
@ -1618,7 +1643,7 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||||
setActionDown(_actionSel);
|
setActionDown(_actionSel);
|
||||||
update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
|
update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
|
||||||
} else if (auto selectedIndex = std::get_if<int>(&_selected)) {
|
} else if (auto selectedIndex = std::get_if<int>(&_selected)) {
|
||||||
if (_isInstalled && !_rows[*selectedIndex]->isRecentSet() && _inDragArea) {
|
if (_isInstalledTab && !_rows[*selectedIndex]->isRecentSet() && _inDragArea) {
|
||||||
_above = _dragging = _started = *selectedIndex;
|
_above = _dragging = _started = *selectedIndex;
|
||||||
_dragStart = mapFromGlobal(_mouse);
|
_dragStart = mapFromGlobal(_mouse);
|
||||||
}
|
}
|
||||||
|
@ -1640,12 +1665,12 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
|
||||||
if (_actionDown >= 0 && _actionDown < _rows.size()) {
|
if (_actionDown >= 0 && _actionDown < _rows.size()) {
|
||||||
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
|
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
|
||||||
const auto row = _rows[_actionDown].get();
|
const auto row = _rows[_actionDown].get();
|
||||||
auto removeButton = (_isInstalled && !row->removed);
|
auto removeButton = (_isInstalledTab && !row->removed);
|
||||||
if (!row->ripple) {
|
if (!row->ripple) {
|
||||||
if (_isInstalled) {
|
if (_isInstalledTab) {
|
||||||
if (row->removed) {
|
if (row->removed) {
|
||||||
auto rippleSize = QSize(_undoWidth - st::stickersUndoRemove.width, st::stickersUndoRemove.height);
|
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);
|
ensureRipple(st::stickersUndoRemove.ripple, std::move(rippleMask), removeButton);
|
||||||
} else {
|
} else {
|
||||||
auto rippleSize = st::stickersRemove.rippleAreaSize;
|
auto rippleSize = st::stickersRemove.rippleAreaSize;
|
||||||
|
@ -1654,12 +1679,12 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
|
||||||
}
|
}
|
||||||
} else if (!row->isInstalled() || row->isArchived() || row->removed) {
|
} else if (!row->isInstalled() || row->isArchived() || row->removed) {
|
||||||
auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
|
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);
|
ensureRipple(st::stickersTrendingAdd.ripple, std::move(rippleMask), removeButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (row->ripple) {
|
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()));
|
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) {
|
void StickersBox::Inner::ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton) {
|
||||||
_rows[_actionDown]->ripple = std::make_unique<Ui::RippleAnimation>(st, std::move(mask), [this, index = _actionDown, removeButton] {
|
_rows[_actionDown]->ripple = std::make_unique<Ui::RippleAnimation>(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;
|
selected = selectedIndex;
|
||||||
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
|
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
|
||||||
const auto row = _rows[selectedIndex].get();
|
const auto row = _rows[selectedIndex].get();
|
||||||
if (!_megagroupSet && (_isInstalled || !row->isInstalled() || row->isArchived() || row->removed)) {
|
if (!_megagroupSet && (_isInstalledTab || !row->isInstalled() || row->isArchived() || row->removed)) {
|
||||||
auto removeButton = (_isInstalled && !row->removed);
|
auto removeButton = (_isInstalledTab && !row->removed);
|
||||||
auto rect = myrtlrect(relativeButtonRect(removeButton));
|
auto rect = myrtlrect(relativeButtonRect(removeButton, false));
|
||||||
actionSel = rect.contains(local) ? selectedIndex : -1;
|
actionSel = rect.contains(local) ? selectedIndex : -1;
|
||||||
} else {
|
} else {
|
||||||
actionSel = -1;
|
actionSel = -1;
|
||||||
}
|
}
|
||||||
if (!_megagroupSet && _isInstalled && !row->isRecentSet()) {
|
if (!_megagroupSet && _isInstalledTab && !row->isRecentSet()) {
|
||||||
auto dragAreaWidth = st::contactsPadding.left() + st::stickersReorderIcon.width() + st::stickersReorderSkip;
|
auto dragAreaWidth = st::contactsPadding.left() + st::stickersReorderIcon.width() + st::stickersReorderSkip;
|
||||||
auto dragArea = myrtlrect(0, 0, dragAreaWidth, _rowHeight);
|
auto dragArea = myrtlrect(0, 0, dragAreaWidth, _rowHeight);
|
||||||
inDragArea = dragArea.contains(local);
|
inDragArea = dragArea.contains(local);
|
||||||
|
@ -1816,7 +1841,7 @@ void StickersBox::Inner::updateSelected() {
|
||||||
void StickersBox::Inner::updateCursor() {
|
void StickersBox::Inner::updateCursor() {
|
||||||
setCursor(_inDragArea
|
setCursor(_inDragArea
|
||||||
? style::cur_sizeall
|
? style::cur_sizeall
|
||||||
: (!_megagroupSet && _isInstalled)
|
: (!_megagroupSet && _isInstalledTab)
|
||||||
? ((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel))
|
? ((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel))
|
||||||
? style::cur_pointer
|
? style::cur_pointer
|
||||||
: style::cur_default)
|
: style::cur_default)
|
||||||
|
@ -1841,7 +1866,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
_mouse = e->globalPos();
|
_mouse = e->globalPos();
|
||||||
updateSelected();
|
updateSelected();
|
||||||
if (_actionDown == _actionSel && _actionSel >= 0) {
|
if (_actionDown == _actionSel && _actionSel >= 0) {
|
||||||
if (_isInstalled) {
|
if (_isInstalledTab) {
|
||||||
setRowRemoved(_actionDown, !_rows[_actionDown]->removed);
|
setRowRemoved(_actionDown, !_rows[_actionDown]->removed);
|
||||||
} else if (_installSetCallback) {
|
} else if (_installSetCallback) {
|
||||||
_installSetCallback(_rows[_actionDown]->set->id);
|
_installSetCallback(_rows[_actionDown]->set->id);
|
||||||
|
@ -2079,15 +2104,15 @@ void StickersBox::Inner::rebuildMegagroupSet() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto set = it->second.get();
|
const auto set = it->second.get();
|
||||||
auto maxNameWidth = countMaxNameWidth();
|
|
||||||
auto titleWidth = 0;
|
|
||||||
auto title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
|
||||||
auto count = fillSetCount(set);
|
auto count = fillSetCount(set);
|
||||||
auto sticker = (DocumentData*)nullptr;
|
auto sticker = (DocumentData*)nullptr;
|
||||||
auto pixw = 0, pixh = 0;
|
auto pixw = 0, pixh = 0;
|
||||||
fillSetCover(set, &sticker, &pixw, &pixh);
|
fillSetCover(set, &sticker, &pixw, &pixh);
|
||||||
auto flagsOverride = SetFlag::Installed;
|
auto flagsOverride = SetFlag::Installed;
|
||||||
auto removed = false;
|
auto removed = false;
|
||||||
|
auto maxNameWidth = countMaxNameWidth(!_isInstalledTab);
|
||||||
|
auto titleWidth = 0;
|
||||||
|
auto title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
||||||
if (!_megagroupSelectedSet
|
if (!_megagroupSelectedSet
|
||||||
|| _megagroupSelectedSet->set->id != set->id) {
|
|| _megagroupSelectedSet->set->id != set->id) {
|
||||||
_megagroupSetField->setText(set->shortName);
|
_megagroupSetField->setText(set->shortName);
|
||||||
|
@ -2125,8 +2150,6 @@ void StickersBox::Inner::rebuild(bool masks) {
|
||||||
rebuildMegagroupSet();
|
rebuildMegagroupSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto maxNameWidth = countMaxNameWidth();
|
|
||||||
|
|
||||||
_oldRows = std::move(_rows);
|
_oldRows = std::move(_rows);
|
||||||
clear();
|
clear();
|
||||||
const auto &order = ([&]() -> const StickersSetsOrder & {
|
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_featured(tr::now)
|
||||||
: tr::lng_stickers_group_from_your(tr::now));
|
: tr::lng_stickers_group_from_your(tr::now));
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
} else if (_isInstalled) {
|
} else if (_isInstalledTab) {
|
||||||
const auto cloudIt = sets.find((_section == Section::Masks)
|
const auto cloudIt = sets.find((_section == Section::Masks)
|
||||||
? Data::Stickers::CloudRecentAttachedSetId
|
? Data::Stickers::CloudRecentAttachedSetId
|
||||||
: Data::Stickers::CloudRecentSetId); // Section::Installed.
|
: Data::Stickers::CloudRecentSetId); // Section::Installed.
|
||||||
if (cloudIt != sets.cend() && !cloudIt->second->stickers.isEmpty()) {
|
if (cloudIt != sets.cend() && !cloudIt->second->stickers.isEmpty()) {
|
||||||
rebuildAppendSet(cloudIt->second.get(), maxNameWidth);
|
rebuildAppendSet(cloudIt->second.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto setId : order) {
|
for (const auto setId : order) {
|
||||||
|
@ -2170,7 +2193,7 @@ void StickersBox::Inner::rebuild(bool masks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto set = it->second.get();
|
const auto set = it->second.get();
|
||||||
rebuildAppendSet(set, maxNameWidth);
|
rebuildAppendSet(set);
|
||||||
|
|
||||||
if (set->stickers.isEmpty()
|
if (set->stickers.isEmpty()
|
||||||
|| (set->flags & SetFlag::NotLoaded)) {
|
|| (set->flags & SetFlag::NotLoaded)) {
|
||||||
|
@ -2205,7 +2228,8 @@ void StickersBox::Inner::updateSize(int newWidth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickersBox::Inner::updateRows() {
|
void StickersBox::Inner::updateRows() {
|
||||||
int maxNameWidth = countMaxNameWidth();
|
const auto maxNameWidth = countMaxNameWidth(false);
|
||||||
|
const auto maxNameWidthInstalled = countMaxNameWidth(true);
|
||||||
const auto &sets = session().data().stickers().sets();
|
const auto &sets = session().data().stickers().sets();
|
||||||
for (const auto &row : _rows) {
|
for (const auto &row : _rows) {
|
||||||
const auto it = sets.find(row->set->id);
|
const auto it = sets.find(row->set->id);
|
||||||
|
@ -2231,7 +2255,7 @@ void StickersBox::Inner::updateRows() {
|
||||||
auto wasInstalled = row->isInstalled();
|
auto wasInstalled = row->isInstalled();
|
||||||
auto wasArchived = row->isArchived();
|
auto wasArchived = row->isArchived();
|
||||||
row->flagsOverride = fillSetFlags(set);
|
row->flagsOverride = fillSetFlags(set);
|
||||||
if (_isInstalled) {
|
if (_isInstalledTab) {
|
||||||
row->flagsOverride &= ~SetFlag::Archived;
|
row->flagsOverride &= ~SetFlag::Archived;
|
||||||
}
|
}
|
||||||
if (row->isInstalled() != wasInstalled
|
if (row->isInstalled() != wasInstalled
|
||||||
|
@ -2239,7 +2263,14 @@ void StickersBox::Inner::updateRows() {
|
||||||
row->ripple.reset();
|
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);
|
row->count = fillSetCount(set);
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
|
@ -2251,7 +2282,7 @@ bool StickersBox::Inner::appendSet(not_null<StickersSet*> set) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rebuildAppendSet(set, countMaxNameWidth());
|
rebuildAppendSet(set);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2259,18 +2290,20 @@ bool StickersBox::Inner::skipPremium() const {
|
||||||
return !_session->premiumPossible();
|
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();
|
int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left();
|
||||||
if (!_megagroupSet && _isInstalled) {
|
if (!_megagroupSet && _isInstalledTab) {
|
||||||
namex += st::stickersReorderIcon.width() + st::stickersReorderSkip;
|
namex += st::stickersReorderIcon.width() + st::stickersReorderSkip;
|
||||||
}
|
}
|
||||||
int namew = st::boxWideWidth - namex - st::contactsPadding.right() - st::contactsCheckPosition.x();
|
int namew = st::boxWideWidth - namex - st::contactsPadding.right() - st::contactsCheckPosition.x();
|
||||||
if (_isInstalled) {
|
if (_isInstalledTab) {
|
||||||
if (!_megagroupSet) {
|
if (!_megagroupSet) {
|
||||||
namew -= _undoWidth - st::stickersUndoRemove.width;
|
namew -= _undoWidth - st::stickersUndoRemove.width;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
namew -= _addWidth - st::stickersTrendingAdd.width;
|
namew -= installedSet
|
||||||
|
? (_installedWidth - st::stickersTrendingInstalled.width)
|
||||||
|
: (_addWidth - st::stickersTrendingAdd.width);
|
||||||
if (_section == Section::Featured) {
|
if (_section == Section::Featured) {
|
||||||
namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip;
|
namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip;
|
||||||
}
|
}
|
||||||
|
@ -2278,14 +2311,12 @@ int StickersBox::Inner::countMaxNameWidth() const {
|
||||||
return namew;
|
return namew;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickersBox::Inner::rebuildAppendSet(
|
void StickersBox::Inner::rebuildAppendSet(not_null<StickersSet*> set) {
|
||||||
not_null<StickersSet*> set,
|
|
||||||
int maxNameWidth) {
|
|
||||||
auto flagsOverride = (set->id != Data::Stickers::CloudRecentSetId)
|
auto flagsOverride = (set->id != Data::Stickers::CloudRecentSetId)
|
||||||
? fillSetFlags(set)
|
? fillSetFlags(set)
|
||||||
: SetFlag::Installed;
|
: SetFlag::Installed;
|
||||||
auto removed = false;
|
auto removed = false;
|
||||||
if (_isInstalled && (flagsOverride & SetFlag::Archived)) {
|
if (_isInstalledTab && (flagsOverride & SetFlag::Archived)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2293,6 +2324,10 @@ void StickersBox::Inner::rebuildAppendSet(
|
||||||
int pixw = 0, pixh = 0;
|
int pixw = 0, pixh = 0;
|
||||||
fillSetCover(set, &sticker, &pixw, &pixh);
|
fillSetCover(set, &sticker, &pixw, &pixh);
|
||||||
|
|
||||||
|
const auto maxNameWidth = countMaxNameWidth(!_isInstalledTab
|
||||||
|
&& (flagsOverride & SetFlag::Installed)
|
||||||
|
&& !(flagsOverride & SetFlag::Archived)
|
||||||
|
&& !removed);
|
||||||
int titleWidth = 0;
|
int titleWidth = 0;
|
||||||
QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
||||||
int count = fillSetCount(set);
|
int count = fillSetCount(set);
|
||||||
|
|
|
@ -393,8 +393,12 @@ void UsernamesBox(
|
||||||
editor->submitted(
|
editor->submitted(
|
||||||
) | rpl::start_with_next(finish, editor->lifetime());
|
) | rpl::start_with_next(finish, editor->lifetime());
|
||||||
|
|
||||||
box->addButton(tr::lng_settings_save(), finish);
|
if (isBot) {
|
||||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
|
||||||
|
} else {
|
||||||
|
box->addButton(tr::lng_settings_save(), finish);
|
||||||
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddUsernameCheckLabel(
|
void AddUsernameCheckLabel(
|
||||||
|
|
|
@ -744,6 +744,11 @@ groupCallShareBoxComment: InputField(groupCallField) {
|
||||||
}
|
}
|
||||||
groupCallShareBoxList: PeerList(groupCallMembersList) {
|
groupCallShareBoxList: PeerList(groupCallMembersList) {
|
||||||
item: PeerListItem(groupCallMembersListItem) {
|
item: PeerListItem(groupCallMembersListItem) {
|
||||||
|
nameStyle: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(11px);
|
||||||
|
linkFont: font(11px);
|
||||||
|
linkFontOver: font(11px);
|
||||||
|
}
|
||||||
checkbox: RoundImageCheckbox(groupCallMembersListCheckbox) {
|
checkbox: RoundImageCheckbox(groupCallMembersListCheckbox) {
|
||||||
imageRadius: 28px;
|
imageRadius: 28px;
|
||||||
imageSmallRadius: 24px;
|
imageSmallRadius: 24px;
|
||||||
|
@ -1333,13 +1338,7 @@ groupCallNiceTooltip: ImportantTooltip(defaultImportantTooltip) {
|
||||||
radius: 4px;
|
radius: 4px;
|
||||||
arrow: 4px;
|
arrow: 4px;
|
||||||
}
|
}
|
||||||
groupCallNiceTooltipLabel: FlatLabel(defaultImportantTooltipLabel) {
|
groupCallNiceTooltipLabel: defaultImportantTooltipLabel;
|
||||||
style: TextStyle(defaultTextStyle) {
|
|
||||||
font: font(11px);
|
|
||||||
linkFont: font(11px);
|
|
||||||
linkFontOver: font(11px underline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
groupCallStickedTooltip: ImportantTooltip(groupCallNiceTooltip) {
|
groupCallStickedTooltip: ImportantTooltip(groupCallNiceTooltip) {
|
||||||
padding: margins(10px, 1px, 6px, 3px);
|
padding: margins(10px, 1px, 6px, 3px);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2060,6 +2060,7 @@ void Panel::showNiceTooltip(
|
||||||
(normal ? widget().get() : container),
|
(normal ? widget().get() : container),
|
||||||
std::move(text),
|
std::move(text),
|
||||||
st::groupCallNiceTooltipLabel);
|
st::groupCallNiceTooltipLabel);
|
||||||
|
label->resizeToNaturalWidth(label->naturalWidth());
|
||||||
if (normal) {
|
if (normal) {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,6 @@ EmojiPan {
|
||||||
trendingHeaderFg: color;
|
trendingHeaderFg: color;
|
||||||
trendingSubheaderFg: color;
|
trendingSubheaderFg: color;
|
||||||
trendingUnreadFg: color;
|
trendingUnreadFg: color;
|
||||||
trendingInstalled: icon;
|
|
||||||
overBg: color;
|
overBg: color;
|
||||||
pathBg: color;
|
pathBg: color;
|
||||||
pathFg: color;
|
pathFg: color;
|
||||||
|
@ -204,6 +203,7 @@ ComposeControls {
|
||||||
record: RecordBar;
|
record: RecordBar;
|
||||||
files: ComposeFiles;
|
files: ComposeFiles;
|
||||||
premium: PremiumLimits;
|
premium: PremiumLimits;
|
||||||
|
boxField: InputField;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportBox {
|
ReportBox {
|
||||||
|
@ -287,6 +287,12 @@ stickersTrendingAdd: RoundButton(defaultActiveButton) {
|
||||||
height: 26px;
|
height: 26px;
|
||||||
textTop: 4px;
|
textTop: 4px;
|
||||||
}
|
}
|
||||||
|
stickersTrendingInstalled: RoundButton(stickersTrendingAdd) {
|
||||||
|
textFg: activeButtonBg;
|
||||||
|
textFgOver: activeButtonBgOver;
|
||||||
|
textBg: activeButtonSecondaryFg;
|
||||||
|
textBgOver: activeButtonSecondaryFgOver;
|
||||||
|
}
|
||||||
stickersRemove: IconButton(defaultIconButton) {
|
stickersRemove: IconButton(defaultIconButton) {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
@ -323,7 +329,6 @@ stickersFeaturedUnreadBg: msgFileInBg;
|
||||||
stickersFeaturedUnreadSize: 5px;
|
stickersFeaturedUnreadSize: 5px;
|
||||||
stickersFeaturedUnreadSkip: 5px;
|
stickersFeaturedUnreadSkip: 5px;
|
||||||
stickersFeaturedUnreadTop: 7px;
|
stickersFeaturedUnreadTop: 7px;
|
||||||
stickersFeaturedInstalled: icon {{ "chat/input_save", lightButtonFg }};
|
|
||||||
|
|
||||||
stickersMaxHeight: 320px;
|
stickersMaxHeight: 320px;
|
||||||
stickersPadding: margins(19px, 13px, 19px, 13px);
|
stickersPadding: margins(19px, 13px, 19px, 13px);
|
||||||
|
@ -590,7 +595,6 @@ defaultEmojiPan: EmojiPan {
|
||||||
trendingHeaderFg: stickersTrendingHeaderFg;
|
trendingHeaderFg: stickersTrendingHeaderFg;
|
||||||
trendingSubheaderFg: stickersTrendingSubheaderFg;
|
trendingSubheaderFg: stickersTrendingSubheaderFg;
|
||||||
trendingUnreadFg: stickersFeaturedUnreadBg;
|
trendingUnreadFg: stickersFeaturedUnreadBg;
|
||||||
trendingInstalled: stickersFeaturedInstalled;
|
|
||||||
overBg: emojiPanHover;
|
overBg: emojiPanHover;
|
||||||
pathBg: windowBgRipple;
|
pathBg: windowBgRipple;
|
||||||
pathFg: windowBgOver;
|
pathFg: windowBgOver;
|
||||||
|
@ -1148,6 +1152,7 @@ defaultComposeControls: ComposeControls {
|
||||||
record: defaultRecordBar;
|
record: defaultRecordBar;
|
||||||
files: defaultComposeFiles;
|
files: defaultComposeFiles;
|
||||||
premium: defaultPremiumLimits;
|
premium: defaultPremiumLimits;
|
||||||
|
boxField: defaultInputField;
|
||||||
}
|
}
|
||||||
|
|
||||||
moreChatsBarHeight: 48px;
|
moreChatsBarHeight: 48px;
|
||||||
|
|
|
@ -188,7 +188,7 @@ StickersListWidget::StickersListWidget(
|
||||||
, _mode(descriptor.mode)
|
, _mode(descriptor.mode)
|
||||||
, _show(std::move(descriptor.show))
|
, _show(std::move(descriptor.show))
|
||||||
, _features(descriptor.features)
|
, _features(descriptor.features)
|
||||||
, _overBg(st::roundRadiusSmall, st().overBg)
|
, _overBg(st::roundRadiusLarge, st().overBg)
|
||||||
, _api(&session().mtp())
|
, _api(&session().mtp())
|
||||||
, _localSetsManager(std::make_unique<LocalStickersManager>(&session()))
|
, _localSetsManager(std::make_unique<LocalStickersManager>(&session()))
|
||||||
, _section(Section::Stickers)
|
, _section(Section::Stickers)
|
||||||
|
@ -196,22 +196,27 @@ StickersListWidget::StickersListWidget(
|
||||||
, _updateItemsTimer([=] { updateItems(); })
|
, _updateItemsTimer([=] { updateItems(); })
|
||||||
, _updateSetsTimer([=] { updateSets(); })
|
, _updateSetsTimer([=] { updateSets(); })
|
||||||
, _trendingAddBgOver(
|
, _trendingAddBgOver(
|
||||||
ImageRoundRadius::Small,
|
ImageRoundRadius::Large,
|
||||||
st::stickersTrendingAdd.textBgOver)
|
st::stickersTrendingAdd.textBgOver)
|
||||||
, _trendingAddBg(ImageRoundRadius::Small, st::stickersTrendingAdd.textBg)
|
, _trendingAddBg(ImageRoundRadius::Large, st::stickersTrendingAdd.textBg)
|
||||||
|
, _inactiveButtonBg(
|
||||||
|
ImageRoundRadius::Large,
|
||||||
|
st::stickersTrendingInstalled.textBg)
|
||||||
, _groupCategoryAddBgOver(
|
, _groupCategoryAddBgOver(
|
||||||
ImageRoundRadius::Small,
|
ImageRoundRadius::Large,
|
||||||
st::stickerGroupCategoryAdd.textBgOver)
|
st::stickerGroupCategoryAdd.textBgOver)
|
||||||
, _groupCategoryAddBg(
|
, _groupCategoryAddBg(
|
||||||
ImageRoundRadius::Small,
|
ImageRoundRadius::Large,
|
||||||
st::stickerGroupCategoryAdd.textBg)
|
st::stickerGroupCategoryAdd.textBg)
|
||||||
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
|
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
|
||||||
st().pathBg,
|
st().pathBg,
|
||||||
st().pathFg,
|
st().pathFg,
|
||||||
[=] { update(); }))
|
[=] { update(); }))
|
||||||
, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st().headerLeft)
|
, _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))
|
, _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))
|
, _settings(this, tr::lng_stickers_you_have(tr::now))
|
||||||
, _previewTimer([=] { showPreview(); })
|
, _previewTimer([=] { showPreview(); })
|
||||||
, _premiumMark(std::make_unique<StickerPremiumMark>(&session()))
|
, _premiumMark(std::make_unique<StickerPremiumMark>(&session()))
|
||||||
|
@ -898,26 +903,40 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
||||||
: loadedCount;
|
: loadedCount;
|
||||||
|
|
||||||
auto widthForTitle = stickersRight() - (st().headerLeft - st().margin.left());
|
auto widthForTitle = stickersRight() - (st().headerLeft - st().margin.left());
|
||||||
if (featuredHasAddButton(info.section)) {
|
{
|
||||||
auto add = featuredAddRect(info);
|
const auto installedSet = !featuredHasAddButton(info.section);
|
||||||
auto selected = selectedButton ? (selectedButton->section == info.section) : false;
|
const auto add = featuredAddRect(info, installedSet);
|
||||||
(selected ? _trendingAddBgOver : _trendingAddBg).paint(p, myrtlrect(add));
|
const auto selected = selectedButton
|
||||||
|
? (selectedButton->section == info.section)
|
||||||
|
: false;
|
||||||
|
(installedSet
|
||||||
|
? _inactiveButtonBg
|
||||||
|
: selected
|
||||||
|
? _trendingAddBgOver
|
||||||
|
: _trendingAddBg).paint(p, myrtlrect(add));
|
||||||
if (set.ripple) {
|
if (set.ripple) {
|
||||||
set.ripple->paint(p, add.x(), add.y(), width());
|
set.ripple->paint(p, add.x(), add.y(), width());
|
||||||
if (set.ripple->empty()) {
|
if (set.ripple->empty()) {
|
||||||
set.ripple.reset();
|
set.ripple.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.setFont(st::stickersTrendingAdd.font);
|
const auto &text = installedSet ? _installedText : _addText;
|
||||||
p.setPen(selected ? st::stickersTrendingAdd.textFgOver : st::stickersTrendingAdd.textFg);
|
const auto textWidth = installedSet
|
||||||
p.drawTextLeft(add.x() - (st::stickersTrendingAdd.width / 2), add.y() + st::stickersTrendingAdd.textTop, width(), _addText, _addWidth);
|
? _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);
|
widthForTitle -= add.width() - (st.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());
|
|
||||||
}
|
}
|
||||||
if (set.flags & SetFlag::Unread) {
|
if (set.flags & SetFlag::Unread) {
|
||||||
widthForTitle -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip;
|
widthForTitle -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip;
|
||||||
|
@ -1455,14 +1474,17 @@ bool StickersListWidget::featuredHasAddButton(int index) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect StickersListWidget::featuredAddRect(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 {
|
QRect StickersListWidget::featuredAddRect(
|
||||||
auto addw = _addWidth - st::stickersTrendingAdd.width;
|
const SectionInfo &info,
|
||||||
auto addh = st::stickersTrendingAdd.height;
|
bool installedSet) const {
|
||||||
auto addx = stickersRight() - addw;
|
const auto addw = (installedSet ? _installedWidth : _addWidth)
|
||||||
auto addy = info.top + st::stickersTrendingAddTop;
|
- 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);
|
return QRect(addx, addy, addw, addh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1538,7 +1560,7 @@ void StickersListWidget::setPressed(OverState newPressed) {
|
||||||
} else if (std::get_if<OverGroupAdd>(&_pressed)) {
|
} else if (std::get_if<OverGroupAdd>(&_pressed)) {
|
||||||
if (!_megagroupSetButtonRipple) {
|
if (!_megagroupSetButtonRipple) {
|
||||||
auto maskSize = _megagroupSetButtonRect.size();
|
auto maskSize = _megagroupSetButtonRect.size();
|
||||||
auto mask = Ui::RippleAnimation::RoundRectMask(maskSize, st::roundRadiusSmall);
|
auto mask = Ui::RippleAnimation::RoundRectMask(maskSize, st::roundRadiusLarge);
|
||||||
_megagroupSetButtonRipple = std::make_unique<Ui::RippleAnimation>(st::stickerGroupCategoryAdd.ripple, std::move(mask), [this] {
|
_megagroupSetButtonRipple = std::make_unique<Ui::RippleAnimation>(st::stickerGroupCategoryAdd.ripple, std::move(mask), [this] {
|
||||||
rtlupdate(megagroupSetButtonRectFinal());
|
rtlupdate(megagroupSetButtonRectFinal());
|
||||||
});
|
});
|
||||||
|
@ -1566,7 +1588,7 @@ std::unique_ptr<Ui::RippleAnimation> StickersListWidget::createButtonRipple(int
|
||||||
|
|
||||||
if (shownSets()[section].externalLayout) {
|
if (shownSets()[section].externalLayout) {
|
||||||
auto maskSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
|
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<Ui::RippleAnimation>(
|
return std::make_unique<Ui::RippleAnimation>(
|
||||||
st::stickersTrendingAdd.ripple,
|
st::stickersTrendingAdd.ripple,
|
||||||
std::move(mask),
|
std::move(mask),
|
||||||
|
@ -1768,6 +1790,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
Assert(button->section >= 0 && button->section < sets.size());
|
Assert(button->section >= 0 && button->section < sets.size());
|
||||||
if (sets[button->section].externalLayout) {
|
if (sets[button->section].externalLayout) {
|
||||||
_localSetsManager->install(sets[button->section].id);
|
_localSetsManager->install(sets[button->section].id);
|
||||||
|
update();
|
||||||
} else {
|
} else {
|
||||||
removeSet(sets[button->section].id);
|
removeSet(sets[button->section].id);
|
||||||
}
|
}
|
||||||
|
@ -2384,7 +2407,7 @@ void StickersListWidget::updateSelected() {
|
||||||
if (p.y() >= info.top && p.y() < info.rowsTop) {
|
if (p.y() >= info.top && p.y() < info.rowsTop) {
|
||||||
if (hasRemoveButton(section) && myrtlrect(removeButtonRect(info)).contains(p.x(), p.y())) {
|
if (hasRemoveButton(section) && myrtlrect(removeButtonRect(info)).contains(p.x(), p.y())) {
|
||||||
newSelected = OverButton{ section };
|
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 };
|
newSelected = OverButton{ section };
|
||||||
} else if (_features.openStickerSets
|
} else if (_features.openStickerSets
|
||||||
&& !(sets[section].flags & SetFlag::Special)) {
|
&& !(sets[section].flags & SetFlag::Special)) {
|
||||||
|
@ -2587,7 +2610,7 @@ void StickersListWidget::showMegagroupSet(ChannelData *megagroup) {
|
||||||
_megagroupSetAbout.setText(
|
_megagroupSetAbout.setText(
|
||||||
st::stickerGroupCategoryAbout,
|
st::stickerGroupCategoryAbout,
|
||||||
tr::lng_group_stickers_description(tr::now));
|
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();
|
refreshMegagroupSetGeometry();
|
||||||
}
|
}
|
||||||
_megagroupSetButtonRipple.reset();
|
_megagroupSetButtonRipple.reset();
|
||||||
|
|
|
@ -302,7 +302,9 @@ private:
|
||||||
[[nodiscard]] int stickersRight() const;
|
[[nodiscard]] int stickersRight() const;
|
||||||
[[nodiscard]] bool featuredHasAddButton(int index) const;
|
[[nodiscard]] bool featuredHasAddButton(int index) const;
|
||||||
[[nodiscard]] QRect featuredAddRect(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]] bool hasRemoveButton(int index) const;
|
||||||
[[nodiscard]] QRect removeButtonRect(int index) const;
|
[[nodiscard]] QRect removeButtonRect(int index) const;
|
||||||
[[nodiscard]] QRect removeButtonRect(const SectionInfo &info) const;
|
[[nodiscard]] QRect removeButtonRect(const SectionInfo &info) const;
|
||||||
|
@ -392,7 +394,7 @@ private:
|
||||||
OverState _pressed;
|
OverState _pressed;
|
||||||
QPoint _lastMousePosition;
|
QPoint _lastMousePosition;
|
||||||
|
|
||||||
Ui::RoundRect _trendingAddBgOver, _trendingAddBg;
|
Ui::RoundRect _trendingAddBgOver, _trendingAddBg, _inactiveButtonBg;
|
||||||
Ui::RoundRect _groupCategoryAddBgOver, _groupCategoryAddBg;
|
Ui::RoundRect _groupCategoryAddBgOver, _groupCategoryAddBg;
|
||||||
|
|
||||||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||||
|
@ -405,6 +407,8 @@ private:
|
||||||
|
|
||||||
QString _addText;
|
QString _addText;
|
||||||
int _addWidth;
|
int _addWidth;
|
||||||
|
QString _installedText;
|
||||||
|
int _installedWidth;
|
||||||
|
|
||||||
object_ptr<Ui::LinkButton> _settings;
|
object_ptr<Ui::LinkButton> _settings;
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,7 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) {
|
||||||
my.show->showBox(std::move(box));
|
my.show->showBox(std::move(box));
|
||||||
} else if (use) {
|
} else if (use) {
|
||||||
use->show(std::move(box));
|
use->show(std::move(box));
|
||||||
|
use->activate();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
open();
|
open();
|
||||||
|
|
|
@ -202,7 +202,8 @@ QByteArray Settings::serialize() const {
|
||||||
+ sizeof(qint32) * 3
|
+ sizeof(qint32) * 3
|
||||||
+ Serialize::bytearraySize(mediaViewPosition)
|
+ Serialize::bytearraySize(mediaViewPosition)
|
||||||
+ sizeof(qint32)
|
+ sizeof(qint32)
|
||||||
+ sizeof(quint64);
|
+ sizeof(quint64)
|
||||||
|
+ sizeof(qint32);
|
||||||
|
|
||||||
auto result = QByteArray();
|
auto result = QByteArray();
|
||||||
result.reserve(size);
|
result.reserve(size);
|
||||||
|
@ -336,7 +337,8 @@ QByteArray Settings::serialize() const {
|
||||||
<< qint32(_windowTitleContent.current().hideTotalUnread ? 1 : 0)
|
<< qint32(_windowTitleContent.current().hideTotalUnread ? 1 : 0)
|
||||||
<< mediaViewPosition
|
<< mediaViewPosition
|
||||||
<< qint32(_ignoreBatterySaving.current() ? 1 : 0)
|
<< qint32(_ignoreBatterySaving.current() ? 1 : 0)
|
||||||
<< quint64(_macRoundIconDigest.value_or(0));
|
<< quint64(_macRoundIconDigest.value_or(0))
|
||||||
|
<< qint32(_storiesClickTooltipHidden.current() ? 1 : 0);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -444,6 +446,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
QByteArray mediaViewPosition;
|
QByteArray mediaViewPosition;
|
||||||
qint32 ignoreBatterySaving = _ignoreBatterySaving.current() ? 1 : 0;
|
qint32 ignoreBatterySaving = _ignoreBatterySaving.current() ? 1 : 0;
|
||||||
quint64 macRoundIconDigest = _macRoundIconDigest.value_or(0);
|
quint64 macRoundIconDigest = _macRoundIconDigest.value_or(0);
|
||||||
|
qint32 storiesClickTooltipHidden = _storiesClickTooltipHidden.current() ? 1 : 0;
|
||||||
|
|
||||||
stream >> themesAccentColors;
|
stream >> themesAccentColors;
|
||||||
if (!stream.atEnd()) {
|
if (!stream.atEnd()) {
|
||||||
|
@ -678,6 +681,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
if (!stream.atEnd()) {
|
if (!stream.atEnd()) {
|
||||||
stream >> macRoundIconDigest;
|
stream >> macRoundIconDigest;
|
||||||
}
|
}
|
||||||
|
if (!stream.atEnd()) {
|
||||||
|
stream >> storiesClickTooltipHidden;
|
||||||
|
}
|
||||||
if (stream.status() != QDataStream::Ok) {
|
if (stream.status() != QDataStream::Ok) {
|
||||||
LOG(("App Error: "
|
LOG(("App Error: "
|
||||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||||
|
@ -869,6 +875,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
}
|
}
|
||||||
_ignoreBatterySaving = (ignoreBatterySaving == 1);
|
_ignoreBatterySaving = (ignoreBatterySaving == 1);
|
||||||
_macRoundIconDigest = macRoundIconDigest ? macRoundIconDigest : std::optional<uint64>();
|
_macRoundIconDigest = macRoundIconDigest ? macRoundIconDigest : std::optional<uint64>();
|
||||||
|
_storiesClickTooltipHidden = (storiesClickTooltipHidden == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Settings::getSoundPath(const QString &key) const {
|
QString Settings::getSoundPath(const QString &key) const {
|
||||||
|
@ -1159,6 +1166,7 @@ void Settings::resetOnLastLogout() {
|
||||||
_tabbedReplacedWithInfo = false; // per-window
|
_tabbedReplacedWithInfo = false; // per-window
|
||||||
_systemDarkModeEnabled = false;
|
_systemDarkModeEnabled = false;
|
||||||
_hiddenGroupCallTooltips = 0;
|
_hiddenGroupCallTooltips = 0;
|
||||||
|
_storiesClickTooltipHidden = 0;
|
||||||
|
|
||||||
_recentEmojiPreload.clear();
|
_recentEmojiPreload.clear();
|
||||||
_recentEmoji.clear();
|
_recentEmoji.clear();
|
||||||
|
|
|
@ -802,6 +802,15 @@ public:
|
||||||
[[nodiscard]] std::optional<uint64> macRoundIconDigest() const {
|
[[nodiscard]] std::optional<uint64> macRoundIconDigest() const {
|
||||||
return _macRoundIconDigest;
|
return _macRoundIconDigest;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] bool storiesClickTooltipHidden() const {
|
||||||
|
return _storiesClickTooltipHidden.current();
|
||||||
|
}
|
||||||
|
[[nodiscard]] rpl::producer<bool> storiesClickTooltipHiddenValue() const {
|
||||||
|
return _storiesClickTooltipHidden.value();
|
||||||
|
}
|
||||||
|
void setStoriesClickTooltipHidden(bool value) {
|
||||||
|
_storiesClickTooltipHidden = value;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] static bool ThirdColumnByDefault();
|
[[nodiscard]] static bool ThirdColumnByDefault();
|
||||||
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
|
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
|
||||||
|
@ -923,6 +932,7 @@ private:
|
||||||
WindowPosition _mediaViewPosition = { .maximized = 2 };
|
WindowPosition _mediaViewPosition = { .maximized = 2 };
|
||||||
rpl::variable<bool> _ignoreBatterySaving = false;
|
rpl::variable<bool> _ignoreBatterySaving = false;
|
||||||
std::optional<uint64> _macRoundIconDigest;
|
std::optional<uint64> _macRoundIconDigest;
|
||||||
|
rpl::variable<bool> _storiesClickTooltipHidden = false;
|
||||||
|
|
||||||
bool _tabbedReplacedWithInfo = false; // per-window
|
bool _tabbedReplacedWithInfo = false; // per-window
|
||||||
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
|
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
|
||||||
|
|
|
@ -394,6 +394,18 @@ int Launcher::exec() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Launcher::validateCustomWorkingDir() {
|
||||||
|
if (customWorkingDir()) {
|
||||||
|
if (_customWorkingDir == cWorkingDir()) {
|
||||||
|
_customWorkingDir = QString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cForceWorkingDir(_customWorkingDir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Launcher::workingFolderReady() {
|
void Launcher::workingFolderReady() {
|
||||||
srand((unsigned int)time(nullptr));
|
srand((unsigned int)time(nullptr));
|
||||||
|
|
||||||
|
@ -435,7 +447,7 @@ const QStringList &Launcher::arguments() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launcher::customWorkingDir() const {
|
bool Launcher::customWorkingDir() const {
|
||||||
return _customWorkingDir;
|
return !_customWorkingDir.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::prepareSettings() {
|
void Launcher::prepareSettings() {
|
||||||
|
@ -534,9 +546,9 @@ void Launcher::processArguments() {
|
||||||
gStartInTray = parseResult.contains("-startintray");
|
gStartInTray = parseResult.contains("-startintray");
|
||||||
gQuit = parseResult.contains("-quit");
|
gQuit = parseResult.contains("-quit");
|
||||||
gSendPaths = parseResult.value("-sendpath", {});
|
gSendPaths = parseResult.value("-sendpath", {});
|
||||||
cForceWorkingDir(parseResult.value("-workdir", {}).join(QString()));
|
_customWorkingDir = parseResult.value("-workdir", {}).join(QString());
|
||||||
if (!gWorkingDir.isEmpty()) {
|
if (!_customWorkingDir.isEmpty()) {
|
||||||
_customWorkingDir = true;
|
_customWorkingDir = QDir(_customWorkingDir).absolutePath() + '/';
|
||||||
}
|
}
|
||||||
gStartUrl = parseResult.value("--", {}).join(QString());
|
gStartUrl = parseResult.value("--", {}).join(QString());
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ public:
|
||||||
uint64 installationTag() const;
|
uint64 installationTag() const;
|
||||||
|
|
||||||
bool checkPortableVersionFolder();
|
bool checkPortableVersionFolder();
|
||||||
|
bool validateCustomWorkingDir();
|
||||||
void workingFolderReady();
|
void workingFolderReady();
|
||||||
void writeDebugModeSetting();
|
void writeDebugModeSetting();
|
||||||
void writeInstallBetaVersionsSetting();
|
void writeInstallBetaVersionsSetting();
|
||||||
|
@ -83,7 +84,7 @@ private:
|
||||||
QStringList _arguments;
|
QStringList _arguments;
|
||||||
BaseIntegration _baseIntegration;
|
BaseIntegration _baseIntegration;
|
||||||
|
|
||||||
bool _customWorkingDir = false;
|
QString _customWorkingDir;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
|
||||||
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
||||||
constexpr auto AppName = "AyuGram Desktop"_cs;
|
constexpr auto AppName = "AyuGram Desktop"_cs;
|
||||||
constexpr auto AppFile = "AyuGram"_cs;
|
constexpr auto AppFile = "AyuGram"_cs;
|
||||||
constexpr auto AppVersion = 4008007;
|
constexpr auto AppVersion = 4008010;
|
||||||
constexpr auto AppVersionStr = "4.8.7";
|
constexpr auto AppVersionStr = "4.8.10";
|
||||||
constexpr auto AppBetaVersion = false;
|
constexpr auto AppBetaVersion = false;
|
||||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||||
|
|
|
@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kDefaultCoverThumbnailSize = 100;
|
constexpr auto kDefaultCoverThumbnailSize = 100;
|
||||||
|
constexpr auto kMaxAllowedPreloadPrefix = 6 * 1024 * 1024;
|
||||||
|
|
||||||
const auto kLottieStickerDimensions = QSize(
|
const auto kLottieStickerDimensions = QSize(
|
||||||
kStickerSideSize,
|
kStickerSideSize,
|
||||||
|
@ -393,7 +394,7 @@ void DocumentData::setattributes(
|
||||||
if (data.is_round_message()) {
|
if (data.is_round_message()) {
|
||||||
_additional = std::make_unique<RoundData>();
|
_additional = std::make_unique<RoundData>();
|
||||||
} else if (const auto size = data.vpreload_prefix_size()) {
|
} else if (const auto size = data.vpreload_prefix_size()) {
|
||||||
if (size->v > 0) {
|
if (size->v > 0 && size->v < kMaxAllowedPreloadPrefix) {
|
||||||
_videoPreloadPrefix = size->v;
|
_videoPreloadPrefix = size->v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,7 +185,8 @@ rpl::producer<SparseIdsMergedSlice> SharedScheduledMediaViewer(
|
||||||
SharedMediaMergedKey key,
|
SharedMediaMergedKey key,
|
||||||
int limitBefore,
|
int limitBefore,
|
||||||
int limitAfter) {
|
int limitAfter) {
|
||||||
Expects(!IsServerMsgId(key.mergedKey.universalId));
|
Expects(!key.mergedKey.universalId
|
||||||
|
|| Data::IsScheduledMsgId(key.mergedKey.universalId));
|
||||||
Expects((key.mergedKey.universalId != 0)
|
Expects((key.mergedKey.universalId != 0)
|
||||||
|| (limitBefore == 0 && limitAfter == 0));
|
|| (limitBefore == 0 && limitAfter == 0));
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,7 @@ public:
|
||||||
void registerPolling(not_null<Story*> story, Polling polling);
|
void registerPolling(not_null<Story*> story, Polling polling);
|
||||||
void unregisterPolling(not_null<Story*> story, Polling polling);
|
void unregisterPolling(not_null<Story*> story, Polling polling);
|
||||||
|
|
||||||
bool registerPolling(FullStoryId id, Polling polling);
|
[[nodiscard]] bool registerPolling(FullStoryId id, Polling polling);
|
||||||
void unregisterPolling(FullStoryId id, Polling polling);
|
void unregisterPolling(FullStoryId id, Polling polling);
|
||||||
void requestUserStories(
|
void requestUserStories(
|
||||||
not_null<UserData*> user,
|
not_null<UserData*> user,
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "media/streaming/media_streaming_reader.h"
|
#include "media/streaming/media_streaming_reader.h"
|
||||||
#include "storage/download_manager_mtproto.h"
|
#include "storage/download_manager_mtproto.h"
|
||||||
|
#include "storage/file_download.h" // kMaxFileInMemory
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -574,6 +575,7 @@ void StoryPreload::load() {
|
||||||
}
|
}
|
||||||
_task = std::make_unique<LoadTask>(id(), video, [=](QByteArray data) {
|
_task = std::make_unique<LoadTask>(id(), video, [=](QByteArray data) {
|
||||||
if (!data.isEmpty()) {
|
if (!data.isEmpty()) {
|
||||||
|
Assert(data.size() < Storage::kMaxFileInMemory);
|
||||||
_story->owner().cacheBigFile().putIfEmpty(
|
_story->owner().cacheBigFile().putIfEmpty(
|
||||||
key,
|
key,
|
||||||
Storage::Cache::Database::TaggedValue(std::move(data), 0));
|
Storage::Cache::Database::TaggedValue(std::move(data), 0));
|
||||||
|
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lottie/lottie_frame_generator.h"
|
#include "lottie/lottie_frame_generator.h"
|
||||||
#include "ffmpeg/ffmpeg_frame_generator.h"
|
#include "ffmpeg/ffmpeg_frame_generator.h"
|
||||||
#include "chat_helpers/stickers_lottie.h"
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
|
#include "storage/file_download.h" // kMaxFileInMemory
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/text/text_custom_emoji.h"
|
#include "ui/text/text_custom_emoji.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
@ -345,7 +346,7 @@ void CustomEmojiLoader::check() {
|
||||||
};
|
};
|
||||||
auto put = [=, key = cacheKey(document)](QByteArray value) {
|
auto put = [=, key = cacheKey(document)](QByteArray value) {
|
||||||
const auto size = value.size();
|
const auto size = value.size();
|
||||||
if (size <= Storage::Cache::Database::Settings().maxDataSize) {
|
if (size <= Storage::kMaxFileInMemory) {
|
||||||
document->owner().cacheBigFile().put(key, std::move(value));
|
document->owner().cacheBigFile().put(key, std::move(value));
|
||||||
} else {
|
} else {
|
||||||
LOG(("Data Error: Cached emoji size too big: %1.").arg(size));
|
LOG(("Data Error: Cached emoji size too big: %1.").arg(size));
|
||||||
|
|
|
@ -562,3 +562,16 @@ dialogsStoriesListInfo: DialogsStoriesList(dialogsStoriesList) {
|
||||||
dialogsStoriesListMine: DialogsStoriesList(dialogsStoriesListInfo) {
|
dialogsStoriesListMine: DialogsStoriesList(dialogsStoriesListInfo) {
|
||||||
readOpacity: 1.;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -798,6 +798,22 @@ void Widget::setupStories() {
|
||||||
_scroll->viewportEvent(e);
|
_scroll->viewportEvent(e);
|
||||||
}, _stories->lifetime());
|
}, _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(
|
_storiesContents.fire(Stories::ContentForSession(
|
||||||
&controller()->session(),
|
&controller()->session(),
|
||||||
Data::StorySourcesList::NotHidden));
|
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) {
|
void Widget::scrollToDefault(bool verytop) {
|
||||||
if (verytop) {
|
if (verytop) {
|
||||||
//_scroll->verticalScrollBar()->setMinimum(0);
|
//_scroll->verticalScrollBar()->setMinimum(0);
|
||||||
|
@ -1447,6 +1472,7 @@ void Widget::stopWidthAnimation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::updateStoriesVisibility() {
|
void Widget::updateStoriesVisibility() {
|
||||||
|
updateLockUnlockVisibility();
|
||||||
if (!_stories) {
|
if (!_stories) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1476,7 +1502,6 @@ void Widget::updateStoriesVisibility() {
|
||||||
if (_aboveScrollAdded > 0 && _updateScrollGeometryCached) {
|
if (_aboveScrollAdded > 0 && _updateScrollGeometryCached) {
|
||||||
_updateScrollGeometryCached();
|
_updateScrollGeometryCached();
|
||||||
}
|
}
|
||||||
updateLockUnlockVisibility();
|
|
||||||
updateLockUnlockPosition();
|
updateLockUnlockPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,7 @@ public:
|
||||||
void setInnerFocus();
|
void setInnerFocus();
|
||||||
|
|
||||||
void jumpToTop(bool belowPinned = false);
|
void jumpToTop(bool belowPinned = false);
|
||||||
|
void raiseWithTooltip();
|
||||||
|
|
||||||
void startWidthAnimation();
|
void startWidthAnimation();
|
||||||
void stopWidthAnimation();
|
void stopWidthAnimation();
|
||||||
|
|
|
@ -7,14 +7,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "dialogs/ui/dialogs_stories_list.h"
|
#include "dialogs/ui/dialogs_stories_list.h"
|
||||||
|
|
||||||
|
#include "base/event_filter.h"
|
||||||
|
#include "base/qt_signal_producer.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/effects/outline_segments.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/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/popup_menu.h"
|
||||||
|
#include "ui/widgets/tooltip.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtGui/QPainter>
|
||||||
|
|
||||||
#include "base/debug_log.h"
|
#include "base/debug_log.h"
|
||||||
|
|
||||||
|
@ -27,6 +35,8 @@ constexpr auto kExpandAfterRatio = 0.72;
|
||||||
constexpr auto kCollapseAfterRatio = 0.68;
|
constexpr auto kCollapseAfterRatio = 0.68;
|
||||||
constexpr auto kFrictionRatio = 0.15;
|
constexpr auto kFrictionRatio = 0.15;
|
||||||
constexpr auto kExpandCatchUpDuration = crl::time(200);
|
constexpr auto kExpandCatchUpDuration = crl::time(200);
|
||||||
|
constexpr auto kMaxTooltipNames = 3;
|
||||||
|
constexpr auto kStoriesTooltipHideBgOpacity = 0.2;
|
||||||
|
|
||||||
[[nodiscard]] int AvailableNameWidth(const style::DialogsStoriesList &st) {
|
[[nodiscard]] int AvailableNameWidth(const style::DialogsStoriesList &st) {
|
||||||
const auto &full = st.full;
|
const auto &full = st.full;
|
||||||
|
@ -35,6 +45,33 @@ constexpr auto kExpandCatchUpDuration = crl::time(200);
|
||||||
return full.photoLeft * 2 + full.photo - 2 * skip;
|
return full.photoLeft * 2 + full.photo - 2 * skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] object_ptr<Ui::RpWidget> MakeTooltipContent(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
rpl::producer<TextWithEntities> text,
|
||||||
|
Fn<void()> hide) {
|
||||||
|
const auto size = st::dialogsStoriesTooltipHide.width;
|
||||||
|
const auto skip = st::defaultImportantTooltip.padding.right();
|
||||||
|
auto result = object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||||
|
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<Ui::IconButton>(
|
||||||
|
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
|
} // namespace
|
||||||
|
|
||||||
struct List::Layout {
|
struct List::Layout {
|
||||||
|
@ -112,6 +149,7 @@ void List::showContent(Content &&content) {
|
||||||
_data.items.emplace_back(Item{ .element = element });
|
_data.items.emplace_back(Item{ .element = element });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_lastCollapsedGeometry = {};
|
||||||
if (int(_data.items.size()) != wasCount) {
|
if (int(_data.items.size()) != wasCount) {
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
@ -120,6 +158,8 @@ void List::showContent(Content &&content) {
|
||||||
if (!wasCount) {
|
if (!wasCount) {
|
||||||
_empty = false;
|
_empty = false;
|
||||||
}
|
}
|
||||||
|
_tooltipText = computeTooltipText();
|
||||||
|
updateTooltipGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void List::updateScrollMax() {
|
void List::updateScrollMax() {
|
||||||
|
@ -162,10 +202,16 @@ void List::requestExpanded(bool expanded) {
|
||||||
const auto from = _expanded ? 0. : 1.;
|
const auto from = _expanded ? 0. : 1.;
|
||||||
const auto till = _expanded ? 2. : 0.;
|
const auto till = _expanded ? 2. : 0.;
|
||||||
const auto duration = (_expanded ? 2 : 1) * st::slideWrapDuration;
|
const auto duration = (_expanded ? 2 : 1) * st::slideWrapDuration;
|
||||||
|
if (!isHidden() && _expanded) {
|
||||||
|
toggleTooltip(false);
|
||||||
|
}
|
||||||
_expandedAnimation.start([=] {
|
_expandedAnimation.start([=] {
|
||||||
checkForFullState();
|
checkForFullState();
|
||||||
update();
|
update();
|
||||||
_collapsedGeometryChanged.fire({});
|
_collapsedGeometryChanged.fire({});
|
||||||
|
if (!isHidden() && !_expandedAnimation.animating()) {
|
||||||
|
toggleTooltip(false);
|
||||||
|
}
|
||||||
}, from, till, duration, anim::sineInOut);
|
}, from, till, duration, anim::sineInOut);
|
||||||
}
|
}
|
||||||
_toggleExpandedRequests.fire_copy(_expanded);
|
_toggleExpandedRequests.fire_copy(_expanded);
|
||||||
|
@ -179,6 +225,12 @@ void List::resizeEvent(QResizeEvent *e) {
|
||||||
updateScrollMax();
|
updateScrollMax();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void List::updateExpanding() {
|
||||||
|
updateExpanding(
|
||||||
|
_lastExpandedHeight * _expandCatchUpAnimation.value(1.),
|
||||||
|
_st.full.height);
|
||||||
|
}
|
||||||
|
|
||||||
void List::updateExpanding(int expandingHeight, int expandedHeight) {
|
void List::updateExpanding(int expandingHeight, int expandedHeight) {
|
||||||
Expects(!expandingHeight || expandedHeight > 0);
|
Expects(!expandingHeight || expandedHeight > 0);
|
||||||
|
|
||||||
|
@ -196,12 +248,10 @@ void List::updateExpanding(int expandingHeight, int expandedHeight) {
|
||||||
if (change) {
|
if (change) {
|
||||||
requestExpanded(!_expanded);
|
requestExpanded(!_expanded);
|
||||||
}
|
}
|
||||||
|
updateTooltipGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
List::Layout List::computeLayout() {
|
List::Layout List::computeLayout() {
|
||||||
updateExpanding(
|
|
||||||
_lastExpandedHeight * _expandCatchUpAnimation.value(1.),
|
|
||||||
_st.full.height);
|
|
||||||
return computeLayout(_expandedAnimation.value(_expanded ? 2. : 0.));
|
return computeLayout(_expandedAnimation.value(_expanded ? 2. : 0.));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,11 +382,7 @@ void List::paint(
|
||||||
bool layered) {
|
bool layered) {
|
||||||
const auto &st = _st.small;
|
const auto &st = _st.small;
|
||||||
const auto &full = _st.full;
|
const auto &full = _st.full;
|
||||||
const auto ratio = layout.ratio;
|
|
||||||
const auto expandRatio = layout.expandRatio;
|
const auto expandRatio = layout.expandRatio;
|
||||||
const auto lerp = [&](float64 a, float64 b) {
|
|
||||||
return a + (b - a) * ratio;
|
|
||||||
};
|
|
||||||
const auto elerp = [&](float64 a, float64 b) {
|
const auto elerp = [&](float64 a, float64 b) {
|
||||||
return a + (b - a) * expandRatio;
|
return a + (b - a) * expandRatio;
|
||||||
};
|
};
|
||||||
|
@ -682,6 +728,9 @@ void List::mousePressEvent(QMouseEvent *e) {
|
||||||
return;
|
return;
|
||||||
} else if (_state == State::Small) {
|
} else if (_state == State::Small) {
|
||||||
requestExpanded(true);
|
requestExpanded(true);
|
||||||
|
if (const auto onstack = _tooltipHide) {
|
||||||
|
onstack();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
} else if (_state != State::Full) {
|
} else if (_state != State::Full) {
|
||||||
return;
|
return;
|
||||||
|
@ -761,6 +810,7 @@ void List::setExpandedHeight(int height, bool momentum) {
|
||||||
} else if (!momentum && _expandIgnored && height > 0) {
|
} else if (!momentum && _expandIgnored && height > 0) {
|
||||||
_expandIgnored = false;
|
_expandIgnored = false;
|
||||||
_expandCatchUpAnimation.start([=] {
|
_expandCatchUpAnimation.start([=] {
|
||||||
|
updateExpanding();
|
||||||
update();
|
update();
|
||||||
checkForFullState();
|
checkForFullState();
|
||||||
}, 0., 1., kExpandCatchUpDuration);
|
}, 0., 1., kExpandCatchUpDuration);
|
||||||
|
@ -768,6 +818,7 @@ void List::setExpandedHeight(int height, bool momentum) {
|
||||||
_expandCatchUpAnimation.stop();
|
_expandCatchUpAnimation.stop();
|
||||||
}
|
}
|
||||||
_lastExpandedHeight = height;
|
_lastExpandedHeight = height;
|
||||||
|
updateExpanding();
|
||||||
if (!checkForFullState()) {
|
if (!checkForFullState()) {
|
||||||
setState(!height ? State::Small : State::Changing);
|
setState(!height ? State::Small : State::Changing);
|
||||||
}
|
}
|
||||||
|
@ -788,17 +839,189 @@ void List::setLayoutConstraints(
|
||||||
QPoint positionSmall,
|
QPoint positionSmall,
|
||||||
style::align alignSmall,
|
style::align alignSmall,
|
||||||
QRect geometryFull) {
|
QRect geometryFull) {
|
||||||
|
if (_positionSmall == positionSmall
|
||||||
|
&& _alignSmall == alignSmall
|
||||||
|
&& _geometryFull == geometryFull) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_positionSmall = positionSmall;
|
_positionSmall = positionSmall;
|
||||||
_alignSmall = alignSmall;
|
_alignSmall = alignSmall;
|
||||||
_geometryFull = geometryFull;
|
_geometryFull = geometryFull;
|
||||||
|
_lastCollapsedGeometry = {};
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
update();
|
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<QWidget*> tooltipParent,
|
||||||
|
rpl::producer<bool> shown,
|
||||||
|
Fn<void()> 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<Ui::ImportantTooltip>(
|
||||||
|
tooltipParent,
|
||||||
|
MakeTooltipContent(
|
||||||
|
tooltipParent,
|
||||||
|
_tooltipText.value() | rpl::filter(notEmpty),
|
||||||
|
_tooltipHide),
|
||||||
|
st::dialogsStoriesTooltip);
|
||||||
|
const auto tooltip = _tooltip.get();
|
||||||
|
const auto weak = QPointer<QWidget>(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<QEvent*>;
|
||||||
|
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<QWidget>(_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 {
|
List::CollapsedGeometry List::collapsedGeometryCurrent() const {
|
||||||
const auto expanded = _expandedAnimation.value(_expanded ? 2. : 0.);
|
const auto expanded = _expandedAnimation.value(_expanded ? 2. : 0.);
|
||||||
if (expanded >= 1.) {
|
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 layout = computeLayout(0.);
|
||||||
const auto small = countSmallGeometry();
|
const auto small = countSmallGeometry();
|
||||||
|
@ -807,10 +1030,21 @@ List::CollapsedGeometry List::collapsedGeometryCurrent() const {
|
||||||
const auto left = int(base::SafeRound(
|
const auto left = int(base::SafeRound(
|
||||||
shift + layout.left + layout.single * index));
|
shift + layout.left + layout.single * index));
|
||||||
const auto width = small.x() + small.width() - left;
|
const auto width = small.x() + small.width() - left;
|
||||||
return {
|
const auto photoTopSmall = _st.small.photoTop;
|
||||||
QRect(left, small.y(), width, small.height()),
|
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,
|
expanded,
|
||||||
|
layout.photoLeft * 2 + photo,
|
||||||
};
|
};
|
||||||
|
return _lastCollapsedGeometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<> List::collapsedGeometryChanged() const {
|
rpl::producer<> List::collapsedGeometryChanged() const {
|
||||||
|
@ -826,6 +1060,7 @@ void List::updateGeometry() {
|
||||||
} break;
|
} break;
|
||||||
case State::Full: setGeometry(_geometryFull);
|
case State::Full: setGeometry(_geometryFull);
|
||||||
}
|
}
|
||||||
|
updateTooltipGeometry();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ struct DialogsStoriesList;
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
struct OutlineSegment;
|
struct OutlineSegment;
|
||||||
|
class ImportantTooltip;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Dialogs::Stories {
|
namespace Dialogs::Stories {
|
||||||
|
@ -72,9 +73,16 @@ public:
|
||||||
QPoint positionSmall,
|
QPoint positionSmall,
|
||||||
style::align alignSmall,
|
style::align alignSmall,
|
||||||
QRect geometryFull = QRect());
|
QRect geometryFull = QRect());
|
||||||
|
void setShowTooltip(
|
||||||
|
not_null<QWidget*> tooltipParent,
|
||||||
|
rpl::producer<bool> shown,
|
||||||
|
Fn<void()> hide);
|
||||||
|
void raiseTooltip();
|
||||||
|
|
||||||
struct CollapsedGeometry {
|
struct CollapsedGeometry {
|
||||||
QRect geometry;
|
QRect geometry;
|
||||||
float64 expanded = 0.;
|
float64 expanded = 0.;
|
||||||
|
float64 singleWidth = 0.;
|
||||||
};
|
};
|
||||||
[[nodiscard]] CollapsedGeometry collapsedGeometryCurrent() const;
|
[[nodiscard]] CollapsedGeometry collapsedGeometryCurrent() const;
|
||||||
[[nodiscard]] rpl::producer<> collapsedGeometryChanged() const;
|
[[nodiscard]] rpl::producer<> collapsedGeometryChanged() const;
|
||||||
|
@ -142,10 +150,15 @@ private:
|
||||||
void checkLoadMore();
|
void checkLoadMore();
|
||||||
void requestExpanded(bool expanded);
|
void requestExpanded(bool expanded);
|
||||||
|
|
||||||
|
void updateTooltipGeometry();
|
||||||
|
[[nodiscard]] TextWithEntities computeTooltipText() const;
|
||||||
|
void toggleTooltip(bool fast);
|
||||||
|
|
||||||
bool checkForFullState();
|
bool checkForFullState();
|
||||||
void setState(State state);
|
void setState(State state);
|
||||||
void updateGeometry();
|
void updateGeometry();
|
||||||
[[nodiscard]] QRect countSmallGeometry() const;
|
[[nodiscard]] QRect countSmallGeometry() const;
|
||||||
|
void updateExpanding();
|
||||||
void updateExpanding(int expandingHeight, int expandedHeight);
|
void updateExpanding(int expandingHeight, int expandedHeight);
|
||||||
void validateSegments(
|
void validateSegments(
|
||||||
not_null<Item*> item,
|
not_null<Item*> item,
|
||||||
|
@ -189,11 +202,20 @@ private:
|
||||||
bool _expandIgnored : 1 = false;
|
bool _expandIgnored : 1 = false;
|
||||||
bool _expanded : 1 = false;
|
bool _expanded : 1 = false;
|
||||||
|
|
||||||
|
mutable CollapsedGeometry _lastCollapsedGeometry;
|
||||||
|
mutable float64 _lastCollapsedRatio = 0.;
|
||||||
|
|
||||||
int _selected = -1;
|
int _selected = -1;
|
||||||
int _pressed = -1;
|
int _pressed = -1;
|
||||||
|
|
||||||
rpl::event_stream<not_null<QWheelEvent*>> _verticalScrollEvents;
|
rpl::event_stream<not_null<QWheelEvent*>> _verticalScrollEvents;
|
||||||
|
|
||||||
|
rpl::variable<TextWithEntities> _tooltipText;
|
||||||
|
rpl::variable<bool> _tooltipNotHidden;
|
||||||
|
Fn<void()> _tooltipHide;
|
||||||
|
std::unique_ptr<Ui::ImportantTooltip> _tooltip;
|
||||||
|
bool _tooltipWindowActive = false;
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
base::has_weak_ptr _menuGuard;
|
base::has_weak_ptr _menuGuard;
|
||||||
|
|
||||||
|
|
|
@ -1055,6 +1055,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
_translateTracker->startBunch();
|
_translateTracker->startBunch();
|
||||||
auto readTill = (HistoryItem*)nullptr;
|
auto readTill = (HistoryItem*)nullptr;
|
||||||
auto readContents = base::flat_set<not_null<HistoryItem*>>();
|
auto readContents = base::flat_set<not_null<HistoryItem*>>();
|
||||||
|
const auto markingAsViewed = _widget->markingContentsRead();
|
||||||
const auto guard = gsl::finally([&] {
|
const auto guard = gsl::finally([&] {
|
||||||
if (_pinnedItem) {
|
if (_pinnedItem) {
|
||||||
_translateTracker->add(_pinnedItem);
|
_translateTracker->add(_pinnedItem);
|
||||||
|
@ -1063,7 +1064,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
if (readTill && _widget->markingMessagesRead()) {
|
if (readTill && _widget->markingMessagesRead()) {
|
||||||
session().data().histories().readInboxTill(readTill);
|
session().data().histories().readInboxTill(readTill);
|
||||||
}
|
}
|
||||||
if (!readContents.empty() && _widget->markingContentsRead()) {
|
if (markingAsViewed && !readContents.empty()) {
|
||||||
session().api().markContentsRead(readContents);
|
session().api().markContentsRead(readContents);
|
||||||
}
|
}
|
||||||
_userpicsCache.clear();
|
_userpicsCache.clear();
|
||||||
|
@ -1096,7 +1097,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
} else if (isUnread) {
|
} else if (isUnread) {
|
||||||
readTill = item;
|
readTill = item;
|
||||||
}
|
}
|
||||||
if (item->hasViews()) {
|
if (markingAsViewed && item->hasViews()) {
|
||||||
session().api().views().scheduleIncrement(item);
|
session().api().views().scheduleIncrement(item);
|
||||||
}
|
}
|
||||||
if (withReaction) {
|
if (withReaction) {
|
||||||
|
|
|
@ -2851,14 +2851,35 @@ void HistoryWidget::updateControlsVisibility() {
|
||||||
if (_botMenuButton) {
|
if (_botMenuButton) {
|
||||||
_botMenuButton->show();
|
_botMenuButton->show();
|
||||||
}
|
}
|
||||||
if (_silent) {
|
{
|
||||||
_silent->setVisible(!_editMsgId);
|
auto rightButtonsChanged = false;
|
||||||
}
|
if (_silent) {
|
||||||
if (_scheduled) {
|
const auto was = _silent->isVisible();
|
||||||
_scheduled->show();
|
const auto now = (!_editMsgId);
|
||||||
}
|
if (was != now) {
|
||||||
if (_ttlInfo) {
|
_silent->setVisible(now);
|
||||||
_ttlInfo->show();
|
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) {
|
if (_sendAs) {
|
||||||
_sendAs->show();
|
_sendAs->show();
|
||||||
|
@ -5028,19 +5049,33 @@ void HistoryWidget::moveFieldControls() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::updateFieldSize() {
|
void HistoryWidget::updateFieldSize() {
|
||||||
auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup();
|
const auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup();
|
||||||
auto fieldWidth = width()
|
auto fieldWidth = width()
|
||||||
- _attachToggle->width()
|
- _attachToggle->width()
|
||||||
- st::historySendRight
|
- st::historySendRight
|
||||||
- _send->width()
|
- _send->width()
|
||||||
- _tabbedSelectorToggle->width();
|
- _tabbedSelectorToggle->width();
|
||||||
if (_botMenuButton) fieldWidth -= st::historyBotMenuSkip + _botMenuButton->width();
|
if (_botMenuButton) {
|
||||||
if (_sendAs) fieldWidth -= _sendAs->width();
|
fieldWidth -= st::historyBotMenuSkip + _botMenuButton->width();
|
||||||
if (kbShowShown) fieldWidth -= _botKeyboardShow->width();
|
}
|
||||||
if (_cmdStartShown) fieldWidth -= _botCommandStart->width();
|
if (_sendAs) {
|
||||||
if (_silent && !_silent->isHidden()) fieldWidth -= _silent->width();
|
fieldWidth -= _sendAs->width();
|
||||||
if (_scheduled) fieldWidth -= _scheduled->width();
|
}
|
||||||
if (_ttlInfo) fieldWidth -= _ttlInfo->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) {
|
if (_fieldDisabled) {
|
||||||
_fieldDisabled->resize(fieldWidth, fieldHeight());
|
_fieldDisabled->resize(fieldWidth, fieldHeight());
|
||||||
|
@ -6547,6 +6582,8 @@ void HistoryWidget::checkPinnedBarState() {
|
||||||
_list->setShownPinned(
|
_list->setShownPinned(
|
||||||
session().data().message(
|
session().data().message(
|
||||||
_pinnedTracker->currentMessageId().message));
|
_pinnedTracker->currentMessageId().message));
|
||||||
|
} else {
|
||||||
|
_list->setShownPinned(nullptr);
|
||||||
}
|
}
|
||||||
return std::move(content);
|
return std::move(content);
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -347,7 +347,7 @@ public:
|
||||||
void setHistory(const SetHistoryArgs &args);
|
void setHistory(const SetHistoryArgs &args);
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void editMessage(FullMsgId id);
|
void editMessage(FullMsgId id, bool photoEditAllowed = false);
|
||||||
void replyToMessage(FullMsgId id);
|
void replyToMessage(FullMsgId id);
|
||||||
void updateForwarding(
|
void updateForwarding(
|
||||||
Data::Thread *thread,
|
Data::Thread *thread,
|
||||||
|
@ -363,8 +363,10 @@ public:
|
||||||
[[nodiscard]] bool readyToForward() const;
|
[[nodiscard]] bool readyToForward() const;
|
||||||
[[nodiscard]] const HistoryItemsList &forwardItems() const;
|
[[nodiscard]] const HistoryItemsList &forwardItems() const;
|
||||||
[[nodiscard]] FullMsgId replyingToMessage() const;
|
[[nodiscard]] FullMsgId replyingToMessage() const;
|
||||||
[[nodiscard]] rpl::producer<FullMsgId> editMsgId() const;
|
[[nodiscard]] FullMsgId editMsgId() const;
|
||||||
|
[[nodiscard]] rpl::producer<FullMsgId> editMsgIdValue() const;
|
||||||
[[nodiscard]] rpl::producer<FullMsgId> scrollToItemRequests() const;
|
[[nodiscard]] rpl::producer<FullMsgId> scrollToItemRequests() const;
|
||||||
|
[[nodiscard]] rpl::producer<> editPhotoRequests() const;
|
||||||
[[nodiscard]] MessageToEdit queryToEdit();
|
[[nodiscard]] MessageToEdit queryToEdit();
|
||||||
[[nodiscard]] WebPageId webPageId() const;
|
[[nodiscard]] WebPageId webPageId() const;
|
||||||
|
|
||||||
|
@ -425,16 +427,24 @@ private:
|
||||||
HistoryItem *_shownMessage = nullptr;
|
HistoryItem *_shownMessage = nullptr;
|
||||||
Ui::Text::String _shownMessageName;
|
Ui::Text::String _shownMessageName;
|
||||||
Ui::Text::String _shownMessageText;
|
Ui::Text::String _shownMessageText;
|
||||||
|
std::unique_ptr<Ui::SpoilerAnimation> _shownPreviewSpoiler;
|
||||||
|
Ui::Animations::Simple _inPhotoEditOver;
|
||||||
int _shownMessageNameVersion = -1;
|
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::Session*> _data;
|
const not_null<Data::Session*> _data;
|
||||||
const not_null<Ui::IconButton*> _cancel;
|
const not_null<Ui::IconButton*> _cancel;
|
||||||
|
|
||||||
QRect _clickableRect;
|
QRect _clickableRect;
|
||||||
|
QRect _shownMessagePreviewRect;
|
||||||
|
|
||||||
rpl::event_stream<bool> _visibleChanged;
|
rpl::event_stream<bool> _visibleChanged;
|
||||||
rpl::event_stream<FullMsgId> _scrollToItemRequests;
|
rpl::event_stream<FullMsgId> _scrollToItemRequests;
|
||||||
|
rpl::event_stream<> _editPhotoRequests;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -554,26 +564,45 @@ void FieldHeader::init() {
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
const auto inClickable = lifetime().make_state<bool>(false);
|
|
||||||
events(
|
events(
|
||||||
) | rpl::filter([=](not_null<QEvent*> event) {
|
) | rpl::filter([=](not_null<QEvent*> 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()
|
&& (isEditingMessage()
|
||||||
|| readyToForward()
|
|| readyToForward()
|
||||||
|| replyingToMessage());
|
|| replyingToMessage());
|
||||||
}) | rpl::start_with_next([=](not_null<QEvent*> event) {
|
}) | rpl::start_with_next([=](not_null<QEvent*> event) {
|
||||||
const auto type = event->type();
|
const auto updateOver = [&](bool inClickable, bool inPhotoEdit) {
|
||||||
const auto e = static_cast<QMouseEvent*>(event.get());
|
if (_inClickable != inClickable) {
|
||||||
const auto pos = e ? e->pos() : mapFromGlobal(QCursor::pos());
|
_inClickable = inClickable;
|
||||||
const auto inPreviewRect = _clickableRect.contains(pos);
|
setCursor(_inClickable
|
||||||
|
|
||||||
if (type == QEvent::MouseMove) {
|
|
||||||
if (inPreviewRect != *inClickable) {
|
|
||||||
*inClickable = inPreviewRect;
|
|
||||||
setCursor(*inClickable
|
|
||||||
? style::cur_pointer
|
? style::cur_pointer
|
||||||
: style::cur_default);
|
: 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<QMouseEvent*>(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;
|
return;
|
||||||
}
|
}
|
||||||
const auto isLeftIcon = (pos.x() < st::historyReplySkip);
|
const auto isLeftIcon = (pos.x() < st::historyReplySkip);
|
||||||
|
@ -582,6 +611,8 @@ void FieldHeader::init() {
|
||||||
if (isLeftButton && isLeftIcon) {
|
if (isLeftButton && isLeftIcon) {
|
||||||
*leftIconPressed = true;
|
*leftIconPressed = true;
|
||||||
update();
|
update();
|
||||||
|
} else if (isLeftButton && inPhotoEdit) {
|
||||||
|
_editPhotoRequests.fire({});
|
||||||
} else if (isLeftButton && inPreviewRect) {
|
} else if (isLeftButton && inPreviewRect) {
|
||||||
if (!isEditingMessage() && readyToForward()) {
|
if (!isEditingMessage() && readyToForward()) {
|
||||||
_forwardPanel->editOptions(_show);
|
_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<Ui::SpoilerAnimation>([=] {
|
||||||
|
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.setPen(st::historyReplyNameFg);
|
||||||
p.setFont(st::msgServiceNameFont);
|
p.setFont(st::msgServiceNameFont);
|
||||||
_shownMessageName.drawElided(
|
_shownMessageName.drawElided(
|
||||||
p,
|
p,
|
||||||
replySkip,
|
textLeft,
|
||||||
st::msgReplyPadding.top(),
|
st::msgReplyPadding.top(),
|
||||||
availableWidth);
|
textAvailableWidth);
|
||||||
|
|
||||||
p.setPen(st::historyComposeAreaFg);
|
p.setPen(st::historyComposeAreaFg);
|
||||||
_shownMessageText.draw(p, {
|
_shownMessageText.draw(p, {
|
||||||
.position = QPoint(
|
.position = QPoint(
|
||||||
replySkip,
|
textLeft,
|
||||||
st::msgReplyPadding.top() + st::msgServiceNameFont->height),
|
st::msgReplyPadding.top() + st::msgServiceNameFont->height),
|
||||||
.availableWidth = availableWidth,
|
.availableWidth = textAvailableWidth,
|
||||||
.palette = &st::historyComposeAreaPalette,
|
.palette = &st::historyComposeAreaPalette,
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
.now = crl::now(),
|
.now = crl::now(),
|
||||||
|
@ -848,6 +933,10 @@ bool FieldHeader::isEditingMessage() const {
|
||||||
return !!_editMsgId.current();
|
return !!_editMsgId.current();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FullMsgId FieldHeader::editMsgId() const {
|
||||||
|
return _editMsgId.current();
|
||||||
|
}
|
||||||
|
|
||||||
bool FieldHeader::readyToForward() const {
|
bool FieldHeader::readyToForward() const {
|
||||||
return !_forwardPanel->empty();
|
return !_forwardPanel->empty();
|
||||||
}
|
}
|
||||||
|
@ -879,10 +968,21 @@ void FieldHeader::updateControlsGeometry(QSize size) {
|
||||||
0,
|
0,
|
||||||
width() - st::historyReplySkip - _cancel->width(),
|
width() - st::historyReplySkip - _cancel->width(),
|
||||||
height());
|
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;
|
_editMsgId = id;
|
||||||
|
if (!photoEditAllowed) {
|
||||||
|
_inPhotoEdit = false;
|
||||||
|
_inPhotoEditOver.stop();
|
||||||
|
}
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldHeader::replyToMessage(FullMsgId id) {
|
void FieldHeader::replyToMessage(FullMsgId id) {
|
||||||
|
@ -898,7 +998,7 @@ void FieldHeader::updateForwarding(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<FullMsgId> FieldHeader::editMsgId() const {
|
rpl::producer<FullMsgId> FieldHeader::editMsgIdValue() const {
|
||||||
return _editMsgId.value();
|
return _editMsgId.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -906,6 +1006,10 @@ rpl::producer<FullMsgId> FieldHeader::scrollToItemRequests() const {
|
||||||
return _scrollToItemRequests.events();
|
return _scrollToItemRequests.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> FieldHeader::editPhotoRequests() const {
|
||||||
|
return _editPhotoRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
MessageToEdit FieldHeader::queryToEdit() {
|
MessageToEdit FieldHeader::queryToEdit() {
|
||||||
const auto item = _data->message(_editMsgId.current());
|
const auto item = _data->message(_editMsgId.current());
|
||||||
if (!isEditingMessage() || !item) {
|
if (!isEditingMessage() || !item) {
|
||||||
|
@ -1470,7 +1574,7 @@ void ComposeControls::init() {
|
||||||
paintBackground(clip);
|
paintBackground(clip);
|
||||||
}, _wrap->lifetime());
|
}, _wrap->lifetime());
|
||||||
|
|
||||||
_header->editMsgId(
|
_header->editMsgIdValue(
|
||||||
) | rpl::start_with_next([=](const auto &id) {
|
) | rpl::start_with_next([=](const auto &id) {
|
||||||
unregisterDraftSources();
|
unregisterDraftSources();
|
||||||
updateSendButtonType();
|
updateSendButtonType();
|
||||||
|
@ -1482,6 +1586,16 @@ void ComposeControls::init() {
|
||||||
registerDraftSource();
|
registerDraftSource();
|
||||||
}, _wrap->lifetime());
|
}, _wrap->lifetime());
|
||||||
|
|
||||||
|
_header->editPhotoRequests(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
EditCaptionBox::StartPhotoEdit(
|
||||||
|
_regularWindow,
|
||||||
|
_photoEditMedia,
|
||||||
|
_editingId,
|
||||||
|
_field->getTextWithTags(),
|
||||||
|
crl::guard(_wrap.get(), [=] { cancelEditMessage(); }));
|
||||||
|
}, _wrap->lifetime());
|
||||||
|
|
||||||
_header->previewCancelled(
|
_header->previewCancelled(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
if (_preview) {
|
if (_preview) {
|
||||||
|
@ -1521,7 +1635,7 @@ void ComposeControls::init() {
|
||||||
_voiceRecordBar->requestToSendWithOptions(options);
|
_voiceRecordBar->requestToSendWithOptions(options);
|
||||||
}, _wrap->lifetime());
|
}, _wrap->lifetime());
|
||||||
|
|
||||||
_header->editMsgId(
|
_header->editMsgIdValue(
|
||||||
) | rpl::start_with_next([=](const auto &id) {
|
) | rpl::start_with_next([=](const auto &id) {
|
||||||
_editingId = id;
|
_editingId = id;
|
||||||
}, _wrap->lifetime());
|
}, _wrap->lifetime());
|
||||||
|
@ -1663,6 +1777,8 @@ void ComposeControls::initField() {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
_field->setEditLinkCallback(
|
||||||
|
DefaultEditLinkCallback(_show, _field, &_st.boxField));
|
||||||
initAutocomplete();
|
initAutocomplete();
|
||||||
const auto allow = [=](const auto &) {
|
const auto allow = [=](const auto &) {
|
||||||
return _history && Data::AllowEmojiWithoutPremium(_history->peer);
|
return _history && Data::AllowEmojiWithoutPremium(_history->peer);
|
||||||
|
@ -2049,7 +2165,43 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (draft == editDraft) {
|
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({});
|
_header->replyToMessage({});
|
||||||
} else {
|
} else {
|
||||||
_canReplaceMedia = false;
|
_canReplaceMedia = false;
|
||||||
|
@ -2708,17 +2860,6 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) {
|
||||||
cursor,
|
cursor,
|
||||||
previewState));
|
previewState));
|
||||||
applyDraft();
|
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()) {
|
if (updateReplaceMediaButton()) {
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
updateControlsGeometry(_wrap->size());
|
updateControlsGeometry(_wrap->size());
|
||||||
|
|
|
@ -50,6 +50,14 @@ void TTLButton::hide() {
|
||||||
_button.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) {
|
void TTLButton::move(int x, int y) {
|
||||||
_button.move(x, y);
|
_button.move(x, y);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ public:
|
||||||
|
|
||||||
void show();
|
void show();
|
||||||
void hide();
|
void hide();
|
||||||
|
void setVisible(bool visible);
|
||||||
|
[[nodiscard]] bool isVisible() const;
|
||||||
void move(int x, int y);
|
void move(int x, int y);
|
||||||
|
|
||||||
[[nodiscard]] int width() const;
|
[[nodiscard]] int width() const;
|
||||||
|
|
|
@ -846,8 +846,8 @@ void Element::validateText() {
|
||||||
_media = nullptr;
|
_media = nullptr;
|
||||||
if (!storyMention) {
|
if (!storyMention) {
|
||||||
if (_text.isEmpty()) {
|
if (_text.isEmpty()) {
|
||||||
setTextWithLinks(
|
setTextWithLinks(Ui::Text::Italic(
|
||||||
Ui::Text::Italic(u"This story has expired"_q));
|
tr::lng_forwarded_story_expired(tr::now)));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -933,7 +933,7 @@ Element *ListWidget::viewByPosition(Data::MessagePosition position) const {
|
||||||
const auto result = (index < 0) ? nullptr : _items[index].get();
|
const auto result = (index < 0) ? nullptr : _items[index].get();
|
||||||
return (position == Data::MinMessagePosition
|
return (position == Data::MinMessagePosition
|
||||||
|| position == Data::MaxMessagePosition
|
|| position == Data::MaxMessagePosition
|
||||||
|| result->data()->position() == position)
|
|| (result && result->data()->position() == position))
|
||||||
? result
|
? result
|
||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
@ -2059,12 +2059,13 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
auto readTill = (HistoryItem*)nullptr;
|
auto readTill = (HistoryItem*)nullptr;
|
||||||
auto readContents = base::flat_set<not_null<HistoryItem*>>();
|
auto readContents = base::flat_set<not_null<HistoryItem*>>();
|
||||||
|
const auto markingAsViewed = markingMessagesRead();
|
||||||
const auto guard = gsl::finally([&] {
|
const auto guard = gsl::finally([&] {
|
||||||
if (_translateTracker) {
|
if (_translateTracker) {
|
||||||
_delegate->listAddTranslatedItems(_translateTracker.get());
|
_delegate->listAddTranslatedItems(_translateTracker.get());
|
||||||
_translateTracker->finishBunch();
|
_translateTracker->finishBunch();
|
||||||
}
|
}
|
||||||
if (readTill && markingMessagesRead()) {
|
if (markingAsViewed && readTill) {
|
||||||
_delegate->listMarkReadTill(readTill);
|
_delegate->listMarkReadTill(readTill);
|
||||||
}
|
}
|
||||||
if (!readContents.empty() && markingContentsRead()) {
|
if (!readContents.empty() && markingContentsRead()) {
|
||||||
|
@ -2136,7 +2137,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
||||||
} else if (isUnread) {
|
} else if (isUnread) {
|
||||||
readTill = item;
|
readTill = item;
|
||||||
}
|
}
|
||||||
if (item->hasViews()) {
|
if (markingAsViewed && item->hasViews()) {
|
||||||
session->api().views().scheduleIncrement(item);
|
session->api().views().scheduleIncrement(item);
|
||||||
}
|
}
|
||||||
if (withReaction) {
|
if (withReaction) {
|
||||||
|
|
|
@ -88,14 +88,13 @@ Gif::Gif(
|
||||||
bool spoiler)
|
bool spoiler)
|
||||||
: File(parent, realParent)
|
: File(parent, realParent)
|
||||||
, _data(document)
|
, _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<MediaSpoiler>() : nullptr)
|
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr)
|
||||||
, _downloadSize(Ui::FormatSizeText(_data->size)) {
|
, _downloadSize(Ui::FormatSizeText(_data->size)) {
|
||||||
if (const auto media = realParent->media()) {
|
|
||||||
if (media->storyId()) {
|
|
||||||
_story = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setDocumentLinks(_data, realParent, [=] {
|
setDocumentLinks(_data, realParent, [=] {
|
||||||
if (!_data->createMediaView()->canBePlayed(realParent)
|
if (!_data->createMediaView()->canBePlayed(realParent)
|
||||||
|| !_data->isAnimation()
|
|| !_data->isAnimation()
|
||||||
|
@ -1438,15 +1437,14 @@ void Gif::dataMediaCreated() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gif::togglePollingStory(bool enabled) const {
|
void Gif::togglePollingStory(bool enabled) const {
|
||||||
if (!_story || _pollingStory == enabled) {
|
if (!_storyId || _pollingStory == enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto polling = Data::Stories::Polling::Chat;
|
const auto polling = Data::Stories::Polling::Chat;
|
||||||
const auto media = _parent->data()->media();
|
|
||||||
const auto id = media ? media->storyId() : FullStoryId();
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
_data->owner().stories().unregisterPolling(id, polling);
|
_data->owner().stories().unregisterPolling(_storyId, polling);
|
||||||
} else if (!_data->owner().stories().registerPolling(id, polling)) {
|
} else if (
|
||||||
|
!_data->owner().stories().registerPolling(_storyId, polling)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_pollingStory = enabled;
|
_pollingStory = enabled;
|
||||||
|
@ -1464,7 +1462,7 @@ void Gif::hideSpoilers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Gif::needsBubble() const {
|
bool Gif::needsBubble() const {
|
||||||
if (_story) {
|
if (_storyId) {
|
||||||
return true;
|
return true;
|
||||||
} else if (_data->isVideoMessage()) {
|
} else if (_data->isVideoMessage()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -211,6 +211,7 @@ private:
|
||||||
void togglePollingStory(bool enabled) const;
|
void togglePollingStory(bool enabled) const;
|
||||||
|
|
||||||
const not_null<DocumentData*> _data;
|
const not_null<DocumentData*> _data;
|
||||||
|
const FullStoryId _storyId;
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
std::unique_ptr<Streamed> _streamed;
|
std::unique_ptr<Streamed> _streamed;
|
||||||
const std::unique_ptr<MediaSpoiler> _spoiler;
|
const std::unique_ptr<MediaSpoiler> _spoiler;
|
||||||
|
@ -223,7 +224,6 @@ private:
|
||||||
mutable std::optional<Ui::BubbleRounding> _thumbCacheRounding;
|
mutable std::optional<Ui::BubbleRounding> _thumbCacheRounding;
|
||||||
mutable bool _thumbCacheBlurred : 1 = false;
|
mutable bool _thumbCacheBlurred : 1 = false;
|
||||||
mutable bool _thumbIsEllipse : 1 = false;
|
mutable bool _thumbIsEllipse : 1 = false;
|
||||||
mutable bool _story : 1 = false;
|
|
||||||
mutable bool _pollingStory : 1 = false;
|
mutable bool _pollingStory : 1 = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -69,13 +69,11 @@ Photo::Photo(
|
||||||
bool spoiler)
|
bool spoiler)
|
||||||
: File(parent, realParent)
|
: File(parent, realParent)
|
||||||
, _data(photo)
|
, _data(photo)
|
||||||
|
, _storyId(realParent->media()
|
||||||
|
? realParent->media()->storyId()
|
||||||
|
: FullStoryId())
|
||||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
||||||
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr) {
|
, _spoiler(spoiler ? std::make_unique<MediaSpoiler>() : nullptr) {
|
||||||
if (const auto media = realParent->media()) {
|
|
||||||
if (media->storyId()) {
|
|
||||||
_story = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_caption = createCaption(realParent);
|
_caption = createCaption(realParent);
|
||||||
create(realParent->fullId());
|
create(realParent->fullId());
|
||||||
}
|
}
|
||||||
|
@ -168,15 +166,14 @@ void Photo::unloadHeavyPart() {
|
||||||
|
|
||||||
void Photo::togglePollingStory(bool enabled) const {
|
void Photo::togglePollingStory(bool enabled) const {
|
||||||
const auto pollingStory = (enabled ? 1 : 0);
|
const auto pollingStory = (enabled ? 1 : 0);
|
||||||
if (!_story || _pollingStory == pollingStory) {
|
if (!_storyId || _pollingStory == pollingStory) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto polling = Data::Stories::Polling::Chat;
|
const auto polling = Data::Stories::Polling::Chat;
|
||||||
const auto media = _parent->data()->media();
|
|
||||||
const auto id = media ? media->storyId() : FullStoryId();
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
_data->owner().stories().unregisterPolling(id, polling);
|
_data->owner().stories().unregisterPolling(_storyId, polling);
|
||||||
} else if (!_data->owner().stories().registerPolling(id, polling)) {
|
} else if (
|
||||||
|
!_data->owner().stories().registerPolling(_storyId, polling)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_pollingStory = pollingStory;
|
_pollingStory = pollingStory;
|
||||||
|
@ -285,7 +282,7 @@ int Photo::adjustHeightForLessCrop(QSize dimensions, QSize current) const {
|
||||||
void Photo::draw(Painter &p, const PaintContext &context) const {
|
void Photo::draw(Painter &p, const PaintContext &context) const {
|
||||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||||
return;
|
return;
|
||||||
} else if (_story && _data->isNull()) {
|
} else if (_storyId && _data->isNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,7 +620,7 @@ void Photo::paintUserpicFrame(
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Photo::photoSize() const {
|
QSize Photo::photoSize() const {
|
||||||
if (_story) {
|
if (_storyId) {
|
||||||
return { kStoryWidth, kStoryHeight };
|
return { kStoryWidth, kStoryHeight };
|
||||||
}
|
}
|
||||||
return QSize(_data->width(), _data->height());
|
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) {
|
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||||
return result;
|
return result;
|
||||||
} else if (_story && _data->isNull()) {
|
} else if (_storyId && _data->isNull()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||||
|
@ -1054,7 +1051,7 @@ void Photo::hideSpoilers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Photo::needsBubble() const {
|
bool Photo::needsBubble() const {
|
||||||
if (_story || !_caption.isEmpty()) {
|
if (_storyId || !_caption.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
|
|
|
@ -163,6 +163,7 @@ private:
|
||||||
void togglePollingStory(bool enabled) const;
|
void togglePollingStory(bool enabled) const;
|
||||||
|
|
||||||
const not_null<PhotoData*> _data;
|
const not_null<PhotoData*> _data;
|
||||||
|
const FullStoryId _storyId;
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
||||||
mutable std::unique_ptr<Streamed> _streamed;
|
mutable std::unique_ptr<Streamed> _streamed;
|
||||||
|
@ -172,7 +173,6 @@ private:
|
||||||
uint32 _serviceWidth : 28 = 0;
|
uint32 _serviceWidth : 28 = 0;
|
||||||
mutable uint32 _imageCacheForum : 1 = 0;
|
mutable uint32 _imageCacheForum : 1 = 0;
|
||||||
mutable uint32 _imageCacheBlurred : 1 = 0;
|
mutable uint32 _imageCacheBlurred : 1 = 0;
|
||||||
mutable uint32 _story : 1 = 0;
|
|
||||||
mutable uint32 _pollingStory : 1 = 0;
|
mutable uint32 _pollingStory : 1 = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -283,9 +283,10 @@ BaseLayout *Provider::getLayout(
|
||||||
if (auto layout = createLayout(id, delegate)) {
|
if (auto layout = createLayout(id, delegate)) {
|
||||||
layout->initDimensions();
|
layout->initDimensions();
|
||||||
it = _layouts.emplace(id, std::move(layout)).first;
|
it = _layouts.emplace(id, std::move(layout)).first;
|
||||||
_peer->owner().stories().registerPolling(
|
const auto ok = _peer->owner().stories().registerPolling(
|
||||||
{ _peer->id, id },
|
{ _peer->id, id },
|
||||||
Data::Stories::Polling::Chat);
|
Data::Stories::Polling::Chat);
|
||||||
|
Assert(ok);
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -346,7 +346,8 @@ bool WritingEntry() {
|
||||||
void start() {
|
void start() {
|
||||||
Assert(LogsData == nullptr);
|
Assert(LogsData == nullptr);
|
||||||
|
|
||||||
if (!Core::Launcher::Instance().checkPortableVersionFolder()) {
|
auto &launcher = Core::Launcher::Instance();
|
||||||
|
if (!launcher.checkPortableVersionFolder()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +363,6 @@ void start() {
|
||||||
|
|
||||||
if (!cWorkingDir().isEmpty()) {
|
if (!cWorkingDir().isEmpty()) {
|
||||||
// This value must come from TelegramForcePortable
|
// This value must come from TelegramForcePortable
|
||||||
// or from the "-workdir" command line argument.
|
|
||||||
cForceWorkingDir(cWorkingDir());
|
cForceWorkingDir(cWorkingDir());
|
||||||
workingDirChosen = true;
|
workingDirChosen = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -390,7 +390,6 @@ void start() {
|
||||||
|
|
||||||
if (!cWorkingDir().isEmpty()) {
|
if (!cWorkingDir().isEmpty()) {
|
||||||
// This value must come from TelegramForcePortable
|
// This value must come from TelegramForcePortable
|
||||||
// or from the "-workdir" command line argument.
|
|
||||||
cForceWorkingDir(cWorkingDir());
|
cForceWorkingDir(cWorkingDir());
|
||||||
workingDirChosen = true;
|
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.
|
// WinRT build requires the working dir to stay the same for plugin loading.
|
||||||
#ifndef Q_OS_WINRT
|
#ifndef Q_OS_WINRT
|
||||||
QDir().setCurrent(cWorkingDir());
|
QDir().setCurrent(cWorkingDir());
|
||||||
|
@ -414,7 +418,7 @@ void start() {
|
||||||
|
|
||||||
QDir().mkpath(cWorkingDir() + u"tdata"_q);
|
QDir().mkpath(cWorkingDir() + u"tdata"_q);
|
||||||
|
|
||||||
Core::Launcher::Instance().workingFolderReady();
|
launcher.workingFolderReady();
|
||||||
CrashReports::StartCatching();
|
CrashReports::StartCatching();
|
||||||
|
|
||||||
if (!LogsData->openMain()) {
|
if (!LogsData->openMain()) {
|
||||||
|
@ -430,7 +434,7 @@ void start() {
|
||||||
LOG(("Executable dir: %1, name: %2").arg(cExeDir(), cExeName()));
|
LOG(("Executable dir: %1, name: %2").arg(cExeDir(), cExeName()));
|
||||||
LOG(("Initial working dir: %1").arg(initialWorkingDir));
|
LOG(("Initial working dir: %1").arg(initialWorkingDir));
|
||||||
LOG(("Working dir: %1").arg(cWorkingDir()));
|
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) {
|
if (!LogsData) {
|
||||||
LOG(("FATAL: Could not open '%1' for writing log!"
|
LOG(("FATAL: Could not open '%1' for writing log!"
|
||||||
|
|
|
@ -1926,7 +1926,7 @@ void MainWidget::showBackFromStack(
|
||||||
|
|
||||||
void MainWidget::orderWidgets() {
|
void MainWidget::orderWidgets() {
|
||||||
if (_dialogs) {
|
if (_dialogs) {
|
||||||
_dialogs->raise();
|
_dialogs->raiseWithTooltip();
|
||||||
}
|
}
|
||||||
if (_player) {
|
if (_player) {
|
||||||
_player->raise();
|
_player->raise();
|
||||||
|
|
|
@ -106,7 +106,7 @@ mediaPlayerRepeatButton: IconButton {
|
||||||
icon: icon {
|
icon: icon {
|
||||||
{ "player/player_repeat", mediaPlayerActiveFg }
|
{ "player/player_repeat", mediaPlayerActiveFg }
|
||||||
};
|
};
|
||||||
iconPosition: point(3px, 6px);
|
iconPosition: point(2px, 5px);
|
||||||
|
|
||||||
rippleAreaPosition: point(2px, 6px);
|
rippleAreaPosition: point(2px, 6px);
|
||||||
rippleAreaSize: 24px;
|
rippleAreaSize: 24px;
|
||||||
|
@ -135,6 +135,10 @@ mediaPlayerReverseDisabledIconOver: icon {
|
||||||
mediaPlayerShuffleIcon: icon {
|
mediaPlayerShuffleIcon: icon {
|
||||||
{ "player/player_shuffle", mediaPlayerActiveFg }
|
{ "player/player_shuffle", mediaPlayerActiveFg }
|
||||||
};
|
};
|
||||||
|
mediaPlayerOrderButton: IconButton(mediaPlayerRepeatButton) {
|
||||||
|
iconPosition: point(2px, 6px);
|
||||||
|
rippleAreaPosition: point(2px, 6px);
|
||||||
|
}
|
||||||
mediaPlayerRepeatDisabledRippleBg: windowBgOver;
|
mediaPlayerRepeatDisabledRippleBg: windowBgOver;
|
||||||
|
|
||||||
mediaPlayerPlayButton: IconButton(mediaPlayerRepeatButton) {
|
mediaPlayerPlayButton: IconButton(mediaPlayerRepeatButton) {
|
||||||
|
@ -233,7 +237,7 @@ mediaPlayerVolumeToggle: IconButton(mediaPlayerRepeatButton) {
|
||||||
{ "player/player_mini_full", mediaPlayerActiveFg },
|
{ "player/player_mini_full", mediaPlayerActiveFg },
|
||||||
};
|
};
|
||||||
iconPosition: point(5px, 6px);
|
iconPosition: point(5px, 6px);
|
||||||
rippleAreaPosition: point(4px, 5px);
|
rippleAreaPosition: point(5px, 6px);
|
||||||
}
|
}
|
||||||
mediaPlayerVolumeMargin: 10px;
|
mediaPlayerVolumeMargin: 10px;
|
||||||
mediaPlayerVolumeSize: size(27px, 100px);
|
mediaPlayerVolumeSize: size(27px, 100px);
|
||||||
|
@ -267,9 +271,9 @@ mediaPlayerClose: IconButton(mediaPlayerRepeatButton) {
|
||||||
width: 39px;
|
width: 39px;
|
||||||
icon: icon {{ "player/panel_close", menuIconFg }};
|
icon: icon {{ "player/panel_close", menuIconFg }};
|
||||||
iconOver: icon {{ "player/panel_close", menuIconFgOver }};
|
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) {
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
color: windowBgOver;
|
color: windowBgOver;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ Widget::Widget(
|
||||||
, _playPause(this, st::mediaPlayerPlayButton)
|
, _playPause(this, st::mediaPlayerPlayButton)
|
||||||
, _volumeToggle(rightControls(), st::mediaPlayerVolumeToggle)
|
, _volumeToggle(rightControls(), st::mediaPlayerVolumeToggle)
|
||||||
, _repeatToggle(rightControls(), st::mediaPlayerRepeatButton)
|
, _repeatToggle(rightControls(), st::mediaPlayerRepeatButton)
|
||||||
, _orderToggle(rightControls(), st::mediaPlayerRepeatButton)
|
, _orderToggle(rightControls(), st::mediaPlayerOrderButton)
|
||||||
, _speedToggle(rightControls(), st::mediaPlayerSpeedButton)
|
, _speedToggle(rightControls(), st::mediaPlayerSpeedButton)
|
||||||
, _close(this, st::mediaPlayerClose)
|
, _close(this, st::mediaPlayerClose)
|
||||||
, _shadow(this)
|
, _shadow(this)
|
||||||
|
|
|
@ -7,79 +7,168 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "media/stories/media_stories_caption_full_view.h"
|
#include "media/stories/media_stories_caption_full_view.h"
|
||||||
|
|
||||||
|
#include "base/event_filter.h"
|
||||||
#include "core/ui_integration.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/widgets/labels.h"
|
||||||
|
#include "ui/click_handler.h"
|
||||||
#include "styles/style_media_view.h"
|
#include "styles/style_media_view.h"
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
CaptionFullView::CaptionFullView(
|
CaptionFullView::CaptionFullView(not_null<Controller*> controller)
|
||||||
not_null<Ui::RpWidget*> parent,
|
: _controller(controller)
|
||||||
not_null<Main::Session*> session,
|
, _scroll(std::make_unique<Ui::ElasticScroll>(controller->wrap()))
|
||||||
const TextWithEntities &text,
|
, _wrap(_scroll->setOwnedWidget(
|
||||||
Fn<void()> close)
|
|
||||||
: RpWidget(parent)
|
|
||||||
, _scroll(std::make_unique<Ui::ScrollArea>((RpWidget*)this))
|
|
||||||
, _text(_scroll->setOwnedWidget(
|
|
||||||
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||||
_scroll.get(),
|
_scroll.get(),
|
||||||
object_ptr<Ui::FlatLabel>(_scroll.get(), st::storiesCaptionFull),
|
object_ptr<Ui::FlatLabel>(_scroll.get(), st::storiesCaptionFull),
|
||||||
st::mediaviewCaptionPadding))->entity())
|
st::mediaviewCaptionPadding)))
|
||||||
, _close(std::move(close))
|
, _text(_wrap->entity()) {
|
||||||
, _background(st::storiesRadius, st::mediaviewCaptionBg) {
|
_text->setMarkedText(controller->captionText(), Core::MarkedTextContext{
|
||||||
_text->setMarkedText(text, Core::MarkedTextContext{
|
.session = &controller->uiShow()->session(),
|
||||||
.session = session,
|
|
||||||
.customEmojiRepaint = [=] { _text->update(); },
|
.customEmojiRepaint = [=] { _text->update(); },
|
||||||
});
|
});
|
||||||
|
|
||||||
parent->sizeValue() | rpl::start_with_next([=](QSize size) {
|
startAnimation();
|
||||||
setGeometry(QRect(QPoint(), size));
|
_controller->layoutValue(
|
||||||
}, lifetime());
|
) | 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();
|
const auto filter = [=](not_null<QEvent*> e) {
|
||||||
setFocus();
|
const auto mouse = [&] {
|
||||||
|
return static_cast<QMouseEvent*>(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<QKeyEvent*>(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;
|
CaptionFullView::~CaptionFullView() = default;
|
||||||
|
|
||||||
void CaptionFullView::paintEvent(QPaintEvent *e) {
|
bool CaptionFullView::closing() const {
|
||||||
auto p = QPainter(this);
|
return _closing;
|
||||||
_background.paint(p, _scroll->geometry());
|
|
||||||
_background.paint(p, _scroll->geometry());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CaptionFullView::resizeEvent(QResizeEvent *e) {
|
bool CaptionFullView::focused() const {
|
||||||
const auto wanted = _text->naturalWidth();
|
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 padding = st::mediaviewCaptionPadding;
|
||||||
const auto margin = st::mediaviewCaptionMargin * 2;
|
_text->resizeToWidth(_outer.width() - padding.left() - padding.right());
|
||||||
const auto available = (rect() - padding).width()
|
const auto add = padding.top() + padding.bottom();
|
||||||
- (margin.width() * 2);
|
const auto maxShownHeight = lineHeight * kMaxShownCaptionLines;
|
||||||
const auto use = std::min(wanted, available);
|
const auto shownHeight = (_text->height() > maxShownHeight)
|
||||||
_text->resizeToWidth(use);
|
? (lineHeight * kCollapsedCaptionLines)
|
||||||
const auto fullw = use + padding.left() + padding.right();
|
: _text->height();
|
||||||
const auto fullh = std::min(
|
const auto collapsedHeight = shownHeight + add;
|
||||||
_text->height() + padding.top() + padding.bottom(),
|
const auto addedToBottom = lineHeight;
|
||||||
height() - (margin.height() * 2));
|
const auto expandedHeight = _text->height() + add + addedToBottom;
|
||||||
const auto left = (width() - fullw) / 2;
|
const auto fullHeight = std::min(expandedHeight, _outer.height());
|
||||||
const auto top = (height() - fullh) / 2;
|
const auto shown = _animation.value(_closing ? 0. : 1.);
|
||||||
_scroll->setGeometry(left, top, fullw, fullh);
|
const auto height = (_closing || _animation.animating())
|
||||||
}
|
? anim::interpolate(collapsedHeight, fullHeight, shown)
|
||||||
|
: _outer.height();
|
||||||
void CaptionFullView::keyPressEvent(QKeyEvent *e) {
|
const auto added = anim::interpolate(0, _closingTopAdded, shown);
|
||||||
if (e->key() == Qt::Key_Escape) {
|
const auto bottomPadding = anim::interpolate(0, addedToBottom, shown);
|
||||||
if (const auto onstack = _close) {
|
const auto use = padding + ((_closing || _animation.animating())
|
||||||
onstack();
|
? 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) {
|
void CaptionFullView::startAnimation() {
|
||||||
if (e->button() == Qt::LeftButton) {
|
_animation.start(
|
||||||
if (const auto onstack = _close) {
|
[=] { updateGeometry(); },
|
||||||
onstack();
|
_closing ? 1. : 0.,
|
||||||
}
|
_closing ? 0. : 1.,
|
||||||
}
|
st::fadeWrapDuration,
|
||||||
|
anim::sineInOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Media::Stories
|
} // namespace Media::Stories
|
||||||
|
|
|
@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/round_rect.h"
|
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
|
@ -16,30 +15,38 @@ class Session;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class FlatLabel;
|
class FlatLabel;
|
||||||
class ScrollArea;
|
class ElasticScroll;
|
||||||
|
template <typename Widget>
|
||||||
|
class PaddingWrap;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Media::Stories {
|
namespace Media::Stories {
|
||||||
|
|
||||||
class CaptionFullView final : private Ui::RpWidget {
|
class Controller;
|
||||||
|
|
||||||
|
class CaptionFullView final {
|
||||||
public:
|
public:
|
||||||
CaptionFullView(
|
explicit CaptionFullView(not_null<Controller*> controller);
|
||||||
not_null<Ui::RpWidget*> parent,
|
|
||||||
not_null<Main::Session*> session,
|
|
||||||
const TextWithEntities &text,
|
|
||||||
Fn<void()> close);
|
|
||||||
~CaptionFullView();
|
~CaptionFullView();
|
||||||
|
|
||||||
private:
|
void close();
|
||||||
void paintEvent(QPaintEvent *e) override;
|
[[nodiscard]] bool closing() const;
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
[[nodiscard]] bool focused() const;
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
|
||||||
void mousePressEvent(QMouseEvent *e) override;
|
|
||||||
|
|
||||||
std::unique_ptr<Ui::ScrollArea> _scroll;
|
private:
|
||||||
|
void updateGeometry();
|
||||||
|
void startAnimation();
|
||||||
|
|
||||||
|
const not_null<Controller*> _controller;
|
||||||
|
const std::unique_ptr<Ui::ElasticScroll> _scroll;
|
||||||
|
const not_null<Ui::PaddingWrap<Ui::FlatLabel>*> _wrap;
|
||||||
const not_null<Ui::FlatLabel*> _text;
|
const not_null<Ui::FlatLabel*> _text;
|
||||||
Fn<void()> _close;
|
Ui::Animations::Simple _animation;
|
||||||
Ui::RoundRect _background;
|
QRect _outer;
|
||||||
|
int _closingTopAdded = 0;
|
||||||
|
bool _pulling = false;
|
||||||
|
bool _closing = false;
|
||||||
|
bool _down = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kPhotoProgressInterval = crl::time(100);
|
constexpr auto kPhotoProgressInterval = crl::time(100);
|
||||||
constexpr auto kPhotoDuration = 5 * crl::time(1000);
|
constexpr auto kPhotoDuration = 5 * crl::time(1000);
|
||||||
constexpr auto kFullContentFade = 0.35;
|
constexpr auto kFullContentFade = 0.6;
|
||||||
constexpr auto kSiblingMultiplierDefault = 0.448;
|
constexpr auto kSiblingMultiplierDefault = 0.448;
|
||||||
constexpr auto kSiblingMultiplierMax = 0.72;
|
constexpr auto kSiblingMultiplierMax = 0.72;
|
||||||
constexpr auto kSiblingOutsidePart = 0.24;
|
constexpr auto kSiblingOutsidePart = 0.24;
|
||||||
|
@ -280,9 +280,6 @@ Controller::Controller(not_null<Delegate*> delegate)
|
||||||
_1 || _2
|
_1 || _2
|
||||||
) | rpl::distinct_until_changed(
|
) | rpl::distinct_until_changed(
|
||||||
) | rpl::start_with_next([=](bool active) {
|
) | rpl::start_with_next([=](bool active) {
|
||||||
if (active) {
|
|
||||||
_captionFullView = nullptr;
|
|
||||||
}
|
|
||||||
_replyActive = active;
|
_replyActive = active;
|
||||||
updateContentFaded();
|
updateContentFaded();
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
@ -322,8 +319,18 @@ Controller::Controller(not_null<Delegate*> delegate)
|
||||||
|
|
||||||
_delegate->storiesLayerShown(
|
_delegate->storiesLayerShown(
|
||||||
) | rpl::start_with_next([=](bool shown) {
|
) | rpl::start_with_next([=](bool shown) {
|
||||||
_layerShown = shown;
|
if (_layerShown != shown) {
|
||||||
updatePlayingAllowed();
|
_layerShown = shown;
|
||||||
|
updatePlayingAllowed();
|
||||||
|
}
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_header->tooltipShownValue(
|
||||||
|
) | rpl::start_with_next([=](bool shown) {
|
||||||
|
if (_tooltipShown != shown) {
|
||||||
|
_tooltipShown = shown;
|
||||||
|
updatePlayingAllowed();
|
||||||
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
const auto window = _wrap->window()->windowHandle();
|
const auto window = _wrap->window()->windowHandle();
|
||||||
|
@ -345,7 +352,8 @@ Controller::~Controller() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::updateContentFaded() {
|
void Controller::updateContentFaded() {
|
||||||
const auto faded = _replyActive || _captionFullView || _captionExpanded;
|
const auto faded = _replyActive
|
||||||
|
|| (_captionFullView && !_captionFullView->closing());
|
||||||
if (_contentFaded == faded) {
|
if (_contentFaded == faded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -378,6 +386,8 @@ void Controller::initLayout() {
|
||||||
|
|
||||||
_layout = _wrap->sizeValue(
|
_layout = _wrap->sizeValue(
|
||||||
) | rpl::map([=](QSize size) {
|
) | rpl::map([=](QSize size) {
|
||||||
|
const auto topNotchSkip = _delegate->storiesTopNotchSkip();
|
||||||
|
|
||||||
size = QSize(
|
size = QSize(
|
||||||
std::max(size.width(), st::mediaviewMinWidth),
|
std::max(size.width(), st::mediaviewMinWidth),
|
||||||
std::max(size.height(), st::mediaviewMinHeight));
|
std::max(size.height(), st::mediaviewMinHeight));
|
||||||
|
@ -387,7 +397,8 @@ void Controller::initLayout() {
|
||||||
? HeaderLayout::Outside
|
? HeaderLayout::Outside
|
||||||
: HeaderLayout::Normal;
|
: HeaderLayout::Normal;
|
||||||
|
|
||||||
const auto topSkip = st::storiesFieldMargin.bottom()
|
const auto topSkip = topNotchSkip
|
||||||
|
+ st::storiesFieldMargin.bottom()
|
||||||
+ (layout.headerLayout == HeaderLayout::Outside
|
+ (layout.headerLayout == HeaderLayout::Outside
|
||||||
? outsideHeaderHeight
|
? outsideHeaderHeight
|
||||||
: 0);
|
: 0);
|
||||||
|
@ -571,26 +582,31 @@ TextWithEntities Controller::captionText() const {
|
||||||
return _captionText;
|
return _captionText;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::setCaptionExpanded(bool expanded) {
|
bool Controller::skipCaption() const {
|
||||||
if (_captionExpanded == expanded) {
|
return _captionFullView != nullptr;
|
||||||
return;
|
|
||||||
}
|
|
||||||
_captionExpanded = expanded;
|
|
||||||
updateContentFaded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::showFullCaption() {
|
void Controller::showFullCaption() {
|
||||||
if (_captionText.empty()) {
|
if (_captionText.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_captionFullView = std::make_unique<CaptionFullView>(
|
_captionFullView = std::make_unique<CaptionFullView>(this);
|
||||||
wrap(),
|
|
||||||
&_delegate->storiesShow()->session(),
|
|
||||||
_captionText,
|
|
||||||
[=] { _captionFullView = nullptr; updateContentFaded(); });
|
|
||||||
updateContentFaded();
|
updateContentFaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::captionClosing() {
|
||||||
|
updateContentFaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::captionClosed() {
|
||||||
|
if (!_captionFullView) {
|
||||||
|
return;
|
||||||
|
} else if (_captionFullView->focused()) {
|
||||||
|
_wrap->setFocus();
|
||||||
|
}
|
||||||
|
_captionFullView = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<ChatHelpers::Show> Controller::uiShow() const {
|
std::shared_ptr<ChatHelpers::Show> Controller::uiShow() const {
|
||||||
return _delegate->storiesShow();
|
return _delegate->storiesShow();
|
||||||
}
|
}
|
||||||
|
@ -807,9 +823,8 @@ void Controller::show(
|
||||||
_slider->raise();
|
_slider->raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
captionClosed();
|
||||||
_captionText = story->caption();
|
_captionText = story->caption();
|
||||||
_captionFullView = nullptr;
|
|
||||||
_captionExpanded = false;
|
|
||||||
_contentFaded = false;
|
_contentFaded = false;
|
||||||
_contentFadeAnimation.stop();
|
_contentFadeAnimation.stop();
|
||||||
const auto document = story->document();
|
const auto document = story->document();
|
||||||
|
@ -823,6 +838,7 @@ void Controller::show(
|
||||||
.video = (document != nullptr),
|
.video = (document != nullptr),
|
||||||
.silent = (document && document->isSilentVideo()),
|
.silent = (document && document->isSilentVideo()),
|
||||||
});
|
});
|
||||||
|
uiShow()->hideLayer(anim::type::instant);
|
||||||
if (!changeShown(story)) {
|
if (!changeShown(story)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -856,9 +872,10 @@ bool Controller::changeShown(Data::Story *story) {
|
||||||
if (_shown == id && !sessionChanged) {
|
if (_shown == id && !sessionChanged) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (const auto now = this->story()) {
|
if (_shown) {
|
||||||
now->owner().stories().unregisterPolling(
|
Assert(_session != nullptr);
|
||||||
now,
|
_session->data().stories().unregisterPolling(
|
||||||
|
_shown,
|
||||||
Data::Stories::Polling::Viewer);
|
Data::Stories::Polling::Viewer);
|
||||||
}
|
}
|
||||||
if (sessionChanged) {
|
if (sessionChanged) {
|
||||||
|
@ -958,16 +975,13 @@ void Controller::updatePlayingAllowed() {
|
||||||
&& _windowActive
|
&& _windowActive
|
||||||
&& !_paused
|
&& !_paused
|
||||||
&& !_replyActive
|
&& !_replyActive
|
||||||
&& !_captionFullView
|
&& (!_captionFullView || _captionFullView->closing())
|
||||||
&& !_captionExpanded
|
|
||||||
&& !_layerShown
|
&& !_layerShown
|
||||||
&& !_menuShown);
|
&& !_menuShown
|
||||||
|
&& !_tooltipShown);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::setPlayingAllowed(bool allowed) {
|
void Controller::setPlayingAllowed(bool allowed) {
|
||||||
if (allowed) {
|
|
||||||
_captionFullView = nullptr;
|
|
||||||
}
|
|
||||||
if (_photoPlayback) {
|
if (_photoPlayback) {
|
||||||
_photoPlayback->togglePaused(!allowed);
|
_photoPlayback->togglePaused(!allowed);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1177,6 +1191,9 @@ void Controller::togglePaused(bool paused) {
|
||||||
|
|
||||||
void Controller::contentPressed(bool pressed) {
|
void Controller::contentPressed(bool pressed) {
|
||||||
togglePaused(pressed);
|
togglePaused(pressed);
|
||||||
|
if (_captionFullView) {
|
||||||
|
_captionFullView->close();
|
||||||
|
}
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
_reactions->collapse();
|
_reactions->collapse();
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,8 +122,10 @@ public:
|
||||||
[[nodiscard]] bool closeByClickAt(QPoint position) const;
|
[[nodiscard]] bool closeByClickAt(QPoint position) const;
|
||||||
[[nodiscard]] Data::FileOrigin fileOrigin() const;
|
[[nodiscard]] Data::FileOrigin fileOrigin() const;
|
||||||
[[nodiscard]] TextWithEntities captionText() const;
|
[[nodiscard]] TextWithEntities captionText() const;
|
||||||
void setCaptionExpanded(bool expanded);
|
[[nodiscard]] bool skipCaption() const;
|
||||||
void showFullCaption();
|
void showFullCaption();
|
||||||
|
void captionClosing();
|
||||||
|
void captionClosed();
|
||||||
|
|
||||||
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
|
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
|
||||||
[[nodiscard]] auto stickerOrEmojiChosen() const
|
[[nodiscard]] auto stickerOrEmojiChosen() const
|
||||||
|
@ -250,13 +252,13 @@ private:
|
||||||
Ui::Animations::Simple _contentFadeAnimation;
|
Ui::Animations::Simple _contentFadeAnimation;
|
||||||
bool _contentFaded = false;
|
bool _contentFaded = false;
|
||||||
|
|
||||||
bool _captionExpanded = false;
|
|
||||||
bool _windowActive = false;
|
bool _windowActive = false;
|
||||||
bool _replyFocused = false;
|
bool _replyFocused = false;
|
||||||
bool _replyActive = false;
|
bool _replyActive = false;
|
||||||
bool _hasSendText = false;
|
bool _hasSendText = false;
|
||||||
bool _layerShown = false;
|
bool _layerShown = false;
|
||||||
bool _menuShown = false;
|
bool _menuShown = false;
|
||||||
|
bool _tooltipShown = false;
|
||||||
bool _paused = false;
|
bool _paused = false;
|
||||||
|
|
||||||
FullStoryId _shown;
|
FullStoryId _shown;
|
||||||
|
|
|
@ -64,6 +64,7 @@ public:
|
||||||
virtual void storiesVolumeToggle() = 0;
|
virtual void storiesVolumeToggle() = 0;
|
||||||
virtual void storiesVolumeChanged(float64 volume) = 0;
|
virtual void storiesVolumeChanged(float64 volume) = 0;
|
||||||
virtual void storiesVolumeChangeFinished() = 0;
|
virtual void storiesVolumeChangeFinished() = 0;
|
||||||
|
[[nodiscard]] virtual int storiesTopNotchSkip() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Media::Stories
|
} // namespace Media::Stories
|
||||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/continuous_sliders.h"
|
#include "ui/widgets/continuous_sliders.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/tooltip.h"
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
@ -50,10 +51,9 @@ struct PrivacyBadge {
|
||||||
|
|
||||||
class UserpicBadge final : public Ui::RpWidget {
|
class UserpicBadge final : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
UserpicBadge(
|
UserpicBadge(not_null<QWidget*> userpic, PrivacyBadge badge);
|
||||||
not_null<QWidget*> userpic,
|
|
||||||
PrivacyBadge badge,
|
[[nodiscard]] QRect badgeGeometry() const;
|
||||||
Fn<void()> clicked);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool eventFilter(QObject *o, QEvent *e) override;
|
bool eventFilter(QObject *o, QEvent *e) override;
|
||||||
|
@ -63,7 +63,6 @@ private:
|
||||||
|
|
||||||
const not_null<QWidget*> _userpic;
|
const not_null<QWidget*> _userpic;
|
||||||
const PrivacyBadge _badgeData;
|
const PrivacyBadge _badgeData;
|
||||||
const std::unique_ptr<Ui::AbstractButton> _clickable;
|
|
||||||
QRect _badge;
|
QRect _badge;
|
||||||
QImage _layer;
|
QImage _layer;
|
||||||
bool _grabbing = false;
|
bool _grabbing = false;
|
||||||
|
@ -95,15 +94,10 @@ private:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
UserpicBadge::UserpicBadge(
|
UserpicBadge::UserpicBadge(not_null<QWidget*> userpic, PrivacyBadge badge)
|
||||||
not_null<QWidget*> userpic,
|
|
||||||
PrivacyBadge badge,
|
|
||||||
Fn<void()> clicked)
|
|
||||||
: RpWidget(userpic->parentWidget())
|
: RpWidget(userpic->parentWidget())
|
||||||
, _userpic(userpic)
|
, _userpic(userpic)
|
||||||
, _badgeData(badge)
|
, _badgeData(badge) {
|
||||||
, _clickable(std::make_unique<Ui::AbstractButton>(parentWidget())) {
|
|
||||||
_clickable->setClickedCallback(std::move(clicked));
|
|
||||||
userpic->installEventFilter(this);
|
userpic->installEventFilter(this);
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
@ -113,6 +107,10 @@ UserpicBadge::UserpicBadge(
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect UserpicBadge::badgeGeometry() const {
|
||||||
|
return _badge;
|
||||||
|
}
|
||||||
|
|
||||||
bool UserpicBadge::eventFilter(QObject *o, QEvent *e) {
|
bool UserpicBadge::eventFilter(QObject *o, QEvent *e) {
|
||||||
if (o != _userpic) {
|
if (o != _userpic) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -173,22 +171,27 @@ void UserpicBadge::updateGeometry() {
|
||||||
_badge = QRect(
|
_badge = QRect(
|
||||||
QPoint(width - badge.width(), height - badge.height()),
|
QPoint(width - badge.width(), height - badge.height()),
|
||||||
badge);
|
badge);
|
||||||
_clickable->setGeometry(_badge.translated(pos()));
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<Ui::RpWidget> MakePrivacyBadge(
|
struct MadePrivacyBadge {
|
||||||
|
std::unique_ptr<Ui::RpWidget> widget;
|
||||||
|
QRect geometry;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] MadePrivacyBadge MakePrivacyBadge(
|
||||||
not_null<QWidget*> userpic,
|
not_null<QWidget*> userpic,
|
||||||
Data::StoryPrivacy privacy,
|
Data::StoryPrivacy privacy) {
|
||||||
Fn<void()> clicked) {
|
|
||||||
const auto badge = LookupPrivacyBadge(privacy);
|
const auto badge = LookupPrivacyBadge(privacy);
|
||||||
if (!badge.icon) {
|
if (!badge.icon) {
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
return std::make_unique<UserpicBadge>(
|
auto widget = std::make_unique<UserpicBadge>(userpic, badge);
|
||||||
userpic,
|
const auto geometry = widget->badgeGeometry();
|
||||||
badge,
|
return {
|
||||||
std::move(clicked));
|
.widget = std::move(widget),
|
||||||
|
.geometry = geometry,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] Timestamp ComposeTimestamp(TimeId when, TimeId now) {
|
[[nodiscard]] Timestamp ComposeTimestamp(TimeId when, TimeId now) {
|
||||||
|
@ -277,6 +280,8 @@ void Header::show(HeaderData data) {
|
||||||
_info->setGeometry({ 0, 0, r, _widget->height() });
|
_info->setGeometry({ 0, 0, r, _widget->height() });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
_tooltip = nullptr;
|
||||||
|
_tooltipShown = false;
|
||||||
if (userChanged) {
|
if (userChanged) {
|
||||||
_volume = nullptr;
|
_volume = nullptr;
|
||||||
_date = nullptr;
|
_date = nullptr;
|
||||||
|
@ -310,7 +315,7 @@ void Header::show(HeaderData data) {
|
||||||
raw,
|
raw,
|
||||||
rpl::single(data.user->isSelf()
|
rpl::single(data.user->isSelf()
|
||||||
? tr::lng_stories_my_name(tr::now)
|
? tr::lng_stories_my_name(tr::now)
|
||||||
: data.user->shortName()),
|
: data.user->name()),
|
||||||
st::storiesHeaderName);
|
st::storiesHeaderName);
|
||||||
_name->setAttribute(Qt::WA_TransparentForMouseEvents);
|
_name->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
_name->setOpacity(kNameOpacity);
|
_name->setOpacity(kNameOpacity);
|
||||||
|
@ -328,6 +333,8 @@ void Header::show(HeaderData data) {
|
||||||
_controller->layoutValue(
|
_controller->layoutValue(
|
||||||
) | rpl::start_with_next([=](const Layout &layout) {
|
) | rpl::start_with_next([=](const Layout &layout) {
|
||||||
raw->setGeometry(layout.header);
|
raw->setGeometry(layout.header);
|
||||||
|
_contentGeometry = layout.content;
|
||||||
|
updateTooltipGeometry();
|
||||||
}, raw->lifetime());
|
}, raw->lifetime());
|
||||||
}
|
}
|
||||||
auto timestamp = ComposeDetails(data, base::unixtime::now());
|
auto timestamp = ComposeDetails(data, base::unixtime::now());
|
||||||
|
@ -357,8 +364,29 @@ void Header::show(HeaderData data) {
|
||||||
_counter = nullptr;
|
_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<QEvent*> 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<QMouseEvent*>(e.get())->pos());
|
||||||
|
return (_privacyBadgeOver != over);
|
||||||
|
}) | rpl::start_with_next([=] {
|
||||||
|
_privacyBadgeOver = !_privacyBadgeOver;
|
||||||
|
toggleTooltip(Tooltip::Privacy, _privacyBadgeOver);
|
||||||
|
}, _privacy->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
if (data.video) {
|
if (data.video) {
|
||||||
createPlayPause();
|
createPlayPause();
|
||||||
|
@ -369,6 +397,7 @@ void Header::show(HeaderData data) {
|
||||||
_playPause->moveToRight(playPause.x(), playPause.y(), width);
|
_playPause->moveToRight(playPause.x(), playPause.y(), width);
|
||||||
const auto volume = st::storiesVolumeButtonPosition;
|
const auto volume = st::storiesVolumeButtonPosition;
|
||||||
_volumeToggle->moveToRight(volume.x(), volume.y(), width);
|
_volumeToggle->moveToRight(volume.x(), volume.y(), width);
|
||||||
|
updateTooltipGeometry();
|
||||||
}, _playPause->lifetime());
|
}, _playPause->lifetime());
|
||||||
|
|
||||||
_pauseState = _controller->pauseState();
|
_pauseState = _controller->pauseState();
|
||||||
|
@ -496,15 +525,14 @@ void Header::createVolumeToggle() {
|
||||||
|
|
||||||
_volumeToggle->events(
|
_volumeToggle->events(
|
||||||
) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||||
if (state->silent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto type = e->type();
|
const auto type = e->type();
|
||||||
if (type == QEvent::Enter || type == QEvent::Leave) {
|
if (type == QEvent::Enter || type == QEvent::Leave) {
|
||||||
const auto over = (e->type() == QEvent::Enter);
|
const auto over = (e->type() == QEvent::Enter);
|
||||||
if (state->over != over) {
|
if (state->over != over) {
|
||||||
state->over = over;
|
state->over = over;
|
||||||
if (over) {
|
if (state->silent) {
|
||||||
|
toggleTooltip(Tooltip::SilentVideo, over);
|
||||||
|
} else if (over) {
|
||||||
state->hideTimer.cancel();
|
state->hideTimer.cancel();
|
||||||
_volume->toggle(true, anim::type::normal);
|
_volume->toggle(true, anim::type::normal);
|
||||||
} else if (!state->dropdownOver) {
|
} 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<Ui::ImportantTooltip>(
|
||||||
|
_widget->parentWidget(),
|
||||||
|
Ui::MakeNiceTooltipLabel(
|
||||||
|
_widget.get(),
|
||||||
|
rpl::single(text),
|
||||||
|
st::storiesInfoTooltipMaxWidth,
|
||||||
|
st::storiesInfoTooltipLabel),
|
||||||
|
st::storiesInfoTooltip);
|
||||||
|
const auto tooltip = _tooltip.get();
|
||||||
|
const auto weak = QPointer<QWidget>(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<QWidget>(_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(
|
void Header::rebuildVolumeControls(
|
||||||
not_null<Ui::RpWidget*> dropdown,
|
not_null<Ui::RpWidget*> dropdown,
|
||||||
bool horizontal) {
|
bool horizontal) {
|
||||||
|
@ -682,11 +827,14 @@ void Header::raise() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Header::ignoreWindowMove(QPoint position) const {
|
bool Header::ignoreWindowMove(QPoint position) const {
|
||||||
return _ignoreWindowMove;
|
return _ignoreWindowMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> Header::tooltipShownValue() const {
|
||||||
|
return _tooltipShown.value();
|
||||||
|
}
|
||||||
|
|
||||||
void Header::updateDateText() {
|
void Header::updateDateText() {
|
||||||
if (!_date || !_data || !_data->date) {
|
if (!_date || !_data || !_data->date) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -20,6 +20,7 @@ class FlatLabel;
|
||||||
class IconButton;
|
class IconButton;
|
||||||
class AbstractButton;
|
class AbstractButton;
|
||||||
class UserpicButton;
|
class UserpicButton;
|
||||||
|
class ImportantTooltip;
|
||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
class FadeWrap;
|
class FadeWrap;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
@ -55,8 +56,15 @@ public:
|
||||||
void raise();
|
void raise();
|
||||||
|
|
||||||
[[nodiscard]] bool ignoreWindowMove(QPoint position) const;
|
[[nodiscard]] bool ignoreWindowMove(QPoint position) const;
|
||||||
|
[[nodiscard]] rpl::producer<bool> tooltipShownValue() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class Tooltip {
|
||||||
|
None,
|
||||||
|
SilentVideo,
|
||||||
|
Privacy,
|
||||||
|
};
|
||||||
|
|
||||||
void updateDateText();
|
void updateDateText();
|
||||||
void applyPauseState();
|
void applyPauseState();
|
||||||
void createPlayPause();
|
void createPlayPause();
|
||||||
|
@ -64,6 +72,8 @@ private:
|
||||||
void rebuildVolumeControls(
|
void rebuildVolumeControls(
|
||||||
not_null<Ui::RpWidget*> dropdown,
|
not_null<Ui::RpWidget*> dropdown,
|
||||||
bool horizontal);
|
bool horizontal);
|
||||||
|
void toggleTooltip(Tooltip type, bool show);
|
||||||
|
void updateTooltipGeometry();
|
||||||
|
|
||||||
const not_null<Controller*> _controller;
|
const not_null<Controller*> _controller;
|
||||||
|
|
||||||
|
@ -81,9 +91,15 @@ private:
|
||||||
std::unique_ptr<Ui::FadeWrap<Ui::RpWidget>> _volume;
|
std::unique_ptr<Ui::FadeWrap<Ui::RpWidget>> _volume;
|
||||||
rpl::variable<const style::icon*> _volumeIcon;
|
rpl::variable<const style::icon*> _volumeIcon;
|
||||||
std::unique_ptr<Ui::RpWidget> _privacy;
|
std::unique_ptr<Ui::RpWidget> _privacy;
|
||||||
|
QRect _privacyBadgeGeometry;
|
||||||
std::optional<HeaderData> _data;
|
std::optional<HeaderData> _data;
|
||||||
|
std::unique_ptr<Ui::ImportantTooltip> _tooltip;
|
||||||
|
rpl::variable<bool> _tooltipShown = false;
|
||||||
|
QRect _contentGeometry;
|
||||||
|
Tooltip _tooltipType = {};
|
||||||
base::Timer _dateUpdateTimer;
|
base::Timer _dateUpdateTimer;
|
||||||
bool _ignoreWindowMove = false;
|
bool _ignoreWindowMove = false;
|
||||||
|
bool _privacyBadgeOver = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -186,8 +186,8 @@ void ReplyArea::send(
|
||||||
|
|
||||||
session().api().sendMessage(std::move(message));
|
session().api().sendMessage(std::move(message));
|
||||||
|
|
||||||
_controls->clear();
|
|
||||||
finishSending(skipToast);
|
finishSending(skipToast);
|
||||||
|
_controls->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplyArea::sendVoice(VoiceToSend &&data) {
|
void ReplyArea::sendVoice(VoiceToSend &&data) {
|
||||||
|
@ -276,8 +276,6 @@ void ReplyArea::sendInlineResult(
|
||||||
action.generateLocal = true;
|
action.generateLocal = true;
|
||||||
session().api().sendInlineResult(bot, result, action, localMessageId);
|
session().api().sendInlineResult(bot, result, action, localMessageId);
|
||||||
|
|
||||||
_controls->clear();
|
|
||||||
|
|
||||||
auto &bots = cRefRecentInlineBots();
|
auto &bots = cRefRecentInlineBots();
|
||||||
const auto index = bots.indexOf(bot);
|
const auto index = bots.indexOf(bot);
|
||||||
if (index) {
|
if (index) {
|
||||||
|
@ -290,11 +288,12 @@ void ReplyArea::sendInlineResult(
|
||||||
bot->session().local().writeRecentHashtagsAndBots();
|
bot->session().local().writeRecentHashtagsAndBots();
|
||||||
}
|
}
|
||||||
finishSending();
|
finishSending();
|
||||||
|
_controls->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplyArea::finishSending(bool skipToast) {
|
void ReplyArea::finishSending(bool skipToast) {
|
||||||
_controls->hidePanelsAnimated();
|
_controls->hidePanelsAnimated();
|
||||||
_controller->wrap()->setFocus();
|
_controller->unfocusReply();
|
||||||
if (!skipToast) {
|
if (!skipToast) {
|
||||||
_controller->uiShow()->showToast(
|
_controller->uiShow()->showToast(
|
||||||
tr::lng_stories_reply_sent(tr::now));
|
tr::lng_stories_reply_sent(tr::now));
|
||||||
|
|
|
@ -32,6 +32,7 @@ constexpr auto kSiblingFade = 0.5;
|
||||||
constexpr auto kSiblingFadeOver = 0.4;
|
constexpr auto kSiblingFadeOver = 0.4;
|
||||||
constexpr auto kSiblingNameOpacity = 0.8;
|
constexpr auto kSiblingNameOpacity = 0.8;
|
||||||
constexpr auto kSiblingNameOpacityOver = 1.;
|
constexpr auto kSiblingNameOpacityOver = 1.;
|
||||||
|
constexpr auto kSiblingScaleOver = 0.05;
|
||||||
|
|
||||||
[[nodiscard]] StoryId LookupShownId(
|
[[nodiscard]] StoryId LookupShownId(
|
||||||
const Data::StoriesSource &source,
|
const Data::StoriesSource &source,
|
||||||
|
@ -325,6 +326,7 @@ SiblingView Sibling::view(const SiblingLayout &layout, float64 over) {
|
||||||
.namePosition = namePosition(layout, name),
|
.namePosition = namePosition(layout, name),
|
||||||
.nameOpacity = (kSiblingNameOpacity * (1 - over)
|
.nameOpacity = (kSiblingNameOpacity * (1 - over)
|
||||||
+ kSiblingNameOpacityOver * over),
|
+ kSiblingNameOpacityOver * over),
|
||||||
|
.scale = 1. + (over * kSiblingScaleOver),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,8 +123,8 @@ TextWithEntities View::captionText() const {
|
||||||
return _controller->captionText();
|
return _controller->captionText();
|
||||||
}
|
}
|
||||||
|
|
||||||
void View::setCaptionExpanded(bool expanded) {
|
bool View::skipCaption() const {
|
||||||
_controller->setCaptionExpanded(expanded);
|
return _controller->skipCaption();
|
||||||
}
|
}
|
||||||
|
|
||||||
void View::showFullCaption() {
|
void View::showFullCaption() {
|
||||||
|
|
|
@ -25,6 +25,7 @@ class Controller;
|
||||||
struct ContentLayout {
|
struct ContentLayout {
|
||||||
QRect geometry;
|
QRect geometry;
|
||||||
float64 fade = 0.;
|
float64 fade = 0.;
|
||||||
|
float64 scale = 1.;
|
||||||
int radius = 0;
|
int radius = 0;
|
||||||
bool headerOutside = false;
|
bool headerOutside = false;
|
||||||
};
|
};
|
||||||
|
@ -39,6 +40,7 @@ struct SiblingView {
|
||||||
QImage name;
|
QImage name;
|
||||||
QPoint namePosition;
|
QPoint namePosition;
|
||||||
float64 nameOpacity = 0.;
|
float64 nameOpacity = 0.;
|
||||||
|
float64 scale = 1.;
|
||||||
|
|
||||||
[[nodiscard]] bool valid() const {
|
[[nodiscard]] bool valid() const {
|
||||||
return !image.isNull();
|
return !image.isNull();
|
||||||
|
@ -48,6 +50,9 @@ struct SiblingView {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline constexpr auto kCollapsedCaptionLines = 2;
|
||||||
|
inline constexpr auto kMaxShownCaptionLines = 4;
|
||||||
|
|
||||||
class View final {
|
class View final {
|
||||||
public:
|
public:
|
||||||
explicit View(not_null<Delegate*> delegate);
|
explicit View(not_null<Delegate*> delegate);
|
||||||
|
@ -64,7 +69,7 @@ public:
|
||||||
[[nodiscard]] SiblingView sibling(SiblingType type) const;
|
[[nodiscard]] SiblingView sibling(SiblingType type) const;
|
||||||
[[nodiscard]] Data::FileOrigin fileOrigin() const;
|
[[nodiscard]] Data::FileOrigin fileOrigin() const;
|
||||||
[[nodiscard]] TextWithEntities captionText() const;
|
[[nodiscard]] TextWithEntities captionText() const;
|
||||||
void setCaptionExpanded(bool expanded);
|
[[nodiscard]] bool skipCaption() const;
|
||||||
void showFullCaption();
|
void showFullCaption();
|
||||||
|
|
||||||
void updatePlayback(const Player::TrackState &state);
|
void updatePlayback(const Player::TrackState &state);
|
||||||
|
|
|
@ -445,7 +445,8 @@ storiesSideSkip: 145px;
|
||||||
storiesCaptionFull: FlatLabel(defaultFlatLabel) {
|
storiesCaptionFull: FlatLabel(defaultFlatLabel) {
|
||||||
style: mediaviewCaptionStyle;
|
style: mediaviewCaptionStyle;
|
||||||
textFg: mediaviewCaptionFg;
|
textFg: mediaviewCaptionFg;
|
||||||
minWidth: 360px;
|
palette: mediaviewTextPalette;
|
||||||
|
minWidth: 36px;
|
||||||
}
|
}
|
||||||
storiesComposeBg: groupCallMembersBg;
|
storiesComposeBg: groupCallMembersBg;
|
||||||
storiesComposeBgOver: groupCallMembersBgOver;
|
storiesComposeBgOver: groupCallMembersBgOver;
|
||||||
|
@ -570,7 +571,6 @@ storiesEmojiPan: EmojiPan(defaultEmojiPan) {
|
||||||
trendingHeaderFg: storiesComposeWhiteText;
|
trendingHeaderFg: storiesComposeWhiteText;
|
||||||
trendingSubheaderFg: storiesComposeGrayText;
|
trendingSubheaderFg: storiesComposeGrayText;
|
||||||
trendingUnreadFg: storiesComposeBlue;
|
trendingUnreadFg: storiesComposeBlue;
|
||||||
trendingInstalled: icon {{ "chat/input_save", storiesComposeBlue }};
|
|
||||||
overBg: storiesComposeBgOver;
|
overBg: storiesComposeBgOver;
|
||||||
pathBg: storiesComposeBgRipple;
|
pathBg: storiesComposeBgRipple;
|
||||||
pathFg: storiesComposeBgOver;
|
pathFg: storiesComposeBgOver;
|
||||||
|
@ -752,6 +752,20 @@ storiesComposeControls: ComposeControls(defaultComposeControls) {
|
||||||
statusFg: storiesComposeGrayText;
|
statusFg: storiesComposeGrayText;
|
||||||
}
|
}
|
||||||
premium: storiesComposePremium;
|
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) {
|
storiesViewsMenu: PopupMenu(storiesPopupMenuWithIcons) {
|
||||||
scrollPadding: margins(0px, 6px, 0px, 4px);
|
scrollPadding: margins(0px, 6px, 0px, 4px);
|
||||||
|
@ -891,3 +905,9 @@ storiesVolumeSlider: MediaSlider {
|
||||||
seekSize: size(12px, 12px);
|
seekSize: size(12px, 12px);
|
||||||
duration: mediaviewOverDuration;
|
duration: mediaviewOverDuration;
|
||||||
}
|
}
|
||||||
|
storiesInfoTooltipLabel: defaultImportantTooltipLabel;
|
||||||
|
storiesInfoTooltip: defaultImportantTooltip;
|
||||||
|
storiesInfoTooltipMaxWidth: 360px;
|
||||||
|
storiesCaptionPullThreshold: 50px;
|
||||||
|
storiesShowMorePadding: margins(6px, 4px, 6px, 4px);
|
||||||
|
storiesShowMoreFont: semiboldFont;
|
||||||
|
|
|
@ -21,7 +21,8 @@ namespace {
|
||||||
|
|
||||||
using namespace Ui::GL;
|
using namespace Ui::GL;
|
||||||
|
|
||||||
constexpr auto kRadialLoadingOffset = 4;
|
constexpr auto kNotchOffset = 4;
|
||||||
|
constexpr auto kRadialLoadingOffset = kNotchOffset + 4;
|
||||||
constexpr auto kThemePreviewOffset = kRadialLoadingOffset + 4;
|
constexpr auto kThemePreviewOffset = kRadialLoadingOffset + 4;
|
||||||
constexpr auto kDocumentBubbleOffset = kThemePreviewOffset + 4;
|
constexpr auto kDocumentBubbleOffset = kThemePreviewOffset + 4;
|
||||||
constexpr auto kSaveMsgOffset = kDocumentBubbleOffset + 4;
|
constexpr auto kSaveMsgOffset = kDocumentBubbleOffset + 4;
|
||||||
|
@ -129,7 +130,7 @@ OverlayWidget::RendererGL::RendererGL(not_null<OverlayWidget*> owner)
|
||||||
void OverlayWidget::RendererGL::init(
|
void OverlayWidget::RendererGL::init(
|
||||||
not_null<QOpenGLWidget*> widget,
|
not_null<QOpenGLWidget*> widget,
|
||||||
QOpenGLFunctions &f) {
|
QOpenGLFunctions &f) {
|
||||||
constexpr auto kQuads = 8;
|
constexpr auto kQuads = 9;
|
||||||
constexpr auto kQuadVertices = kQuads * 4;
|
constexpr auto kQuadVertices = kQuads * 4;
|
||||||
constexpr auto kQuadValues = kQuadVertices * 4;
|
constexpr auto kQuadValues = kQuadVertices * 4;
|
||||||
constexpr auto kControlsValues = kControlsCount * kControlValues;
|
constexpr auto kControlsValues = kControlsCount * kControlValues;
|
||||||
|
@ -291,6 +292,28 @@ bool OverlayWidget::RendererGL::handleHideWorkaround(QOpenGLFunctions &f) {
|
||||||
|
|
||||||
void OverlayWidget::RendererGL::paintBackground() {
|
void OverlayWidget::RendererGL::paintBackground() {
|
||||||
_contentBuffer->bind();
|
_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(
|
void OverlayWidget::RendererGL::paintTransformedVideoFrame(
|
||||||
|
@ -465,7 +488,9 @@ void OverlayWidget::RendererGL::paintTransformedContent(
|
||||||
not_null<QOpenGLShaderProgram*> program,
|
not_null<QOpenGLShaderProgram*> program,
|
||||||
ContentGeometry geometry,
|
ContentGeometry geometry,
|
||||||
bool fillTransparentBackground) {
|
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 centerx = rect.x() + rect.width() / 2;
|
||||||
const auto centery = rect.y() + rect.height() / 2;
|
const auto centery = rect.y() + rect.height() / 2;
|
||||||
const auto rsin = float(std::sin(geometry.rotation * M_PI / 180.));
|
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);
|
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
|
} // namespace Media::View
|
||||||
|
|
|
@ -97,6 +97,9 @@ private:
|
||||||
[[nodiscard]] Ui::GL::Rect transformRect(const QRectF &raster) const;
|
[[nodiscard]] Ui::GL::Rect transformRect(const QRectF &raster) const;
|
||||||
[[nodiscard]] Ui::GL::Rect transformRect(
|
[[nodiscard]] Ui::GL::Rect transformRect(
|
||||||
const Ui::GL::Rect &raster) const;
|
const Ui::GL::Rect &raster) const;
|
||||||
|
[[nodiscard]] Ui::GL::Rect scaleRect(
|
||||||
|
const Ui::GL::Rect &unscaled,
|
||||||
|
float64 scale) const;
|
||||||
|
|
||||||
void uploadTexture(
|
void uploadTexture(
|
||||||
GLint internalformat,
|
GLint internalformat,
|
||||||
|
|
|
@ -45,6 +45,12 @@ void OverlayWidget::RendererSW::paintBackground() {
|
||||||
for (const auto &rect : region) {
|
for (const auto &rect : region) {
|
||||||
_p->fillRect(rect, bg);
|
_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);
|
_p->setCompositionMode(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,8 +107,8 @@ void OverlayWidget::RendererSW::paintTransformedStaticContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::RendererSW::paintControlsFade(
|
void OverlayWidget::RendererSW::paintControlsFade(
|
||||||
QRect content,
|
QRect content,
|
||||||
const ContentGeometry &geometry) {
|
const ContentGeometry &geometry) {
|
||||||
auto opacity = geometry.controlsOpacity;
|
auto opacity = geometry.controlsOpacity;
|
||||||
if (geometry.fade > 0.) {
|
if (geometry.fade > 0.) {
|
||||||
_p->setOpacity(geometry.fade);
|
_p->setOpacity(geometry.fade);
|
||||||
|
|
|
@ -587,6 +587,16 @@ OverlayWidget::OverlayWidget()
|
||||||
update();
|
update();
|
||||||
}, lifetime());
|
}, 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->setTitle(tr::lng_mediaview_title(tr::now));
|
||||||
_window->setTitleStyle(st::mediaviewTitle);
|
_window->setTitleStyle(st::mediaviewTitle);
|
||||||
|
|
||||||
|
@ -673,7 +683,11 @@ void OverlayWidget::showSaveMsgToastWith(
|
||||||
const auto h = st::mediaviewSaveMsgStyle.font->height
|
const auto h = st::mediaviewSaveMsgStyle.font->height
|
||||||
+ st::mediaviewSaveMsgPadding.top()
|
+ st::mediaviewSaveMsgPadding.top()
|
||||||
+ st::mediaviewSaveMsgPadding.bottom();
|
+ 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) {
|
const auto callback = [=](float64 value) {
|
||||||
updateSaveMsg();
|
updateSaveMsg();
|
||||||
if (!_saveMsgAnimation.animating()) {
|
if (!_saveMsgAnimation.animating()) {
|
||||||
|
@ -703,7 +717,7 @@ void OverlayWidget::setupWindow() {
|
||||||
&& _streamed->controls
|
&& _streamed->controls
|
||||||
&& _streamed->controls->dragging())) {
|
&& _streamed->controls->dragging())) {
|
||||||
return Flag::None | Flag(0);
|
return Flag::None | Flag(0);
|
||||||
} else if ((_w > _widget->width() || _h > _widget->height())
|
} else if ((_w > _widget->width() || _h > _maxUsedHeight)
|
||||||
&& (widgetPoint.y() > st::mediaviewHeaderTop)
|
&& (widgetPoint.y() > st::mediaviewHeaderTop)
|
||||||
&& QRect(_x, _y, _w, _h).contains(widgetPoint)) {
|
&& QRect(_x, _y, _w, _h).contains(widgetPoint)) {
|
||||||
return Flag::None | Flag(0);
|
return Flag::None | Flag(0);
|
||||||
|
@ -940,8 +954,14 @@ void OverlayWidget::updateGeometryToScreen(bool inMove) {
|
||||||
void OverlayWidget::updateControlsGeometry() {
|
void OverlayWidget::updateControlsGeometry() {
|
||||||
updateNavigationControlsGeometry();
|
updateNavigationControlsGeometry();
|
||||||
|
|
||||||
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
|
_saveMsg.moveTo(
|
||||||
_photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize);
|
(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 bottom = st::mediaviewShadowBottom.height();
|
||||||
const auto top = st::mediaviewShadowTop.size();
|
const auto top = st::mediaviewShadowTop.size();
|
||||||
|
@ -960,6 +980,9 @@ void OverlayWidget::updateControlsGeometry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::updateNavigationControlsGeometry() {
|
void OverlayWidget::updateNavigationControlsGeometry() {
|
||||||
|
_minUsedTop = topNotchSkip();
|
||||||
|
_maxUsedHeight = height() - _minUsedTop;
|
||||||
|
|
||||||
const auto overRect = QRect(
|
const auto overRect = QRect(
|
||||||
QPoint(),
|
QPoint(),
|
||||||
QSize(st::mediaviewIconOver, st::mediaviewIconOver));
|
QSize(st::mediaviewIconOver, st::mediaviewIconOver));
|
||||||
|
@ -969,14 +992,22 @@ void OverlayWidget::updateNavigationControlsGeometry() {
|
||||||
const auto navSkip = st::mediaviewHeaderTop;
|
const auto navSkip = st::mediaviewHeaderTop;
|
||||||
const auto xLeft = _stories ? (_x - navSize) : 0;
|
const auto xLeft = _stories ? (_x - navSize) : 0;
|
||||||
const auto xRight = _stories ? (_x + _w) : (width() - navSize);
|
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
|
_leftNavOver = _stories
|
||||||
? QRect()
|
? QRect()
|
||||||
: style::centerrect(_leftNav, overRect);
|
: style::centerrect(_leftNav, overRect);
|
||||||
_leftNavIcon = style::centerrect(
|
_leftNavIcon = style::centerrect(
|
||||||
_leftNav,
|
_leftNav,
|
||||||
_stories ? st::storiesLeft : st::mediaviewLeft);
|
_stories ? st::storiesLeft : st::mediaviewLeft);
|
||||||
_rightNav = QRect(xRight, navSkip, navSize, height() - 2 * navSkip);
|
_rightNav = QRect(
|
||||||
|
xRight,
|
||||||
|
_minUsedTop + navSkip,
|
||||||
|
navSize,
|
||||||
|
_maxUsedHeight - 2 * navSkip);
|
||||||
_rightNavOver = _stories
|
_rightNavOver = _stories
|
||||||
? QRect()
|
? QRect()
|
||||||
: style::centerrect(_rightNav, overRect);
|
: style::centerrect(_rightNav, overRect);
|
||||||
|
@ -1213,7 +1244,7 @@ void OverlayWidget::updateControls() {
|
||||||
if (_document && documentBubbleShown()) {
|
if (_document && documentBubbleShown()) {
|
||||||
_docRect = QRect(
|
_docRect = QRect(
|
||||||
(width() - st::mediaviewFileSize.width()) / 2,
|
(width() - st::mediaviewFileSize.width()) / 2,
|
||||||
(height() - st::mediaviewFileSize.height()) / 2,
|
_minUsedTop + (_maxUsedHeight - st::mediaviewFileSize.height()) / 2,
|
||||||
st::mediaviewFileSize.width(),
|
st::mediaviewFileSize.width(),
|
||||||
st::mediaviewFileSize.height());
|
st::mediaviewFileSize.height());
|
||||||
_docIconRect = QRect(
|
_docIconRect = QRect(
|
||||||
|
@ -1244,7 +1275,7 @@ void OverlayWidget::updateControls() {
|
||||||
} else {
|
} else {
|
||||||
_docIconRect = QRect(
|
_docIconRect = QRect(
|
||||||
(width() - st::mediaviewFileIconSize) / 2,
|
(width() - st::mediaviewFileIconSize) / 2,
|
||||||
(height() - st::mediaviewFileIconSize) / 2,
|
_minUsedTop + (_maxUsedHeight - st::mediaviewFileIconSize) / 2,
|
||||||
st::mediaviewFileIconSize,
|
st::mediaviewFileIconSize,
|
||||||
st::mediaviewFileIconSize);
|
st::mediaviewFileIconSize);
|
||||||
_docDownload->hide();
|
_docDownload->hide();
|
||||||
|
@ -1263,7 +1294,8 @@ void OverlayWidget::updateControls() {
|
||||||
_shareVisible = story && story->canShare();
|
_shareVisible = story && story->canShare();
|
||||||
_rotateVisible = !_themePreviewShown && !story;
|
_rotateVisible = !_themePreviewShown && !story;
|
||||||
const auto navRect = [&](int i) {
|
const auto navRect = [&](int i) {
|
||||||
return QRect(width() - st::mediaviewIconSize.width() * i,
|
return QRect(
|
||||||
|
width() - st::mediaviewIconSize.width() * i,
|
||||||
height() - st::mediaviewIconSize.height(),
|
height() - st::mediaviewIconSize.height(),
|
||||||
st::mediaviewIconSize.width(),
|
st::mediaviewIconSize.width(),
|
||||||
st::mediaviewIconSize.height());
|
st::mediaviewIconSize.height());
|
||||||
|
@ -1302,12 +1334,27 @@ void OverlayWidget::updateControls() {
|
||||||
}();
|
}();
|
||||||
_dateText = d.isValid() ? Ui::FormatDateTime(d) : QString();
|
_dateText = d.isValid() ? Ui::FormatDateTime(d) : QString();
|
||||||
if (!_fromName.isEmpty()) {
|
if (!_fromName.isEmpty()) {
|
||||||
_fromNameLabel.setText(st::mediaviewTextStyle, _fromName, Ui::NameTextOptions());
|
_fromNameLabel.setText(
|
||||||
_nameNav = QRect(st::mediaviewTextLeft, height() - st::mediaviewTextTop, qMin(_fromNameLabel.maxWidth(), width() / 3), st::mediaviewFont->height);
|
st::mediaviewTextStyle,
|
||||||
_dateNav = QRect(st::mediaviewTextLeft + _nameNav.width() + st::mediaviewTextSkip, height() - st::mediaviewTextTop, st::mediaviewFont->width(_dateText), st::mediaviewFont->height);
|
_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 {
|
} else {
|
||||||
_nameNav = QRect();
|
_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();
|
updateHeader();
|
||||||
refreshNavVisibility();
|
refreshNavVisibility();
|
||||||
|
@ -1336,6 +1383,10 @@ void OverlayWidget::resizeCenteredControls() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::refreshCaptionGeometry() {
|
void OverlayWidget::refreshCaptionGeometry() {
|
||||||
|
_caption.updateSkipBlock(0, 0);
|
||||||
|
_captionShowMoreWidth = 0;
|
||||||
|
_captionSkipBlockWidth = 0;
|
||||||
|
|
||||||
if (_caption.isEmpty()) {
|
if (_caption.isEmpty()) {
|
||||||
_captionRect = QRect();
|
_captionRect = QRect();
|
||||||
return;
|
return;
|
||||||
|
@ -1361,32 +1412,28 @@ void OverlayWidget::refreshCaptionGeometry() {
|
||||||
- st::mediaviewCaptionPadding.left()
|
- st::mediaviewCaptionPadding.left()
|
||||||
- st::mediaviewCaptionPadding.right()),
|
- st::mediaviewCaptionPadding.right()),
|
||||||
_caption.maxWidth());
|
_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 lineHeight = st::mediaviewCaptionStyle.font->height;
|
||||||
const auto wantedHeight = _caption.countHeight(captionWidth);
|
const auto wantedHeight = _caption.countHeight(captionWidth);
|
||||||
const auto maxHeight = _captionExpanded
|
const auto maxHeight = !_stories
|
||||||
? maxExpandedHeight
|
? (_maxUsedHeight / 4)
|
||||||
: maxCollapsedHeight;
|
: (wantedHeight > lineHeight * Stories::kMaxShownCaptionLines)
|
||||||
|
? (lineHeight * Stories::kCollapsedCaptionLines)
|
||||||
|
: wantedHeight;
|
||||||
const auto captionHeight = std::min(
|
const auto captionHeight = std::min(
|
||||||
wantedHeight,
|
wantedHeight,
|
||||||
(maxHeight / lineHeight) * lineHeight);
|
(maxHeight / lineHeight) * lineHeight);
|
||||||
_captionFitsIfExpanded = _stories
|
if (_stories && captionHeight < wantedHeight) {
|
||||||
&& (wantedHeight <= maxExpandedHeight);
|
const auto padding = st::storiesShowMorePadding;
|
||||||
_captionShownFull = (wantedHeight <= maxCollapsedHeight);
|
_captionShowMoreWidth = st::storiesShowMoreFont->width(
|
||||||
if (_captionShownFull && _captionExpanded && _stories) {
|
tr::lng_stories_show_more(tr::now));
|
||||||
_captionExpanded = false;
|
_captionSkipBlockWidth = _captionShowMoreWidth
|
||||||
_stories->setCaptionExpanded(false);
|
+ padding.left()
|
||||||
|
+ padding.right()
|
||||||
|
- st::mediaviewCaptionPadding.right();
|
||||||
|
const auto skiph = st::storiesShowMoreFont->height
|
||||||
|
+ padding.bottom()
|
||||||
|
- st::mediaviewCaptionPadding.bottom();
|
||||||
|
_caption.updateSkipBlock(_captionSkipBlockWidth, skiph);
|
||||||
}
|
}
|
||||||
_captionRect = QRect(
|
_captionRect = QRect(
|
||||||
(width() - captionWidth) / 2,
|
(width() - captionWidth) / 2,
|
||||||
|
@ -1748,11 +1795,13 @@ OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayWidget::ContentGeometry OverlayWidget::storiesContentGeometry(
|
OverlayWidget::ContentGeometry OverlayWidget::storiesContentGeometry(
|
||||||
const Stories::ContentLayout &layout) const {
|
const Stories::ContentLayout &layout,
|
||||||
|
float64 scale) const {
|
||||||
return {
|
return {
|
||||||
.rect = QRectF(layout.geometry),
|
.rect = QRectF(layout.geometry),
|
||||||
.controlsOpacity = kStoriesControlsOpacity,
|
.controlsOpacity = kStoriesControlsOpacity,
|
||||||
.fade = layout.fade,
|
.fade = layout.fade,
|
||||||
|
.scale = scale,
|
||||||
.roundRadius = layout.radius,
|
.roundRadius = layout.radius,
|
||||||
.topShadowShown = !layout.headerOutside,
|
.topShadowShown = !layout.headerOutside,
|
||||||
};
|
};
|
||||||
|
@ -1777,7 +1826,7 @@ void OverlayWidget::recountSkipTop() {
|
||||||
? height()
|
? height()
|
||||||
: (_streamed->controls->y() - st::mediaviewCaptionPadding.bottom());
|
: (_streamed->controls->y() - st::mediaviewCaptionPadding.bottom());
|
||||||
const auto skipHeightBottom = (height() - bottom);
|
const auto skipHeightBottom = (height() - bottom);
|
||||||
_skipTop = std::min(
|
_skipTop = _minUsedTop + std::min(
|
||||||
std::max(
|
std::max(
|
||||||
st::mediaviewCaptionMargin.height(),
|
st::mediaviewCaptionMargin.height(),
|
||||||
height() - _height - skipHeightBottom),
|
height() - _height - skipHeightBottom),
|
||||||
|
@ -1785,7 +1834,7 @@ void OverlayWidget::recountSkipTop() {
|
||||||
_availableHeight = height() - skipHeightBottom - _skipTop;
|
_availableHeight = height() - skipHeightBottom - _skipTop;
|
||||||
if (_fullScreenVideo && skipHeightBottom > 0 && _width > 0) {
|
if (_fullScreenVideo && skipHeightBottom > 0 && _width > 0) {
|
||||||
const auto h = width() * _height / _width;
|
const auto h = width() * _height / _width;
|
||||||
const auto topAllFit = height() - skipHeightBottom - h;
|
const auto topAllFit = _maxUsedHeight - skipHeightBottom - h;
|
||||||
if (_skipTop > topAllFit) {
|
if (_skipTop > topAllFit) {
|
||||||
_skipTop = std::max(topAllFit, 0);
|
_skipTop = std::max(topAllFit, 0);
|
||||||
}
|
}
|
||||||
|
@ -1818,12 +1867,12 @@ void OverlayWidget::resizeContentByScreenSize() {
|
||||||
};
|
};
|
||||||
if (_width > 0 && _height > 0) {
|
if (_width > 0 && _height > 0) {
|
||||||
_zoomToDefault = countZoomFor(availableWidth, _availableHeight);
|
_zoomToDefault = countZoomFor(availableWidth, _availableHeight);
|
||||||
_zoomToScreen = countZoomFor(width(), height());
|
_zoomToScreen = countZoomFor(width(), _maxUsedHeight);
|
||||||
} else {
|
} else {
|
||||||
_zoomToDefault = _zoomToScreen = 0;
|
_zoomToDefault = _zoomToScreen = 0;
|
||||||
}
|
}
|
||||||
const auto usew = _fullScreenVideo ? width() : availableWidth;
|
const auto usew = _fullScreenVideo ? width() : availableWidth;
|
||||||
const auto useh = _fullScreenVideo ? height() : _availableHeight;
|
const auto useh = _fullScreenVideo ? _maxUsedHeight : _availableHeight;
|
||||||
if ((_width > usew) || (_height > useh) || _fullScreenVideo) {
|
if ((_width > usew) || (_height > useh) || _fullScreenVideo) {
|
||||||
const auto use = _fullScreenVideo ? _zoomToScreen : _zoomToDefault;
|
const auto use = _fullScreenVideo ? _zoomToScreen : _zoomToDefault;
|
||||||
_zoom = kZoomToScreenLevel;
|
_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 (photo) {
|
||||||
if (contextItem && contextPeer) {
|
if (contextItem && contextPeer) {
|
||||||
return;
|
return;
|
||||||
|
@ -3444,7 +3497,6 @@ void OverlayWidget::updateThemePreviewGeometry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::displayFinished(anim::activation activation) {
|
void OverlayWidget::displayFinished(anim::activation activation) {
|
||||||
_captionExpanded = _captionFitsIfExpanded = _captionShownFull = false;
|
|
||||||
updateControls();
|
updateControls();
|
||||||
if (isHidden()) {
|
if (isHidden()) {
|
||||||
_helper->beforeShow(_fullscreen);
|
_helper->beforeShow(_fullscreen);
|
||||||
|
@ -3646,6 +3698,7 @@ bool OverlayWidget::createStreamingObjects() {
|
||||||
_streamed->instance.setPriority(kOverlayLoaderPriority);
|
_streamed->instance.setPriority(kOverlayLoaderPriority);
|
||||||
_streamed->instance.lockPlayer();
|
_streamed->instance.lockPlayer();
|
||||||
_streamed->withSound = _document
|
_streamed->withSound = _document
|
||||||
|
&& !_document->isSilentVideo()
|
||||||
&& (_document->isAudioFile()
|
&& (_document->isAudioFile()
|
||||||
|| _document->isVideoFile()
|
|| _document->isVideoFile()
|
||||||
|| _document->isVoiceMessage()
|
|| _document->isVoiceMessage()
|
||||||
|
@ -3986,12 +4039,14 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) {
|
||||||
};
|
};
|
||||||
if (!_streamed->withSound) {
|
if (!_streamed->withSound) {
|
||||||
options.mode = Streaming::Mode::Video;
|
options.mode = Streaming::Mode::Video;
|
||||||
options.loop = true;
|
options.loop = !_stories;
|
||||||
} else {
|
} else {
|
||||||
Assert(_document != nullptr);
|
Assert(_document != nullptr);
|
||||||
const auto messageId = _message ? _message->fullId() : FullMsgId();
|
const auto messageId = _message ? _message->fullId() : FullMsgId();
|
||||||
options.audioId = AudioMsgId(_document, messageId);
|
options.audioId = AudioMsgId(_document, messageId);
|
||||||
options.speed = Core::App().settings().videoPlaybackSpeed();
|
options.speed = _stories
|
||||||
|
? Core::App().settings().videoPlaybackSpeed()
|
||||||
|
: 1.;
|
||||||
if (_pip) {
|
if (_pip) {
|
||||||
_pip = nullptr;
|
_pip = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -4059,7 +4114,7 @@ void OverlayWidget::playbackControlsSpeedChanged(float64 speed) {
|
||||||
Core::App().settings().setVideoPlaybackSpeed(speed);
|
Core::App().settings().setVideoPlaybackSpeed(speed);
|
||||||
Core::App().saveSettingsDelayed();
|
Core::App().saveSettingsDelayed();
|
||||||
}
|
}
|
||||||
if (_streamed && _streamed->controls) {
|
if (_streamed && _streamed->controls && !_stories) {
|
||||||
DEBUG_LOG(("Media playback speed: %1 to _streamed.").arg(speed));
|
DEBUG_LOG(("Media playback speed: %1 to _streamed.").arg(speed));
|
||||||
_streamed->instance.setSpeed(speed);
|
_streamed->instance.setSpeed(speed);
|
||||||
}
|
}
|
||||||
|
@ -4240,6 +4295,14 @@ void OverlayWidget::storiesVolumeChangeFinished() {
|
||||||
playbackControlsVolumeChangeFinished();
|
playbackControlsVolumeChangeFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int OverlayWidget::topNotchSkip() const {
|
||||||
|
return _fullscreen ? _topNotchSize : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int OverlayWidget::storiesTopNotchSkip() {
|
||||||
|
return topNotchSkip();
|
||||||
|
}
|
||||||
|
|
||||||
void OverlayWidget::playbackToggleFullScreen() {
|
void OverlayWidget::playbackToggleFullScreen() {
|
||||||
Expects(_streamed != nullptr);
|
Expects(_streamed != nullptr);
|
||||||
|
|
||||||
|
@ -4400,7 +4463,7 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
|
||||||
const auto paint = [&](const SiblingView &view, int index) {
|
const auto paint = [&](const SiblingView &view, int index) {
|
||||||
renderer->paintTransformedStaticContent(
|
renderer->paintTransformedStaticContent(
|
||||||
view.image,
|
view.image,
|
||||||
storiesContentGeometry(view.layout),
|
storiesContentGeometry(view.layout, view.scale),
|
||||||
false, // semi-transparent
|
false, // semi-transparent
|
||||||
false, // fill transparent background
|
false, // fill transparent background
|
||||||
index);
|
index);
|
||||||
|
@ -4443,7 +4506,8 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
|
||||||
if (!_stories) {
|
if (!_stories) {
|
||||||
renderer->paintFooter(footerGeometry(), opacity);
|
renderer->paintFooter(footerGeometry(), opacity);
|
||||||
}
|
}
|
||||||
if (!_caption.isEmpty()) {
|
if (!_caption.isEmpty()
|
||||||
|
&& (!_stories || !_stories->skipCaption())) {
|
||||||
renderer->paintCaption(captionGeometry(), opacity);
|
renderer->paintCaption(captionGeometry(), opacity);
|
||||||
}
|
}
|
||||||
if (_groupThumbs) {
|
if (_groupThumbs) {
|
||||||
|
@ -4853,6 +4917,7 @@ void OverlayWidget::paintCaptionContent(
|
||||||
}
|
}
|
||||||
if (inner.intersects(clip)) {
|
if (inner.intersects(clip)) {
|
||||||
p.setPen(st::mediaviewCaptionFg);
|
p.setPen(st::mediaviewCaptionFg);
|
||||||
|
const auto lineHeight = st::mediaviewCaptionStyle.font->height;
|
||||||
_caption.draw(p, {
|
_caption.draw(p, {
|
||||||
.position = inner.topLeft(),
|
.position = inner.topLeft(),
|
||||||
.availableWidth = inner.width(),
|
.availableWidth = inner.width(),
|
||||||
|
@ -4860,8 +4925,31 @@ void OverlayWidget::paintCaptionContent(
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
.pausedEmoji = On(PowerSaving::kEmojiChat),
|
.pausedEmoji = On(PowerSaving::kEmojiChat),
|
||||||
.pausedSpoiler = On(PowerSaving::kChatSpoiler),
|
.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<QKeyEvent*> e) {
|
||||||
if (_controlsHideTimer.isActive()) {
|
if (_controlsHideTimer.isActive()) {
|
||||||
activateControls();
|
activateControls();
|
||||||
}
|
}
|
||||||
moveToNext(1);
|
if (!moveToNext(1) && _stories) {
|
||||||
|
storiesClose();
|
||||||
|
}
|
||||||
} else if (ctrl) {
|
} else if (ctrl) {
|
||||||
if (key == Qt::Key_Plus
|
if (key == Qt::Key_Plus
|
||||||
|| key == Qt::Key_Equal
|
|| key == Qt::Key_Equal
|
||||||
|
@ -4978,20 +5068,23 @@ void OverlayWidget::handleKeyPress(not_null<QKeyEvent*> e) {
|
||||||
void OverlayWidget::handleWheelEvent(not_null<QWheelEvent*> e) {
|
void OverlayWidget::handleWheelEvent(not_null<QWheelEvent*> e) {
|
||||||
constexpr auto step = int(QWheelEvent::DefaultDeltasPerStep);
|
constexpr auto step = int(QWheelEvent::DefaultDeltasPerStep);
|
||||||
|
|
||||||
|
const auto acceptForJump = !_stories
|
||||||
|
&& ((e->source() == Qt::MouseEventNotSynthesized)
|
||||||
|
|| (e->source() == Qt::MouseEventSynthesizedBySystem));
|
||||||
_verticalWheelDelta += e->angleDelta().y();
|
_verticalWheelDelta += e->angleDelta().y();
|
||||||
while (qAbs(_verticalWheelDelta) >= step) {
|
while (qAbs(_verticalWheelDelta) >= step) {
|
||||||
if (_verticalWheelDelta < 0) {
|
if (_verticalWheelDelta < 0) {
|
||||||
_verticalWheelDelta += step;
|
_verticalWheelDelta += step;
|
||||||
if (e->modifiers().testFlag(Qt::ControlModifier)) {
|
if (e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||||
zoomOut();
|
zoomOut();
|
||||||
} else if (e->source() == Qt::MouseEventNotSynthesized) {
|
} else if (acceptForJump) {
|
||||||
moveToNext(1);
|
moveToNext(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_verticalWheelDelta -= step;
|
_verticalWheelDelta -= step;
|
||||||
if (e->modifiers().testFlag(Qt::ControlModifier)) {
|
if (e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||||
zoomIn();
|
zoomIn();
|
||||||
} else if (e->source() == Qt::MouseEventNotSynthesized) {
|
} else if (acceptForJump) {
|
||||||
moveToNext(-1);
|
moveToNext(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5366,7 +5459,7 @@ bool OverlayWidget::handleDoubleClick(
|
||||||
|
|
||||||
void OverlayWidget::snapXY() {
|
void OverlayWidget::snapXY() {
|
||||||
auto xmin = width() - _w, xmax = 0;
|
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_min(xmin, (width() - _w) / 2);
|
||||||
accumulate_max(xmax, (width() - _w) / 2);
|
accumulate_max(xmax, (width() - _w) / 2);
|
||||||
accumulate_min(ymin, _skipTop + (_availableHeight - _h) / 2);
|
accumulate_min(ymin, _skipTop + (_availableHeight - _h) / 2);
|
||||||
|
@ -5390,7 +5483,7 @@ void OverlayWidget::handleMouseMove(QPoint position) {
|
||||||
>= QApplication::startDragDistance())) {
|
>= QApplication::startDragDistance())) {
|
||||||
_dragging = QRect(_x, _y, _w, _h).contains(_mStart) ? 1 : -1;
|
_dragging = QRect(_x, _y, _w, _h).contains(_mStart) ? 1 : -1;
|
||||||
if (_dragging > 0) {
|
if (_dragging > 0) {
|
||||||
if (_w > width() || _h > height()) {
|
if (_w > width() || _h > _maxUsedHeight) {
|
||||||
setCursor(style::cur_sizeall);
|
setCursor(style::cur_sizeall);
|
||||||
} else {
|
} else {
|
||||||
setCursor(style::cur_default);
|
setCursor(style::cur_default);
|
||||||
|
@ -5486,9 +5579,13 @@ void OverlayWidget::updateOver(QPoint pos) {
|
||||||
lnk = textState.link;
|
lnk = textState.link;
|
||||||
lnkhost = this;
|
lnkhost = this;
|
||||||
} else if (_captionRect.contains(pos)) {
|
} 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;
|
lnk = textState.link;
|
||||||
if (_stories && !_captionShownFull && !lnk) {
|
if (_stories && !lnk) {
|
||||||
lnk = ensureCaptionExpandLink();
|
lnk = ensureCaptionExpandLink();
|
||||||
}
|
}
|
||||||
lnkhost = this;
|
lnkhost = this;
|
||||||
|
@ -5567,19 +5664,7 @@ void OverlayWidget::updateOver(QPoint pos) {
|
||||||
ClickHandlerPtr OverlayWidget::ensureCaptionExpandLink() {
|
ClickHandlerPtr OverlayWidget::ensureCaptionExpandLink() {
|
||||||
if (!_captionExpandLink) {
|
if (!_captionExpandLink) {
|
||||||
const auto toggle = crl::guard(_widget, [=] {
|
const auto toggle = crl::guard(_widget, [=] {
|
||||||
if (!_stories) {
|
if (_stories) {
|
||||||
return;
|
|
||||||
} else if (_captionExpanded) {
|
|
||||||
_captionExpanded = false;
|
|
||||||
_stories->setCaptionExpanded(false);
|
|
||||||
refreshCaptionGeometry();
|
|
||||||
update();
|
|
||||||
} else if (_captionFitsIfExpanded) {
|
|
||||||
_captionExpanded = true;
|
|
||||||
_stories->setCaptionExpanded(true);
|
|
||||||
refreshCaptionGeometry();
|
|
||||||
update();
|
|
||||||
} else {
|
|
||||||
_stories->showFullCaption();
|
_stories->showFullCaption();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -180,6 +180,7 @@ private:
|
||||||
|
|
||||||
// Stories.
|
// Stories.
|
||||||
qreal fade = 0.;
|
qreal fade = 0.;
|
||||||
|
qreal scale = 1.;
|
||||||
int bottomShadowSkip = 0;
|
int bottomShadowSkip = 0;
|
||||||
int roundRadius = 0;
|
int roundRadius = 0;
|
||||||
bool topShadowShown = false;
|
bool topShadowShown = false;
|
||||||
|
@ -243,6 +244,7 @@ private:
|
||||||
void playbackResumeOnCall();
|
void playbackResumeOnCall();
|
||||||
void playbackPauseMusic();
|
void playbackPauseMusic();
|
||||||
void switchToPip();
|
void switchToPip();
|
||||||
|
[[nodiscard]] int topNotchSkip() const;
|
||||||
|
|
||||||
not_null<Ui::RpWidget*> storiesWrap() override;
|
not_null<Ui::RpWidget*> storiesWrap() override;
|
||||||
std::shared_ptr<ChatHelpers::Show> storiesShow() override;
|
std::shared_ptr<ChatHelpers::Show> storiesShow() override;
|
||||||
|
@ -264,6 +266,7 @@ private:
|
||||||
void storiesVolumeToggle() override;
|
void storiesVolumeToggle() override;
|
||||||
void storiesVolumeChanged(float64 volume) override;
|
void storiesVolumeChanged(float64 volume) override;
|
||||||
void storiesVolumeChangeFinished() override;
|
void storiesVolumeChangeFinished() override;
|
||||||
|
int storiesTopNotchSkip() override;
|
||||||
|
|
||||||
void hideControls(bool force = false);
|
void hideControls(bool force = false);
|
||||||
void subscribeToScreenGeometry();
|
void subscribeToScreenGeometry();
|
||||||
|
@ -419,7 +422,8 @@ private:
|
||||||
[[nodiscard]] QRect finalContentRect() const;
|
[[nodiscard]] QRect finalContentRect() const;
|
||||||
[[nodiscard]] ContentGeometry contentGeometry() const;
|
[[nodiscard]] ContentGeometry contentGeometry() const;
|
||||||
[[nodiscard]] ContentGeometry storiesContentGeometry(
|
[[nodiscard]] ContentGeometry storiesContentGeometry(
|
||||||
const Stories::ContentLayout &layout) const;
|
const Stories::ContentLayout &layout,
|
||||||
|
float64 scale = 1.) const;
|
||||||
void updateContentRect();
|
void updateContentRect();
|
||||||
void contentSizeChanged();
|
void contentSizeChanged();
|
||||||
|
|
||||||
|
@ -585,14 +589,16 @@ private:
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
QRect _captionRect;
|
QRect _captionRect;
|
||||||
ClickHandlerPtr _captionExpandLink;
|
ClickHandlerPtr _captionExpandLink;
|
||||||
bool _captionShownFull = false;
|
int _captionShowMoreWidth = 0;
|
||||||
bool _captionFitsIfExpanded = false;
|
int _captionSkipBlockWidth = 0;
|
||||||
bool _captionExpanded = false;
|
|
||||||
|
|
||||||
|
int _topNotchSize = 0;
|
||||||
int _width = 0;
|
int _width = 0;
|
||||||
int _height = 0;
|
int _height = 0;
|
||||||
int _skipTop = 0;
|
int _skipTop = 0;
|
||||||
int _availableHeight = 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 _x = 0, _y = 0, _w = 0, _h = 0;
|
||||||
int _xStart = 0, _yStart = 0;
|
int _xStart = 0, _yStart = 0;
|
||||||
int _zoom = 0; // < 0 - out, 0 - none, > 0 - in
|
int _zoom = 0; // < 0 - out, 0 - none, > 0 - in
|
||||||
|
|
|
@ -700,7 +700,7 @@ Voice::Voice(
|
||||||
|
|
||||||
updateName();
|
updateName();
|
||||||
const auto dateText = Ui::Text::Link(
|
const auto dateText = Ui::Text::Link(
|
||||||
langDateTime(base::unixtime::parse(_data->date))); // Link 1.
|
langDateTime(base::unixtime::parse(parent->date()))); // Link 1.
|
||||||
_details.setMarkedText(
|
_details.setMarkedText(
|
||||||
st::defaultTextStyle,
|
st::defaultTextStyle,
|
||||||
tr::lng_date_and_duration(
|
tr::lng_date_and_duration(
|
||||||
|
@ -1020,7 +1020,7 @@ Document::Document(
|
||||||
, _forceFileLayout(fields.forceFileLayout)
|
, _forceFileLayout(fields.forceFileLayout)
|
||||||
, _date(langDateTime(base::unixtime::parse(fields.dateOverride
|
, _date(langDateTime(base::unixtime::parse(fields.dateOverride
|
||||||
? fields.dateOverride
|
? fields.dateOverride
|
||||||
: _data->date)))
|
: parent->date())))
|
||||||
, _ext(_generic.ext)
|
, _ext(_generic.ext)
|
||||||
, _datew(st::normalFont->width(_date)) {
|
, _datew(st::normalFont->width(_date)) {
|
||||||
_name.setMarkedText(
|
_name.setMarkedText(
|
||||||
|
|
|
@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "base/event_filter.h"
|
#include "base/event_filter.h"
|
||||||
|
#include "ui/platform/ui_platform_window_title.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
|
|
|
@ -309,6 +309,10 @@ bool GenerateDesktopFile(
|
||||||
hashMd5Hex(d.constData(), d.size(), md5Hash);
|
hashMd5Hex(d.constData(), d.size(), md5Hash);
|
||||||
|
|
||||||
if (!Core::Launcher::Instance().customWorkingDir()) {
|
if (!Core::Launcher::Instance().customWorkingDir()) {
|
||||||
|
QFile::remove(u"%1org.telegram.desktop._%2.desktop"_q.arg(
|
||||||
|
targetPath,
|
||||||
|
md5Hash));
|
||||||
|
|
||||||
const auto exePath = QFile::encodeName(
|
const auto exePath = QFile::encodeName(
|
||||||
cExeDir() + cExeName());
|
cExeDir() + cExeName());
|
||||||
hashMd5Hex(exePath.constData(), exePath.size(), md5Hash);
|
hashMd5Hex(exePath.constData(), exePath.size(), md5Hash);
|
||||||
|
@ -335,7 +339,7 @@ bool GenerateServiceFile(bool silent = false) {
|
||||||
+ QGuiApplication::desktopFileName()
|
+ QGuiApplication::desktopFileName()
|
||||||
+ u".service"_q;
|
+ 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);
|
if (!QDir(targetPath).exists()) QDir().mkpath(targetPath);
|
||||||
|
|
||||||
const auto target = Glib::KeyFile::create();
|
const auto target = Glib::KeyFile::create();
|
||||||
|
@ -366,6 +370,18 @@ bool GenerateServiceFile(bool silent = false) {
|
||||||
return 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, {
|
QProcess::execute(u"systemctl"_q, {
|
||||||
u"--user"_q,
|
u"--user"_q,
|
||||||
u"reload"_q,
|
u"reload"_q,
|
||||||
|
|
|
@ -36,6 +36,7 @@ public:
|
||||||
void clearState() override;
|
void clearState() override;
|
||||||
void setControlsOpacity(float64 opacity) override;
|
void setControlsOpacity(float64 opacity) override;
|
||||||
rpl::producer<bool> controlsSideRightValue() override;
|
rpl::producer<bool> controlsSideRightValue() override;
|
||||||
|
rpl::producer<int> topNotchSkipValue() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Control = Ui::Platform::TitleControl;
|
using Control = Ui::Platform::TitleControl;
|
||||||
|
|
|
@ -35,6 +35,7 @@ struct MacOverlayWidgetHelper::Data {
|
||||||
rpl::event_stream<> clearStateRequests;
|
rpl::event_stream<> clearStateRequests;
|
||||||
bool anyOver = false;
|
bool anyOver = false;
|
||||||
NSWindow * __weak native = nil;
|
NSWindow * __weak native = nil;
|
||||||
|
rpl::variable<int> topNotchSkip;
|
||||||
};
|
};
|
||||||
|
|
||||||
MacOverlayWidgetHelper::MacOverlayWidgetHelper(
|
MacOverlayWidgetHelper::MacOverlayWidgetHelper(
|
||||||
|
@ -96,6 +97,9 @@ void MacOverlayWidgetHelper::updateStyles(bool fullscreen) {
|
||||||
[window setTitleVisibility:NSWindowTitleHidden];
|
[window setTitleVisibility:NSWindowTitleHidden];
|
||||||
[window setTitlebarAppearsTransparent:YES];
|
[window setTitlebarAppearsTransparent:YES];
|
||||||
[window setStyleMask:[window styleMask] | NSWindowStyleMaskFullSizeContentView];
|
[window setStyleMask:[window styleMask] | NSWindowStyleMaskFullSizeContentView];
|
||||||
|
if (@available(macOS 12.0, *)) {
|
||||||
|
_data->topNotchSkip = [[window screen] safeAreaInsets].top;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacOverlayWidgetHelper::refreshButtons(bool fullscreen) {
|
void MacOverlayWidgetHelper::refreshButtons(bool fullscreen) {
|
||||||
|
@ -153,6 +157,10 @@ rpl::producer<bool> MacOverlayWidgetHelper::controlsSideRightValue() {
|
||||||
return rpl::single(false);
|
return rpl::single(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> MacOverlayWidgetHelper::topNotchSkipValue() {
|
||||||
|
return _data->topNotchSkip.value();
|
||||||
|
}
|
||||||
|
|
||||||
object_ptr<Ui::AbstractButton> MacOverlayWidgetHelper::create(
|
object_ptr<Ui::AbstractButton> MacOverlayWidgetHelper::create(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
Control control) {
|
Control control) {
|
||||||
|
|
|
@ -58,6 +58,9 @@ public:
|
||||||
-> rpl::producer<not_null<QMouseEvent*>> {
|
-> rpl::producer<not_null<QMouseEvent*>> {
|
||||||
return rpl::never<not_null<QMouseEvent*>>();
|
return rpl::never<not_null<QMouseEvent*>>();
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] virtual rpl::producer<int> topNotchSkipValue() {
|
||||||
|
return rpl::single(0);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<OverlayWidgetHelper> CreateOverlayWidgetHelper(
|
[[nodiscard]] std::unique_ptr<OverlayWidgetHelper> CreateOverlayWidgetHelper(
|
||||||
|
|
|
@ -1653,7 +1653,7 @@ QPointer<Ui::RpWidget> Premium::createPinnedToBottom(
|
||||||
} else {
|
} else {
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
_radioGroup->setChangedCallback([=](int value) {
|
const auto callback = [=](int value) {
|
||||||
const auto options =
|
const auto options =
|
||||||
_controller->session().api().premium().subscriptionOptions();
|
_controller->session().api().premium().subscriptionOptions();
|
||||||
if (options.empty()) {
|
if (options.empty()) {
|
||||||
|
@ -1665,8 +1665,9 @@ QPointer<Ui::RpWidget> Premium::createPinnedToBottom(
|
||||||
lt_cost,
|
lt_cost,
|
||||||
options[value].costPerMonth);
|
options[value].costPerMonth);
|
||||||
_buttonText = std::move(text);
|
_buttonText = std::move(text);
|
||||||
});
|
};
|
||||||
_radioGroup->setValue(0);
|
_radioGroup->setChangedCallback(callback);
|
||||||
|
callback(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_showFinished.events(
|
_showFinished.events(
|
||||||
|
|
|
@ -165,8 +165,9 @@ Action::Action(
|
||||||
+ _st.itemStyle.font->height
|
+ _st.itemStyle.font->height
|
||||||
+ st::defaultWhoRead.itemPadding.bottom()) {
|
+ st::defaultWhoRead.itemPadding.bottom()) {
|
||||||
const auto parent = parentMenu->menu();
|
const auto parent = parentMenu->menu();
|
||||||
const auto checkAppeared = [=, now = crl::now()] {
|
const auto delay = anim::Disabled() ? 0 : parentMenu->st().duration;
|
||||||
_appeared = (crl::now() - now) >= parentMenu->st().duration;
|
const auto checkAppeared = [=, now = crl::now()](bool force = false) {
|
||||||
|
_appeared = force || ((crl::now() - now) >= delay);
|
||||||
};
|
};
|
||||||
|
|
||||||
setAcceptBoth(true);
|
setAcceptBoth(true);
|
||||||
|
@ -224,8 +225,10 @@ Action::Action(
|
||||||
enableMouseSelecting();
|
enableMouseSelecting();
|
||||||
|
|
||||||
base::call_delayed(parentMenu->st().duration, this, [=] {
|
base::call_delayed(parentMenu->st().duration, this, [=] {
|
||||||
checkAppeared();
|
if (!_appeared) {
|
||||||
updateUserpicsFromContent();
|
checkAppeared(true);
|
||||||
|
updateUserpicsFromContent();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1138,6 +1138,10 @@ void AddGiftOptions(
|
||||||
stCheckbox,
|
stCheckbox,
|
||||||
std::move(radioView));
|
std::move(radioView));
|
||||||
radio->setAttribute(Qt::WA_TransparentForMouseEvents);
|
radio->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
{ // Paint the last frame instantly for the layer animation.
|
||||||
|
group->setValue(0);
|
||||||
|
radio->finishAnimating();
|
||||||
|
}
|
||||||
|
|
||||||
row->sizeValue(
|
row->sizeValue(
|
||||||
) | rpl::start_with_next([=, margins = stCheckbox.margin](
|
) | rpl::start_with_next([=, margins = stCheckbox.margin](
|
||||||
|
|
|
@ -40,6 +40,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Window {
|
namespace Window {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] Dialogs::UnreadState MainListMapUnreadState(
|
||||||
|
not_null<Main::Session*> 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<Dialogs::UnreadState> MainListUnreadState(
|
[[nodiscard]] rpl::producer<Dialogs::UnreadState> MainListUnreadState(
|
||||||
not_null<Dialogs::MainList*> list) {
|
not_null<Dialogs::MainList*> list) {
|
||||||
return rpl::single(rpl::empty) | rpl::then(
|
return rpl::single(rpl::empty) | rpl::then(
|
||||||
|
@ -59,11 +69,7 @@ namespace {
|
||||||
return MainListUnreadState(
|
return MainListUnreadState(
|
||||||
session->data().chatsList()
|
session->data().chatsList()
|
||||||
) | rpl::map([=](const Dialogs::UnreadState &state) {
|
) | rpl::map([=](const Dialogs::UnreadState &state) {
|
||||||
const auto folderId = Data::Folder::kId;
|
return MainListMapUnreadState(session, state);
|
||||||
if (const auto folder = session->data().folderLoaded(folderId)) {
|
|
||||||
return state - folder->chatsList()->unreadState();
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,24 +320,14 @@ base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareButton(
|
||||||
} else if (id >= 0) {
|
} else if (id >= 0) {
|
||||||
_session->setActiveChatsFilter(id);
|
_session->setActiveChatsFilter(id);
|
||||||
} else {
|
} else {
|
||||||
const auto filters = &_session->session().data().chatsFilters();
|
openFiltersSettings();
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (id >= 0) {
|
if (id >= 0) {
|
||||||
raw->setAcceptDrops(true);
|
raw->setAcceptDrops(true);
|
||||||
raw->events(
|
raw->events(
|
||||||
) | rpl::filter([=](not_null<QEvent*> e) {
|
) | rpl::filter([=](not_null<QEvent*> e) {
|
||||||
return ((e->type() == QEvent::ContextMenu) && (id > 0))
|
return ((e->type() == QEvent::ContextMenu) && (id >= 0))
|
||||||
|| e->type() == QEvent::DragEnter
|
|| e->type() == QEvent::DragEnter
|
||||||
|| e->type() == QEvent::DragMove
|
|| e->type() == QEvent::DragMove
|
||||||
|| e->type() == QEvent::DragLeave;
|
|| e->type() == QEvent::DragLeave;
|
||||||
|
@ -362,13 +358,27 @@ base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareButton(
|
||||||
return button;
|
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) {
|
void FiltersMenu::showMenu(QPoint position, FilterId id) {
|
||||||
if (_popupMenu) {
|
if (_popupMenu) {
|
||||||
_popupMenu = nullptr;
|
_popupMenu = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto i = _filters.find(id);
|
const auto i = _filters.find(id);
|
||||||
if (i == end(_filters)) {
|
if ((i == end(_filters)) && id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_popupMenu = base::make_unique_q<Ui::PopupMenu>(
|
_popupMenu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
@ -382,23 +392,46 @@ void FiltersMenu::showMenu(QPoint position, FilterId id) {
|
||||||
args.icon);
|
args.icon);
|
||||||
});
|
});
|
||||||
|
|
||||||
addAction(
|
if (id) {
|
||||||
tr::lng_filters_context_edit(tr::now),
|
addAction(
|
||||||
[=] { showEditBox(id); },
|
tr::lng_filters_context_edit(tr::now),
|
||||||
&st::menuIconEdit);
|
[=] { showEditBox(id); },
|
||||||
|
&st::menuIconEdit);
|
||||||
|
|
||||||
auto filteredChats = [=] {
|
auto filteredChats = [=] {
|
||||||
return _session->session().data().chatsFilters().chatsList(id);
|
return _session->session().data().chatsFilters().chatsList(id);
|
||||||
};
|
};
|
||||||
Window::MenuAddMarkAsReadChatListAction(
|
Window::MenuAddMarkAsReadChatListAction(
|
||||||
_session,
|
_session,
|
||||||
std::move(filteredChats),
|
std::move(filteredChats),
|
||||||
addAction);
|
addAction);
|
||||||
|
|
||||||
addAction(
|
addAction(
|
||||||
tr::lng_filters_context_remove(tr::now),
|
tr::lng_filters_context_remove(tr::now),
|
||||||
[=] { showRemoveBox(id); },
|
[=] { showRemoveBox(id); },
|
||||||
&st::menuIconDelete);
|
&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);
|
_popupMenu->popup(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ private:
|
||||||
void showRemoveBox(FilterId id);
|
void showRemoveBox(FilterId id);
|
||||||
void remove(FilterId id, std::vector<not_null<PeerData*>> leave = {});
|
void remove(FilterId id, std::vector<not_null<PeerData*>> leave = {});
|
||||||
void scrollToButton(not_null<Ui::RpWidget*> widget);
|
void scrollToButton(not_null<Ui::RpWidget*> widget);
|
||||||
|
void openFiltersSettings();
|
||||||
|
|
||||||
const not_null<SessionController*> _session;
|
const not_null<SessionController*> _session;
|
||||||
const not_null<Ui::RpWidget*> _parent;
|
const not_null<Ui::RpWidget*> _parent;
|
||||||
|
|
|
@ -2303,9 +2303,12 @@ void MenuAddMarkAsReadAllChatsAction(
|
||||||
void MenuAddMarkAsReadChatListAction(
|
void MenuAddMarkAsReadChatListAction(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
Fn<not_null<Dialogs::MainList*>()> &&list,
|
Fn<not_null<Dialogs::MainList*>()> &&list,
|
||||||
const PeerMenuCallback &addAction) {
|
const PeerMenuCallback &addAction,
|
||||||
|
Fn<Dialogs::UnreadState()> customUnreadState) {
|
||||||
// There is no async to make weak from controller.
|
// 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) {
|
if (!unreadState.messages && !unreadState.marks && !unreadState.chats) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ class Thread;
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
class MainList;
|
class MainList;
|
||||||
struct EntryState;
|
struct EntryState;
|
||||||
|
struct UnreadState;
|
||||||
} // namespace Dialogs
|
} // namespace Dialogs
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
|
@ -69,7 +70,8 @@ void MenuAddMarkAsReadAllChatsAction(
|
||||||
void MenuAddMarkAsReadChatListAction(
|
void MenuAddMarkAsReadChatListAction(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
Fn<not_null<Dialogs::MainList*>()> &&list,
|
Fn<not_null<Dialogs::MainList*>()> &&list,
|
||||||
const PeerMenuCallback &addAction);
|
const PeerMenuCallback &addAction,
|
||||||
|
Fn<Dialogs::UnreadState()> customUnreadState = nullptr);
|
||||||
|
|
||||||
void PeerMenuExportChat(not_null<PeerData*> peer);
|
void PeerMenuExportChat(not_null<PeerData*> peer);
|
||||||
void PeerMenuDeleteContact(
|
void PeerMenuDeleteContact(
|
||||||
|
|
|
@ -520,7 +520,7 @@ mac:
|
||||||
""")
|
""")
|
||||||
|
|
||||||
stage('mozjpeg', """
|
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
|
cd mozjpeg
|
||||||
win:
|
win:
|
||||||
cmake . ^
|
cmake . ^
|
||||||
|
@ -675,13 +675,31 @@ stage('dav1d', """
|
||||||
win:
|
win:
|
||||||
git clone -b 1.2.1 --depth 1 https://code.videolan.org/videolan/dav1d.git
|
git clone -b 1.2.1 --depth 1 https://code.videolan.org/videolan/dav1d.git
|
||||||
cd dav1d
|
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
|
depends:python/Scripts/activate.bat
|
||||||
%THIRDPARTY_DIR%\\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 compile -C builddir-debug
|
||||||
meson install -C builddir-debug
|
meson install -C builddir-debug
|
||||||
release:
|
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 compile -C builddir-release
|
||||||
meson install -C builddir-release
|
meson install -C builddir-release
|
||||||
win:
|
win:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
AppVersion 4008007
|
AppVersion 4008010
|
||||||
AppVersionStrMajor 4.8
|
AppVersionStrMajor 4.8
|
||||||
AppVersionStrSmall 4.8.7
|
AppVersionStrSmall 4.8.10
|
||||||
AppVersionStr 4.8.7
|
AppVersionStr 4.8.10
|
||||||
BetaChannel 0
|
BetaChannel 0
|
||||||
AlphaVersion 0
|
AlphaVersion 0
|
||||||
AppVersionOriginal 4.8.7
|
AppVersionOriginal 4.8.10
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 2669a04579069942b6208a18abe93c26adfddf2a
|
Subproject commit efd052594db9f3d85a2fbcba76db6fdecf226597
|
|
@ -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)
|
4.8.7 (21.07.23)
|
||||||
|
|
||||||
- Several crash fixes and small stories improvements.
|
- Several crash fixes and small stories improvements.
|
||||||
|
|
0
lib/xdg/org.telegram.desktop.service
Normal file
0
lib/xdg/org.telegram.desktop.service
Normal file
Loading…
Add table
Reference in a new issue