diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..5ace4600a1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7a65b483fb..ac40f3a330 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive @@ -31,7 +31,7 @@ jobs: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin - name: Free up some disk space. - uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be - name: Docker image build. run: | diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8020e46c5b..8eb20a2e55 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -58,7 +58,7 @@ jobs: run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive path: ${{ env.REPO_NAME }} @@ -116,7 +116,7 @@ jobs: cd $REPO_NAME/out/Debug sudo mkdir artifact sudo mv {Telegram,Updater} artifact/ - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. with: diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 562159d0d1..cf3c469896 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -8,7 +8,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v3 + - uses: dessant/lock-threads@v5 with: github-token: ${{ github.token }} issue-inactive-days: 45 diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 8271664c05..e5f5e56206 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -56,7 +56,7 @@ jobs: run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive path: ${{ env.REPO_NAME }} @@ -76,7 +76,7 @@ jobs: - name: ThirdParty cache. id: cache-third-party - uses: actions/cache@v3.0.11 + uses: actions/cache@v4 with: path: ThirdParty key: ${{ runner.OS }}-third-party-${{ hashFiles(format('{0}/{1}', env.REPO_NAME, env.PREPARE_PATH)) }} @@ -84,7 +84,7 @@ jobs: - name: Libraries cache. id: cache-libs - uses: actions/cache@v3.0.11 + uses: actions/cache@v4 with: path: Libraries key: ${{ runner.OS }}-libs-${{ hashFiles(format('{0}/{1}', env.REPO_NAME, env.PREPARE_PATH)) }} @@ -134,7 +134,7 @@ jobs: mkdir artifact mv Telegram.app artifact/ mv Updater artifact/ - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. with: diff --git a/.github/workflows/mac_packaged.yml b/.github/workflows/mac_packaged.yml index d04b284dee..bd51bb74a5 100644 --- a/.github/workflows/mac_packaged.yml +++ b/.github/workflows/mac_packaged.yml @@ -60,7 +60,7 @@ jobs: run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive path: ${{ env.REPO_NAME }} @@ -99,7 +99,7 @@ jobs: - name: WebRTC cache. id: cache-webrtc - uses: actions/cache@v3.0.11 + uses: actions/cache@v4 with: path: ${{ env.LibrariesPath }}/tg_owt key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }} @@ -153,7 +153,7 @@ jobs: cd $REPO_NAME/build mkdir artifact mv Telegram.dmg artifact/ - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. with: diff --git a/.github/workflows/master_updater.yml b/.github/workflows/master_updater.yml index 284bb844e4..c59f62e29a 100644 --- a/.github/workflows/master_updater.yml +++ b/.github/workflows/master_updater.yml @@ -11,7 +11,7 @@ jobs: SKIP: "0" to_branch: "master" steps: - - uses: actions/checkout@v4.1.0 + - uses: actions/checkout@v4 with: fetch-depth: 0 if: env.SKIP == '0' diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml index badcf6cc9f..93ef6ad36f 100644 --- a/.github/workflows/snap.yml +++ b/.github/workflows/snap.yml @@ -47,7 +47,7 @@ jobs: steps: - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: fetch-depth: 0 submodules: recursive @@ -61,7 +61,7 @@ jobs: sudo snap run lxd waitready - name: Free up some disk space. - uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be - name: Telegram Desktop snap build. run: sg lxd -c 'snap run snapcraft --verbosity=debug' @@ -75,7 +75,7 @@ jobs: mkdir artifact mv $artifact_name artifact - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. with: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 219f95a82a..4148917e71 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v5 + - uses: actions/stale@v9 with: stale-issue-message: | Hey there! diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index f8332c36f1..ca0c7bf097 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -69,13 +69,13 @@ jobs: shell: bash run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - uses: ilammy/msvc-dev-cmd@v1.12.0 + - uses: ilammy/msvc-dev-cmd@v1.13.0 name: Native Tools Command Prompt. with: arch: ${{ matrix.arch }} - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive path: ${{ env.TBUILD }}\${{ env.REPO_NAME }} @@ -96,7 +96,7 @@ jobs: - name: Libraries cache. id: cache-libs - uses: actions/cache@v3.0.11 + uses: actions/cache@v4 with: path: ${{ env.TBUILD }}\Libraries key: ${{ runner.OS }}-${{ matrix.arch }}-libs-${{ env.CACHE_KEY }} @@ -183,7 +183,7 @@ jobs: mkdir artifact move %OUT%\Telegram.exe artifact/ move %OUT%\Updater.exe artifact/ - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 name: Upload artifact. if: (env.UPLOAD_ARTIFACT == 'true') || (github.ref == 'refs/heads/nightly') with: diff --git a/.gitignore b/.gitignore index 678ef32e3b..c2896799ea 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ Release/ *.xcodeproj ipch/ .vs/ +.vscode/ /Telegram/log.txt /Telegram/data diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 11f1fc9a3a..210c88d0da 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -757,6 +757,8 @@ PRIVATE history/view/controls/compose_controls_common.h history/view/controls/history_view_compose_controls.cpp history/view/controls/history_view_compose_controls.h + history/view/controls/history_view_compose_media_edit_manager.cpp + history/view/controls/history_view_compose_media_edit_manager.h history/view/controls/history_view_compose_search.cpp history/view/controls/history_view_compose_search.h history/view/controls/history_view_draft_options.cpp diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 0021dd5de1..e22af137ae 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="5.0.2.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index e7d2777da7..22f1064147 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 5,0,1,0 - PRODUCTVERSION 5,0,1,0 + FILEVERSION 5,0,2,0 + PRODUCTVERSION 5,0,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Radolyn Labs" VALUE "FileDescription", "AyuGram Desktop" - VALUE "FileVersion", "5.0.1.0" + VALUE "FileVersion", "5.0.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "AyuGram Desktop" - VALUE "ProductVersion", "5.0.1.0" + VALUE "ProductVersion", "5.0.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index d3e2f5f5b8..402c438bf9 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 5,0,1,0 - PRODUCTVERSION 5,0,1,0 + FILEVERSION 5,0,2,0 + PRODUCTVERSION 5,0,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Radolyn Labs" VALUE "FileDescription", "AyuGram Desktop Updater" - VALUE "FileVersion", "5.0.1.0" + VALUE "FileVersion", "5.0.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "AyuGram Desktop" - VALUE "ProductVersion", "5.0.1.0" + VALUE "ProductVersion", "5.0.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp index f91a73579b..7dcd768cb4 100644 --- a/Telegram/SourceFiles/api/api_editing.cpp +++ b/Telegram/SourceFiles/api/api_editing.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/confirm_box.h" #include "data/business/data_shortcut_messages.h" #include "data/components/scheduled_messages.h" +#include "data/data_file_origin.h" #include "data/data_histories.h" #include "data/data_session.h" #include "data/data_web_page.h" @@ -252,12 +253,100 @@ mtpRequestId EditTextMessage( Data::WebPageDraft webpage, SendOptions options, Fn done, - Fn fail) { + Fn fail, + std::optional spoilerMediaOverride) { + if (spoilerMediaOverride) { + const auto spoiler = *spoilerMediaOverride; + if (const auto media = item->media()) { + auto takeInputMedia = Fn()>(nullptr); + auto takeFileReference = Fn(nullptr); + if (const auto photo = media->photo()) { + using Flag = MTPDinputMediaPhoto::Flag; + const auto flags = Flag() + | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag()) + | (spoiler ? Flag::f_spoiler : Flag()); + takeInputMedia = [=] { + return MTP_inputMediaPhoto( + MTP_flags(flags), + photo->mtpInput(), + MTP_int(media->ttlSeconds())); + }; + takeFileReference = [=] { return photo->fileReference(); }; + } else if (const auto document = media->document()) { + using Flag = MTPDinputMediaDocument::Flag; + const auto flags = Flag() + | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag()) + | (spoiler ? Flag::f_spoiler : Flag()); + takeInputMedia = [=] { + return MTP_inputMediaDocument( + MTP_flags(flags), + document->mtpInput(), + MTP_int(media->ttlSeconds()), + MTPstring()); // query + }; + takeFileReference = [=] { return document->fileReference(); }; + } + + const auto usedFileReference = takeFileReference + ? takeFileReference() + : QByteArray(); + const auto origin = item->fullId(); + const auto api = &item->history()->session().api(); + const auto performRequest = [=]( + const auto &repeatRequest, + mtpRequestId originalRequestId) -> mtpRequestId { + const auto handleReference = [=]( + const QString &error, + mtpRequestId requestId) { + if (error.startsWith(u"FILE_REFERENCE_"_q)) { + api->refreshFileReference(origin, [=](const auto &) { + if (takeFileReference && + (takeFileReference() != usedFileReference)) { + repeatRequest( + repeatRequest, + originalRequestId + ? originalRequestId + : requestId); + } else { + fail(error, requestId); + } + }); + } else { + fail(error, requestId); + } + }; + const auto callback = [=]( + Fn applyUpdates, + mtpRequestId requestId) { + applyUpdates(); + done(originalRequestId ? originalRequestId : requestId); + }; + const auto requestId = EditMessage( + item, + caption, + webpage, + options, + callback, + handleReference, + takeInputMedia ? takeInputMedia() : std::nullopt); + return originalRequestId ? originalRequestId : requestId; + }; + return performRequest(performRequest, 0); + } + } + const auto callback = [=](Fn applyUpdates, mtpRequestId id) { applyUpdates(); done(id); }; - return EditMessage(item, caption, webpage, options, callback, fail); + return EditMessage( + item, + caption, + webpage, + options, + callback, + fail, + std::nullopt); } } // namespace Api diff --git a/Telegram/SourceFiles/api/api_editing.h b/Telegram/SourceFiles/api/api_editing.h index 86f106f45e..c8d0f6c509 100644 --- a/Telegram/SourceFiles/api/api_editing.h +++ b/Telegram/SourceFiles/api/api_editing.h @@ -55,6 +55,7 @@ mtpRequestId EditTextMessage( Data::WebPageDraft webpage, SendOptions options, Fn done, - Fn fail); + Fn fail, + std::optional spoilerMediaOverride); } // namespace Api diff --git a/Telegram/SourceFiles/api/api_text_entities.cpp b/Telegram/SourceFiles/api/api_text_entities.cpp index 6c434bc9ba..44a689ff4b 100644 --- a/Telegram/SourceFiles/api/api_text_entities.cpp +++ b/Telegram/SourceFiles/api/api_text_entities.cpp @@ -7,12 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "api/api_text_entities.h" -#include "main/main_session.h" +#include "data/data_document.h" +#include "data/data_session.h" +#include "data/data_user.h" #include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_stickers_set.h" -#include "data/data_session.h" -#include "data/data_document.h" -#include "data/data_user.h" +#include "main/main_session.h" namespace Api { namespace { @@ -62,67 +62,163 @@ using namespace TextUtilities; EntitiesInText EntitiesFromMTP( Main::Session *session, const QVector &entities) { + if (entities.isEmpty()) { + return {}; + } auto result = EntitiesInText(); - if (!entities.isEmpty()) { - result.reserve(entities.size()); - for (const auto &entity : entities) { - switch (entity.type()) { - case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, qs(d.vurl()) }); } break; - case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityPhone: break; // Skipping phones. - case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityMentionName: if (session) { - const auto &d = entity.c_messageEntityMentionName(); - const auto userId = UserId(d.vuser_id()); - const auto user = session->data().userLoaded(userId); - const auto data = MentionNameDataFromFields({ - .selfId = session->userId().bare, - .userId = userId.bare, - .accessHash = user ? user->accessHash() : 0, - }); - result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data }); - } break; - case mtpc_inputMessageEntityMentionName: if (session) { - const auto &d = entity.c_inputMessageEntityMentionName(); - const auto data = d.vuser_id().match([&]( - const MTPDinputUserSelf &) { - return MentionNameDataFromFields({ - .selfId = session->userId().bare, - .userId = session->userId().bare, - .accessHash = session->user()->accessHash(), - }); - }, [&](const MTPDinputUser &data) { - return MentionNameDataFromFields({ - .selfId = session->userId().bare, - .userId = UserId(data.vuser_id()).bare, - .accessHash = data.vaccess_hash().v, - }); - }, [&](const auto &) { - return QString(); - }); - if (!data.isEmpty()) { - result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data }); - } - } break; - case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back({ EntityType::BotCommand, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back({ EntityType::Bold, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back({ EntityType::Italic, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, qs(d.vlanguage()) }); } break; - case mtpc_messageEntityBlockquote: { auto &d = entity.c_messageEntityBlockquote(); result.push_back({ EntityType::Blockquote, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityBankCard: break; // Skipping cards. // #TODO entities - case mtpc_messageEntitySpoiler: { auto &d = entity.c_messageEntitySpoiler(); result.push_back({ EntityType::Spoiler, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityCustomEmoji: { - const auto &d = entity.c_messageEntityCustomEmoji(); - result.push_back({ EntityType::CustomEmoji, d.voffset().v, d.vlength().v, CustomEmojiEntityData(d) }); - } break; + result.reserve(entities.size()); + + for (const auto &entity : entities) { + entity.match([&](const MTPDmessageEntityUnknown &d) { + }, [&](const MTPDmessageEntityMention &d) { + result.push_back({ + EntityType::Mention, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityHashtag &d) { + result.push_back({ + EntityType::Hashtag, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityBotCommand &d) { + result.push_back({ + EntityType::BotCommand, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityUrl &d) { + result.push_back({ + EntityType::Url, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityEmail &d) { + result.push_back({ + EntityType::Email, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityBold &d) { + result.push_back({ + EntityType::Bold, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityItalic &d) { + result.push_back({ + EntityType::Italic, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityCode &d) { + result.push_back({ + EntityType::Code, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityPre &d) { + result.push_back({ + EntityType::Pre, + d.voffset().v, + d.vlength().v, + qs(d.vlanguage()), + }); + }, [&](const MTPDmessageEntityTextUrl &d) { + result.push_back({ + EntityType::CustomUrl, + d.voffset().v, + d.vlength().v, + qs(d.vurl()), + }); + }, [&](const MTPDmessageEntityMentionName &d) { + if (!session) { + return; } - } + const auto userId = UserId(d.vuser_id()); + const auto user = session->data().userLoaded(userId); + const auto data = MentionNameDataFromFields({ + .selfId = session->userId().bare, + .userId = userId.bare, + .accessHash = user ? user->accessHash() : 0, + }); + result.push_back({ + EntityType::MentionName, + d.voffset().v, + d.vlength().v, + data, + }); + }, [&](const MTPDinputMessageEntityMentionName &d) { + if (!session) { + return; + } + const auto data = d.vuser_id().match([&]( + const MTPDinputUserSelf &) { + return MentionNameDataFromFields({ + .selfId = session->userId().bare, + .userId = session->userId().bare, + .accessHash = session->user()->accessHash(), + }); + }, [&](const MTPDinputUser &data) { + return MentionNameDataFromFields({ + .selfId = session->userId().bare, + .userId = UserId(data.vuser_id()).bare, + .accessHash = data.vaccess_hash().v, + }); + }, [](const auto &) { + return QString(); + }); + if (!data.isEmpty()) { + result.push_back({ + EntityType::MentionName, + d.voffset().v, + d.vlength().v, + data, + }); + } + }, [&](const MTPDmessageEntityPhone &d) { + // Skipping phones. + }, [&](const MTPDmessageEntityCashtag &d) { + result.push_back({ + EntityType::Cashtag, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityUnderline &d) { + result.push_back({ + EntityType::Underline, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityStrike &d) { + result.push_back({ + EntityType::StrikeOut, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityBankCard &d) { + // Skipping cards. // #TODO entities + }, [&](const MTPDmessageEntitySpoiler &d) { + result.push_back({ + EntityType::Spoiler, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityCustomEmoji &d) { + result.push_back({ + EntityType::CustomEmoji, + d.voffset().v, + d.vlength().v, + CustomEmojiEntityData(d), + }); + }, [&](const MTPDmessageEntityBlockquote &d) { + result.push_back({ + EntityType::Blockquote, + d.voffset().v, + d.vlength().v, + }); + }); } return result; } @@ -134,7 +230,9 @@ MTPVector EntitiesToMTP( auto v = QVector(); v.reserve(entities.size()); for (const auto &entity : entities) { - if (entity.length() <= 0) continue; + if (entity.length() <= 0) { + continue; + } if (option == ConvertOption::SkipLocal && entity.type() != EntityType::Bold //&& entity.type() != EntityType::Semibold // Not in API. @@ -154,28 +252,76 @@ MTPVector EntitiesToMTP( auto offset = MTP_int(entity.offset()); auto length = MTP_int(entity.length()); switch (entity.type()) { - case EntityType::Url: v.push_back(MTP_messageEntityUrl(offset, length)); break; - case EntityType::CustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(entity.data()))); break; - case EntityType::Email: v.push_back(MTP_messageEntityEmail(offset, length)); break; - case EntityType::Hashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break; - case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break; - case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break; + case EntityType::Url: { + v.push_back(MTP_messageEntityUrl(offset, length)); + } break; + case EntityType::CustomUrl: { + v.push_back( + MTP_messageEntityTextUrl( + offset, + length, + MTP_string(entity.data()))); + } break; + case EntityType::Email: { + v.push_back(MTP_messageEntityEmail(offset, length)); + } break; + case EntityType::Hashtag: { + v.push_back(MTP_messageEntityHashtag(offset, length)); + } break; + case EntityType::Cashtag: { + v.push_back(MTP_messageEntityCashtag(offset, length)); + } break; + case EntityType::Mention: { + v.push_back(MTP_messageEntityMention(offset, length)); + } break; case EntityType::MentionName: { - if (const auto valid = MentionNameEntity(session, offset, length, entity.data())) { + const auto valid = MentionNameEntity( + session, + offset, + length, + entity.data()); + if (valid) { v.push_back(*valid); } } break; - case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break; - case EntityType::Bold: v.push_back(MTP_messageEntityBold(offset, length)); break; - case EntityType::Italic: v.push_back(MTP_messageEntityItalic(offset, length)); break; - case EntityType::Underline: v.push_back(MTP_messageEntityUnderline(offset, length)); break; - case EntityType::StrikeOut: v.push_back(MTP_messageEntityStrike(offset, length)); break; - case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities - case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break; - case EntityType::Blockquote: v.push_back(MTP_messageEntityBlockquote(offset, length)); break; - case EntityType::Spoiler: v.push_back(MTP_messageEntitySpoiler(offset, length)); break; + case EntityType::BotCommand: { + v.push_back(MTP_messageEntityBotCommand(offset, length)); + } break; + case EntityType::Bold: { + v.push_back(MTP_messageEntityBold(offset, length)); + } break; + case EntityType::Italic: { + v.push_back(MTP_messageEntityItalic(offset, length)); + } break; + case EntityType::Underline: { + v.push_back(MTP_messageEntityUnderline(offset, length)); + } break; + case EntityType::StrikeOut: { + v.push_back(MTP_messageEntityStrike(offset, length)); + } break; + case EntityType::Code: { + // #TODO entities. + v.push_back(MTP_messageEntityCode(offset, length)); + } break; + case EntityType::Pre: { + v.push_back( + MTP_messageEntityPre( + offset, + length, + MTP_string(entity.data()))); + } break; + case EntityType::Blockquote: { + v.push_back(MTP_messageEntityBlockquote(offset, length)); + } break; + case EntityType::Spoiler: { + v.push_back(MTP_messageEntitySpoiler(offset, length)); + } break; case EntityType::CustomEmoji: { - if (const auto valid = CustomEmojiEntity(offset, length, entity.data())) { + const auto valid = CustomEmojiEntity( + offset, + length, + entity.data()); + if (valid) { v.push_back(*valid); } } break; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 5ffdfb6f36..be4d4e7fad 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -206,6 +206,9 @@ void EditPhotoImage( Storage::UpdateImageDetails(file, previewWidth, sideLimit); done(std::move(list)); }; + if (!large) { + return; + } const auto fileImage = std::make_shared(*large); auto editor = base::make_unique_q( parent, diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 7aa48aaa38..dca4a065ba 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -2125,6 +2125,16 @@ bool PeerListContent::submitted() { return false; } +PeerListRowId PeerListContent::updateFromParentDrag(QPoint globalPosition) { + selectByMouse(globalPosition); + const auto row = getRow(_selected.index); + return row ? row->id() : 0; +} + +void PeerListContent::dragLeft() { + clearSelection(); +} + void PeerListContent::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 335001e02c..4349fd8c60 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -625,6 +625,9 @@ public: void searchQueryChanged(QString query); bool submitted(); + PeerListRowId updateFromParentDrag(QPoint globalPosition); + void dragLeft(); + // Interface for the controller. void appendRow(std::unique_ptr row); void appendSearchRow(std::unique_ptr row); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp index 09b5d273b8..b2e292a3cb 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp @@ -1428,14 +1428,15 @@ void SetupPeerColorSample( void AddPeerColorButton( not_null container, std::shared_ptr show, - not_null peer) { + not_null peer, + const style::SettingsButton &st) { auto label = peer->isSelf() ? tr::lng_settings_theme_name_color() : tr::lng_edit_channel_color(); const auto button = AddButtonWithIcon( container, rpl::duplicate(label), - st::settingsColorButton, + st, { &st::menuIconChangeColors }); const auto style = std::make_shared( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.h index 2272123a13..394a13aa88 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.h @@ -48,7 +48,8 @@ void EditPeerColorBox( void AddPeerColorButton( not_null container, std::shared_ptr show, - not_null peer); + not_null peer, + const style::SettingsButton &st); void CheckBoostLevel( std::shared_ptr show, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index f732c794ee..c79ef325d1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -1016,7 +1016,11 @@ void Controller::fillColorIndexButton() { Expects(_controls.buttonsLayout != nullptr); const auto show = _navigation->uiShow(); - AddPeerColorButton(_controls.buttonsLayout, show, _peer); + AddPeerColorButton( + _controls.buttonsLayout, + _navigation->uiShow(), + _peer, + st::managePeerColorsButton); } void Controller::fillSignaturesButton() { diff --git a/Telegram/SourceFiles/calls/calls_video_incoming.cpp b/Telegram/SourceFiles/calls/calls_video_incoming.cpp index 9ab16d73d4..e1a52ebe90 100644 --- a/Telegram/SourceFiles/calls/calls_video_incoming.cpp +++ b/Telegram/SourceFiles/calls/calls_video_incoming.cpp @@ -331,6 +331,7 @@ void Panel::Incoming::RendererGL::paint( shadow.texture.left(), shadow.texture.top(), }; + _contentBuffer->bind(); _contentBuffer->write(0, coords, sizeof(coords)); const auto bottomShadowArea = QRect( diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp index 63e5105fa9..6f6b076204 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp @@ -783,6 +783,7 @@ void Viewport::RendererGL::paintTile( : &*_frameProgram.yuv420; const auto uniformViewport = QSizeF(_viewport) * _factor; + program->bind(); program->setUniformValue("viewport", uniformViewport); program->setUniformValue( "frameBg", @@ -1078,6 +1079,7 @@ void Viewport::RendererGL::drawDownscalePass( ? &*_downscaleProgram.argb32 : &*_downscaleProgram.yuv420; + program->bind(); FillTexturedRectangle(f, program); } diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index fd8fa4017e..0631332f9f 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -785,6 +785,12 @@ emojiScroll: ScrollArea(defaultSolidScroll) { reactPanelScroll: ScrollArea(emojiScroll) { deltab: 7px; } +reactPanelScrollRounded: ScrollArea(emojiScroll) { + width: 8px; + deltax: 3px; + deltat: 14px; + deltab: 14px; +} choosePeerGroupIcon: icon {{ "info/edit/create_group", lightButtonFg }}; choosePeerChannelIcon: icon {{ "info/edit/create_channel", lightButtonFg }}; @@ -983,7 +989,7 @@ historyComposeField: InputField(defaultInputField) { placeholderFg: placeholderFg; placeholderFgActive: placeholderFgActive; placeholderFgError: placeholderFgActive; - placeholderMargins: margins(7px, 5px, 7px, 5px); + placeholderMargins: margins(2px, 0px, 2px, 0px); placeholderAlign: align(topleft); placeholderScale: 0.; placeholderFont: normalFont; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index adc7d889a6..e2b41060e2 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -1134,9 +1134,7 @@ void EmojiListWidget::fillRecentMenu( not_null menu, int section, int index) { - if (section != int(Section::Recent)) { - return; - } + const auto recent = (section == int(Section::Recent)); const auto addAction = Ui::Menu::CreateAddActionCallback(menu); const auto over = OverEmoji{ section, index }; const auto emoji = lookupOverEmoji(&over); @@ -1157,18 +1155,21 @@ void EmojiListWidget::fillRecentMenu( TextUtilities::SetClipboardText(data); }, &st::menuIconCopy); } - if (setId && _features.openStickerSets) { + if (recent && setId && _features.openStickerSets) { addAction( tr::lng_emoji_view_pack(tr::now), crl::guard(this, [=] { displaySet(setId); }), &st::menuIconShowAll); } - } else if (emoji) { + } else if (recent && emoji) { addAction(tr::lng_emoji_copy(tr::now), [=] { const auto text = emoji->text(); TextUtilities::SetClipboardText({ text, { text } }); }, &st::menuIconCopy); } + if (!recent) { + return; + } auto id = RecentEmojiId{ emoji }; if (custom) { id.data = RecentEmojiDocument{ @@ -1194,7 +1195,7 @@ void EmojiListWidget::fillRecentMenu( .confirmed = crl::guard(this, sure), .confirmText = tr::lng_emoji_reset_recent_button(tr::now), .labelStyle = &st().boxLabel, - })); + })); }; addAction({ .text = tr::lng_emoji_reset_recent(tr::now), diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 7de254badb..b63ae69677 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -509,7 +509,7 @@ TabbedSelector::TabbedSelector( _st.categoriesBg); }, lifetime()); - if (hasEmojiTab()) { + if (hasEmojiTab() && _mode == Mode::Full) { session().data().stickers().emojiSetInstalled( ) | rpl::start_with_next([=](uint64 setId) { _tabsSlider->setActiveSection(indexByType(SelectorTab::Emoji)); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index da9e7a96b4..5d00cb5404 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -127,7 +127,8 @@ Settings::Settings() : _sendSubmitWay(Ui::InputSubmitSettings::Enter) , _floatPlayerColumn(Window::Column::Second) , _floatPlayerCorner(RectPart::TopRight) -, _dialogsWidthRatio(DefaultDialogsWidthRatio()) { +, _dialogsWithChatWidthRatio(DefaultDialogsWidthRatio()) +, _dialogsNoChatWidthRatio(DefaultDialogsWidthRatio()) { } Settings::~Settings() = default; @@ -222,7 +223,8 @@ QByteArray Settings::serialize() const { + Serialize::stringSize(_callCaptureDeviceId.current()) + Serialize::bytearraySize(ivPosition) + Serialize::stringSize(noWarningExtensions) - + Serialize::stringSize(_customFontFamily); + + Serialize::stringSize(_customFontFamily) + + sizeof(qint32); auto result = QByteArray(); result.reserve(size); @@ -284,7 +286,7 @@ QByteArray Settings::serialize() const { << qint32(_floatPlayerCorner) << qint32(_thirdSectionInfoEnabled ? 1 : 0) << qint32(std::clamp( - qRound(_dialogsWidthRatio.current() * 1000000), + qRound(_dialogsWithChatWidthRatio.current() * 1000000), 0, 1000000)) << qint32(_thirdColumnWidth.current()) @@ -369,7 +371,11 @@ QByteArray Settings::serialize() const { << _callCaptureDeviceId.current() << ivPosition << noWarningExtensions - << _customFontFamily; + << _customFontFamily + << qint32(std::clamp( + qRound(_dialogsNoChatWidthRatio.current() * 1000000), + 0, + 1000000)); } Ensures(result.size() == size); @@ -440,7 +446,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 floatPlayerColumn = static_cast(Window::Column::Second); qint32 floatPlayerCorner = static_cast(RectPart::TopRight); qint32 thirdSectionInfoEnabled = 0; - float64 dialogsWidthRatio = _dialogsWidthRatio.current(); + float64 dialogsWithChatWidthRatio = _dialogsWithChatWidthRatio.current(); + float64 dialogsNoChatWidthRatio = _dialogsNoChatWidthRatio.current(); qint32 thirdColumnWidth = _thirdColumnWidth.current(); qint32 thirdSectionExtendedBy = _thirdSectionExtendedBy; qint32 notifyFromAll = _notifyFromAll ? 1 : 0; @@ -552,18 +559,18 @@ void Settings::addFromSerialized(const QByteArray &serialized) { >> mainMenuAccountsShown; } if (!stream.atEnd()) { - auto dialogsWidthRatioInt = qint32(); + auto dialogsWithChatWidthRatioInt = qint32(); stream >> tabbedSelectorSectionEnabled >> floatPlayerColumn >> floatPlayerCorner >> thirdSectionInfoEnabled - >> dialogsWidthRatioInt + >> dialogsWithChatWidthRatioInt >> thirdColumnWidth >> thirdSectionExtendedBy >> notifyFromAll; - dialogsWidthRatio = std::clamp( - dialogsWidthRatioInt / 1000000., + dialogsWithChatWidthRatio = std::clamp( + dialogsWithChatWidthRatioInt / 1000000., 0., 1.); } @@ -778,6 +785,15 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> customFontFamily; } + if (!stream.atEnd()) { + auto dialogsNoChatWidthRatioInt = qint32(); + stream + >> dialogsNoChatWidthRatioInt; + dialogsNoChatWidthRatio = std::clamp( + dialogsNoChatWidthRatioInt / 1000000., + 0., + 1.); + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -879,7 +895,10 @@ void Settings::addFromSerialized(const QByteArray &serialized) { case RectPart::BottomRight: _floatPlayerCorner = uncheckedCorner; break; } _thirdSectionInfoEnabled = thirdSectionInfoEnabled; - _dialogsWidthRatio = dialogsWidthRatio; + _dialogsWithChatWidthRatio = dialogsWithChatWidthRatio; + _dialogsNoChatWidthRatio = (dialogsWithChatWidthRatio > 0) + ? dialogsWithChatWidthRatio + : dialogsNoChatWidthRatio; _thirdColumnWidth = thirdColumnWidth; _thirdSectionExtendedBy = thirdSectionExtendedBy; if (_thirdSectionInfoEnabled) { @@ -1032,16 +1051,46 @@ void Settings::setTabbedReplacedWithInfo(bool enabled) { } } -void Settings::setDialogsWidthRatio(float64 ratio) { - _dialogsWidthRatio = ratio; +void Settings::updateDialogsWidthRatio(float64 ratio, bool nochat) { + const auto changeWithChat = !nochat + || (dialogsWithChatWidthRatio() > 0) + || _dialogsWidthSetToZeroWithoutChat; + const auto changedWithChat = changeWithChat + && (dialogsWithChatWidthRatio() != ratio); + + const auto changeNoChat = nochat + || (dialogsWithChatWidthRatio() != ratio); + const auto changedNoChat = changeNoChat + && (dialogsNoChatWidthRatio() != ratio); + + if (changedWithChat) { + _dialogsWidthSetToZeroWithoutChat = nochat && !(ratio > 0); + _dialogsWithChatWidthRatio = ratio; + } + if (changedNoChat) { + _dialogsNoChatWidthRatio = ratio; + } } -float64 Settings::dialogsWidthRatio() const { - return _dialogsWidthRatio.current(); +float64 Settings::dialogsWidthRatio(bool nochat) const { + const auto withchat = dialogsWithChatWidthRatio(); + return (!nochat || withchat > 0) ? withchat : dialogsNoChatWidthRatio(); } -rpl::producer Settings::dialogsWidthRatioChanges() const { - return _dialogsWidthRatio.changes(); +float64 Settings::dialogsWithChatWidthRatio() const { + return _dialogsWithChatWidthRatio.current(); +} + +rpl::producer Settings::dialogsWithChatWidthRatioChanges() const { + return _dialogsWithChatWidthRatio.changes(); +} + +float64 Settings::dialogsNoChatWidthRatio() const { + return _dialogsNoChatWidthRatio.current(); +} + +rpl::producer Settings::dialogsNoChatWidthRatioChanges() const { + return _dialogsNoChatWidthRatio.changes(); } void Settings::setThirdColumnWidth(int width) { @@ -1333,7 +1382,8 @@ void Settings::resetOnLastLogout() { _floatPlayerCorner = RectPart::TopRight; // per-window _thirdSectionInfoEnabled = true; // per-window _thirdSectionExtendedBy = -1; // per-window - _dialogsWidthRatio = DefaultDialogsWidthRatio(); // per-window + _dialogsWithChatWidthRatio = DefaultDialogsWidthRatio(); // per-window + _dialogsNoChatWidthRatio = DefaultDialogsWidthRatio(); // per-window _thirdColumnWidth = kDefaultThirdColumnWidth; // p-w _notifyFromAll = true; _tabbedReplacedWithInfo = false; // per-window diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index a8526f9e6c..442d345980 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -620,9 +620,15 @@ public: [[nodiscard]] RectPart floatPlayerCorner() const { return _floatPlayerCorner; } - void setDialogsWidthRatio(float64 ratio); - [[nodiscard]] float64 dialogsWidthRatio() const; - [[nodiscard]] rpl::producer dialogsWidthRatioChanges() const; + + void updateDialogsWidthRatio(float64 ratio, bool nochat); + [[nodiscard]] float64 dialogsWidthRatio(bool nochat) const; + + [[nodiscard]] float64 dialogsWithChatWidthRatio() const; + [[nodiscard]] rpl::producer dialogsWithChatWidthRatioChanges() const; + [[nodiscard]] float64 dialogsNoChatWidthRatio() const; + [[nodiscard]] rpl::producer dialogsNoChatWidthRatioChanges() const; + void setThirdColumnWidth(int width); [[nodiscard]] int thirdColumnWidth() const; [[nodiscard]] rpl::producer thirdColumnWidthChanges() const; @@ -975,7 +981,8 @@ private: bool _thirdSectionInfoEnabled = true; // per-window rpl::event_stream _thirdSectionInfoEnabledValue; // per-window int _thirdSectionExtendedBy = -1; // per-window - rpl::variable _dialogsWidthRatio; // per-window + rpl::variable _dialogsWithChatWidthRatio; // per-window + rpl::variable _dialogsNoChatWidthRatio; // per-window rpl::variable _thirdColumnWidth = kDefaultThirdColumnWidth; // p-w bool _notifyFromAll = true; rpl::variable _nativeWindowFrame = false; @@ -1021,6 +1028,7 @@ private: float64 _rememberedSongVolume = kDefaultVolume; bool _rememberedSoundNotifyFromTray = false; bool _rememberedFlashBounceNotifyFromTray = false; + bool _dialogsWidthSetToZeroWithoutChat = false; QByteArray _photoEditorBrush; diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index 3daf917841..4110a73a76 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -255,7 +255,10 @@ void Sandbox::setupScreenScale() { Sandbox::~Sandbox() = default; bool Sandbox::event(QEvent *e) { - if (e->type() == QEvent::Quit && !Quitting()) { + if (e->type() == QEvent::Quit) { + if (Quitting()) { + return QCoreApplication::event(e); + } Quit(QuitReason::QtQuitEvent); e->ignore(); return false; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index d58a09fa44..3169174b39 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs; constexpr auto AppNameOld = "AyuGram for Windows"_cs; constexpr auto AppName = "AyuGram Desktop"_cs; constexpr auto AppFile = "AyuGram"_cs; -constexpr auto AppVersion = 5000001; -constexpr auto AppVersionStr = "5.0.1"; +constexpr auto AppVersion = 5000002; +constexpr auto AppVersionStr = "5.0.2"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/SourceFiles/data/components/sponsored_messages.cpp b/Telegram/SourceFiles/data/components/sponsored_messages.cpp index 8f9a1b334a..b86c3c6216 100644 --- a/Telegram/SourceFiles/data/components/sponsored_messages.cpp +++ b/Telegram/SourceFiles/data/components/sponsored_messages.cpp @@ -449,6 +449,22 @@ auto SponsoredMessages::createReportCallback(const FullMsgId &fullId) return; } + const auto erase = [=] { + const auto it = _data.find(history); + if (it != end(_data)) { + auto &list = it->second.entries; + const auto proj = [&](const Entry &e) { + return e.itemFullId == fullId; + }; + list.erase(ranges::remove_if(list, proj), end(list)); + } + }; + + if (optionId == Result::Id("-1")) { + erase(); + return; + } + state->requestId = _session->api().request( MTPchannels_ReportSponsoredMessage( channel->inputChannel, @@ -475,14 +491,7 @@ auto SponsoredMessages::createReportCallback(const FullMsgId &fullId) }, [](const TLAdsHidden &data) -> Result { return { .result = Result::FinalStep::Hidden }; }, [&](const TLReported &data) -> Result { - const auto it = _data.find(history); - if (it != end(_data)) { - auto &list = it->second.entries; - const auto proj = [&](const Entry &e) { - return e.itemFullId == fullId; - }; - list.erase(ranges::remove_if(list, proj), end(list)); - } + erase(); if (optionId == Result::Id("1")) { // I don't like it. return { .result = Result::FinalStep::Silence }; } diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index 0cd53700cb..e12fa8fac6 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -372,6 +372,16 @@ bool RepliesList::buildFromData(not_null viewer) { return viewer->around; } else if (const auto item = lookupRoot()) { return computeInboxReadTillFull(); + } else if (_owningTopic) { + // Somehow we don't want always to jump to computed inboxReadTill + // (this was in the code before, but I don't remember why). + // Maybe in case we "View Thread" from a group we don't really + // want to jump to unread inside thread, cause it isn't defined. + // + // But in case of topics we definitely want to support jumping + // to the first unread, even if it is General topic without the + // actual root message or it is a broken topic without root. + return computeInboxReadTillFull(); } return viewer->around; }(); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index eb16132096..779c35ca90 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -700,7 +700,7 @@ not_null Session::processUser(const MTPUser &data) { result->setAccessHash(accessHash->v); } status = data.vstatus(); - { + if (!minimal) { const auto newUsername = uname; const auto newUsernames = data.vusernames() ? Api::Usernames::FromTL(*data.vusernames()) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 897942c8e6..de738708ee 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -82,6 +82,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/qt/qt_common_adapters.h" #include +#include #include #include @@ -101,6 +102,15 @@ base::options::toggle OptionForumHideChatsList({ .description = "Don't keep a narrow column of chats list.", }); +[[nodiscard]] bool RedirectTextToSearch(const QString &text) { + for (const auto &ch : text) { + if (ch.unicode() >= 32) { + return true; + } + } + return false; +} + } // namespace const char kOptionForumHideChatsList[] = "forum-hide-chats-list"; @@ -360,6 +370,14 @@ Widget::Widget( Ui::PostponeCall(this, [=] { listScrollUpdated(); }); }, lifetime()); + setAttribute(Qt::WA_InputMethodEnabled); + controller->widget()->imeCompositionStarts( + ) | rpl::filter([=] { + return redirectImeToSearch(); + }) | rpl::start_with_next([=] { + _search->setFocusFast(); + }, lifetime()); + _search->changes( ) | rpl::start_with_next([=] { applySearchUpdate(); @@ -449,7 +467,6 @@ Widget::Widget( loadMoreBlockedByDate(); }, lifetime()); - _search->setFocusPolicy(Qt::StrongFocus); _search->customUpDown(true); updateJumpToDateVisibility(true); @@ -569,14 +586,6 @@ void Widget::chosenRow(const ChosenRow &row) { history, ShowAtUnreadMsgId, Window::SectionShow::Way::ClearStack); - } else if (!controller()->adaptive().isOneColumn()) { - const auto item = history->chatListMessage(); - if (const auto topic = item ? item->topic() : nullptr) { - controller()->showThread( - topic, - ShowAtUnreadMsgId, - Window::SectionShow::Way::ClearStack); - } } return; } else if (history) { @@ -1018,7 +1027,9 @@ void Widget::setupShortcuts() { const auto history = forum->history(); controller()->searchInChat(history); return true; - } else if (!_openedFolder && _search->isVisible()) { + } else if (!_openedFolder + && !_childList + && _search->isVisible()) { _search->setFocus(); return true; } @@ -1175,13 +1186,16 @@ void Widget::updateSuggestions(anim::type animated) { } else { _suggestions = nullptr; _hidingSuggestions.clear(); + storiesExplicitCollapse(); + updateStoriesVisibility(); _scroll->show(); } } else if (suggest && !_suggestions) { if (animated == anim::type::normal) { startWidthAnimation(); - updateStoriesVisibility(); } + // Hides stories and passcode lock. + updateStoriesVisibility(); _suggestions = std::make_unique( this, controller(), @@ -1520,16 +1534,17 @@ void Widget::checkUpdateStatus() { updateControlsGeometry(); } -void Widget::setInnerFocus() { +void Widget::setInnerFocus(bool unfocusSearch) { if (_childList) { _childList->setInnerFocus(); } else if ((_openedFolder || _openedForum) && _subsectionTopBar->searchSetFocus()) { return; - } else if (!_search->getLastText().isEmpty() - || _searchInChat - || _searchHasFocus - || _searchSuggestionsLocked) { + } else if (!unfocusSearch + && (!_search->getLastText().isEmpty() + || _searchInChat + || _searchHasFocus + || _searchSuggestionsLocked)) { _search->setFocus(); } else { setFocus(); @@ -2552,15 +2567,18 @@ void Widget::dragMoveEvent(QDragMoveEvent *e) { } else { _chooseByDragTimer.callOnce(ChoosePeerByDragTimeout); } - if (_inner->updateFromParentDrag(mapToGlobal(e->pos()))) { - e->setDropAction(Qt::CopyAction); - } else { - e->setDropAction(Qt::IgnoreAction); - } + const auto global = mapToGlobal(e->pos()); + const auto thread = _suggestions + ? _suggestions->updateFromParentDrag(global) + : _inner->updateFromParentDrag(global); + e->setDropAction(thread ? Qt::CopyAction : Qt::IgnoreAction); } else { if (_dragForward) { updateDragInScroll(false); } + if (_suggestions) { + _suggestions->dragLeft(); + } _inner->dragLeft(); e->setDropAction(Qt::IgnoreAction); } @@ -2573,6 +2591,9 @@ void Widget::dragLeaveEvent(QDragLeaveEvent *e) { } else { _chooseByDragTimer.cancel(); } + if (_suggestions) { + _suggestions->dragLeft(); + } _inner->dragLeft(); e->accept(); } @@ -2591,8 +2612,11 @@ void Widget::updateDragInScroll(bool inScroll) { void Widget::dropEvent(QDropEvent *e) { _chooseByDragTimer.cancel(); if (_scroll->geometry().contains(e->pos())) { - const auto point = mapToGlobal(e->pos()); - if (const auto thread = _inner->updateFromParentDrag(point)) { + const auto globalPosition = mapToGlobal(e->pos()); + const auto thread = _suggestions + ? _suggestions->updateFromParentDrag(globalPosition) + : _inner->updateFromParentDrag(globalPosition); + if (thread) { e->setDropAction(Qt::CopyAction); e->accept(); controller()->content()->filesOrForwardDrop( @@ -2675,8 +2699,9 @@ void Widget::updateForceDisplayWide() { void Widget::showForum( not_null forum, const Window::SectionShow ¶ms) { + const auto nochat = !controller()->mainSectionShown(); if (!params.childColumn - || !Core::App().settings().dialogsWidthRatio() + || (Core::App().settings().dialogsWidthRatio(nochat) == 0.) || (_layout != Layout::Main) || OptionForumHideChatsList.value()) { changeOpenedForum(forum, params.animated); @@ -2892,7 +2917,7 @@ bool Widget::setSearchInChat( if (_searchInChat || !_search->getLastText().isEmpty()) { _search->setFocus(); } else { - setFocus(); + setInnerFocus(true); } updateForceDisplayWide(); return true; @@ -3273,6 +3298,10 @@ void Widget::keyPressEvent(QKeyEvent *e) { //} else { // e->ignore(); //} + } else if ((e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Tab) + && _searchHasFocus + && !_searchInChat) { + escape(); } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { submit(); } else if (_suggestions @@ -3313,16 +3342,47 @@ void Widget::keyPressEvent(QKeyEvent *e) { } } +void Widget::inputMethodEvent(QInputMethodEvent *e) { + const auto cursor = _search->rawTextEdit()->textCursor(); + bool isGettingInput = !e->commitString().isEmpty() + || e->preeditString() != cursor.block().layout()->preeditAreaText() + || e->replacementLength() > 0; + + if (!isGettingInput || _postponeProcessSearchFocusChange) { + Window::AbstractSectionWidget::inputMethodEvent(e); + return; + } + + // This delay in search focus processing allows us not to create + // _suggestions in case the event inserts some non-whitespace search + // query while still show _suggestions animated, if it is a space. + _postponeProcessSearchFocusChange = true; + _search->setFocusFast(); + QCoreApplication::sendEvent(_search->rawTextEdit(), e); + _postponeProcessSearchFocusChange = false; + processSearchFocusChange(); +} + +QVariant Widget::inputMethodQuery(Qt::InputMethodQuery query) const { + return _search->rawTextEdit()->inputMethodQuery(query); +} + +bool Widget::redirectToSearchPossible() const { + return !_openedFolder + && !_openedForum + && !_childList + && _search->isVisible() + && !_search->hasFocus() + && hasFocus(); +} + bool Widget::redirectKeyToSearch(QKeyEvent *e) const { - if (_openedFolder - || _openedForum - || !_search->isVisible() - || _search->hasFocus()) { + if (!redirectToSearchPossible()) { return false; } const auto character = !(e->modifiers() & ~Qt::ShiftModifier) && (e->key() != Qt::Key_Shift) - && !e->text().isEmpty(); + && RedirectTextToSearch(e->text()); if (character) { return true; } else if (e != QKeySequence::Paste) { @@ -3338,6 +3398,10 @@ bool Widget::redirectKeyToSearch(QKeyEvent *e) const { return data && data->hasText(); } +bool Widget::redirectImeToSearch() const { + return redirectToSearchPossible(); +} + void Widget::paintEvent(QPaintEvent *e) { if (controller()->contentOverlapped(this, e)) { return; @@ -3446,7 +3510,7 @@ bool Widget::cancelSearch() { if (!clearingQuery && _subsectionTopBar && _subsectionTopBar->toggleSearch(false, anim::type::normal)) { - setFocus(); + setInnerFocus(true); clearingInChat = true; } const auto clearSearchFocus = !_searchInChat @@ -3457,7 +3521,7 @@ bool Widget::cancelSearch() { } if (!_suggestions && clearSearchFocus) { // Don't create suggestions in unfocus case. - setFocus(); + setInnerFocus(true); } _lastSearchPeer = nullptr; _lastSearchId = _lastSearchMigratedId = 0; @@ -3465,7 +3529,7 @@ bool Widget::cancelSearch() { clearSearchField(); applySearchUpdate(); if (_suggestions && clearSearchFocus) { - setFocus(); + setInnerFocus(true); } return clearingQuery || clearingInChat || clearSearchFocus; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index 7b1beacb3b..903ddfba2d 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -99,7 +99,7 @@ public: not_null forum, const Window::SectionShow ¶ms); void searchInChat(Key chat); - void setInnerFocus(); + void setInnerFocus(bool unfocusSearch = false); [[nodiscard]] bool searchHasFocus() const; void jumpToTop(bool belowPinned = false); @@ -135,6 +135,8 @@ public: bool cancelSearch(); bool cancelSearchByMouseBack(); + QVariant inputMethodQuery(Qt::InputMethodQuery query) const override; + ~Widget(); protected: @@ -144,6 +146,7 @@ protected: void dropEvent(QDropEvent *e) override; void resizeEvent(QResizeEvent *e) override; void keyPressEvent(QKeyEvent *e) override; + void inputMethodEvent(QInputMethodEvent *e) override; void paintEvent(QPaintEvent *e) override; private: @@ -250,7 +253,9 @@ private: void updateSuggestions(anim::type animated); void processSearchFocusChange(); + [[nodiscard]] bool redirectToSearchPossible() const; [[nodiscard]] bool redirectKeyToSearch(QKeyEvent *e) const; + [[nodiscard]] bool redirectImeToSearch() const; MTP::Sender _api; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index 50362e657a..37be201fa9 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -1121,6 +1121,39 @@ void Suggestions::chooseRow() { } } +Data::Thread *Suggestions::updateFromParentDrag(QPoint globalPosition) { + return (_tab.current() == Tab::Chats) + ? updateFromChatsDrag(globalPosition) + : updateFromChannelsDrag(globalPosition); +} + +Data::Thread *Suggestions::updateFromChatsDrag(QPoint globalPosition) { + if (const auto top = _topPeers->updateFromParentDrag(globalPosition)) { + return _controller->session().data().history(PeerId(top)); + } + return fromListId(_recentUpdateFromParentDrag(globalPosition)); +} + +Data::Thread *Suggestions::updateFromChannelsDrag(QPoint globalPosition) { + if (const auto id = _myChannelsUpdateFromParentDrag(globalPosition)) { + return fromListId(id); + } + return fromListId(_recommendationsUpdateFromParentDrag(globalPosition)); +} + +Data::Thread *Suggestions::fromListId(uint64 peerListRowId) { + return peerListRowId + ? _controller->session().data().history(PeerId(peerListRowId)).get() + : nullptr; +} + +void Suggestions::dragLeft() { + _topPeers->dragLeft(); + _recentDragLeft(); + _myChannelsDragLeft(); + _recommendationsDragLeft(); +} + void Suggestions::show(anim::type animated, Fn finish) { RpWidget::show(); @@ -1304,6 +1337,12 @@ object_ptr> Suggestions::setupRecentPeers( } return JumpResult::NotApplied; }; + _recentUpdateFromParentDrag = [=](QPoint globalPosition) { + return raw->updateFromParentDrag(globalPosition); + }; + _recentDragLeft = [=] { + raw->dragLeft(); + }; raw->scrollToRequests( ) | rpl::start_with_next([this](Ui::ScrollToRequest request) { const auto add = _topPeersWrap->toggled() ? _topPeers->height() : 0; @@ -1438,6 +1477,12 @@ object_ptr> Suggestions::setupMyChannels() { } return JumpResult::NotApplied; }; + _myChannelsUpdateFromParentDrag = [=](QPoint globalPosition) { + return raw->updateFromParentDrag(globalPosition); + }; + _myChannelsDragLeft = [=] { + raw->dragLeft(); + }; raw->scrollToRequests( ) | rpl::start_with_next([this](Ui::ScrollToRequest request) { _channelsScroll->scrollToY(request.ymin, request.ymax); @@ -1499,6 +1544,12 @@ object_ptr> Suggestions::setupRecommendations() { } return JumpResult::NotApplied; }; + _recommendationsUpdateFromParentDrag = [=](QPoint globalPosition) { + return raw->updateFromParentDrag(globalPosition); + }; + _recommendationsDragLeft = [=] { + raw->dragLeft(); + }; raw->scrollToRequests( ) | rpl::start_with_next([this](Ui::ScrollToRequest request) { const auto add = _myChannels->toggled() ? _myChannels->height() : 0; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h index 49b74fa740..947bf2fa9d 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h @@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "ui/rp_widget.h" +namespace Data { +class Thread; +} // namespace Data + namespace Main { class Session; } // namespace Main @@ -46,6 +50,9 @@ public: void selectJump(Qt::Key direction, int pageSize = 0); void chooseRow(); + [[nodiscard]] Data::Thread *updateFromParentDrag(QPoint globalPosition); + void dragLeft(); + void show(anim::type animated, Fn finish); void hide(anim::type animated, Fn finish); [[nodiscard]] float64 shownOpacity() const; @@ -90,6 +97,11 @@ private: void selectJumpChats(Qt::Key direction, int pageSize); void selectJumpChannels(Qt::Key direction, int pageSize); + [[nodiscard]] Data::Thread *updateFromChatsDrag(QPoint globalPosition); + [[nodiscard]] Data::Thread *updateFromChannelsDrag( + QPoint globalPosition); + [[nodiscard]] Data::Thread *fromListId(uint64 peerListRowId); + [[nodiscard]] object_ptr> setupRecentPeers( RecentPeersList recentPeers); [[nodiscard]] object_ptr> setupEmptyRecent(); @@ -121,6 +133,8 @@ private: rpl::variable _recentCount; Fn _recentPeersChoose; Fn _recentSelectJump; + Fn _recentUpdateFromParentDrag; + Fn _recentDragLeft; const not_null*> _recentPeers; const not_null*> _emptyRecent; @@ -130,11 +144,15 @@ private: rpl::variable _myChannelsCount; Fn _myChannelsChoose; Fn _myChannelsSelectJump; + Fn _myChannelsUpdateFromParentDrag; + Fn _myChannelsDragLeft; const not_null*> _myChannels; rpl::variable _recommendationsCount; Fn _recommendationsChoose; Fn _recommendationsSelectJump; + Fn _recommendationsUpdateFromParentDrag; + Fn _recommendationsDragLeft; const not_null*> _recommendations; const not_null*> _emptyChannels; diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp index f793da346c..40e758ad2a 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -248,7 +248,7 @@ void TopPeersStrip::stripWheelEvent(QWheelEvent *e) { void TopPeersStrip::stripLeaveEvent(QEvent *e) { if (!_selectionByKeyboard) { - setSelected(-1); + clearSelection(); } if (!_dragging) { _lastMousePosition = std::nullopt; @@ -299,9 +299,7 @@ void TopPeersStrip::stripMouseMoveEvent(QMouseEvent *e) { && (_lastMousePosition == e->globalPos())) { return; } - _lastMousePosition = e->globalPos(); - _selectionByKeyboard = false; - updateSelected(); + selectByMouse(e->globalPos()); if (!_dragging && _mouseDownPosition) { if ((*_lastMousePosition - *_mouseDownPosition).manhattanLength() @@ -315,6 +313,12 @@ void TopPeersStrip::stripMouseMoveEvent(QMouseEvent *e) { checkDragging(); } +void TopPeersStrip::selectByMouse(QPoint globalPosition) { + _lastMousePosition = globalPosition; + _selectionByKeyboard = false; + updateSelected(); +} + void TopPeersStrip::checkDragging() { if (_dragging && !_expandAnimation.animating()) { const auto sign = (style::RightToLeft() ? -1 : 1); @@ -551,6 +555,20 @@ bool TopPeersStrip::chooseRow() { return false; } +uint64 TopPeersStrip::updateFromParentDrag(QPoint globalPosition) { + if (!rect().contains(mapFromGlobal(globalPosition))) { + dragLeft(); + return 0; + } + selectByMouse(globalPosition); + + return (_selected >= 0) ? _entries[_selected].id : 0; +} + +void TopPeersStrip::dragLeft() { + clearSelection(); +} + void TopPeersStrip::apply(const TopPeersList &list) { if (_hiddenLocally) { return; @@ -908,6 +926,10 @@ void TopPeersStrip::setSelected(int selected) { } } +void TopPeersStrip::clearSelection() { + setSelected(-1); +} + void TopPeersStrip::scrollToSelected() { if (_selected < 0) { return; diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h index 9894235c69..23db15d1f2 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -62,6 +62,9 @@ public: void deselectByKeyboard(); bool chooseRow(); + uint64 updateFromParentDrag(QPoint globalPosition); + void dragLeft(); + [[nodiscard]] auto verticalScrollEvents() const -> rpl::producer>; @@ -92,6 +95,8 @@ private: void subscribeUserpic(Entry &entry); void unsubscribeUserpics(bool all = false); void paintUserpic(Painter &p, int x, int y, int index, bool selected); + void clearSelection(); + void selectByMouse(QPoint globalPosition); [[nodiscard]] QRect outer() const; [[nodiscard]] QRect innerRounded() const; diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_frame_generator.cpp b/Telegram/SourceFiles/ffmpeg/ffmpeg_frame_generator.cpp index 116d998a2a..de478b784b 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_frame_generator.cpp +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_frame_generator.cpp @@ -291,7 +291,11 @@ void FrameGenerator::Impl::jumpToStart() { void FrameGenerator::Impl::resolveNextFrameTiming() { const auto base = _format->streams[_streamId]->time_base; +#if DA_FFMPEG_HAVE_DURATION + const auto duration = _next.frame->duration; +#else const auto duration = _next.frame->pkt_duration; +#endif const auto framePts = _next.frame->pts; auto framePosition = (framePts * 1000LL * base.num) / base.den; _currentFrameDelay = _nextFrameDelay; diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp index 2a42d0034c..0f7083df36 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp @@ -230,7 +230,11 @@ enum AVPixelFormat GetFormatImplementation( IOPointer MakeIOPointer( void *opaque, int(*read)(void *opaque, uint8_t *buffer, int bufferSize), +#if DA_FFMPEG_CONST_WRITE_CALLBACK + int(*write)(void *opaque, const uint8_t *buffer, int bufferSize), +#else int(*write)(void *opaque, uint8_t *buffer, int bufferSize), +#endif int64_t(*seek)(void *opaque, int64_t offset, int whence)) { auto buffer = reinterpret_cast(av_malloc(kAvioBlockSize)); if (!buffer) { @@ -263,7 +267,11 @@ void IODeleter::operator()(AVIOContext *value) { FormatPointer MakeFormatPointer( void *opaque, int(*read)(void *opaque, uint8_t *buffer, int bufferSize), +#if DA_FFMPEG_CONST_WRITE_CALLBACK + int(*write)(void *opaque, const uint8_t *buffer, int bufferSize), +#else int(*write)(void *opaque, uint8_t *buffer, int bufferSize), +#endif int64_t(*seek)(void *opaque, int64_t offset, int whence)) { auto io = MakeIOPointer(opaque, read, write, seek); if (!io) { diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h index bc9c8764b9..d96daa9c79 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h @@ -22,8 +22,14 @@ extern "C" { #include } // extern "C" -#define DA_FFMPEG_NEW_CHANNEL_LAYOUT (LIBAVUTIL_VERSION_MAJOR > 57 \ - || (LIBAVUTIL_VERSION_MAJOR == 57 && LIBAVUTIL_VERSION_MINOR >= 28)) +#define DA_FFMPEG_NEW_CHANNEL_LAYOUT (LIBAVUTIL_VERSION_INT >= \ + AV_VERSION_INT(57, 28, 100)) + +#define DA_FFMPEG_CONST_WRITE_CALLBACK (LIBAVFORMAT_VERSION_INT >= \ + AV_VERSION_INT(61, 01, 100)) + +#define DA_FFMPEG_HAVE_DURATION (LIBAVUTIL_VERSION_INT >= \ + AV_VERSION_INT(58, 02, 100)) class QImage; @@ -112,7 +118,11 @@ using IOPointer = std::unique_ptr; [[nodiscard]] IOPointer MakeIOPointer( void *opaque, int(*read)(void *opaque, uint8_t *buffer, int bufferSize), +#if DA_FFMPEG_CONST_WRITE_CALLBACK + int(*write)(void *opaque, const uint8_t *buffer, int bufferSize), +#else int(*write)(void *opaque, uint8_t *buffer, int bufferSize), +#endif int64_t(*seek)(void *opaque, int64_t offset, int whence)); struct FormatDeleter { @@ -122,7 +132,11 @@ using FormatPointer = std::unique_ptr; [[nodiscard]] FormatPointer MakeFormatPointer( void *opaque, int(*read)(void *opaque, uint8_t *buffer, int bufferSize), +#if DA_FFMPEG_CONST_WRITE_CALLBACK + int(*write)(void *opaque, const uint8_t *buffer, int bufferSize), +#else int(*write)(void *opaque, uint8_t *buffer, int bufferSize), +#endif int64_t(*seek)(void *opaque, int64_t offset, int whence)); struct CodecDeleter { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 144a4483a5..9a62d4a349 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -3647,7 +3647,8 @@ void HistoryItem::createComponentsHelper(HistoryItemCommonFields &&fields) { const auto topicPost = config.reply.externalPeerId ? (replyTo.topicRootId && (replyTo.topicRootId != Data::ForumTopic::kGeneralId)) - : (LookupReplyIsTopicPost(to) + : (topic + || LookupReplyIsTopicPost(to) || (to && to->Has()) || (forum && forum->creating(config.reply.topMessageId))); config.reply.topicPost = topicPost ? 1 : 0; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 441a0f8c00..a8ca276ff4 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2681,6 +2681,7 @@ void HistoryWidget::setEditMsgId(MsgId msgId) { unregisterDraftSources(); _editMsgId = msgId; if (!msgId) { + _mediaEditSpoiler.setSpoilerOverride(std::nullopt); _canReplaceMedia = false; if (_preview) { _preview->setDisabled(false); @@ -4071,7 +4072,8 @@ void HistoryWidget::saveEditMsg() { webPageDraft, options, done, - fail); + fail, + _mediaEditSpoiler.spoilerOverride()); } void HistoryWidget::hideChildWidgets() { @@ -6602,7 +6604,14 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { return; } const auto isReadyToForward = readyToForward(); - if (_inPhotoEdit && _photoEditMedia) { + if (_editMsgId + && (_inDetails || _inPhotoEdit) + && (e->button() == Qt::RightButton)) { + _mediaEditSpoiler.showMenu( + _list, + session().data().message(_history->peer, _editMsgId), + [=](bool) { mouseMoveEvent(nullptr); }); + } else if (_inPhotoEdit && _photoEditMedia) { EditCaptionBox::StartPhotoEdit( controller(), _photoEditMedia, @@ -8209,12 +8218,14 @@ void HistoryWidget::updateReplyEditTexts(bool force) { } } if (_replyEditMsg) { - const auto media = _replyEditMsg->media(); - _canReplaceMedia = media && media->allowsEditMedia(); + const auto editMedia = _editMsgId + ? _replyEditMsg->media() + : nullptr; + _canReplaceMedia = editMedia && editMedia->allowsEditMedia(); _photoEditMedia = (_canReplaceMedia - && media->photo() - && !media->photo()->isNull()) - ? media->photo()->createMediaView() + && editMedia->photo() + && !editMedia->photo()->isNull()) + ? editMedia->photo()->createMediaView() : nullptr; if (_photoEditMedia) { _photoEditMedia->wanted( @@ -8303,8 +8314,14 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { ? drawMsgText->media() : nullptr; const auto hasPreview = media && media->hasReplyPreview(); - const auto preview = hasPreview ? media->replyPreview() : nullptr; - const auto spoilered = preview && media->hasSpoiler(); + const auto preview = _mediaEditSpoiler.spoilerOverride() + ? _mediaEditSpoiler.mediaPreview(drawMsgText) + : hasPreview + ? media->replyPreview() + : nullptr; + const auto spoilered = _mediaEditSpoiler.spoilerOverride() + ? (*_mediaEditSpoiler.spoilerOverride()) + : (preview && media->hasSpoiler()); if (!spoilered) { _replySpoiler = nullptr; } else if (!_replySpoiler) { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 76d3e6940b..47bdaecfa0 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "history/view/controls/history_view_compose_media_edit_manager.h" #include "history/view/history_view_corner_buttons.h" #include "history/history_drag_area.h" #include "history/history_view_highlight_manager.h" @@ -103,6 +104,7 @@ class ForwardPanel; class TTLButton; class WebpageProcessor; class CharactersLimitLabel; +class PhotoEditSpoilerManager; } // namespace HistoryView::Controls class BotKeyboard; @@ -661,6 +663,7 @@ private: MsgId _editMsgId = 0; std::shared_ptr _photoEditMedia; bool _canReplaceMedia = false; + HistoryView::MediaEditSpoilerManager _mediaEditSpoiler; HistoryItem *_replyEditMsg = nullptr; Ui::Text::String _replyEditMsgText; diff --git a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h index e707523ecc..01237072b6 100644 --- a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h +++ b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h @@ -21,6 +21,7 @@ struct MessageToEdit { FullMsgId fullId; Api::SendOptions options; TextWithTags textWithTags; + std::optional spoilerMediaOverride; }; struct VoiceToSend { QByteArray bytes; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 859a4d83a6..5e988e51e6 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -50,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/view/controls/history_view_characters_limit.h" +#include "history/view/controls/history_view_compose_media_edit_manager.h" #include "history/view/controls/history_view_forward_panel.h" #include "history/view/controls/history_view_draft_options.h" #include "history/view/controls/history_view_voice_record_bar.h" @@ -72,6 +73,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "ui/widgets/fields/input_field.h" #include "ui/widgets/dropdown_menu.h" +#include "ui/widgets/popup_menu.h" #include "ui/text/format_values.h" #include "ui/controls/emoji_button.h" #include "ui/controls/send_button.h" @@ -84,6 +86,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "styles/style_chat.h" #include "styles/style_chat_helpers.h" +#include "styles/style_menu_icons.h" namespace HistoryView { namespace { @@ -210,6 +213,8 @@ private: bool _repaintScheduled : 1 = false; bool _inClickable : 1 = false; + HistoryView::MediaEditSpoilerManager _mediaEditSpoiler; + const not_null _data; const not_null _cancel; @@ -398,7 +403,12 @@ void FieldHeader::init() { _editOptionsRequests.fire({}); } } else if (!isLeftButton) { - if (const auto reply = replyingToMessage()) { + if (inPreviewRect && isEditingMessage()) { + _mediaEditSpoiler.showMenu( + this, + _data->message(_editMsgId.current()), + [=](bool) { update(); }); + } else if (const auto reply = replyingToMessage()) { _jumpToItemRequests.fire_copy(reply); } } @@ -572,10 +582,14 @@ void FieldHeader::paintEditOrReplyToMessage(Painter &p) { const auto media = _shownMessage->media(); _shownMessageHasPreview = media && media->hasReplyPreview(); - const auto preview = _shownMessageHasPreview + const auto preview = _mediaEditSpoiler.spoilerOverride() + ? _mediaEditSpoiler.mediaPreview(_shownMessage) + : _shownMessageHasPreview ? media->replyPreview() : nullptr; - const auto spoilered = preview && media->hasSpoiler(); + const auto spoilered = _mediaEditSpoiler.spoilerOverride() + ? (*_mediaEditSpoiler.spoilerOverride()) + : (preview && media->hasSpoiler()); if (!spoilered) { _shownPreviewSpoiler = nullptr; } else if (!_shownPreviewSpoiler) { @@ -720,6 +734,7 @@ void FieldHeader::editMessage(FullMsgId id, bool photoEditAllowed) { _photoEditAllowed = photoEditAllowed; _editMsgId = id; if (!photoEditAllowed) { + _mediaEditSpoiler.setSpoilerOverride(std::nullopt); _inPhotoEdit = false; _inPhotoEditOver.stop(); } @@ -767,6 +782,7 @@ MessageToEdit FieldHeader::queryToEdit() { .scheduled = item->isScheduled() ? item->date() : 0, .shortcutId = item->shortcutId(), }, + .spoilerMediaOverride = _mediaEditSpoiler.spoilerOverride(), }; } @@ -3440,4 +3456,49 @@ rpl::producer SendDisabledBySlowmode(not_null peer) { _1 && _2); } +void ShowPhotoEditSpoilerMenu( + not_null parent, + not_null item, + const std::optional &override, + Fn callback) { + const auto media = item->media(); + const auto hasPreview = media && media->hasReplyPreview(); + const auto preview = hasPreview ? media->replyPreview() : nullptr; + if (!preview) { + return; + } + const auto spoilered = override + ? (*override) + : (preview && media->hasSpoiler()); + const auto menu = Ui::CreateChild( + parent, + st::popupMenuWithIcons); + menu->addAction( + spoilered + ? tr::lng_context_disable_spoiler(tr::now) + : tr::lng_context_spoiler_effect(tr::now), + [=] { callback(!spoilered); }, + spoilered ? &st::menuIconSpoilerOff : &st::menuIconSpoiler); + menu->popup(QCursor::pos()); +} + +Image *MediaPreviewWithOverriddenSpoiler( + not_null item, + bool spoiler) { + if (const auto media = item->media()) { + if (const auto photo = media->photo()) { + return photo->getReplyPreview( + item->fullId(), + item->history()->peer, + spoiler); + } else if (const auto document = media->document()) { + return document->getReplyPreview( + item->fullId(), + item->history()->peer, + spoiler); + } + } + return nullptr; +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index b24e0213ab..f6c915968c 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class History; class DocumentData; class FieldAutocomplete; +class Image; namespace style { struct ComposeControls; @@ -451,4 +452,14 @@ private: [[nodiscard]] rpl::producer SendDisabledBySlowmode( not_null peer); +void ShowPhotoEditSpoilerMenu( + not_null parent, + not_null item, + const std::optional &override, + Fn callback); + +[[nodiscard]] Image *MediaPreviewWithOverriddenSpoiler( + not_null item, + bool spoiler); + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.cpp new file mode 100644 index 0000000000..940ff7ff24 --- /dev/null +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.cpp @@ -0,0 +1,83 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "history/view/controls/history_view_compose_media_edit_manager.h" + +#include "data/data_document.h" +#include "data/data_file_origin.h" +#include "data/data_photo.h" +#include "history/history.h" +#include "history/history_item.h" +#include "lang/lang_keys.h" +#include "ui/widgets/popup_menu.h" +#include "styles/style_menu_icons.h" + +namespace HistoryView { + +MediaEditSpoilerManager::MediaEditSpoilerManager() = default; + +void MediaEditSpoilerManager::showMenu( + not_null parent, + not_null item, + Fn callback) { + const auto media = item->media(); + const auto hasPreview = media && media->hasReplyPreview(); + const auto preview = hasPreview ? media->replyPreview() : nullptr; + if (!preview) { + return; + } + const auto spoilered = _spoilerOverride + ? (*_spoilerOverride) + : (preview && media->hasSpoiler()); + const auto menu = Ui::CreateChild( + parent, + st::popupMenuWithIcons); + menu->addAction( + spoilered + ? tr::lng_context_disable_spoiler(tr::now) + : tr::lng_context_spoiler_effect(tr::now), + [=] { + _spoilerOverride = (!spoilered); + if (callback) { + callback(!spoilered); + } + }, + spoilered ? &st::menuIconSpoilerOff : &st::menuIconSpoiler); + menu->popup(QCursor::pos()); +} + +[[nodiscard]] Image *MediaEditSpoilerManager::mediaPreview( + not_null item) { + if (!_spoilerOverride) { + return nullptr; + } + if (const auto media = item->media()) { + if (const auto photo = media->photo()) { + return photo->getReplyPreview( + item->fullId(), + item->history()->peer, + *_spoilerOverride); + } else if (const auto document = media->document()) { + return document->getReplyPreview( + item->fullId(), + item->history()->peer, + *_spoilerOverride); + } + } + return nullptr; +} + +void MediaEditSpoilerManager::setSpoilerOverride( + std::optional spoilerOverride) { + _spoilerOverride = spoilerOverride; +} + +std::optional MediaEditSpoilerManager::spoilerOverride() const { + return _spoilerOverride; +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.h new file mode 100644 index 0000000000..8bc34b7c4e --- /dev/null +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.h @@ -0,0 +1,39 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Ui { +class RpWidget; +} // namespace Ui + +class Image; +class HistoryItem; + +namespace HistoryView { + +class MediaEditSpoilerManager final { +public: + MediaEditSpoilerManager(); + + void showMenu( + not_null parent, + not_null item, + Fn callback); + + [[nodiscard]] Image *mediaPreview(not_null item); + + void setSpoilerOverride(std::optional spoilerOverride); + + std::optional spoilerOverride() const; + +private: + std::optional _spoilerOverride; + +}; + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index b17f1ba4f5..8afeff663c 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -3860,7 +3860,7 @@ void Message::fromNameUpdated(int width) const { const auto nameText = [&]() -> const Ui::Text::String * { if (from) { return &_fromName; - } else if (const auto info= item->originalHiddenSenderInfo()) { + } else if (const auto info = item->originalHiddenSenderInfo()) { return &info->nameText(); } else { Unexpected("Corrupted forwarded information in message."); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 90241557b7..f0b8d99906 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -744,7 +744,8 @@ void RepliesWidget::setupComposeControls() { _composeControls->editRequests( ) | rpl::start_with_next([=](auto data) { if (const auto item = session().data().message(data.fullId)) { - edit(item, data.options, saveEditMsgRequestId); + const auto spoiler = data.spoilerMediaOverride; + edit(item, data.options, saveEditMsgRequestId, spoiler); } }, lifetime()); @@ -1215,7 +1216,8 @@ void RepliesWidget::send(Api::SendOptions options) { void RepliesWidget::edit( not_null item, Api::SendOptions options, - mtpRequestId *const saveEditMsgRequestId) { + mtpRequestId *const saveEditMsgRequestId, + std::optional spoilerMediaOverride) { if (*saveEditMsgRequestId) { return; } @@ -1283,7 +1285,8 @@ void RepliesWidget::edit( webpage, options, crl::guard(this, done), - crl::guard(this, fail)); + crl::guard(this, fail), + spoilerMediaOverride); _composeControls->hidePanelsAnimated(); doSetInnerFocus(); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 3da1c44f0a..33b7859b48 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -246,7 +246,8 @@ private: void edit( not_null item, Api::SendOptions options, - mtpRequestId *const saveEditMsgRequestId); + mtpRequestId *const saveEditMsgRequestId, + std::optional spoilerMediaOverride); void chooseAttach(std::optional overrideSendImagesAsPhotos); [[nodiscard]] SendMenu::Type sendMenuType() const; [[nodiscard]] FullReplyTo replyTo() const; diff --git a/Telegram/SourceFiles/history/view/history_view_reply.cpp b/Telegram/SourceFiles/history/view/history_view_reply.cpp index 51ec4f320b..4e7f6c48b8 100644 --- a/Telegram/SourceFiles/history/view/history_view_reply.cpp +++ b/Telegram/SourceFiles/history/view/history_view_reply.cpp @@ -587,7 +587,7 @@ QSize Reply::countMultilineOptimalSize( textGeometry(max, previewSkip, &elided)); _minHeightExpandable = elided ? 1 : 0; return { - result.width, + result.width + st::historyReplyPadding.right(), std::max(result.height, st::normalFont->height), }; } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 632132a132..3a02b72fbc 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -326,7 +326,8 @@ void ScheduledWidget::setupComposeControls() { ) | rpl::start_with_next([=](auto data) { if (const auto item = session().data().message(data.fullId)) { if (item->isScheduled()) { - edit(item, data.options, saveEditMsgRequestId); + const auto spoiler = data.spoilerMediaOverride; + edit(item, data.options, saveEditMsgRequestId, spoiler); } } }, lifetime()); @@ -734,7 +735,8 @@ void ScheduledWidget::sendVoice( void ScheduledWidget::edit( not_null item, Api::SendOptions options, - mtpRequestId *const saveEditMsgRequestId) { + mtpRequestId *const saveEditMsgRequestId, + std::optional spoilerMediaOverride) { if (*saveEditMsgRequestId) { return; } @@ -802,7 +804,8 @@ void ScheduledWidget::edit( webpage, options, crl::guard(this, done), - crl::guard(this, fail)); + crl::guard(this, fail), + spoilerMediaOverride); _composeControls->hidePanelsAnimated(); _composeControls->focus(); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index 43702688fe..6685c113a4 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -213,7 +213,8 @@ private: void edit( not_null item, Api::SendOptions options, - mtpRequestId *const saveEditMsgRequestId); + mtpRequestId *const saveEditMsgRequestId, + std::optional spoilerMediaOverride); void highlightSingleNewMessage(const Data::MessagesSlice &slice); void chooseAttach(); [[nodiscard]] SendMenu::Type sendMenuType() const; diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index bb5ebf46e4..b00df78b4a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -447,6 +447,7 @@ QSize Document::countOptimalSize() { const auto session = &history->session(); const auto transcribes = &session->api().transcribes(); if (_parent->data()->media()->ttlSeconds() + || _realParent->isScheduled() || (!session->premium() && !transcribes->freeFor(_realParent) && !transcribes->trialsSupport())) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 26c7f48fe3..a89a760838 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -2076,6 +2076,7 @@ void Gif::ensureTranscribeButton() const { if (_data->isVideoMessage() && !_parent->data()->media()->ttlSeconds() + && !_parent->data()->isScheduled() && (_data->session().premium() || _data->session().api().transcribes().trialsSupport())) { if (!_transcribe) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp index 9a40c97078..905536c903 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp @@ -445,7 +445,7 @@ TextState UnwrappedMedia::textState(QPoint point, StateRequest request) const { } if (recth) { int rectx = _additionalOnTop - ? (rightAligned ? (inner.width() + st::msgReplyPadding.left() - rectw) : 0) + ? (rightAligned ? (inner.x() + inner.width() - rectw) : 0) : (rightAligned ? 0 : (inner.width() + st::msgReplyPadding.left())); int recty = surrounding.height - recth; if (rtl()) rectx = width() - rectx - rectw; diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp index a07545d88e..ab401b9fe5 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -921,7 +921,9 @@ void Selector::createList() { &_show->session(), _strip ? _reactions.recent : std::vector(), _strip.get()); - _scroll = Ui::CreateChild(this, st::reactPanelScroll); + _scroll = Ui::CreateChild(this, _reactions.customAllowed + ? st::reactPanelScroll + : st::reactPanelScrollRounded); _scroll->hide(); const auto st = lifetime().make_state(_st); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 1b1924c983..d076cc9494 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -665,6 +665,10 @@ manageGroupTopicsButton: SettingsCountButton(manageGroupTopButtonWithText) { } } } +managePeerColorsButton: SettingsButton(infoProfileButton) { + padding: margins(60px, 10px, 48px, 8px); +} + manageGroupNoIconButtonInner: SettingsButton(infoProfileButton) { padding: margins(25px, 11px, 24px, 8px); } diff --git a/Telegram/SourceFiles/main/main_session_settings.cpp b/Telegram/SourceFiles/main/main_session_settings.cpp index 7806dea9f1..6349f6b6a3 100644 --- a/Telegram/SourceFiles/main/main_session_settings.cpp +++ b/Telegram/SourceFiles/main/main_session_settings.cpp @@ -128,7 +128,7 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { base::flat_set groupEmojiSectionHidden; qint32 appThirdSectionInfoEnabled = 0; qint32 legacySmallDialogsList = 0; - float64 appDialogsWidthRatio = app.dialogsWidthRatio(); + float64 appDialogsWidthRatio = app.dialogsWidthRatio(false); int appThirdColumnWidth = app.thirdColumnWidth(); int appThirdSectionExtendedBy = app.thirdSectionExtendedBy(); qint32 appSendFilesWay = app.sendFilesWay().serialize(); @@ -535,7 +535,7 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { case RectPart::BottomRight: app.setFloatPlayerCorner(uncheckedCorner); break; } app.setThirdSectionInfoEnabled(appThirdSectionInfoEnabled); - app.setDialogsWidthRatio(appDialogsWidthRatio); + app.updateDialogsWidthRatio(appDialogsWidthRatio, false); app.setThirdColumnWidth(appThirdColumnWidth); app.setThirdSectionExtendedBy(appThirdSectionExtendedBy); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 1a7a16c015..31ce8e064b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -303,8 +303,16 @@ MainWidget::MainWidget( }); }, lifetime()); + const auto filter = [=](bool mainSectionShown) { + return rpl::filter([=] { + return (_controller->mainSectionShown() == mainSectionShown); + }); + }; rpl::merge( - Core::App().settings().dialogsWidthRatioChanges() | rpl::to_empty, + Core::App().settings().dialogsWithChatWidthRatioChanges( + ) | filter(true) | rpl::to_empty, + Core::App().settings().dialogsNoChatWidthRatioChanges( + ) | filter(false) | rpl::to_empty, Core::App().settings().thirdColumnWidthChanges() | rpl::to_empty ) | rpl::start_with_next([=] { updateControlsGeometry(); @@ -1408,6 +1416,7 @@ void MainWidget::showHistory( auto onlyDialogs = noPeer && isOneColumn(); _mainSection.destroy(); + updateMainSectionShown(); updateControlsGeometry(); if (noPeer) { @@ -1781,6 +1790,7 @@ void MainWidget::showNewSection( if (const auto entry = _mainSection->activeChat(); entry.key) { _controller->setActiveChatEntry(entry); } + updateMainSectionShown(); // Depends on SessionController::activeChatEntry // for tabbed selector showing in the third column. @@ -1815,22 +1825,16 @@ void MainWidget::checkMainSectionToLayer() { } Ui::FocusPersister persister(this); if (auto layer = _mainSection->moveContentToLayer(rect())) { - dropMainSection(_mainSection); + _mainSection.destroy(); + _controller->showBackFromStack( + SectionShow( + anim::type::instant, + anim::activation::background)); _controller->showSpecialLayer( std::move(layer), anim::type::instant); } -} - -void MainWidget::dropMainSection(Window::SectionWidget *widget) { - if (_mainSection != widget) { - return; - } - _mainSection.destroy(); - _controller->showBackFromStack( - SectionShow( - anim::type::instant, - anim::activation::background)); + updateMainSectionShown(); } PeerData *MainWidget::singlePeer() const { @@ -2236,13 +2240,18 @@ void MainWidget::resizeEvent(QResizeEvent *e) { updateControlsGeometry(); } +void MainWidget::updateMainSectionShown() { + _controller->setMainSectionShown(_mainSection || _history->peer()); +} + void MainWidget::updateControlsGeometry() { if (!width()) { return; } updateWindowAdaptiveLayout(); if (_dialogs) { - if (Core::App().settings().dialogsWidthRatio() > 0) { + const auto nochat = !_controller->mainSectionShown(); + if (Core::App().settings().dialogsWidthRatio(nochat) > 0) { _a_dialogsWidth.stop(); } if (!_a_dialogsWidth.animating()) { @@ -2444,19 +2453,22 @@ void MainWidget::ensureFirstColumnResizeAreaCreated() { return; } auto moveLeftCallback = [=](int globalLeft) { - auto newWidth = globalLeft - mapToGlobal(QPoint(0, 0)).x(); - auto newRatio = (newWidth < st::columnMinimalWidthLeft / 2) + const auto newWidth = globalLeft - mapToGlobal(QPoint(0, 0)).x(); + const auto newRatio = (newWidth < st::columnMinimalWidthLeft / 2) ? 0. : float64(newWidth) / width(); - Core::App().settings().setDialogsWidthRatio(newRatio); + const auto nochat = !_controller->mainSectionShown(); + Core::App().settings().updateDialogsWidthRatio(newRatio, nochat); }; auto moveFinishedCallback = [=] { if (isOneColumn()) { return; } - if (Core::App().settings().dialogsWidthRatio() > 0) { - Core::App().settings().setDialogsWidthRatio( - float64(_dialogsWidth) / width()); + const auto nochat = !_controller->mainSectionShown(); + if (Core::App().settings().dialogsWidthRatio(nochat) > 0) { + Core::App().settings().updateDialogsWidthRatio( + float64(_dialogsWidth) / width(), + nochat); } Core::App().saveSettingsDelayed(); }; @@ -2491,12 +2503,13 @@ void MainWidget::ensureThirdColumnResizeAreaCreated() { } void MainWidget::updateDialogsWidthAnimated() { - if (!_dialogs || Core::App().settings().dialogsWidthRatio() > 0) { + const auto nochat = !_controller->mainSectionShown(); + if (!_dialogs || Core::App().settings().dialogsWidthRatio(nochat) > 0) { return; } auto dialogsWidth = _dialogsWidth; updateWindowAdaptiveLayout(); - if (!Core::App().settings().dialogsWidthRatio() + if (Core::App().settings().dialogsWidthRatio(nochat) == 0. && (_dialogsWidth != dialogsWidth || _a_dialogsWidth.animating())) { _dialogs->startWidthAnimation(); @@ -2709,8 +2722,10 @@ void MainWidget::handleHistoryBack() { } void MainWidget::updateWindowAdaptiveLayout() { + const auto nochat = !_controller->mainSectionShown(); + auto layout = _controller->computeColumnLayout(); - auto dialogsWidthRatio = Core::App().settings().dialogsWidthRatio(); + auto dialogsWidthRatio = Core::App().settings().dialogsWidthRatio(nochat); // Check if we are in a single-column layout in a wide enough window // for the normal layout. If so, switch to the normal layout. @@ -2753,7 +2768,7 @@ void MainWidget::updateWindowAdaptiveLayout() { //} } - Core::App().settings().setDialogsWidthRatio(dialogsWidthRatio); + Core::App().settings().updateDialogsWidthRatio(dialogsWidthRatio, nochat); auto useSmallColumnWidth = !isOneColumn() && !dialogsWidthRatio diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 1e0d35d1ca..be26770ce3 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -250,6 +250,7 @@ private: void handleAudioUpdate(const Media::Player::TrackState &state); void updateMediaPlaylistPosition(int x); void updateControlsGeometry(); + void updateMainSectionShown(); void updateDialogsWidthAnimated(); void updateThirdColumnToCurrentChat( Dialogs::Key key, @@ -278,7 +279,6 @@ private: void showNewSection( std::shared_ptr memento, const SectionShow ¶ms); - void dropMainSection(Window::SectionWidget *widget); Window::SectionSlideParams prepareThirdSectionAnimation(Window::SectionWidget *section); diff --git a/Telegram/SourceFiles/media/audio/media_audio_capture.cpp b/Telegram/SourceFiles/media/audio/media_audio_capture.cpp index b9151db2a0..1bbf9b10c2 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_capture.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_capture.cpp @@ -249,7 +249,11 @@ struct Instance::Inner::Private { return nbytes; } +#if DA_FFMPEG_CONST_WRITE_CALLBACK + static int WriteData(void *opaque, const uint8_t *buf, int buf_size) { +#else static int WriteData(void *opaque, uint8_t *buf, int buf_size) { +#endif auto l = reinterpret_cast(opaque); if (buf_size <= 0) return 0; diff --git a/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp b/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp index 9f1c7b2349..1188f02324 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp +++ b/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp @@ -144,7 +144,11 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() { } void FFMpegReaderImplementation::processReadFrame() { +#if DA_FFMPEG_HAVE_DURATION + int64 duration = _frame->duration; +#else int64 duration = _frame->pkt_duration; +#endif int64 framePts = _frame->pts; crl::time frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; _currentFrameDelay = _nextFrameDelay; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index 31ea352379..99be5557f6 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -544,6 +544,7 @@ void OverlayWidget::RendererGL::paintTransformedContent( 0.f, 0.f, }; + _contentBuffer->bind(); _contentBuffer->write(0, coords, sizeof(coords)); program->setUniformValue("viewport", _uniformViewport); @@ -712,6 +713,7 @@ void OverlayWidget::RendererGL::paintControl( }; _controlsProgram->bind(); _controlsProgram->setUniformValue("viewport", _uniformViewport); + _contentBuffer->bind(); if (!over.isEmpty() && overOpacity > 0) { _contentBuffer->write( offset * 4 * sizeof(GLfloat), @@ -915,6 +917,7 @@ void OverlayWidget::RendererGL::paintRoundedCorners(int radius) { const auto offset = kControlsOffset + (kControlsCount * kControlValues) / 4; const auto byteOffset = offset * 4 * sizeof(GLfloat); + _contentBuffer->bind(); _contentBuffer->write(byteOffset, coords, sizeof(coords)); _roundedCornersProgram->bind(); _roundedCornersProgram->setUniformValue("viewport", _uniformViewport); @@ -981,6 +984,7 @@ void OverlayWidget::RendererGL::paintStoriesSiblingPart( + (6 * 2 * 4) / 4 // rounding + (index * 4); const auto byteOffset = offset * 4 * sizeof(GLfloat); + _contentBuffer->bind(); _contentBuffer->write(byteOffset, coords, sizeof(coords)); _controlsProgram->bind(); @@ -1059,6 +1063,7 @@ void OverlayWidget::RendererGL::paintUsingRaster( geometry.left(), geometry.bottom(), textured.texture.left(), textured.texture.top(), }; + _contentBuffer->bind(); _contentBuffer->write( bufferOffset * 4 * sizeof(GLfloat), coords, diff --git a/Telegram/SourceFiles/media/view/media_view_pip_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_pip_opengl.cpp index 8e4e8e7c2c..f4be398c20 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip_opengl.cpp @@ -427,6 +427,7 @@ void Pip::RendererGL::paintTransformedContent( (geometry.outer.height() - geometry.inner.y()) * yscale, }; + _contentBuffer->bind(); _contentBuffer->write(0, coords, sizeof(coords)); const auto rgbaFrame = _chromaSize.isEmpty(); @@ -588,6 +589,7 @@ void Pip::RendererGL::paintButton( iconOverRect.texture.right(), iconOverRect.texture.top(), iconOverRect.texture.left(), iconOverRect.texture.top(), }; + _contentBuffer->bind(); _contentBuffer->write( offset * 4 * sizeof(GLfloat), coords, @@ -741,6 +743,7 @@ void Pip::RendererGL::paintUsingRaster( geometry.left(), geometry.bottom(), textured.texture.left(), textured.texture.top(), }; + _contentBuffer->bind(); _contentBuffer->write( bufferOffset * 4 * sizeof(GLfloat), coords, diff --git a/Telegram/SourceFiles/menu/menu_sponsored.cpp b/Telegram/SourceFiles/menu/menu_sponsored.cpp index e917c3f077..495da6a856 100644 --- a/Telegram/SourceFiles/menu/menu_sponsored.cpp +++ b/Telegram/SourceFiles/menu/menu_sponsored.cpp @@ -37,11 +37,13 @@ namespace { void AboutBox( not_null box, - not_null session) { + std::shared_ptr show) { constexpr auto kUrl = "https://promote.telegram.org"_cs; box->setNoContentMargin(true); + const auto session = &show->session(); + const auto content = box->verticalLayout().get(); const auto levels = Data::LevelLimits(session) .channelRestrictSponsoredLevelMin(); @@ -101,7 +103,7 @@ void AboutBox( st::channelEarnSemiboldLabel), padding); Ui::AddSkip(content, st::channelEarnHistoryThreeSkip); - content->add( + const auto label = content->add( object_ptr( content, std::move(about), @@ -121,6 +123,7 @@ void AboutBox( (g.left() - left->width()) / 2, g.top() + st::channelEarnHistoryThreeSkip); }, left->lifetime()); + return label; }; addEntry( tr::lng_sponsored_revenued_info1_title(), @@ -144,10 +147,14 @@ void AboutBox( lt_link, tr::lng_settings_privacy_premium_link( ) | rpl::map([=](QString t) { - return Ui::Text::Link(t, kUrl.utf16()); + return Ui::Text::Link(std::move(t), u"internal:"_q); }), Ui::Text::RichLangValue), - st::sponsoredAboutRemoveIcon); + st::sponsoredAboutRemoveIcon)->setClickHandlerFilter([=]( + const auto &...) { + ShowPremiumPreviewBox(show, PremiumFeature::NoAds); + return true; + }); Ui::AddSkip(content); Ui::AddSkip(content); } @@ -332,16 +339,14 @@ void ShowSponsored( not_null item) { Expects(item->isSponsored()); - struct State final { - }; - const auto state = std::make_shared(); + const auto session = &item->history()->session(); const auto menu = Ui::CreateChild( parent.get(), st::popupMenuWithIcons); menu->addAction(tr::lng_sponsored_menu_revenued_about(tr::now), [=] { - show->show(Box(AboutBox, &item->history()->session())); + show->show(Box(AboutBox, show)); }, &st::menuIconInfo); menu->addAction(tr::lng_sponsored_menu_revenued_report(tr::now), [=] { @@ -351,7 +356,13 @@ void ShowSponsored( menu->addSeparator(&st::expandedMenuSeparator); menu->addAction(tr::lng_sponsored_hide_ads(tr::now), [=] { - ShowPremiumPreviewBox(show, PremiumFeature::NoAds); + if (session->premium()) { + using Result = Data::SponsoredReportResult; + session->sponsoredMessages().createReportCallback( + item->fullId())(Result::Id("-1"), [](const auto &) {}); + } else { + ShowPremiumPreviewBox(show, PremiumFeature::NoAds); + } }, &st::menuIconCancel); menu->popup(QCursor::pos()); @@ -359,7 +370,7 @@ void ShowSponsored( void ShowSponsoredAbout(std::shared_ptr show) { show->showBox(Box([=](not_null box) { - AboutBox(box, &show->session()); + AboutBox(box, show); })); } diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 76c5cb623e..ee10958370 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -1020,12 +1020,25 @@ void Instance::Private::sendRequest( const auto signedDcId = toMainDc ? -realShiftedDcId : realShiftedDcId; registerRequest(requestId, signedDcId); - if (afterRequestId) { - request->after = getRequest(afterRequestId); - } request->lastSentTime = crl::now(); request->needsLayer = needsLayer; + if (afterRequestId) { + request->after = getRequest(afterRequestId); + + if (request->after) { + // Check if this after request is waiting in _dependentRequests. + // This happens if it was after some other request and failed + // to wait for it, but that other request is still processed. + QMutexLocker locker(&_dependentRequestsLock); + const auto i = _dependentRequests.find(afterRequestId); + if (i != end(_dependentRequests)) { + _dependentRequests.emplace(requestId, afterRequestId); + return; + } + } + } + session->sendPrepared(request, msCanWait); } diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index e75af14641..c56582bc42 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -8,11 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "overview/overview_layout.h" #include "overview/overview_layout_delegate.h" +#include "core/ui_integration.h" // Core::MarkedTextContext. #include "data/data_document.h" #include "data/data_document_resolver.h" #include "data/data_session.h" #include "data/data_web_page.h" -#include "data/data_media_types.h" #include "data/data_peer.h" #include "data/data_photo_media.h" #include "data/data_document_media.h" @@ -20,9 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/confirm_box.h" #include "lang/lang_keys.h" #include "layout/layout_selection.h" -#include "mainwidget.h" #include "storage/file_upload.h" -#include "mainwindow.h" #include "main/main_session.h" #include "media/audio/media_audio.h" #include "media/player/media_player_instance.h" @@ -867,6 +865,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const p.drawTextLeft(nameleft, statustop, _width, _status.text(), statusw); unreadx += statusw; } + auto captionLeft = unreadx + st::mediaUnreadSkip; if (parent()->hasUnreadMediaFlag() && unreadx + st::mediaUnreadSkip + st::mediaUnreadSize <= _width) { p.setPen(Qt::NoPen); p.setBrush(selected ? st::msgFileInBgSelected : st::msgFileInBg); @@ -875,6 +874,22 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const PainterHighQualityEnabler hq(p); p.drawEllipse(style::rtlrect(unreadx + st::mediaUnreadSkip, statustop + st::mediaUnreadTop, st::mediaUnreadSize, st::mediaUnreadSize, _width)); } + captionLeft += st::mediaUnreadSkip + st::mediaUnreadSize; + } + if (!_caption.isEmpty()) { + p.setPen(st::historyFileNameInFg); + const auto w = _width - captionLeft - st::defaultScrollArea.width; + _caption.draw(p, Ui::Text::PaintContext{ + .position = QPoint(captionLeft, statustop), + .availableWidth = w, + .spoiler = Ui::Text::DefaultSpoilerCache(), + .paused = context + ? context->paused + : On(PowerSaving::kEmojiChat), + .pausedEmoji = On(PowerSaving::kEmojiChat), + .pausedSpoiler = On(PowerSaving::kChatSpoiler), + .elisionLines = 1, + }); } } @@ -981,23 +996,19 @@ const style::RoundCheckbox &Voice::checkboxStyle() const { void Voice::updateName() { if (const auto forwarded = parent()->Get()) { - if (parent()->fromOriginal()->isChannel()) { - _name.setText( - st::semiboldTextStyle, - tr::lng_forwarded_channel( - tr::now, - lt_channel, - parent()->fromOriginal()->name()), - Ui::NameTextOptions()); - } else { - _name.setText( - st::semiboldTextStyle, - tr::lng_forwarded( - tr::now, - lt_user, - parent()->fromOriginal()->name()), - Ui::NameTextOptions()); - } + const auto info = parent()->originalHiddenSenderInfo(); + const auto name = info + ? tr::lng_forwarded(tr::now, lt_user, info->nameText().toString()) + : parent()->fromOriginal()->isChannel() + ? tr::lng_forwarded_channel( + tr::now, + lt_channel, + parent()->fromOriginal()->name()) + : tr::lng_forwarded( + tr::now, + lt_user, + parent()->fromOriginal()->name()); + _name.setText(st::semiboldTextStyle, name, Ui::NameTextOptions()); } else { _name.setText( st::semiboldTextStyle, @@ -1005,6 +1016,14 @@ void Voice::updateName() { Ui::NameTextOptions()); } _nameVersion = parent()->fromOriginal()->nameVersion(); + _caption.setMarkedText( + st::defaultTextStyle, + parent()->originalText(), + Ui::DialogTextOptions(), + Core::MarkedTextContext{ + .session = &parent()->history()->session(), + .customEmojiRepaint = [=] { delegate()->repaintItem(this); }, + }); } bool Voice::updateStatusText() { diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index 3526b5e57c..936379e36b 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -362,7 +362,9 @@ private: const style::OverviewFileLayout &_st; - Ui::Text::String _name, _details; + Ui::Text::String _name; + Ui::Text::String _details; + Ui::Text::String _caption; int _nameVersion = 0; void updateName(); diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.cpp b/Telegram/SourceFiles/payments/ui/payments_panel.cpp index f509fdb706..d97628285e 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_panel.cpp @@ -70,8 +70,8 @@ Panel::Progress::Progress(QWidget *parent, Fn rect) Panel::Panel(not_null delegate) : _delegate(delegate) , _widget(std::make_unique()) { - _widget->setInnerSize(st::paymentsPanelSize); _widget->setWindowFlag(Qt::WindowStaysOnTopHint, false); + _widget->setInnerSize(st::paymentsPanelSize); _widget->closeRequests( ) | rpl::start_with_next([=] { @@ -534,12 +534,15 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) { const auto bottom = _webviewBottom.get(); bottom->show(); - bottom->heightValue( - ) | rpl::start_with_next([=](int height) { - const auto inner = _widget->innerGeometry(); + rpl::combine( + container->geometryValue() | rpl::map([=] { + return _widget->innerGeometry(); + }), + bottom->heightValue() + ) | rpl::start_with_next([=](QRect inner, int height) { bottom->move(inner.x(), inner.y() + inner.height() - height); - container->resize(inner.width(), inner.height() - height); bottom->resizeToWidth(inner.width()); + _footerHeight = bottom->height(); }, bottom->lifetime()); container->show(); @@ -584,10 +587,12 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) { }); }); - container->geometryValue( - ) | rpl::start_with_next([=](QRect geometry) { - if (raw->widget()) { - raw->widget()->setGeometry(geometry); + rpl::combine( + container->geometryValue(), + _footerHeight.value() + ) | rpl::start_with_next([=](QRect geometry, int footer) { + if (const auto view = raw->widget()) { + view->setGeometry(geometry.marginsRemoved({ 0, 0, 0, footer })); } }, _webview->lifetime); diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.h b/Telegram/SourceFiles/payments/ui/payments_panel.h index b65def0156..6c64c661f8 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel.h +++ b/Telegram/SourceFiles/payments/ui/payments_panel.h @@ -115,11 +115,13 @@ private: [[nodiscard]] bool progressWithBackground() const; [[nodiscard]] QRect progressRect() const; void setupProgressGeometry(); + void updateFooterHeight(); const not_null _delegate; std::unique_ptr _widget; std::unique_ptr _webview; std::unique_ptr _webviewBottom; + rpl::variable _footerHeight; std::unique_ptr _progress; QPointer _saveWebviewInformation; QPointer _weakFormSummary; diff --git a/Telegram/SourceFiles/platform/linux/integration_linux.cpp b/Telegram/SourceFiles/platform/linux/integration_linux.cpp index b2a8cf6113..147a9f03c6 100644 --- a/Telegram/SourceFiles/platform/linux/integration_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/integration_linux.cpp @@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/random.h" #include -#include #include #include @@ -190,23 +189,24 @@ private: const gi::ref_ptr _application; XdpInhibit::InhibitProxy _inhibitProxy; +#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) base::Platform::XDP::SettingWatcher _darkModeWatcher; +#endif // Qt < 6.5.0 }; LinuxIntegration::LinuxIntegration() : _application(MakeApplication()) +#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) , _darkModeWatcher( "org.freedesktop.appearance", "color-scheme", [](GLib::Variant value) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) - QWindowSystemInterface::handleThemeChange(); -#else // Qt >= 6.5.0 Core::Sandbox::Instance().customEnterFromEventLoop([&] { Core::App().settings().setSystemDarkMode(value.get_uint32() == 1); }); +}) #endif // Qt < 6.5.0 -}) { +{ LOG(("Icon theme: %1").arg(QIcon::themeName())); LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName())); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index 36dc140bda..6c2d15d072 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -20,8 +20,6 @@ class MainWindow : public Window::MainWindow { public: explicit MainWindow(not_null controller); - bool psFilterNativeEvent(void *event); - int getCustomTitleHeight() const { return _customTitleHeight; } @@ -47,6 +45,11 @@ protected: private: friend class Private; + bool nativeEvent( + const QByteArray &eventType, + void *message, + qintptr *result) override; + void hideAndDeactivate(); void updateDockCounter(); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 88d15b5f7d..a6d9d0b0b1 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -63,6 +63,26 @@ namespace { // fullscreen mode, after that we'll hide the window no matter what. constexpr auto kHideAfterFullscreenTimeoutMs = 3000; +[[nodiscard]] bool PossiblyTextTypingEvent(NSEvent *e) { + if ([e type] != NSEventTypeKeyDown) { + return false; + } + NSEventModifierFlags flags = [e modifierFlags] + & NSEventModifierFlagDeviceIndependentFlagsMask; + if ((flags & ~NSEventModifierFlagShift) != 0) { + return false; + } + NSString *text = [e characters]; + const auto length = int([text length]); + for (auto i = 0; i != length; ++i) { + const auto utf16 = [text characterAtIndex:i]; + if (utf16 >= 32) { + return true; + } + } + return false; +} + } // namespace class MainWindow::Private { @@ -280,6 +300,21 @@ void MainWindow::initHook() { void MainWindow::updateWindowIcon() { } +bool MainWindow::nativeEvent( + const QByteArray &eventType, + void *message, + qintptr *result) { + if (message && eventType == "NSEvent") { + const auto event = static_cast(message); + if (PossiblyTextTypingEvent(event)) { + Core::Sandbox::Instance().customEnterFromEventLoop([&] { + imeCompositionStartReceived(); + }); + } + } + return false; +} + void MainWindow::hideAndDeactivate() { hide(); } diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index cc747016cc..de28809077 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -550,6 +550,12 @@ void Manager::Private::checkFocusState() { } Manager::Private::~Private() { + if (_waitingDnd) { + _dnd.kill(); + } + if (_waitingFocus) { + _focus.kill(); + } if (_clearingThread.joinable()) { putClearTask(ClearFinish()); _clearingThread.join(); diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 8c852b837a..dd2f3dafec 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -480,6 +480,21 @@ bool MainWindow::initGeometryFromSystem() { return true; } +bool MainWindow::nativeEvent( + const QByteArray &eventType, + void *message, + long *result) { + if (message) { + const auto msg = static_cast(message); + if (msg->message == WM_IME_STARTCOMPOSITION) { + Core::Sandbox::Instance().customEnterFromEventLoop([&] { + imeCompositionStartReceived(); + }); + } + } + return false; +} + void MainWindow::updateWindowIcon() { updateTaskbarAndIconCounters(); } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 856bee773e..63dc6ba17b 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -48,6 +48,11 @@ protected: bool initGeometryFromSystem() override; + bool nativeEvent( + const QByteArray &eventType, + void *message, + long *result) override; + private: struct Private; diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index efeffaac98..ca82da3298 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -238,7 +238,8 @@ private: void edit( not_null item, Api::SendOptions options, - mtpRequestId *const saveEditMsgRequestId); + mtpRequestId *const saveEditMsgRequestId, + std::optional spoilerMediaOverride); void chooseAttach(std::optional overrideSendImagesAsPhotos); [[nodiscard]] SendMenu::Type sendMenuType() const; [[nodiscard]] FullReplyTo replyTo() const; @@ -673,7 +674,8 @@ void ShortcutMessages::setupComposeControls() { ) | rpl::start_with_next([=](auto data) { if (const auto item = _session->data().message(data.fullId)) { if (item->isBusinessShortcut()) { - edit(item, data.options, saveEditMsgRequestId); + const auto spoiler = data.spoilerMediaOverride; + edit(item, data.options, saveEditMsgRequestId, spoiler); } } }, lifetime()); @@ -1213,7 +1215,8 @@ void ShortcutMessages::send(Api::SendOptions options) { void ShortcutMessages::edit( not_null item, Api::SendOptions options, - mtpRequestId *const saveEditMsgRequestId) { + mtpRequestId *const saveEditMsgRequestId, + std::optional spoilerMediaOverride) { if (*saveEditMsgRequestId) { return; } @@ -1281,7 +1284,8 @@ void ShortcutMessages::edit( webpage, options, crl::guard(this, done), - crl::guard(this, fail)); + crl::guard(this, fail), + spoilerMediaOverride); _composeControls->hidePanelsAnimated(); doSetInnerFocus(); diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 31e389b617..359dc84ac9 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -1654,7 +1654,8 @@ void SetupThemeSettings( AddPeerColorButton( container, controller->uiShow(), - controller->session().user()); + controller->session().user(), + st::settingsColorButton); const auto settings = &Core::App().settings(); if (settings->systemDarkMode().has_value()) { diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp index da60290b48..9baca321c9 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -400,7 +400,7 @@ bool ReadSetting( stream >> v; if (!CheckStreamStatus(stream)) return false; - Core::App().settings().setDialogsWidthRatio(v / 1000000.); + Core::App().settings().updateDialogsWidthRatio(v / 1000000., false); context.legacyRead = true; } break; diff --git a/Telegram/SourceFiles/ui/boxes/choose_font_box.cpp b/Telegram/SourceFiles/ui/boxes/choose_font_box.cpp index 512e8a6915..80168ca0fd 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_font_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/choose_font_box.cpp @@ -546,15 +546,25 @@ std::vector Selector::FullList(const QString &now) { result.push_back({ .id = family }); } } - if (!ranges::contains(result, now, &Entry::id)) { + auto nowIt = ranges::find(result, now, &Entry::id); + if (nowIt == end(result)) { result.push_back({ .id = now }); + nowIt = end(result) - 1; } for (auto i = begin(result) + 2; i != end(result); ++i) { i->key = TextUtilities::RemoveAccents(i->id).toLower(); i->text = i->id; i->keywords = TextUtilities::PrepareSearchWords(i->id); } - ranges::sort(begin(result) + 2, end(result), std::less<>(), &Entry::key); + auto skip = 2; + if (nowIt - begin(result) >= skip) { + std::swap(result[2], *nowIt); + ++skip; + } + ranges::sort( + begin(result) + skip, end(result), + std::less<>(), + &Entry::key); return result; } diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index a18c86bf2b..3fbe6e484e 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -323,8 +323,8 @@ Panel::Panel( , _menuButtons(menuButtons) , _widget(std::make_unique()) , _allowClipboardRead(allowClipboardRead) { - _widget->setInnerSize(st::botWebViewPanelSize); _widget->setWindowFlag(Qt::WindowStaysOnTopHint, false); + _widget->setInnerSize(st::botWebViewPanelSize); _widget->closeRequests( ) | rpl::start_with_next([=] { @@ -505,7 +505,7 @@ bool Panel::showWebview( _webview->window.navigate(url); _widget->setBackAllowed(allowBack); _widget->setMenuAllowed([=](const Ui::Menu::MenuCallback &callback) { - if (_hasSettingsButton) { + if (_webview && _webview->window.widget() && _hasSettingsButton) { callback(tr::lng_bot_settings(tr::now), [=] { postEvent("settings_button_pressed"); }, &st::menuIconSettings); @@ -570,17 +570,15 @@ void Panel::createWebviewBottom() { label->show(); _webviewBottom->resize(_webviewBottom->width(), height); - bottom->heightValue( - ) | rpl::start_with_next([=](int height) { - const auto inner = _widget->innerGeometry(); - if (_mainButton && !_mainButton->isHidden()) { - height = _mainButton->height(); - } + rpl::combine( + _webviewParent->geometryValue() | rpl::map([=] { + return _widget->innerGeometry(); + }), + bottom->heightValue() + ) | rpl::start_with_next([=](QRect inner, int height) { bottom->move(inner.x(), inner.y() + inner.height() - height); - if (const auto container = _webviewParent.data()) { - container->setFixedSize(inner.width(), inner.height() - height); - } bottom->resizeToWidth(inner.width()); + updateFooterHeight(); }, bottom->lifetime()); } @@ -636,10 +634,13 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) { }); }); - container->geometryValue( - ) | rpl::start_with_next([=](QRect geometry) { - if (raw->widget()) { - raw->widget()->setGeometry(geometry); + updateFooterHeight(); + rpl::combine( + container->geometryValue(), + _footerHeight.value() + ) | rpl::start_with_next([=](QRect geometry, int footer) { + if (const auto view = raw->widget()) { + view->setGeometry(geometry.marginsRemoved({ 0, 0, 0, footer })); } }, _webview->lifetime); @@ -1185,22 +1186,25 @@ void Panel::createMainButton() { button->hide(); rpl::combine( + _webviewParent->geometryValue() | rpl::map([=] { + return _widget->innerGeometry(); + }), button->shownValue(), button->heightValue() - ) | rpl::start_with_next([=](bool shown, int height) { - const auto inner = _widget->innerGeometry(); - if (!shown) { - height = _webviewBottom->height(); - } + ) | rpl::start_with_next([=](QRect inner, bool shown, int height) { button->move(inner.x(), inner.y() + inner.height() - height); - if (const auto raw = _webviewParent.data()) { - raw->setFixedSize(inner.width(), inner.height() - height); - } button->resizeToWidth(inner.width()); _webviewBottom->setVisible(!shown); + updateFooterHeight(); }, button->lifetime()); } +void Panel::updateFooterHeight() { + _footerHeight = (_mainButton && !_mainButton->isHidden()) + ? _mainButton->height() + : _webviewBottom->height(); +} + void Panel::showBox(object_ptr box) { if (const auto widget = _webview ? _webview->window.widget() : nullptr) { const auto hideNow = !widget->isHidden(); @@ -1290,6 +1294,11 @@ void Panel::postEvent(const QString &event) { } void Panel::postEvent(const QString &event, EventData data) { + if (!_webview) { + LOG(("BotWebView Error: Post event \"%1\" on crashed webview." + ).arg(event)); + return; + } auto written = v::is(data) ? v::get(data).toUtf8() : QJsonDocument( diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h index ee1dae8377..fb0231a012 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h @@ -143,6 +143,7 @@ private: [[nodiscard]] bool progressWithBackground() const; [[nodiscard]] QRect progressRect() const; void setupProgressGeometry(); + void updateFooterHeight(); Webview::StorageId _storageId; const not_null _delegate; @@ -153,9 +154,10 @@ private: std::unique_ptr _webview; std::unique_ptr _webviewBottom; rpl::variable _bottomText; - QPointer _webviewParent; + QPointer _webviewParent; std::unique_ptr