Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b04653f24 | ||
|
|
868015da25 | ||
|
|
7aeffa242e | ||
|
|
3136c0586e | ||
|
|
fb0fcbca7f | ||
|
|
0d449c037b | ||
|
|
4d98230694 | ||
|
|
3d36e501a1 | ||
|
|
b4eb9a0827 | ||
|
|
aaf0015be4 | ||
|
|
1e8e163bb1 | ||
|
|
f3f741e1eb | ||
|
|
44f52ca6cd | ||
|
|
2b6e04bca3 | ||
|
|
784d57a2bc | ||
|
|
f4fdadd3b0 | ||
|
|
1cc9a52461 | ||
|
|
cd52982752 | ||
|
|
4dd58b79e9 | ||
|
|
c5c94276c2 | ||
|
|
8e0b9b685c | ||
|
|
1792bed721 | ||
|
|
a502cbc06e | ||
|
|
ddda7b8c52 | ||
|
|
0f19ba3231 | ||
|
|
36486a3d73 | ||
|
|
02f48a7781 | ||
|
|
c77f4dd794 | ||
|
|
ca31a08182 | ||
|
|
33936195a1 | ||
|
|
67bafa02fe | ||
|
|
1ef0046002 | ||
|
|
f2f19b14eb | ||
|
|
662966ba31 | ||
|
|
5383ae3d96 | ||
|
|
d80b25944e | ||
|
|
76813db3ad | ||
|
|
c3595f2e31 | ||
|
|
5a882d1fdc | ||
|
|
ce6f9f580f | ||
|
|
52b9a1fceb | ||
|
|
1209b2692a | ||
|
|
2abcb51dda | ||
|
|
7a06eccaec | ||
|
|
a1f81e4de8 | ||
|
|
689378ee04 | ||
|
|
b239506150 | ||
|
|
3dadcd9352 | ||
|
|
b9a9520ef5 | ||
|
|
2b46f87d7b | ||
|
|
2667bb3568 | ||
|
|
1bc5277d51 | ||
|
|
436d7b9d82 | ||
|
|
ba7e976fe2 | ||
|
|
c2b1187948 | ||
|
|
1fd28d5cfb | ||
|
|
eec58137e9 | ||
|
|
e7d39e6046 | ||
|
|
63a92cb90a | ||
|
|
85cc3b30a0 | ||
|
|
474a6a71d9 | ||
|
|
393173c1da | ||
|
|
badc27eda4 | ||
|
|
920f3b245b | ||
|
|
f189ffc6ac | ||
|
|
840bb447ba | ||
|
|
414456d003 | ||
|
|
ea3191badf | ||
|
|
4452edcaad | ||
|
|
76eb00cea9 | ||
|
|
b3622b413e | ||
|
|
b55383efe7 | ||
|
|
852e46f0c9 | ||
|
|
84fef1f045 | ||
|
|
6cadf54874 | ||
|
|
e8fc874456 | ||
|
|
c79cd0b692 | ||
|
|
b150ab8ef5 | ||
|
|
8b7b0fa570 | ||
|
|
a3ee1e4ed5 | ||
|
|
349446e6b0 | ||
|
|
97262a99c7 | ||
|
|
1d2e34f5e9 | ||
|
|
bc2fc94e25 | ||
|
|
6f0e94a04a |
5
.github/workflows/linux.yml
vendored
5
.github/workflows/linux.yml
vendored
@@ -63,8 +63,6 @@ jobs:
|
||||
- "DESKTOP_APP_DISABLE_X11_INTEGRATION"
|
||||
- "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION"
|
||||
- "DESKTOP_APP_DISABLE_GTK_INTEGRATION"
|
||||
- "LIBTGVOIP_DISABLE_ALSA"
|
||||
- "LIBTGVOIP_DISABLE_PULSEAUDIO"
|
||||
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
@@ -94,9 +92,6 @@ jobs:
|
||||
if [ "${{ matrix.defines }}" == "DESKTOP_APP_DISABLE_DBUS_INTEGRATION" ]; then
|
||||
DEFINE="$DEFINE -D DESKTOP_APP_DISABLE_GTK_INTEGRATION=ON -D DESKTOP_APP_DISABLE_WEBKITGTK=ON"
|
||||
fi
|
||||
if [ "${{ matrix.defines }}" == "DESKTOP_APP_DISABLE_GTK_INTEGRATION" ]; then
|
||||
DEFINE="$DEFINE -D DESKTOP_APP_DISABLE_WEBKITGTK=ON"
|
||||
fi
|
||||
echo Define from matrix: $DEFINE
|
||||
echo "ARTIFACT_NAME=Telegram_${{ matrix.defines }}" >> $GITHUB_ENV
|
||||
else
|
||||
|
||||
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@@ -2,7 +2,7 @@ name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *'
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
|
||||
3
.github/workflows/mac.yml
vendored
3
.github/workflows/mac.yml
vendored
@@ -449,10 +449,11 @@ jobs:
|
||||
|
||||
git clone git://code.qt.io/qt/qt5.git qt_$QT
|
||||
cd qt_$QT
|
||||
perl init-repository --module-subset=qtbase,qtimageformats
|
||||
perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
|
||||
git checkout v5.15.2
|
||||
git submodule update qtbase
|
||||
git submodule update qtimageformats
|
||||
git submodule update qtsvg
|
||||
cd qtbase
|
||||
find ../../patches/qtbase_$QT -type f -print0 | sort -z | xargs -0 git apply
|
||||
cd ..
|
||||
|
||||
3
.github/workflows/win.yml
vendored
3
.github/workflows/win.yml
vendored
@@ -343,10 +343,11 @@ jobs:
|
||||
run: |
|
||||
git clone git://code.qt.io/qt/qt5.git qt_%QT%
|
||||
cd qt_%QT%
|
||||
perl init-repository --module-subset=qtbase,qtimageformats
|
||||
perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
|
||||
git checkout v%QT_VER%
|
||||
git submodule update qtbase
|
||||
git submodule update qtimageformats
|
||||
git submodule update qtsvg
|
||||
cd qtbase
|
||||
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
|
||||
cd ..
|
||||
|
||||
@@ -1298,8 +1298,6 @@ else()
|
||||
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
|
||||
target_link_libraries(Telegram PRIVATE X11)
|
||||
endif()
|
||||
|
||||
target_link_libraries(Telegram PRIVATE rt)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1317,10 +1315,6 @@ if (build_macstore)
|
||||
COMMAND mkdir -p $<TARGET_FILE_DIR:Telegram>/../Frameworks
|
||||
COMMAND cp -a ${libs_loc}/breakpad/src/client/mac/build/Release/Breakpad.framework $<TARGET_FILE_DIR:Telegram>/../Frameworks/Breakpad.framework
|
||||
)
|
||||
elseif (build_osx)
|
||||
set(bundle_identifier "com.tdesktop.Telegram$<$<CONFIG:Debug>:DebugOsx>")
|
||||
set(bundle_entitlements "Telegram.entitlements")
|
||||
set(output_name "Telegram")
|
||||
else()
|
||||
set(bundle_identifier "com.tdesktop.Telegram$<$<CONFIG:Debug>:Debug>")
|
||||
set(bundle_entitlements "Telegram.entitlements")
|
||||
@@ -1438,10 +1432,6 @@ if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR APPLE) AND NOT build_macstore AND NOT
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
target_link_options(Updater PRIVATE -static-libstdc++)
|
||||
endif()
|
||||
|
||||
if (DESKTOP_APP_SPECIAL_TARGET)
|
||||
add_executable(Packer)
|
||||
init_target(Packer)
|
||||
|
||||
@@ -577,6 +577,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passcode_autolock_minutes#other" = "{count} minutes";
|
||||
"lng_passcode_autolock_hours#one" = "{count} hour";
|
||||
"lng_passcode_autolock_hours#other" = "{count} hours";
|
||||
"lng_passcode_autolock_hours_minutes" = "{hours_count}h {minutes_count}m";
|
||||
"lng_passcode_enter_old" = "Enter current passcode";
|
||||
"lng_passcode_enter_first" = "Enter a passcode";
|
||||
"lng_passcode_enter_new" = "Enter new passcode";
|
||||
@@ -1640,6 +1641,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_mark_read_sure" = "Are you sure you want to mark all chats from this folder as read?";
|
||||
"lng_context_mark_read_all" = "Mark all chats as read";
|
||||
"lng_context_mark_read_all_sure" = "Are you sure you want to mark all chats as read?";
|
||||
"lng_context_mark_read_mentions_all" = "Mark all mentions as read";
|
||||
"lng_context_archive_expand" = "Expand";
|
||||
"lng_context_archive_collapse" = "Collapse";
|
||||
"lng_context_archive_to_menu" = "Move to main menu";
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.9.3.0" />
|
||||
Version="2.9.7.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,9,3,0
|
||||
PRODUCTVERSION 2,9,3,0
|
||||
FILEVERSION 2,9,7,0
|
||||
PRODUCTVERSION 2,9,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.9.3.0"
|
||||
VALUE "FileVersion", "2.9.7.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.9.3.0"
|
||||
VALUE "ProductVersion", "2.9.7.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,9,3,0
|
||||
PRODUCTVERSION 2,9,3,0
|
||||
FILEVERSION 2,9,7,0
|
||||
PRODUCTVERSION 2,9,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "2.9.3.0"
|
||||
VALUE "FileVersion", "2.9.7.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.9.3.0"
|
||||
VALUE "ProductVersion", "2.9.7.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -496,11 +496,7 @@ int main(int argc, char *argv[])
|
||||
#elif defined Q_OS_MAC
|
||||
QString outName(QString("tmacupd%1").arg(AlphaVersion ? AlphaVersion : version));
|
||||
#elif defined Q_OS_UNIX
|
||||
#ifndef _LP64
|
||||
QString outName(QString("tlinux32upd%1").arg(AlphaVersion ? AlphaVersion : version));
|
||||
#else
|
||||
QString outName(QString("tlinuxupd%1").arg(AlphaVersion ? AlphaVersion : version));
|
||||
#endif
|
||||
#else
|
||||
#error Unknown platform!
|
||||
#endif
|
||||
|
||||
@@ -384,8 +384,6 @@ int main(int argc, char *argv[]) {
|
||||
bool writeprotected = false;
|
||||
bool tosettings = false;
|
||||
bool startintray = false;
|
||||
bool testmode = false;
|
||||
bool externalupdater = false;
|
||||
bool customWorkingDir = false;
|
||||
|
||||
char *key = 0;
|
||||
@@ -399,10 +397,6 @@ int main(int argc, char *argv[]) {
|
||||
debug = _debug = true;
|
||||
} else if (equal(argv[i], "-startintray")) {
|
||||
startintray = true;
|
||||
} else if (equal(argv[i], "-testmode")) {
|
||||
testmode = true;
|
||||
} else if (equal(argv[i], "-externalupdater")) {
|
||||
externalupdater = true;
|
||||
} else if (equal(argv[i], "-tosettings")) {
|
||||
tosettings = true;
|
||||
} else if (equal(argv[i], "-workdir_custom")) {
|
||||
@@ -503,8 +497,6 @@ int main(int argc, char *argv[]) {
|
||||
if (autostart) push("-autostart");
|
||||
if (debug) push("-debug");
|
||||
if (startintray) push("-startintray");
|
||||
if (testmode) push("-testmode");
|
||||
if (externalupdater) push("-externalupdater");
|
||||
if (tosettings) push("-tosettings");
|
||||
if (key) {
|
||||
push("-key");
|
||||
|
||||
@@ -90,7 +90,7 @@ int main(int argc, const char * argv[]) {
|
||||
|
||||
openLog();
|
||||
pid_t procId = 0;
|
||||
BOOL update = YES, toSettings = NO, autoStart = NO, startInTray = NO, testMode = NO, freeType = NO, externalUpdater = NO;
|
||||
BOOL update = YES, toSettings = NO, autoStart = NO, startInTray = NO, freeType = NO;
|
||||
BOOL customWorkingDir = NO;
|
||||
NSString *key = nil;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
@@ -114,12 +114,8 @@ int main(int argc, const char * argv[]) {
|
||||
_debug = YES;
|
||||
} else if ([@"-startintray" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
startInTray = YES;
|
||||
} else if ([@"-testmode" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
testMode = YES;
|
||||
} else if ([@"-freetype" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
freeType = YES;
|
||||
} else if ([@"-externalupdater" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
externalUpdater = YES;
|
||||
} else if ([@"-workdir_custom" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
customWorkingDir = YES;
|
||||
} else if ([@"-key" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
@@ -256,9 +252,7 @@ int main(int argc, const char * argv[]) {
|
||||
if (toSettings) [args addObject:@"-tosettings"];
|
||||
if (_debug) [args addObject:@"-debug"];
|
||||
if (startInTray) [args addObject:@"-startintray"];
|
||||
if (testMode) [args addObject:@"-testmode"];
|
||||
if (freeType) [args addObject:@"-freetype"];
|
||||
if (externalUpdater) [args addObject:@"-externalupdater"];
|
||||
if (autoStart) [args addObject:@"-autostart"];
|
||||
if (key) {
|
||||
[args addObject:@"-key"];
|
||||
|
||||
@@ -343,7 +343,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
|
||||
LPWSTR *args;
|
||||
int argsCount;
|
||||
|
||||
bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false, testmode = false, freetype = false, externalupdater = false;
|
||||
bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false, freetype = false;
|
||||
args = CommandLineToArgvW(GetCommandLine(), &argsCount);
|
||||
if (args) {
|
||||
for (int i = 1; i < argsCount; ++i) {
|
||||
@@ -357,12 +357,8 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
|
||||
openLog();
|
||||
} else if (equal(args[i], L"-startintray")) {
|
||||
startintray = true;
|
||||
} else if (equal(args[i], L"-testmode")) {
|
||||
testmode = true;
|
||||
} else if (equal(args[i], L"-freetype")) {
|
||||
freetype = true;
|
||||
} else if (equal(args[i], L"-externalupdater")) {
|
||||
externalupdater = true;
|
||||
} else if (equal(args[i], L"-writeprotected") && ++i < argsCount) {
|
||||
writeLog(std::wstring(L"Argument: ") + args[i]);
|
||||
writeprotected = true;
|
||||
@@ -432,9 +428,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
|
||||
if (autostart) targs += L" -autostart";
|
||||
if (debug) targs += L" -debug";
|
||||
if (startintray) targs += L" -startintray";
|
||||
if (testmode) targs += L" -testmode";
|
||||
if (freetype) targs += L" -freetype";
|
||||
if (externalupdater) targs += L" -externalupdater";
|
||||
if (!customWorkingDir.empty()) {
|
||||
targs += L" -workdir \"" + customWorkingDir + L"\"";
|
||||
}
|
||||
|
||||
@@ -112,6 +112,11 @@ ConfirmInviteBox::ConfirmInviteBox(
|
||||
? tr::lng_channel_invite_private(tr::now)
|
||||
: (!_participants.empty() && _participants.size() < count)
|
||||
? tr::lng_group_invite_members(tr::now, lt_count, count)
|
||||
: (count > 0 && _isChannel)
|
||||
? tr::lng_chat_status_subscribers(
|
||||
tr::now,
|
||||
lt_count_decimal,
|
||||
count)
|
||||
: (count > 0)
|
||||
? tr::lng_chat_status_members(tr::now, lt_count_decimal, count)
|
||||
: _isChannel
|
||||
|
||||
@@ -41,24 +41,6 @@ void CloudPassword::reload() {
|
||||
}).send();
|
||||
}
|
||||
|
||||
void CloudPassword::applyPendingReset(
|
||||
const MTPaccount_ResetPasswordResult &data) {
|
||||
if (!_state) {
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
data.match([&](const MTPDaccount_resetPasswordOk &data) {
|
||||
reload();
|
||||
}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
|
||||
const auto until = data.vuntil_date().v;
|
||||
if (_state->pendingResetDate != until) {
|
||||
_state->pendingResetDate = until;
|
||||
_stateChanges.fire_copy(*_state);
|
||||
}
|
||||
}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
|
||||
});
|
||||
}
|
||||
|
||||
void CloudPassword::clearUnconfirmedPassword() {
|
||||
_requestId = _api.request(MTPaccount_CancelPasswordEmail(
|
||||
)).done([=](const MTPBool &result) {
|
||||
@@ -83,4 +65,48 @@ auto CloudPassword::stateCurrent() const
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
auto CloudPassword::resetPassword()
|
||||
-> rpl::producer<CloudPassword::ResetRetryDate, QString> {
|
||||
return [=](auto consumer) {
|
||||
_api.request(MTPaccount_ResetPassword(
|
||||
)).done([=](const MTPaccount_ResetPasswordResult &result) {
|
||||
result.match([&](const MTPDaccount_resetPasswordOk &data) {
|
||||
reload();
|
||||
}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
|
||||
if (!_state) {
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
const auto until = data.vuntil_date().v;
|
||||
if (_state->pendingResetDate != until) {
|
||||
_state->pendingResetDate = until;
|
||||
_stateChanges.fire_copy(*_state);
|
||||
}
|
||||
}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
|
||||
consumer.put_next_copy(data.vretry_date().v);
|
||||
});
|
||||
consumer.put_done();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
consumer.put_error_copy(error.type());
|
||||
}).send();
|
||||
|
||||
return rpl::lifetime();
|
||||
};
|
||||
}
|
||||
|
||||
auto CloudPassword::cancelResetPassword()
|
||||
-> rpl::producer<rpl::no_value, QString> {
|
||||
return [=](auto consumer) {
|
||||
_api.request(MTPaccount_DeclinePasswordReset(
|
||||
)).done([=] {
|
||||
reload();
|
||||
consumer.put_done();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
consumer.put_error_copy(error.type());
|
||||
}).send();
|
||||
|
||||
return rpl::lifetime();
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -23,14 +23,17 @@ namespace Api {
|
||||
|
||||
class CloudPassword final {
|
||||
public:
|
||||
using ResetRetryDate = int;
|
||||
explicit CloudPassword(not_null<ApiWrap*> api);
|
||||
|
||||
void reload();
|
||||
void applyPendingReset(const MTPaccount_ResetPasswordResult &data);
|
||||
void clearUnconfirmedPassword();
|
||||
rpl::producer<Core::CloudPasswordState> state() const;
|
||||
std::optional<Core::CloudPasswordState> stateCurrent() const;
|
||||
|
||||
rpl::producer<ResetRetryDate, QString> resetPassword();
|
||||
rpl::producer<rpl::no_value, QString> cancelResetPassword();
|
||||
|
||||
private:
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
@@ -32,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/file_upload.h"
|
||||
#include "mainwidget.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
@@ -80,7 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "facades.h"
|
||||
#include "app.h"
|
||||
#include "app.h" // App::quitting
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -7,52 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "app.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_abstract_structure.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_document.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_location_manager.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "inline_bots/inline_bot_layout_item.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "core/application.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "storage/file_upload.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "apiwrap.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_media_view.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QFontDatabase>
|
||||
|
||||
#ifdef OS_MAC_OLD
|
||||
#include <libexif/exif-data.h>
|
||||
#endif // OS_MAC_OLD
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kImageAreaLimit = 12'032 * 9'024;
|
||||
@@ -69,15 +32,6 @@ HistoryView::Element *hoveredItem = nullptr,
|
||||
|
||||
namespace App {
|
||||
|
||||
void initMedia() {
|
||||
Ui::StartCachedCorners();
|
||||
}
|
||||
|
||||
void deinitMedia() {
|
||||
Ui::FinishCachedCorners();
|
||||
Data::clearGlobalStructures();
|
||||
}
|
||||
|
||||
void hoveredItem(HistoryView::Element *item) {
|
||||
::hoveredItem = item;
|
||||
}
|
||||
@@ -168,80 +122,4 @@ namespace App {
|
||||
App::quit();
|
||||
}
|
||||
|
||||
QImage readImage(QByteArray data, QByteArray *format, bool opaque, bool *animated) {
|
||||
if (data.isEmpty()) {
|
||||
return QImage();
|
||||
}
|
||||
QByteArray tmpFormat;
|
||||
QImage result;
|
||||
QBuffer buffer(&data);
|
||||
if (!format) {
|
||||
format = &tmpFormat;
|
||||
}
|
||||
{
|
||||
QImageReader reader(&buffer, *format);
|
||||
#ifndef OS_MAC_OLD
|
||||
reader.setAutoTransform(true);
|
||||
#endif // OS_MAC_OLD
|
||||
if (animated) *animated = reader.supportsAnimation() && reader.imageCount() > 1;
|
||||
if (!reader.canRead()) {
|
||||
return QImage();
|
||||
}
|
||||
const auto imageSize = reader.size();
|
||||
if (imageSize.width() * imageSize.height() > kImageAreaLimit) {
|
||||
return QImage();
|
||||
}
|
||||
QByteArray fmt = reader.format();
|
||||
if (!fmt.isEmpty()) *format = fmt;
|
||||
if (!reader.read(&result)) {
|
||||
return QImage();
|
||||
}
|
||||
fmt = reader.format();
|
||||
if (!fmt.isEmpty()) *format = fmt;
|
||||
}
|
||||
buffer.seek(0);
|
||||
auto fmt = QString::fromUtf8(*format).toLower();
|
||||
if (fmt == "jpg" || fmt == "jpeg") {
|
||||
#ifdef OS_MAC_OLD
|
||||
if (auto exifData = exif_data_new_from_data((const uchar*)(data.constData()), data.size())) {
|
||||
auto byteOrder = exif_data_get_byte_order(exifData);
|
||||
if (auto exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION)) {
|
||||
auto orientationFix = [exifEntry, byteOrder] {
|
||||
auto orientation = exif_get_short(exifEntry->data, byteOrder);
|
||||
switch (orientation) {
|
||||
case 2: return QTransform(-1, 0, 0, 1, 0, 0);
|
||||
case 3: return QTransform(-1, 0, 0, -1, 0, 0);
|
||||
case 4: return QTransform(1, 0, 0, -1, 0, 0);
|
||||
case 5: return QTransform(0, -1, -1, 0, 0, 0);
|
||||
case 6: return QTransform(0, 1, -1, 0, 0, 0);
|
||||
case 7: return QTransform(0, 1, 1, 0, 0, 0);
|
||||
case 8: return QTransform(0, -1, 1, 0, 0, 0);
|
||||
}
|
||||
return QTransform();
|
||||
};
|
||||
result = result.transformed(orientationFix());
|
||||
}
|
||||
exif_data_free(exifData);
|
||||
}
|
||||
#endif // OS_MAC_OLD
|
||||
} else if (opaque) {
|
||||
result = Images::prepareOpaque(std::move(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QImage readImage(const QString &file, QByteArray *format, bool opaque, bool *animated, QByteArray *content) {
|
||||
QFile f(file);
|
||||
if (f.size() > kImageSizeLimit || !f.open(QIODevice::ReadOnly)) {
|
||||
if (animated) *animated = false;
|
||||
return QImage();
|
||||
}
|
||||
auto imageBytes = f.readAll();
|
||||
auto result = readImage(imageBytes, format, opaque, animated);
|
||||
if (content && !result.isNull()) {
|
||||
*content = imageBytes;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/data_types.h"
|
||||
|
||||
namespace HistoryView {
|
||||
class Element;
|
||||
} // namespace HistoryView
|
||||
@@ -26,9 +24,6 @@ namespace App {
|
||||
HistoryView::Element *mousedItem();
|
||||
void clearMousedItems();
|
||||
|
||||
void initMedia();
|
||||
void deinitMedia();
|
||||
|
||||
enum LaunchState {
|
||||
Launched = 0,
|
||||
QuitRequested = 1,
|
||||
@@ -40,8 +35,4 @@ namespace App {
|
||||
void setLaunchState(LaunchState state);
|
||||
void restart();
|
||||
|
||||
constexpr auto kImageSizeLimit = 64 * 1024 * 1024; // Open images up to 64mb jpg/png/gif
|
||||
QImage readImage(QByteArray data, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr);
|
||||
QImage readImage(const QString &file, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr, QByteArray *content = 0);
|
||||
|
||||
};
|
||||
|
||||
@@ -95,9 +95,7 @@ void AboutBox::showVersionHistory() {
|
||||
url += qsl("win64/%1.zip");
|
||||
} else if (Platform::IsMac()) {
|
||||
url += qsl("mac/%1.zip");
|
||||
} else if (Platform::IsLinux32Bit()) {
|
||||
url += qsl("linux32/%1.tar.xz");
|
||||
} else if (Platform::IsLinux64Bit()) {
|
||||
} else if (Platform::IsLinux()) {
|
||||
url += qsl("linux/%1.tar.xz");
|
||||
} else {
|
||||
Unexpected("Platform value.");
|
||||
|
||||
@@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "app.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace internal {
|
||||
|
||||
@@ -7,41 +7,139 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/auto_lock_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "core/application.h"
|
||||
#include "mainwindow.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/time_input.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
AutoLockBox::AutoLockBox(QWidget*, not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
namespace {
|
||||
|
||||
constexpr auto kCustom = std::numeric_limits<int>::max();
|
||||
constexpr auto kOptions = { 60, 300, 3600, 18000, kCustom };
|
||||
constexpr auto kDefaultCustom = "10:00"_cs;
|
||||
|
||||
auto TimeString(int seconds) {
|
||||
const auto hours = seconds / 3600;
|
||||
const auto minutes = (seconds - hours * 3600) / 60;
|
||||
return QString("%1:%2").arg(hours).arg(minutes, 2, 10, QLatin1Char('0'));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AutoLockBox::AutoLockBox(QWidget*) {
|
||||
}
|
||||
|
||||
void AutoLockBox::prepare() {
|
||||
setTitle(tr::lng_passcode_autolock());
|
||||
|
||||
addButton(tr::lng_box_ok(), [this] { closeBox(); });
|
||||
addButton(tr::lng_box_ok(), [=] { closeBox(); });
|
||||
|
||||
auto options = { 60, 300, 3600, 18000 };
|
||||
const auto currentTime = Core::App().settings().autoLock();
|
||||
|
||||
auto group = std::make_shared<Ui::RadiobuttonGroup>(
|
||||
Core::App().settings().autoLock());
|
||||
const auto group = std::make_shared<Ui::RadiobuttonGroup>(
|
||||
ranges::contains(kOptions, currentTime) ? currentTime : kCustom);
|
||||
|
||||
const auto x = st::boxPadding.left() + st::boxOptionListPadding.left();
|
||||
auto y = st::boxOptionListPadding.top() + st::autolockButton.margin.top();
|
||||
auto count = int(options.size());
|
||||
const auto count = int(kOptions.size());
|
||||
_options.reserve(count);
|
||||
for (auto seconds : options) {
|
||||
_options.emplace_back(this, group, seconds, (seconds % 3600) ? tr::lng_passcode_autolock_minutes(tr::now, lt_count, seconds / 60) : tr::lng_passcode_autolock_hours(tr::now, lt_count, seconds / 3600), st::autolockButton);
|
||||
_options.back()->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
|
||||
for (const auto seconds : kOptions) {
|
||||
const auto text = [&] {
|
||||
if (seconds == kCustom) {
|
||||
return QString();
|
||||
}
|
||||
const auto minutes = (seconds % 3600);
|
||||
return (minutes
|
||||
? tr::lng_passcode_autolock_minutes
|
||||
: tr::lng_passcode_autolock_hours)(
|
||||
tr::now,
|
||||
lt_count,
|
||||
minutes ? (seconds / 60) : (seconds / 3600));
|
||||
}();
|
||||
_options.emplace_back(
|
||||
this,
|
||||
group,
|
||||
seconds,
|
||||
text,
|
||||
st::autolockButton);
|
||||
_options.back()->moveToLeft(x, y);
|
||||
y += _options.back()->heightNoMargins() + st::boxOptionListSkip;
|
||||
}
|
||||
group->setChangedCallback([this](int value) { durationChanged(value); });
|
||||
|
||||
setDimensions(st::autolockWidth, st::boxOptionListPadding.top() + count * _options.back()->heightNoMargins() + (count - 1) * st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::boxPadding.bottom());
|
||||
const auto timeInput = [&] {
|
||||
const auto &last = _options.back();
|
||||
const auto &st = st::autolockButton;
|
||||
|
||||
const auto textLeft = st.checkPosition.x()
|
||||
+ last->checkRect().width()
|
||||
+ st.textPosition.x();
|
||||
const auto textTop = st.margin.top() + st.textPosition.y();
|
||||
|
||||
const auto timeInput = Ui::CreateChild<Ui::TimeInput>(
|
||||
this,
|
||||
(group->value() == kCustom)
|
||||
? TimeString(currentTime)
|
||||
: kDefaultCustom.utf8(),
|
||||
st::autolockTimeField,
|
||||
st::autolockDateField,
|
||||
st::scheduleTimeSeparator,
|
||||
st::scheduleTimeSeparatorPadding);
|
||||
timeInput->resizeToWidth(st::autolockTimeWidth);
|
||||
timeInput->moveToLeft(last->x() + textLeft, last->y() + textTop);
|
||||
return timeInput;
|
||||
}();
|
||||
|
||||
const auto collect = [=] {
|
||||
const auto timeValue = timeInput->valueCurrent().split(':');
|
||||
if (timeValue.size() != 2) {
|
||||
return 0;
|
||||
}
|
||||
return timeValue[0].toInt() * 3600 + timeValue[1].toInt() * 60;
|
||||
};
|
||||
|
||||
timeInput->focuses(
|
||||
) | rpl::start_with_next([=] {
|
||||
group->setValue(kCustom);
|
||||
}, lifetime());
|
||||
|
||||
group->setChangedCallback([=](int value) {
|
||||
if (value != kCustom) {
|
||||
durationChanged(value);
|
||||
} else {
|
||||
timeInput->setFocusFast();
|
||||
}
|
||||
});
|
||||
|
||||
rpl::merge(
|
||||
boxClosing() | rpl::filter([=] { return group->value() == kCustom; }),
|
||||
timeInput->submitRequests()
|
||||
) | rpl::start_with_next([=] {
|
||||
if (const auto result = collect()) {
|
||||
durationChanged(result);
|
||||
} else {
|
||||
timeInput->showError();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
const auto timeInputBottom = timeInput->y() + timeInput->height();
|
||||
setDimensions(
|
||||
st::autolockWidth,
|
||||
st::boxOptionListPadding.top()
|
||||
+ (timeInputBottom - _options.back()->bottomNoMargins())
|
||||
+ count * _options.back()->heightNoMargins()
|
||||
+ (count - 1) * st::boxOptionListSkip
|
||||
+ st::boxOptionListPadding.bottom()
|
||||
+ st::boxPadding.bottom());
|
||||
}
|
||||
|
||||
void AutoLockBox::durationChanged(int seconds) {
|
||||
if (Core::App().settings().autoLock() == seconds) {
|
||||
closeBox();
|
||||
return;
|
||||
}
|
||||
Core::App().settings().setAutoLock(seconds);
|
||||
Core::App().saveSettingsDelayed();
|
||||
|
||||
|
||||
@@ -9,17 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class Radiobutton;
|
||||
} // namespace Ui
|
||||
|
||||
class AutoLockBox : public Ui::BoxContent {
|
||||
public:
|
||||
AutoLockBox(QWidget*, not_null<Main::Session*> session);
|
||||
AutoLockBox(QWidget*);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -27,8 +23,6 @@ protected:
|
||||
private:
|
||||
void durationChanged(int seconds);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
std::vector<object_ptr<Ui::Radiobutton>> _options;
|
||||
|
||||
};
|
||||
|
||||
@@ -255,7 +255,7 @@ void BackgroundBox::Inner::updatePapers() {
|
||||
|
||||
_papers = _session->data().wallpapers(
|
||||
) | ranges::views::filter([](const Data::WallPaper &paper) {
|
||||
return !paper.isPattern() || paper.backgroundColor().has_value();
|
||||
return !paper.isPattern() || !paper.backgroundColors().empty();
|
||||
}) | ranges::views::transform([](const Data::WallPaper &paper) {
|
||||
return Paper{ paper };
|
||||
}) | ranges::to_vector;
|
||||
@@ -326,8 +326,18 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
||||
paper.dataMedia = document->createMediaView();
|
||||
paper.dataMedia->thumbnailWanted(paper.data.fileOrigin());
|
||||
}
|
||||
}
|
||||
if (!paper.dataMedia || !paper.dataMedia->thumbnail()) {
|
||||
if (!paper.dataMedia->thumbnail()) {
|
||||
return;
|
||||
}
|
||||
} else if (!paper.data.backgroundColors().empty()) {
|
||||
paper.thumbnail = Ui::PixmapFromImage(
|
||||
Data::GenerateWallPaper(
|
||||
st::backgroundSize * cIntRetinaFactor(),
|
||||
paper.data.backgroundColors(),
|
||||
paper.data.gradientRotation()));
|
||||
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -336,12 +346,11 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
||||
: paper.dataMedia->thumbnail();
|
||||
auto original = thumbnail->original();
|
||||
if (paper.data.isPattern()) {
|
||||
const auto color = *paper.data.backgroundColor();
|
||||
original = Data::PreparePatternImage(
|
||||
std::move(original),
|
||||
color,
|
||||
Data::PatternColor(color),
|
||||
paper.data.patternIntensity());
|
||||
paper.data.backgroundColors(),
|
||||
paper.data.gradientRotation(),
|
||||
paper.data.patternOpacity());
|
||||
}
|
||||
paper.thumbnail = Ui::PixmapFromImage(TakeMiddleSample(
|
||||
original,
|
||||
|
||||
@@ -279,7 +279,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
|
||||
});
|
||||
}
|
||||
|
||||
AdminLog::OwnedItem GenerateTextItem(
|
||||
[[nodiscard]] AdminLog::OwnedItem GenerateTextItem(
|
||||
not_null<HistoryView::ElementDelegate*> delegate,
|
||||
not_null<History*> history,
|
||||
const QString &text,
|
||||
@@ -308,7 +308,7 @@ AdminLog::OwnedItem GenerateTextItem(
|
||||
return AdminLog::OwnedItem(delegate, item);
|
||||
}
|
||||
|
||||
QImage PrepareScaledNonPattern(
|
||||
[[nodiscard]] QImage PrepareScaledNonPattern(
|
||||
const QImage &image,
|
||||
Images::Option blur) {
|
||||
const auto size = st::boxWideWidth;
|
||||
@@ -331,66 +331,31 @@ QImage PrepareScaledNonPattern(
|
||||
size);
|
||||
}
|
||||
|
||||
QImage ColorizePattern(QImage image, QColor color) {
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
image = std::move(image).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
// Similar to style::colorizeImage.
|
||||
// But style::colorizeImage takes pattern with all pixels having the
|
||||
// same components value, from (0, 0, 0, 0) to (255, 255, 255, 255).
|
||||
//
|
||||
// While in patterns we have different value ranges, usually they are
|
||||
// from (0, 0, 0, 0) to (0, 0, 0, 255), so we should use only 'alpha'.
|
||||
|
||||
const auto width = image.width();
|
||||
const auto height = image.height();
|
||||
const auto pattern = anim::shifted(color);
|
||||
|
||||
constexpr auto resultIntsPerPixel = 1;
|
||||
const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
|
||||
const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
|
||||
auto resultInts = reinterpret_cast<uint32*>(image.bits());
|
||||
Assert(resultIntsAdded >= 0);
|
||||
Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
|
||||
Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
|
||||
|
||||
const auto maskBytesPerPixel = (image.depth() >> 3);
|
||||
const auto maskBytesPerLine = image.bytesPerLine();
|
||||
const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
|
||||
|
||||
// We want to read the last byte of four available.
|
||||
// This is the difference with style::colorizeImage.
|
||||
auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
|
||||
Assert(maskBytesAdded >= 0);
|
||||
Assert(image.depth() == (maskBytesPerPixel << 3));
|
||||
for (auto y = 0; y != height; ++y) {
|
||||
for (auto x = 0; x != width; ++x) {
|
||||
auto maskOpacity = static_cast<anim::ShiftedMultiplier>(*maskBytes) + 1;
|
||||
*resultInts = anim::unshifted(pattern * maskOpacity);
|
||||
maskBytes += maskBytesPerPixel;
|
||||
resultInts += resultIntsPerPixel;
|
||||
}
|
||||
maskBytes += maskBytesAdded;
|
||||
resultInts += resultIntsAdded;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
QImage PrepareScaledFromFull(
|
||||
[[nodiscard]] QImage PrepareScaledFromFull(
|
||||
const QImage &image,
|
||||
std::optional<QColor> patternBackground,
|
||||
bool isPattern,
|
||||
const std::vector<QColor> &background,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity,
|
||||
Images::Option blur = Images::Option(0)) {
|
||||
auto result = PrepareScaledNonPattern(image, blur);
|
||||
if (patternBackground) {
|
||||
result = ColorizePattern(
|
||||
if (isPattern) {
|
||||
result = Data::PreparePatternImage(
|
||||
std::move(result),
|
||||
Data::PatternColor(*patternBackground));
|
||||
background,
|
||||
gradientRotation,
|
||||
patternOpacity);
|
||||
}
|
||||
return std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage BlackImage(QSize size) {
|
||||
auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
|
||||
result.fill(Qt::black);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
@@ -415,12 +380,28 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
if (_media) {
|
||||
_media->thumbnailWanted(_paper.fileOrigin());
|
||||
}
|
||||
generateBackground();
|
||||
_controller->session().downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::generateBackground() {
|
||||
if (_paper.backgroundColors().empty()) {
|
||||
return;
|
||||
}
|
||||
const auto size = QSize(st::boxWideWidth, st::boxWideWidth)
|
||||
* cIntRetinaFactor();
|
||||
_generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.)
|
||||
? Data::GenerateWallPaper(
|
||||
size,
|
||||
_paper.backgroundColors(),
|
||||
_paper.gradientRotation())
|
||||
: BlackImage(size));
|
||||
_generated.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
|
||||
not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
|
||||
return static_cast<HistoryView::ElementDelegate*>(this);
|
||||
}
|
||||
@@ -433,7 +414,7 @@ void BackgroundPreviewBox::prepare() {
|
||||
if (_paper.hasShareUrl()) {
|
||||
addLeftButton(tr::lng_background_share(), [=] { share(); });
|
||||
}
|
||||
updateServiceBg(_paper.backgroundColor());
|
||||
updateServiceBg(_paper.backgroundColors());
|
||||
|
||||
_paper.loadDocument();
|
||||
const auto document = _paper.document();
|
||||
@@ -521,21 +502,23 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
const auto ms = crl::now();
|
||||
const auto color = _paper.backgroundColor();
|
||||
if (color) {
|
||||
p.fillRect(e->rect(), *color);
|
||||
if (_scaled.isNull()) {
|
||||
setScaledFromThumb();
|
||||
}
|
||||
if (!color || _paper.isPattern()) {
|
||||
if (!_scaled.isNull() || setScaledFromThumb()) {
|
||||
paintImage(p);
|
||||
paintRadial(p);
|
||||
} else if (!color) {
|
||||
p.fillRect(e->rect(), st::boxBg);
|
||||
return;
|
||||
} else {
|
||||
// Progress of pattern loading.
|
||||
paintRadial(p);
|
||||
}
|
||||
if (!_generated.isNull()
|
||||
&& (_scaled.isNull()
|
||||
|| (_fadeOutThumbnail.isNull() && _fadeIn.animating()))) {
|
||||
p.drawPixmap(0, 0, _generated);
|
||||
}
|
||||
if (!_scaled.isNull()) {
|
||||
paintImage(p);
|
||||
paintRadial(p);
|
||||
} else if (_generated.isNull()) {
|
||||
p.fillRect(e->rect(), st::boxBg);
|
||||
return;
|
||||
} else {
|
||||
// Progress of pattern loading.
|
||||
paintRadial(p);
|
||||
}
|
||||
paintTexts(p, ms);
|
||||
}
|
||||
@@ -543,10 +526,6 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||
void BackgroundPreviewBox::paintImage(Painter &p) {
|
||||
Expects(!_scaled.isNull());
|
||||
|
||||
const auto master = _paper.isPattern()
|
||||
? std::clamp(_paper.patternIntensity() / 100., 0., 1.)
|
||||
: 1.;
|
||||
|
||||
const auto factor = cIntRetinaFactor();
|
||||
const auto size = st::boxWideWidth;
|
||||
const auto from = QRect(
|
||||
@@ -563,7 +542,7 @@ void BackgroundPreviewBox::paintImage(Painter &p) {
|
||||
const auto &pixmap = (!_blurred.isNull() && _paper.isBlurred())
|
||||
? _blurred
|
||||
: _scaled;
|
||||
p.setOpacity(master * fade);
|
||||
p.setOpacity(fade);
|
||||
p.drawPixmap(rect(), pixmap, from);
|
||||
checkBlurAnimationStart();
|
||||
}
|
||||
@@ -610,11 +589,18 @@ QRect BackgroundPreviewBox::radialRect() const {
|
||||
void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
|
||||
const auto height1 = _text1->height();
|
||||
const auto height2 = _text2->height();
|
||||
const auto context = HistoryView::PaintContext{
|
||||
.bubblesPattern = nullptr, // #TODO bubbles
|
||||
.viewport = rect(),
|
||||
.clip = rect(),
|
||||
.selection = TextSelection(),
|
||||
.now = ms,
|
||||
};
|
||||
p.translate(0, textsTop());
|
||||
paintDate(p);
|
||||
_text1->draw(p, rect(), TextSelection(), ms);
|
||||
_text1->draw(p, context);
|
||||
p.translate(0, height1);
|
||||
_text2->draw(p, rect(), TextSelection(), ms);
|
||||
_text2->draw(p, context);
|
||||
p.translate(0, height2);
|
||||
}
|
||||
|
||||
@@ -655,7 +641,10 @@ void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
|
||||
checkLoadedDocument();
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::setScaledFromThumb() {
|
||||
void BackgroundPreviewBox::setScaledFromThumb() {
|
||||
if (!_scaled.isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto localThumbnail = _paper.localThumbnail();
|
||||
const auto thumbnail = localThumbnail
|
||||
? localThumbnail
|
||||
@@ -663,13 +652,16 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
|
||||
? _media->thumbnail()
|
||||
: nullptr;
|
||||
if (!thumbnail) {
|
||||
return false;
|
||||
return;
|
||||
} else if (_paper.isPattern() && _paper.document() != nullptr) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
auto scaled = PrepareScaledFromFull(
|
||||
thumbnail->original(),
|
||||
patternBackgroundColor(),
|
||||
_paper.isPattern(),
|
||||
_paper.backgroundColors(),
|
||||
_paper.gradientRotation(),
|
||||
_paper.patternOpacity(),
|
||||
_paper.document() ? Images::Option::Blurred : Images::Option(0));
|
||||
auto blurred = (_paper.document() || _paper.isPattern())
|
||||
? QImage()
|
||||
@@ -677,13 +669,12 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
|
||||
Data::PrepareBlurredBackground(thumbnail->original()),
|
||||
Images::Option(0));
|
||||
setScaledFromImage(std::move(scaled), std::move(blurred));
|
||||
return true;
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::setScaledFromImage(
|
||||
QImage &&image,
|
||||
QImage &&blurred) {
|
||||
updateServiceBg(Window::Theme::CountAverageColor(image));
|
||||
updateServiceBg({ Window::Theme::CountAverageColor(image) });
|
||||
if (!_full.isNull()) {
|
||||
startFadeInFrom(std::move(_scaled));
|
||||
}
|
||||
@@ -710,16 +701,20 @@ void BackgroundPreviewBox::checkBlurAnimationStart() {
|
||||
startFadeInFrom(_paper.isBlurred() ? _scaled : _blurred);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
|
||||
if (background) {
|
||||
_serviceBg = Window::Theme::AdjustedColor(
|
||||
st::msgServiceBg->c,
|
||||
*background);
|
||||
void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
|
||||
const auto count = int(bg.size());
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<QColor> BackgroundPreviewBox::patternBackgroundColor() const {
|
||||
return _paper.isPattern() ? _paper.backgroundColor() : std::nullopt;
|
||||
auto red = 0, green = 0, blue = 0;
|
||||
for (const auto &color : bg) {
|
||||
red += color.red();
|
||||
green += color.green();
|
||||
blue += color.blue();
|
||||
}
|
||||
_serviceBg = Window::Theme::AdjustedColor(
|
||||
st::msgServiceBg->c,
|
||||
QColor(red / count, green / count, blue / count));
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
@@ -737,15 +732,23 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
crl::async([
|
||||
this,
|
||||
image = std::move(image),
|
||||
patternBackground = patternBackgroundColor(),
|
||||
isPattern = _paper.isPattern(),
|
||||
background = _paper.backgroundColors(),
|
||||
gradientRotation = _paper.gradientRotation(),
|
||||
patternOpacity = _paper.patternOpacity(),
|
||||
guard = _generating.make_guard()
|
||||
]() mutable {
|
||||
auto scaled = PrepareScaledFromFull(image, patternBackground);
|
||||
auto blurred = patternBackground
|
||||
? QImage()
|
||||
: PrepareScaledNonPattern(
|
||||
auto scaled = PrepareScaledFromFull(
|
||||
image,
|
||||
isPattern,
|
||||
background,
|
||||
gradientRotation,
|
||||
patternOpacity);
|
||||
auto blurred = !isPattern
|
||||
? PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(image),
|
||||
Images::Option(0));
|
||||
Images::Option(0))
|
||||
: QImage();
|
||||
crl::on_main(std::move(guard), [
|
||||
this,
|
||||
image = std::move(image),
|
||||
@@ -768,7 +771,7 @@ bool BackgroundPreviewBox::Start(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &slug,
|
||||
const QMap<QString, QString> ¶ms) {
|
||||
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
|
||||
if (const auto paper = Data::WallPaper::FromColorsSlug(slug)) {
|
||||
controller->show(Box<BackgroundPreviewBox>(
|
||||
controller,
|
||||
paper->withUrlParams(params)));
|
||||
|
||||
@@ -56,11 +56,11 @@ private:
|
||||
void radialAnimationCallback(crl::time now);
|
||||
QRect radialRect() const;
|
||||
|
||||
void generateBackground();
|
||||
void checkLoadedDocument();
|
||||
bool setScaledFromThumb();
|
||||
void setScaledFromThumb();
|
||||
void setScaledFromImage(QImage &&image, QImage &&blurred);
|
||||
void updateServiceBg(std::optional<QColor> background);
|
||||
std::optional<QColor> patternBackgroundColor() const;
|
||||
void updateServiceBg(const std::vector<QColor> &bg);
|
||||
void paintImage(Painter &p);
|
||||
void paintRadial(Painter &p);
|
||||
void paintTexts(Painter &p, crl::time ms);
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
Data::WallPaper _paper;
|
||||
std::shared_ptr<Data::DocumentMedia> _media;
|
||||
QImage _full;
|
||||
QPixmap _scaled, _blurred, _fadeOutThumbnail;
|
||||
QPixmap _generated, _scaled, _blurred, _fadeOutThumbnail;
|
||||
Ui::Animations::Simple _fadeIn;
|
||||
Ui::RadialAnimation _radial;
|
||||
base::binary_guard _generating;
|
||||
|
||||
@@ -324,7 +324,7 @@ passcodePadding: margins(0px, 0px, 0px, 5px);
|
||||
passcodeTextLine: 28px;
|
||||
passcodeLittleSkip: 5px;
|
||||
passcodeAboutSkip: 7px;
|
||||
passcodeSkip: 20px;
|
||||
passcodeSkip: 23px;
|
||||
|
||||
newGroupAboutFg: windowSubTextFg;
|
||||
newGroupPadding: margins(4px, 6px, 4px, 3px);
|
||||
@@ -965,3 +965,11 @@ scheduleTimeSeparatorPadding: margins(2px, 0px, 2px, 0px);
|
||||
boxAttentionDividerLabel: FlatLabel(boxDividerLabel) {
|
||||
textFg: boxTextFgError;
|
||||
}
|
||||
|
||||
autolockDateField: InputField(scheduleDateField) {
|
||||
heightMin: 22px;
|
||||
}
|
||||
autolockTimeField: InputField(scheduleTimeField) {
|
||||
heightMin: 20px;
|
||||
}
|
||||
autolockTimeWidth: 52px;
|
||||
|
||||
@@ -41,7 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "facades.h" // Ui::showChatsList
|
||||
#include "app.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
|
||||
@@ -67,10 +67,7 @@ auto ListFromMimeData(not_null<const QMimeData*> data) {
|
||||
if (result.error == Error::None) {
|
||||
return result;
|
||||
} else if (data->hasImage()) {
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
return Storage::PrepareMediaFromImage(
|
||||
std::move(image),
|
||||
|
||||
@@ -14,7 +14,6 @@ Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
|
||||
@@ -102,49 +102,53 @@ void StartPendingReset(
|
||||
not_null<Ui::BoxContent*> context,
|
||||
Fn<void()> close) {
|
||||
const auto weak = Ui::MakeWeak(context.get());
|
||||
session->api().request(MTPaccount_ResetPassword(
|
||||
)).done([=](const MTPaccount_ResetPasswordResult &result) {
|
||||
session->api().cloudPassword().applyPendingReset(result);
|
||||
result.match([&](const MTPDaccount_resetPasswordOk &data) {
|
||||
}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
|
||||
}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
|
||||
constexpr auto kMinute = 60;
|
||||
constexpr auto kHour = 3600;
|
||||
constexpr auto kDay = 86400;
|
||||
const auto left = std::max(
|
||||
data.vretry_date().v - base::unixtime::now(),
|
||||
kMinute);
|
||||
const auto days = (left / kDay);
|
||||
const auto hours = (left / kHour);
|
||||
const auto minutes = (left / kMinute);
|
||||
const auto duration = days
|
||||
? tr::lng_group_call_duration_days(tr::now, lt_count, days)
|
||||
: hours
|
||||
? tr::lng_group_call_duration_hours(tr::now, lt_count, hours)
|
||||
: tr::lng_group_call_duration_minutes(
|
||||
tr::now,
|
||||
lt_count,
|
||||
minutes);
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->getDelegate()->show(Box<InformBox>(
|
||||
tr::lng_cloud_password_reset_later(
|
||||
tr::now,
|
||||
lt_duration,
|
||||
duration)));
|
||||
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
|
||||
auto finish = [=](const QString &message) mutable {
|
||||
if (const auto strong = weak.data()) {
|
||||
if (!message.isEmpty()) {
|
||||
strong->getDelegate()->show(Box<InformBox>(message));
|
||||
}
|
||||
});
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
close();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->getDelegate()->show(
|
||||
Box<InformBox>("Error: " + error.type()));
|
||||
strong->closeBox();
|
||||
if (lifetime) {
|
||||
base::take(lifetime)->destroy();
|
||||
}
|
||||
close();
|
||||
}).send();
|
||||
};
|
||||
|
||||
session->api().cloudPassword().resetPassword(
|
||||
) | rpl::start_with_next_error_done([=](
|
||||
Api::CloudPassword::ResetRetryDate retryDate) {
|
||||
constexpr auto kMinute = 60;
|
||||
constexpr auto kHour = 3600;
|
||||
constexpr auto kDay = 86400;
|
||||
const auto left = std::max(
|
||||
retryDate - base::unixtime::now(),
|
||||
kMinute);
|
||||
const auto days = (left / kDay);
|
||||
const auto hours = (left / kHour);
|
||||
const auto minutes = (left / kMinute);
|
||||
const auto duration = days
|
||||
? tr::lng_group_call_duration_days(tr::now, lt_count, days)
|
||||
: hours
|
||||
? tr::lng_group_call_duration_hours(tr::now, lt_count, hours)
|
||||
: tr::lng_group_call_duration_minutes(
|
||||
tr::now,
|
||||
lt_count,
|
||||
minutes);
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->getDelegate()->show(Box<InformBox>(
|
||||
tr::lng_cloud_password_reset_later(
|
||||
tr::now,
|
||||
lt_duration,
|
||||
duration)));
|
||||
}
|
||||
}, [=](const QString &error) mutable {
|
||||
finish("Error: " + error);
|
||||
}, [=]() mutable {
|
||||
finish({});
|
||||
}, *lifetime);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -185,21 +189,33 @@ PasscodeBox::PasscodeBox(
|
||||
|
||||
PasscodeBox::PasscodeBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
not_null<MTP::Instance*> mtp,
|
||||
Main::Session *session,
|
||||
const CloudFields &fields)
|
||||
: _session(session)
|
||||
, _api(&_session->mtp())
|
||||
, _api(mtp)
|
||||
, _turningOff(fields.turningOff)
|
||||
, _cloudPwd(true)
|
||||
, _cloudFields(fields)
|
||||
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
|
||||
, _oldPasscode(this, st::defaultInputField, tr::lng_cloud_password_enter_old())
|
||||
, _newPasscode(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_enter_new() : tr::lng_cloud_password_enter_first())
|
||||
, _newPasscode(
|
||||
this,
|
||||
st::defaultInputField,
|
||||
(fields.curRequest
|
||||
? tr::lng_cloud_password_enter_new()
|
||||
: tr::lng_cloud_password_enter_first()))
|
||||
, _reenterPasscode(this, st::defaultInputField, tr::lng_cloud_password_confirm_new())
|
||||
, _passwordHint(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_change_hint() : tr::lng_cloud_password_hint())
|
||||
, _passwordHint(
|
||||
this,
|
||||
st::defaultInputField,
|
||||
(fields.curRequest
|
||||
? tr::lng_cloud_password_change_hint()
|
||||
: tr::lng_cloud_password_hint()))
|
||||
, _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email())
|
||||
, _recover(this, tr::lng_signin_recover(tr::now))
|
||||
, _showRecoverLink(_cloudFields.hasRecovery || !_cloudFields.pendingResetDate) {
|
||||
Expects(session != nullptr || !fields.fromRecoveryCode.isEmpty());
|
||||
Expects(!_turningOff || _cloudFields.curRequest);
|
||||
|
||||
if (!_cloudFields.hint.isEmpty()) {
|
||||
@@ -209,6 +225,13 @@ PasscodeBox::PasscodeBox(
|
||||
}
|
||||
}
|
||||
|
||||
PasscodeBox::PasscodeBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const CloudFields &fields)
|
||||
: PasscodeBox(nullptr, &session->mtp(), session, fields) {
|
||||
}
|
||||
|
||||
rpl::producer<QByteArray> PasscodeBox::newPasswordSet() const {
|
||||
return _newPasswordSet.events();
|
||||
}
|
||||
@@ -221,6 +244,10 @@ rpl::producer<> PasscodeBox::clearUnconfirmedPassword() const {
|
||||
return _clearUnconfirmedPassword.events();
|
||||
}
|
||||
|
||||
rpl::producer<MTPauth_Authorization> PasscodeBox::newAuthorization() const {
|
||||
return _newAuthorization.events();
|
||||
}
|
||||
|
||||
bool PasscodeBox::currentlyHave() const {
|
||||
return _cloudPwd
|
||||
? (!!_cloudFields.curRequest)
|
||||
@@ -268,9 +295,11 @@ void PasscodeBox::prepare() {
|
||||
} else {
|
||||
_oldPasscode->hide();
|
||||
setTitle(_cloudPwd
|
||||
? tr::lng_cloud_password_create()
|
||||
? (_cloudFields.fromRecoveryCode.isEmpty()
|
||||
? tr::lng_cloud_password_create()
|
||||
: tr::lng_cloud_password_change())
|
||||
: tr::lng_passcode_create());
|
||||
setDimensions(st::boxWidth, st::passcodePadding.top() + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + (_cloudPwd ? (st::passcodeLittleSkip + _recoverEmail->height() + st::passcodeSkip) : st::passcodePadding.bottom()));
|
||||
setDimensions(st::boxWidth, st::passcodePadding.top() + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + ((_cloudPwd && _cloudFields.fromRecoveryCode.isEmpty()) ? (st::passcodeLittleSkip + _recoverEmail->height() + st::passcodeSkip) : st::passcodePadding.bottom()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +326,10 @@ void PasscodeBox::prepare() {
|
||||
_newPasscode->setVisible(!onlyCheck);
|
||||
_reenterPasscode->setVisible(!onlyCheck);
|
||||
_passwordHint->setVisible(!onlyCheck && _cloudPwd);
|
||||
_recoverEmail->setVisible(!onlyCheck && _cloudPwd && !has);
|
||||
_recoverEmail->setVisible(!onlyCheck
|
||||
&& _cloudPwd
|
||||
&& !has
|
||||
&& _cloudFields.fromRecoveryCode.isEmpty());
|
||||
}
|
||||
|
||||
void PasscodeBox::submit() {
|
||||
@@ -396,21 +428,44 @@ void PasscodeBox::setInnerFocus() {
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::recoverPasswordDone(
|
||||
const QByteArray &newPasswordBytes,
|
||||
const MTPauth_Authorization &result) {
|
||||
if (_replacedBy) {
|
||||
_replacedBy->closeBox();
|
||||
}
|
||||
_setRequest = 0;
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
_newAuthorization.fire_copy(result);
|
||||
if (weak) {
|
||||
_newPasswordSet.fire_copy(newPasswordBytes);
|
||||
if (weak) {
|
||||
getDelegate()->show(Box<InformBox>(
|
||||
tr::lng_cloud_password_updated(tr::now)));
|
||||
if (weak) {
|
||||
closeBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::setPasswordDone(const QByteArray &newPasswordBytes) {
|
||||
if (_replacedBy) {
|
||||
_replacedBy->closeBox();
|
||||
}
|
||||
_setRequest = 0;
|
||||
_newPasswordSet.fire_copy(newPasswordBytes);
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
const auto text = _reenterPasscode->isHidden()
|
||||
? tr::lng_cloud_password_removed(tr::now)
|
||||
: _oldPasscode->isHidden()
|
||||
? tr::lng_cloud_password_was_set(tr::now)
|
||||
: tr::lng_cloud_password_updated(tr::now);
|
||||
getDelegate()->show(Box<InformBox>(text));
|
||||
_newPasswordSet.fire_copy(newPasswordBytes);
|
||||
if (weak) {
|
||||
closeBox();
|
||||
const auto text = _reenterPasscode->isHidden()
|
||||
? tr::lng_cloud_password_removed(tr::now)
|
||||
: _oldPasscode->isHidden()
|
||||
? tr::lng_cloud_password_was_set(tr::now)
|
||||
: tr::lng_cloud_password_updated(tr::now);
|
||||
getDelegate()->show(Box<InformBox>(text));
|
||||
if (weak) {
|
||||
closeBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,25 +850,44 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
|
||||
}
|
||||
const auto hint = _passwordHint->getLastText();
|
||||
const auto email = _recoverEmail->getLastText().trimmed();
|
||||
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||
const auto flags = Flag::f_new_algo
|
||||
| Flag::f_new_password_hash
|
||||
| Flag::f_hint
|
||||
| (_cloudFields.fromRecoveryCode.isEmpty() ? Flag::f_email : Flag(0));
|
||||
_checkPasswordCallback = nullptr;
|
||||
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_inputCheckPasswordEmpty(),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(flags),
|
||||
Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo),
|
||||
MTP_bytes(newPasswordHash.modpow),
|
||||
MTP_string(hint),
|
||||
MTP_string(email),
|
||||
MTPSecureSecretSettings())
|
||||
)).done([=](const MTPBool &result) {
|
||||
setPasswordDone(newPasswordBytes);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
setPasswordFail(newPasswordBytes, email, error);
|
||||
}).handleFloodErrors().send();
|
||||
|
||||
const auto settings = MTP_account_passwordInputSettings(
|
||||
MTP_flags(flags),
|
||||
Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo),
|
||||
MTP_bytes(newPasswordHash.modpow),
|
||||
MTP_string(hint),
|
||||
MTP_string(email),
|
||||
MTPSecureSecretSettings());
|
||||
if (_cloudFields.fromRecoveryCode.isEmpty()) {
|
||||
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_inputCheckPasswordEmpty(),
|
||||
settings
|
||||
)).done([=](const MTPBool &result) {
|
||||
setPasswordDone(newPasswordBytes);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
setPasswordFail(newPasswordBytes, email, error);
|
||||
}).handleFloodErrors().send();
|
||||
} else {
|
||||
_setRequest = _api.request(MTPauth_RecoverPassword(
|
||||
MTP_flags(MTPauth_RecoverPassword::Flag::f_new_settings),
|
||||
MTP_string(_cloudFields.fromRecoveryCode),
|
||||
settings
|
||||
)).done([=](const MTPauth_Authorization &result) {
|
||||
recoverPasswordDone(newPasswordBytes, result);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (MTP::IsFloodError(error)) {
|
||||
_newError = tr::lng_flood_error(tr::now);
|
||||
update();
|
||||
}
|
||||
setPasswordFail(newPasswordBytes, email, error);
|
||||
}).handleFloodErrors().send();
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::changeCloudPassword(
|
||||
@@ -1001,6 +1075,7 @@ void PasscodeBox::emailChanged() {
|
||||
|
||||
void PasscodeBox::recoverByEmail() {
|
||||
if (!_cloudFields.hasRecovery) {
|
||||
Assert(_session != nullptr);
|
||||
const auto session = _session;
|
||||
const auto confirmBox = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto reset = crl::guard(this, [=] {
|
||||
@@ -1032,19 +1107,19 @@ void PasscodeBox::recoverExpired() {
|
||||
}
|
||||
|
||||
void PasscodeBox::recover() {
|
||||
if (_pattern == "-") return;
|
||||
if (_pattern == "-" || !_session) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
const auto box = getDelegate()->show(Box<RecoverBox>(
|
||||
&_api.instance(),
|
||||
_session,
|
||||
_pattern,
|
||||
_cloudFields.notEmptyPassport,
|
||||
_cloudFields.pendingResetDate != 0,
|
||||
_cloudFields,
|
||||
[weak] { if (weak) { weak->closeBox(); } }));
|
||||
|
||||
box->passwordCleared(
|
||||
) | rpl::map_to(
|
||||
QByteArray()
|
||||
box->newPasswordSet(
|
||||
) | rpl::start_to_stream(_newPasswordSet, lifetime());
|
||||
|
||||
box->recoveryExpired(
|
||||
@@ -1067,18 +1142,19 @@ void PasscodeBox::recoverStartFail(const MTP::Error &error) {
|
||||
|
||||
RecoverBox::RecoverBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
not_null<MTP::Instance*> mtp,
|
||||
Main::Session *session,
|
||||
const QString &pattern,
|
||||
bool notEmptyPassport,
|
||||
bool hasPendingReset,
|
||||
const PasscodeBox::CloudFields &fields,
|
||||
Fn<void()> closeParent)
|
||||
: _api(&session->mtp())
|
||||
: _session(session)
|
||||
, _api(mtp)
|
||||
, _pattern(st::normalFont->elided(tr::lng_signin_recover_hint(tr::now, lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5))
|
||||
, _notEmptyPassport(notEmptyPassport)
|
||||
, _cloudFields(fields)
|
||||
, _recoverCode(this, st::defaultInputField, tr::lng_signin_code())
|
||||
, _noEmailAccess(this, tr::lng_signin_try_password(tr::now))
|
||||
, _closeParent(std::move(closeParent)) {
|
||||
if (hasPendingReset) {
|
||||
if (_cloudFields.pendingResetDate != 0 || !session) {
|
||||
_noEmailAccess.destroy();
|
||||
} else {
|
||||
_noEmailAccess->setClickedCallback([=] {
|
||||
@@ -1102,8 +1178,8 @@ RecoverBox::RecoverBox(
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> RecoverBox::passwordCleared() const {
|
||||
return _passwordCleared.events();
|
||||
rpl::producer<QByteArray> RecoverBox::newPasswordSet() const {
|
||||
return _newPasswordSet.events();
|
||||
}
|
||||
|
||||
rpl::producer<> RecoverBox::recoveryExpired() const {
|
||||
@@ -1169,17 +1245,29 @@ void RecoverBox::submit() {
|
||||
}
|
||||
|
||||
const auto send = crl::guard(this, [=] {
|
||||
_submitRequest = _api.request(MTPauth_RecoverPassword(
|
||||
MTP_flags(0),
|
||||
MTP_string(code),
|
||||
MTPaccount_PasswordInputSettings()
|
||||
)).done([=](const MTPauth_Authorization &result) {
|
||||
codeSubmitDone(result);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
codeSubmitFail(error);
|
||||
}).handleFloodErrors().send();
|
||||
if (_cloudFields.turningOff) {
|
||||
// From "Disable cloud password".
|
||||
_submitRequest = _api.request(MTPauth_RecoverPassword(
|
||||
MTP_flags(0),
|
||||
MTP_string(code),
|
||||
MTPaccount_PasswordInputSettings()
|
||||
)).done([=](const MTPauth_Authorization &result) {
|
||||
proceedToClear();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
checkSubmitFail(error);
|
||||
}).handleFloodErrors().send();
|
||||
} else {
|
||||
// From "Change cloud password".
|
||||
_submitRequest = _api.request(MTPauth_CheckRecoveryPassword(
|
||||
MTP_string(code)
|
||||
)).done([=](const MTPBool &result) {
|
||||
proceedToChange(code);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
checkSubmitFail(error);
|
||||
}).handleFloodErrors().send();
|
||||
}
|
||||
});
|
||||
if (_notEmptyPassport) {
|
||||
if (_cloudFields.notEmptyPassport) {
|
||||
const auto confirmed = [=](Fn<void()> &&close) {
|
||||
send();
|
||||
close();
|
||||
@@ -1205,16 +1293,46 @@ void RecoverBox::codeChanged() {
|
||||
setError(QString());
|
||||
}
|
||||
|
||||
void RecoverBox::codeSubmitDone(const MTPauth_Authorization &result) {
|
||||
void RecoverBox::proceedToClear() {
|
||||
_submitRequest = 0;
|
||||
|
||||
_passwordCleared.fire({});
|
||||
_newPasswordSet.fire({});
|
||||
getDelegate()->show(
|
||||
Box<InformBox>(tr::lng_cloud_password_removed(tr::now)),
|
||||
Ui::LayerOption::CloseOther);
|
||||
}
|
||||
|
||||
void RecoverBox::codeSubmitFail(const MTP::Error &error) {
|
||||
void RecoverBox::proceedToChange(const QString &code) {
|
||||
Expects(!_cloudFields.turningOff);
|
||||
_submitRequest = 0;
|
||||
|
||||
auto fields = _cloudFields;
|
||||
fields.fromRecoveryCode = code;
|
||||
fields.hasRecovery = false;
|
||||
// we could've been turning off, no need to force new password then
|
||||
// like if (_cloudFields.turningOff) { just RecoverPassword else Check }
|
||||
fields.curRequest = {};
|
||||
auto box = Box<PasscodeBox>(_session, fields);
|
||||
|
||||
box->boxClosing(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
if (const auto onstack = _closeParent) {
|
||||
onstack();
|
||||
}
|
||||
if (weak) {
|
||||
weak->closeBox();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
box->newPasswordSet(
|
||||
) | rpl::start_with_next([=](QByteArray &&password) {
|
||||
_newPasswordSet.fire(std::move(password));
|
||||
}, lifetime());
|
||||
|
||||
getDelegate()->show(std::move(box));
|
||||
}
|
||||
|
||||
void RecoverBox::checkSubmitFail(const MTP::Error &error) {
|
||||
if (MTP::IsFloodError(error)) {
|
||||
_submitRequest = 0;
|
||||
setError(tr::lng_flood_error(tr::now));
|
||||
@@ -1225,7 +1343,7 @@ void RecoverBox::codeSubmitFail(const MTP::Error &error) {
|
||||
|
||||
const QString &err = error.type();
|
||||
if (err == qstr("PASSWORD_EMPTY")) {
|
||||
_passwordCleared.fire({});
|
||||
_newPasswordSet.fire(QByteArray());
|
||||
getDelegate()->show(
|
||||
Box<InformBox>(tr::lng_cloud_password_removed(tr::now)),
|
||||
Ui::LayerOption::CloseOther);
|
||||
|
||||
@@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/sender.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
|
||||
namespace MTP {
|
||||
class Instance;
|
||||
} // namespace MTP
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -35,6 +39,7 @@ public:
|
||||
Core::CloudPasswordCheckRequest curRequest;
|
||||
Core::CloudPasswordAlgo newAlgo;
|
||||
bool hasRecovery = false;
|
||||
QString fromRecoveryCode;
|
||||
bool notEmptyPassport = false;
|
||||
QString hint;
|
||||
Core::SecureSecretAlgo newSecureSecretAlgo;
|
||||
@@ -47,6 +52,11 @@ public:
|
||||
std::optional<QString> customDescription;
|
||||
rpl::producer<QString> customSubmitButton;
|
||||
};
|
||||
PasscodeBox(
|
||||
QWidget*,
|
||||
not_null<MTP::Instance*> mtp,
|
||||
Main::Session *session,
|
||||
const CloudFields &fields);
|
||||
PasscodeBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
@@ -56,6 +66,8 @@ public:
|
||||
rpl::producer<> passwordReloadNeeded() const;
|
||||
rpl::producer<> clearUnconfirmedPassword() const;
|
||||
|
||||
rpl::producer<MTPauth_Authorization> newAuthorization() const;
|
||||
|
||||
bool handleCustomCheckError(const MTP::Error &error);
|
||||
bool handleCustomCheckError(const QString &type);
|
||||
|
||||
@@ -83,6 +95,9 @@ private:
|
||||
bool onlyCheckCurrent() const;
|
||||
|
||||
void setPasswordDone(const QByteArray &newPasswordBytes);
|
||||
void recoverPasswordDone(
|
||||
const QByteArray &newPasswordBytes,
|
||||
const MTPauth_Authorization &result);
|
||||
void setPasswordFail(const MTP::Error &error);
|
||||
void setPasswordFail(const QString &type);
|
||||
void setPasswordFail(
|
||||
@@ -132,7 +147,7 @@ private:
|
||||
void passwordChecked();
|
||||
void serverError();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
Main::Session *_session = nullptr;
|
||||
MTP::Sender _api;
|
||||
|
||||
QString _pattern;
|
||||
@@ -163,6 +178,7 @@ private:
|
||||
QString _oldError, _newError, _emailError;
|
||||
|
||||
rpl::event_stream<QByteArray> _newPasswordSet;
|
||||
rpl::event_stream<MTPauth_Authorization> _newAuthorization;
|
||||
rpl::event_stream<> _passwordReloadNeeded;
|
||||
rpl::event_stream<> _clearUnconfirmedPassword;
|
||||
|
||||
@@ -172,14 +188,14 @@ class RecoverBox final : public Ui::BoxContent {
|
||||
public:
|
||||
RecoverBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
not_null<MTP::Instance*> mtp,
|
||||
Main::Session *session,
|
||||
const QString &pattern,
|
||||
bool notEmptyPassport,
|
||||
bool hasPendingReset,
|
||||
const PasscodeBox::CloudFields &fields,
|
||||
Fn<void()> closeParent = nullptr);
|
||||
|
||||
rpl::producer<> passwordCleared() const;
|
||||
rpl::producer<> recoveryExpired() const;
|
||||
[[nodiscard]] rpl::producer<QByteArray> newPasswordSet() const;
|
||||
[[nodiscard]] rpl::producer<> recoveryExpired() const;
|
||||
|
||||
//void reloadPassword();
|
||||
//void recoveryExpired();
|
||||
@@ -194,15 +210,18 @@ protected:
|
||||
private:
|
||||
void submit();
|
||||
void codeChanged();
|
||||
void codeSubmitDone(const MTPauth_Authorization &result);
|
||||
void codeSubmitFail(const MTP::Error &error);
|
||||
void proceedToClear();
|
||||
void proceedToChange(const QString &code);
|
||||
void checkSubmitFail(const MTP::Error &error);
|
||||
void setError(const QString &error);
|
||||
|
||||
Main::Session *_session = nullptr;
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _submitRequest = 0;
|
||||
|
||||
QString _pattern;
|
||||
bool _notEmptyPassport = false;
|
||||
|
||||
PasscodeBox::CloudFields _cloudFields;
|
||||
|
||||
object_ptr<Ui::InputField> _recoverCode;
|
||||
object_ptr<Ui::LinkButton> _noEmailAccess;
|
||||
@@ -210,7 +229,7 @@ private:
|
||||
|
||||
QString _error;
|
||||
|
||||
rpl::event_stream<> _passwordCleared;
|
||||
rpl::event_stream<QByteArray> _newPasswordSet;
|
||||
rpl::event_stream<> _recoveryExpired;
|
||||
|
||||
};
|
||||
|
||||
@@ -30,7 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "apiwrap.h"
|
||||
#include "facades.h" // Ui::showPeerHistory
|
||||
#include "app.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -47,7 +47,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "app.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_invite_links.h"
|
||||
#include "facades.h"
|
||||
|
||||
@@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "mainwindow.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/send_files_box.h"
|
||||
|
||||
#include "platform/platform_specific.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
@@ -52,7 +51,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "facades.h" // App::LambdaDelayed.
|
||||
#include "app.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -800,10 +798,7 @@ bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
||||
if (result.error == Ui::PreparedList::Error::None) {
|
||||
return result;
|
||||
} else if (data->hasImage()) {
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
return Storage::PrepareMediaFromImage(
|
||||
std::move(image),
|
||||
|
||||
@@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "api/api_updates.h"
|
||||
#include "app.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_layers.h" // st::boxLabel.
|
||||
#include "styles/style_calls.h"
|
||||
|
||||
@@ -736,7 +736,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
.receiveTimeout = serverConfig.callPacketTimeoutMs / 1000.,
|
||||
.dataSaving = tgcalls::DataSaving::Never,
|
||||
.enableP2P = call.is_p2p_allowed(),
|
||||
.enableAEC = !Platform::IsMac10_7OrGreater(),
|
||||
.enableAEC = false,
|
||||
.enableNS = true,
|
||||
.enableAGC = true,
|
||||
.enableVolumeControl = true,
|
||||
|
||||
@@ -34,7 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwidget.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "boxes/rate_call_box.h"
|
||||
#include "app.h"
|
||||
#include "app.h" // App::quitting
|
||||
|
||||
#include <tgcalls/VideoCaptureInterface.h>
|
||||
#include <tgcalls/StaticThreads.h>
|
||||
|
||||
@@ -45,7 +45,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "window/main_window.h"
|
||||
#include "app.h"
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "webrtc/webrtc_media_devices.h"
|
||||
#include "styles/style_calls.h"
|
||||
|
||||
@@ -32,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "base/timer.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_calls.h"
|
||||
#include "styles/style_chat.h" // style::GroupCallUserpics
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
@@ -1715,13 +1715,13 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
|
||||
void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
const MTPDgroupCall &data) {
|
||||
setScheduledDate(data.vschedule_date().value_or_empty());
|
||||
if (_acceptFields) {
|
||||
if (!_instance && !_id) {
|
||||
const auto input = MTP_inputGroupCall(
|
||||
data.vid(),
|
||||
data.vaccess_hash());
|
||||
const auto scheduleDate = data.vschedule_date().value_or_empty();
|
||||
setScheduledDate(scheduleDate);
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
chat->setGroupCall(input, scheduleDate);
|
||||
} else if (const auto group = _peer->asChannel()) {
|
||||
@@ -1735,6 +1735,7 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
} else if (_id != data.vid().v || !_instance) {
|
||||
return;
|
||||
}
|
||||
setScheduledDate(data.vschedule_date().value_or_empty());
|
||||
if (const auto streamDcId = data.vstream_dc_id()) {
|
||||
_broadcastDcId = MTP::BareDcId(streamDcId->v);
|
||||
}
|
||||
@@ -2782,6 +2783,8 @@ void GroupCall::checkLastSpoke() {
|
||||
|| muted() == MuteState::Active
|
||||
|| muted() == MuteState::PushToTalk) {
|
||||
real->applyLastSpoke(ssrc, when, now);
|
||||
} else {
|
||||
real->applyLastSpoke(ssrc, { crl::time(), crl::time() }, now);
|
||||
}
|
||||
}
|
||||
_lastSpoke = std::move(list);
|
||||
|
||||
@@ -246,7 +246,20 @@ private:
|
||||
not_null<Row*> row,
|
||||
const std::optional<Data::GroupCallParticipant> &was,
|
||||
const Data::GroupCallParticipant *participant);
|
||||
void updateRowInSoundingMap(
|
||||
not_null<Row*> row,
|
||||
bool wasSounding,
|
||||
uint32 wasSsrc,
|
||||
uint32 wasAdditionalSsrc,
|
||||
const Data::GroupCallParticipant *participant);
|
||||
void updateRowInSoundingMap(
|
||||
not_null<Row*> row,
|
||||
bool wasSounding,
|
||||
uint32 wasSsrc,
|
||||
bool nowSounding,
|
||||
uint32 nowSsrc);
|
||||
void removeRow(not_null<Row*> row);
|
||||
void removeRowFromSoundingMap(not_null<Row*> row);
|
||||
void updateRowLevel(not_null<Row*> row, float level);
|
||||
void checkRowPosition(not_null<Row*> row);
|
||||
[[nodiscard]] bool needToReorder(not_null<Row*> row) const;
|
||||
@@ -804,14 +817,50 @@ void Members::Controller::updateRow(
|
||||
: 0;
|
||||
row->setSkipLevelUpdate(_skipRowLevelUpdate);
|
||||
row->updateState(participant);
|
||||
|
||||
const auto wasNoSounding = _soundingRowBySsrc.empty();
|
||||
updateRowInSoundingMap(
|
||||
row,
|
||||
wasSounding,
|
||||
wasSsrc,
|
||||
wasAdditionalSsrc,
|
||||
participant);
|
||||
const auto nowNoSounding = _soundingRowBySsrc.empty();
|
||||
if (wasNoSounding && !nowNoSounding) {
|
||||
_soundingAnimation.start();
|
||||
} else if (nowNoSounding && !wasNoSounding) {
|
||||
_soundingAnimation.stop();
|
||||
}
|
||||
|
||||
delegate()->peerListUpdateRow(row);
|
||||
}
|
||||
|
||||
void Members::Controller::updateRowInSoundingMap(
|
||||
not_null<Row*> row,
|
||||
bool wasSounding,
|
||||
uint32 wasSsrc,
|
||||
uint32 wasAdditionalSsrc,
|
||||
const Data::GroupCallParticipant *participant) {
|
||||
const auto nowSounding = row->sounding();
|
||||
const auto nowSsrc = participant ? participant->ssrc : 0;
|
||||
const auto nowAdditionalSsrc = participant
|
||||
? GetAdditionalAudioSsrc(participant->videoParams)
|
||||
: 0;
|
||||
updateRowInSoundingMap(row, wasSounding, wasSsrc, nowSounding, nowSsrc);
|
||||
updateRowInSoundingMap(
|
||||
row,
|
||||
wasSounding,
|
||||
wasAdditionalSsrc,
|
||||
nowSounding,
|
||||
nowAdditionalSsrc);
|
||||
}
|
||||
|
||||
const auto wasNoSounding = _soundingRowBySsrc.empty();
|
||||
|
||||
void Members::Controller::updateRowInSoundingMap(
|
||||
not_null<Row*> row,
|
||||
bool wasSounding,
|
||||
uint32 wasSsrc,
|
||||
bool nowSounding,
|
||||
uint32 nowSsrc) {
|
||||
if (wasSsrc == nowSsrc) {
|
||||
if (nowSsrc && nowSounding != wasSounding) {
|
||||
if (nowSounding) {
|
||||
@@ -826,32 +875,14 @@ void Members::Controller::updateRow(
|
||||
_soundingRowBySsrc.emplace(nowSsrc, row);
|
||||
}
|
||||
}
|
||||
if (wasAdditionalSsrc == nowAdditionalSsrc) {
|
||||
if (nowAdditionalSsrc && nowSounding != wasSounding) {
|
||||
if (nowSounding) {
|
||||
_soundingRowBySsrc.emplace(nowAdditionalSsrc, row);
|
||||
} else {
|
||||
_soundingRowBySsrc.remove(nowAdditionalSsrc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_soundingRowBySsrc.remove(wasAdditionalSsrc);
|
||||
if (nowSounding && nowAdditionalSsrc) {
|
||||
_soundingRowBySsrc.emplace(nowAdditionalSsrc, row);
|
||||
}
|
||||
}
|
||||
|
||||
const auto nowNoSounding = _soundingRowBySsrc.empty();
|
||||
if (wasNoSounding && !nowNoSounding) {
|
||||
_soundingAnimation.start();
|
||||
} else if (nowNoSounding && !wasNoSounding) {
|
||||
_soundingAnimation.stop();
|
||||
}
|
||||
|
||||
delegate()->peerListUpdateRow(row);
|
||||
}
|
||||
|
||||
void Members::Controller::removeRow(not_null<Row*> row) {
|
||||
removeRowFromSoundingMap(row);
|
||||
delegate()->peerListRemoveRow(row);
|
||||
}
|
||||
|
||||
void Members::Controller::removeRowFromSoundingMap(not_null<Row*> row) {
|
||||
// There may be 0, 1 or 2 entries for a row.
|
||||
for (auto i = begin(_soundingRowBySsrc); i != end(_soundingRowBySsrc);) {
|
||||
if (i->second == row) {
|
||||
@@ -860,7 +891,6 @@ void Members::Controller::removeRow(not_null<Row*> row) {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
delegate()->peerListRemoveRow(row);
|
||||
}
|
||||
|
||||
void Members::Controller::updateRowLevel(
|
||||
@@ -944,18 +974,22 @@ void Members::Controller::prepareRows(not_null<Data::GroupCall*> real) {
|
||||
auto changed = false;
|
||||
auto count = delegate()->peerListFullRowsCount();
|
||||
for (auto i = 0; i != count;) {
|
||||
auto row = delegate()->peerListRowAt(i);
|
||||
auto participantPeer = row->peer();
|
||||
if (isMe(participantPeer)) {
|
||||
const auto row = static_cast<Row*>(
|
||||
delegate()->peerListRowAt(i).get());
|
||||
removeRowFromSoundingMap(row);
|
||||
const auto participantPeer = row->peer();
|
||||
const auto me = isMe(participantPeer);
|
||||
if (me) {
|
||||
foundMe = true;
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (real->participantByPeer(participantPeer)) {
|
||||
if (const auto found = real->participantByPeer(participantPeer)) {
|
||||
updateRowInSoundingMap(row, false, 0, 0, found);
|
||||
++i;
|
||||
} else if (me) {
|
||||
++i;
|
||||
} else {
|
||||
changed = true;
|
||||
removeRow(static_cast<Row*>(row.get()));
|
||||
removeRow(row);
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "app.h"
|
||||
#include "apiwrap.h" // api().kickParticipant.
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "webrtc/webrtc_media_devices.h" // UniqueDesktopCaptureSource.
|
||||
|
||||
@@ -33,7 +33,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "app.h"
|
||||
#include "storage/storage_account.h" // Account::writeSavedGifs
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/timer.h"
|
||||
#include "inline_bots/inline_bot_layout_item.h"
|
||||
#include "layout/layout_mosaic.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
@@ -133,4 +136,45 @@ void SetupMenuAndShortcuts(
|
||||
}, button->lifetime());
|
||||
}
|
||||
|
||||
void SetupUnreadMentionsMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<PeerData*()> currentPeer) {
|
||||
struct State {
|
||||
base::unique_qptr<Ui::PopupMenu> menu;
|
||||
base::flat_set<not_null<PeerData*>> sentForPeers;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
const auto showMenu = [=] {
|
||||
const auto peer = currentPeer();
|
||||
if (!peer) {
|
||||
return;
|
||||
}
|
||||
state->menu = base::make_unique_q<Ui::PopupMenu>(button);
|
||||
const auto text = tr::lng_context_mark_read_mentions_all(tr::now);
|
||||
state->menu->addAction(text, [=] {
|
||||
if (!state->sentForPeers.emplace(peer).second) {
|
||||
return;
|
||||
}
|
||||
peer->session().api().request(MTPmessages_ReadMentions(
|
||||
peer->input
|
||||
)).done([=](const MTPmessages_AffectedHistory &result) {
|
||||
state->sentForPeers.remove(peer);
|
||||
peer->session().api().applyAffectedHistory(peer, result);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
state->sentForPeers.remove(peer);
|
||||
}).send();
|
||||
});
|
||||
state->menu->popup(QCursor::pos());
|
||||
};
|
||||
|
||||
base::install_event_filter(button, [=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::ContextMenu) {
|
||||
showMenu();
|
||||
return base::EventFilterResult::Cancel;
|
||||
}
|
||||
return base::EventFilterResult::Continue;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
} // namespace SendMenu
|
||||
|
||||
@@ -50,4 +50,8 @@ void SetupMenuAndShortcuts(
|
||||
Fn<void()> silent,
|
||||
Fn<void()> schedule);
|
||||
|
||||
void SetupUnreadMentionsMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<PeerData*()> currentPeer);
|
||||
|
||||
} // namespace SendMenu
|
||||
|
||||
@@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/application.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
|
||||
@@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "mainwindow.h"
|
||||
#include "core/application.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
namespace ChatHelpers {
|
||||
|
||||
@@ -26,7 +26,7 @@ object_ptr<Window::SectionWidget> TabbedMemento::createWidget(
|
||||
TabbedSection::TabbedSection(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Custom)
|
||||
, _selector(controller->tabbedSelector()) {
|
||||
_selector->setParent(this);
|
||||
_selector->setRoundRadius(0);
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "core/application.h"
|
||||
|
||||
#include "data/data_abstract_structure.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -64,6 +65,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "storage/serialize_common.h"
|
||||
#include "storage/storage_domain.h"
|
||||
#include "storage/storage_databases.h"
|
||||
@@ -172,7 +174,8 @@ Application::~Application() {
|
||||
Ui::Emoji::Clear();
|
||||
Media::Clip::Finish();
|
||||
|
||||
App::deinitMedia();
|
||||
Ui::FinishCachedCorners();
|
||||
Data::clearGlobalStructures();
|
||||
|
||||
Window::Theme::Uninitialize();
|
||||
|
||||
@@ -217,6 +220,7 @@ void Application::run() {
|
||||
|
||||
style::startManager(cScale());
|
||||
Ui::InitTextOptions();
|
||||
Ui::StartCachedCorners();
|
||||
Ui::Emoji::Init();
|
||||
startEmojiImageLoader();
|
||||
startSystemDarkModeViewer();
|
||||
@@ -262,7 +266,6 @@ void Application::run() {
|
||||
|
||||
// Depend on activeWindow() for now :(
|
||||
startShortcuts();
|
||||
App::initMedia();
|
||||
startDomain();
|
||||
|
||||
_window->widget()->show();
|
||||
|
||||
@@ -88,6 +88,21 @@ std::map<int, const char*> BetaLogs() {
|
||||
|
||||
"- Fix Direct3D acceleration on basic Windows 7 setup.\n"
|
||||
},
|
||||
{
|
||||
2009004,
|
||||
"- Choose one from dozens of new gorgeous animated backgrounds"
|
||||
" in Chat Settings > Chat background.\n"
|
||||
},
|
||||
{
|
||||
2009005,
|
||||
"- Tile chat background patterns horizontally.\n"
|
||||
|
||||
"- Fix a rare crash in spellchecker on Windows.\n"
|
||||
|
||||
"- Fix animated chat backgrounds in Saved Messages.\n"
|
||||
|
||||
"- Fix \"Sorry, group is inaccessible\" message in scheduled voice chats.\n",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "facades.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
|
||||
@@ -299,10 +299,8 @@ QString PlatformString() {
|
||||
return qsl("MacAppStore");
|
||||
} else if (Platform::IsMac()) {
|
||||
return qsl("MacOS");
|
||||
} else if (Platform::IsLinux32Bit()) {
|
||||
return qsl("Linux32Bit");
|
||||
} else if (Platform::IsLinux64Bit()) {
|
||||
return qsl("Linux64bit");
|
||||
} else if (Platform::IsLinux()) {
|
||||
return qsl("Linux");
|
||||
}
|
||||
Unexpected("Platform in CrashReports::PlatformString.");
|
||||
}
|
||||
|
||||
@@ -286,10 +286,7 @@ void Launcher::init() {
|
||||
initQtMessageLogging();
|
||||
|
||||
QApplication::setApplicationName(qsl("TelegramDesktop"));
|
||||
|
||||
#ifndef OS_MAC_OLD
|
||||
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
|
||||
#endif // OS_MAC_OLD
|
||||
|
||||
// fallback session management is useless for tdesktop since it doesn't have
|
||||
// any "are you sure you want to close this window?" dialogs
|
||||
@@ -427,9 +424,17 @@ void Launcher::initQtMessageLogging() {
|
||||
QtMsgType type,
|
||||
const QMessageLogContext &context,
|
||||
const QString &msg) {
|
||||
if (OriginalMessageHandler) {
|
||||
OriginalMessageHandler(type, context, msg);
|
||||
}
|
||||
const auto InvokeOriginal = [&] {
|
||||
#ifndef _DEBUG
|
||||
if (Logs::DebugEnabled()) {
|
||||
return;
|
||||
}
|
||||
#endif // _DEBUG
|
||||
if (OriginalMessageHandler) {
|
||||
OriginalMessageHandler(type, context, msg);
|
||||
}
|
||||
};
|
||||
InvokeOriginal();
|
||||
if (Logs::DebugEnabled() || !Logs::started()) {
|
||||
if (!Logs::WritingEntry()) {
|
||||
// Sometimes Qt logs something inside our own logging.
|
||||
@@ -450,7 +455,6 @@ void Launcher::processArguments() {
|
||||
AllLeftValues,
|
||||
};
|
||||
auto parseMap = std::map<QByteArray, KeyFormat> {
|
||||
{ "-testmode" , KeyFormat::NoValues },
|
||||
{ "-debug" , KeyFormat::NoValues },
|
||||
{ "-freetype" , KeyFormat::NoValues },
|
||||
{ "-many" , KeyFormat::NoValues },
|
||||
@@ -459,7 +463,6 @@ void Launcher::processArguments() {
|
||||
{ "-fixprevious" , KeyFormat::NoValues },
|
||||
{ "-cleanup" , KeyFormat::NoValues },
|
||||
{ "-noupdate" , KeyFormat::NoValues },
|
||||
{ "-externalupdater", KeyFormat::NoValues },
|
||||
{ "-tosettings" , KeyFormat::NoValues },
|
||||
{ "-startintray" , KeyFormat::NoValues },
|
||||
{ "-sendpath" , KeyFormat::AllLeftValues },
|
||||
@@ -490,9 +493,6 @@ void Launcher::processArguments() {
|
||||
}
|
||||
}
|
||||
|
||||
if (parseResult.contains("-externalupdater")) {
|
||||
SetUpdaterDisabledAtStartup();
|
||||
}
|
||||
gUseFreeType = parseResult.contains("-freetype");
|
||||
gDebugMode = parseResult.contains("-debug");
|
||||
gManyInstance = parseResult.contains("-many");
|
||||
|
||||
@@ -40,7 +40,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
@@ -232,15 +231,16 @@ bool ShowWallPaper(
|
||||
const auto params = url_parse_params(
|
||||
match->captured(1),
|
||||
qthelp::UrlParamNameTransform::ToLower);
|
||||
if (!params.value("gradient").isEmpty()) {
|
||||
Ui::show(Box<InformBox>(
|
||||
tr::lng_background_gradient_unsupported(tr::now)));
|
||||
return false;
|
||||
}
|
||||
const auto bg = params.value("bg_color");
|
||||
const auto color = params.value("color");
|
||||
const auto gradient = params.value("gradient");
|
||||
return BackgroundPreviewBox::Start(
|
||||
controller,
|
||||
(color.isEmpty() ? params.value(qsl("slug")) : color),
|
||||
(!color.isEmpty()
|
||||
? color
|
||||
: !gradient.isEmpty()
|
||||
? gradient
|
||||
: params.value(qsl("slug"))),
|
||||
params);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ QStringList MimeType::globPatterns() const {
|
||||
switch (_type) {
|
||||
case Known::WebP: return QStringList(u"*.webp"_q);
|
||||
case Known::Tgs: return QStringList(u"*.tgs"_q);
|
||||
case Known::Tgv: return QStringList(u"*.tgv"_q);
|
||||
case Known::TDesktopTheme: return QStringList(u"*.tdesktop-theme"_q);
|
||||
case Known::TDesktopPalette: return QStringList(u"*.tdesktop-palette"_q);
|
||||
default: break;
|
||||
@@ -32,6 +33,7 @@ QString MimeType::filterString() const {
|
||||
switch (_type) {
|
||||
case Known::WebP: return u"WebP image (*.webp)"_q;
|
||||
case Known::Tgs: return u"Telegram sticker (*.tgs)"_q;
|
||||
case Known::Tgv: return u"Wallpaper pattern (*.tgv)"_q;
|
||||
case Known::TDesktopTheme: return u"Theme files (*.tdesktop-theme)"_q;
|
||||
case Known::TDesktopPalette: return u"Palette files (*.tdesktop-palette)"_q;
|
||||
default: break;
|
||||
@@ -43,6 +45,7 @@ QString MimeType::name() const {
|
||||
switch (_type) {
|
||||
case Known::WebP: return u"image/webp"_q;
|
||||
case Known::Tgs: return u"application/x-tgsticker"_q;
|
||||
case Known::Tgv: return u"application/x-tgwallpattern"_q;
|
||||
case Known::TDesktopTheme: return u"application/x-tdesktop-theme"_q;
|
||||
case Known::TDesktopPalette: return u"application/x-tdesktop-palette"_q;
|
||||
default: break;
|
||||
@@ -55,6 +58,8 @@ MimeType MimeTypeForName(const QString &mime) {
|
||||
return MimeType(MimeType::Known::WebP);
|
||||
} else if (mime == qstr("application/x-tgsticker")) {
|
||||
return MimeType(MimeType::Known::Tgs);
|
||||
} else if (mime == qstr("application/x-tgwallpattern")) {
|
||||
return MimeType(MimeType::Known::Tgv);
|
||||
} else if (mime == qstr("application/x-tdesktop-theme")
|
||||
|| mime == qstr("application/x-tgtheme-tdesktop")) {
|
||||
return MimeType(MimeType::Known::TDesktopTheme);
|
||||
@@ -72,6 +77,8 @@ MimeType MimeTypeForFile(const QFileInfo &file) {
|
||||
return MimeType(MimeType::Known::WebP);
|
||||
} else if (path.endsWith(qstr(".tgs"), Qt::CaseInsensitive)) {
|
||||
return MimeType(MimeType::Known::Tgs);
|
||||
} else if (path.endsWith(qstr(".tgv"))) {
|
||||
return MimeType(MimeType::Known::Tgv);
|
||||
} else if (path.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)) {
|
||||
return MimeType(MimeType::Known::TDesktopTheme);
|
||||
} else if (path.endsWith(qstr(".tdesktop-palette"), Qt::CaseInsensitive)) {
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
TDesktopPalette,
|
||||
WebP,
|
||||
Tgs,
|
||||
Tgv,
|
||||
};
|
||||
|
||||
explicit MimeType(const QMimeType &type);
|
||||
|
||||
@@ -242,9 +242,7 @@ QString FindUpdateFile() {
|
||||
"tupdate|"
|
||||
"tx64upd|"
|
||||
"tmacupd|"
|
||||
"tosxupd|"
|
||||
"tlinuxupd|"
|
||||
"tlinux32upd"
|
||||
")\\d+(_[a-z\\d]+)?$",
|
||||
QRegularExpression::CaseInsensitiveOption
|
||||
).match(info.fileName()).hasMatch()) {
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 2009003;
|
||||
constexpr auto AppVersionStr = "2.9.3";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppVersion = 2009007;
|
||||
constexpr auto AppVersionStr = "2.9.7";
|
||||
constexpr auto AppBetaVersion = true;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -45,7 +45,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwindow.h"
|
||||
#include "core/application.h"
|
||||
#include "lottie/lottie_animation.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QMimeType>
|
||||
@@ -412,7 +411,7 @@ void DocumentData::setattributes(
|
||||
|
||||
void DocumentData::validateLottieSticker() {
|
||||
if (type == FileDocument
|
||||
&& _mimeString == qstr("application/x-tgsticker")) {
|
||||
&& hasMimeType(qstr("application/x-tgsticker"))) {
|
||||
type = StickerDocument;
|
||||
_additional = std::make_unique<StickerData>();
|
||||
sticker()->animated = true;
|
||||
@@ -443,9 +442,8 @@ bool DocumentData::checkWallPaperProperties() {
|
||||
|| !dimensions.height()
|
||||
|| dimensions.width() > Storage::kMaxWallPaperDimension
|
||||
|| dimensions.height() > Storage::kMaxWallPaperDimension
|
||||
|| size > Storage::kMaxWallPaperInMemory
|
||||
|| mimeString() == qstr("application/x-tgwallpattern")) {
|
||||
return false; // #TODO themes support svg patterns
|
||||
|| size > Storage::kMaxWallPaperInMemory) {
|
||||
return false;
|
||||
}
|
||||
type = WallPaperDocument;
|
||||
return true;
|
||||
@@ -488,9 +486,18 @@ bool DocumentData::isWallPaper() const {
|
||||
}
|
||||
|
||||
bool DocumentData::isPatternWallPaper() const {
|
||||
return isWallPaper()
|
||||
&& (isPatternWallPaperPNG() || isPatternWallPaperSVG());
|
||||
}
|
||||
|
||||
bool DocumentData::isPatternWallPaperPNG() const {
|
||||
return isWallPaper() && hasMimeType(qstr("image/png"));
|
||||
}
|
||||
|
||||
bool DocumentData::isPatternWallPaperSVG() const {
|
||||
return isWallPaper() && hasMimeType(qstr("application/x-tgwallpattern"));
|
||||
}
|
||||
|
||||
bool DocumentData::hasThumbnail() const {
|
||||
return _thumbnail.location.valid();
|
||||
}
|
||||
@@ -662,9 +669,9 @@ bool DocumentData::saveToCache() const {
|
||||
&& ((type == StickerDocument)
|
||||
|| isAnimation()
|
||||
|| isVoiceMessage()
|
||||
|| (type == WallPaperDocument)
|
||||
|| isWallPaper()
|
||||
|| isTheme()
|
||||
|| (mimeString() == qstr("image/png")
|
||||
|| (hasMimeType(qstr("image/png"))
|
||||
&& _filename.startsWith("image_")));
|
||||
}
|
||||
|
||||
@@ -1234,11 +1241,12 @@ QString DocumentData::mimeString() const {
|
||||
}
|
||||
|
||||
bool DocumentData::hasMimeType(QLatin1String mime) const {
|
||||
return !_mimeString.compare(mime, Qt::CaseInsensitive);
|
||||
return (_mimeString == mime);
|
||||
}
|
||||
|
||||
void DocumentData::setMimeString(const QString &mime) {
|
||||
_mimeString = mime;
|
||||
_mimeString = std::move(_mimeString).toLower();
|
||||
}
|
||||
|
||||
MediaKey DocumentData::mediaKey() const {
|
||||
@@ -1264,7 +1272,7 @@ uint8 DocumentData::cacheTag() const {
|
||||
return Data::kVideoMessageCacheTag;
|
||||
} else if (isAnimation()) {
|
||||
return Data::kAnimationCacheTag;
|
||||
} else if (type == WallPaperDocument) {
|
||||
} else if (isWallPaper()) {
|
||||
return Data::kImageCacheTag;
|
||||
}
|
||||
return 0;
|
||||
@@ -1299,14 +1307,9 @@ bool DocumentData::isGifv() const {
|
||||
}
|
||||
|
||||
bool DocumentData::isTheme() const {
|
||||
return
|
||||
_mimeString == qstr("application/x-tgtheme-tdesktop")
|
||||
|| _filename.endsWith(
|
||||
qstr(".tdesktop-theme"),
|
||||
Qt::CaseInsensitive)
|
||||
|| _filename.endsWith(
|
||||
qstr(".tdesktop-palette"),
|
||||
Qt::CaseInsensitive);
|
||||
return hasMimeType(qstr("application/x-tgtheme-tdesktop"))
|
||||
|| _filename.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)
|
||||
|| _filename.endsWith(qstr(".tdesktop-palette"), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool DocumentData::isSong() const {
|
||||
|
||||
@@ -155,6 +155,8 @@ public:
|
||||
bool checkWallPaperProperties();
|
||||
[[nodiscard]] bool isWallPaper() const;
|
||||
[[nodiscard]] bool isPatternWallPaper() const;
|
||||
[[nodiscard]] bool isPatternWallPaperPNG() const;
|
||||
[[nodiscard]] bool isPatternWallPaperSVG() const;
|
||||
|
||||
[[nodiscard]] bool hasThumbnail() const;
|
||||
[[nodiscard]] bool thumbnailLoading() const;
|
||||
|
||||
@@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/application.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QImageReader>
|
||||
@@ -40,6 +39,8 @@ enum class FileType {
|
||||
Video,
|
||||
AnimatedSticker,
|
||||
WallPaper,
|
||||
WallPatternPNG,
|
||||
WallPatternSVG,
|
||||
Theme,
|
||||
};
|
||||
|
||||
@@ -61,6 +62,15 @@ enum class FileType {
|
||||
return Lottie::ReadThumbnail(Lottie::ReadContent(data, path));
|
||||
} else if (type == FileType::Theme) {
|
||||
return Window::Theme::GeneratePreview(data, path);
|
||||
} else if (type == FileType::WallPatternSVG) {
|
||||
return Images::Read({
|
||||
.path = path,
|
||||
.content = std::move(data),
|
||||
.maxSize = QSize(
|
||||
kWallPaperThumbnailLimit,
|
||||
kWallPaperThumbnailLimit),
|
||||
.gzipSvg = true,
|
||||
}).image;
|
||||
}
|
||||
auto buffer = QBuffer(&data);
|
||||
auto file = QFile(path);
|
||||
@@ -391,7 +401,11 @@ void DocumentMedia::checkStickerLarge(not_null<FileLoader*> loader) {
|
||||
void DocumentMedia::GenerateGoodThumbnail(
|
||||
not_null<DocumentData*> document,
|
||||
QByteArray data) {
|
||||
const auto type = document->isWallPaper()
|
||||
const auto type = document->isPatternWallPaperSVG()
|
||||
? FileType::WallPatternSVG
|
||||
: document->isPatternWallPaperPNG()
|
||||
? FileType::WallPatternPNG
|
||||
: document->isWallPaper()
|
||||
? FileType::WallPaper
|
||||
: document->isTheme()
|
||||
? FileType::Theme
|
||||
@@ -416,7 +430,8 @@ void DocumentMedia::GenerateGoodThumbnail(
|
||||
auto buffer = QBuffer(&bytes);
|
||||
const auto format = (type == FileType::AnimatedSticker)
|
||||
? "WEBP"
|
||||
: (type == FileType::WallPaper && result.hasAlphaChannel())
|
||||
: (type == FileType::WallPatternPNG
|
||||
|| type == FileType::WallPatternSVG)
|
||||
? "PNG"
|
||||
: "JPG";
|
||||
result.save(&buffer, format, kGoodThumbQuality);
|
||||
@@ -464,11 +479,11 @@ void DocumentMedia::ReadOrGenerateThumbnail(
|
||||
});
|
||||
} else if (active) {
|
||||
crl::async([=] {
|
||||
const auto image = App::readImage(value, nullptr, false);
|
||||
crl::on_main(guard, [=] {
|
||||
auto image = Images::Read({ .content = value }).image;
|
||||
crl::on_main(guard, [=, image = std::move(image)]() mutable {
|
||||
document->setGoodThumbnailChecked(true);
|
||||
if (const auto active = document->activeMediaView()) {
|
||||
active->setGoodThumbnail(image);
|
||||
active->setGoodThumbnail(std::move(image));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_document_resolver.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "facades.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
@@ -34,6 +33,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxWallpaperSize = 3072;
|
||||
|
||||
void LaunchWithWarning(
|
||||
// not_null<Window::Controller*> controller,
|
||||
const QString &name,
|
||||
@@ -174,29 +175,33 @@ bool IsIpRevealingName(const QString &filepath) {
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage ReadImage(
|
||||
const QString &path,
|
||||
const QByteArray &content,
|
||||
bool gzipSvg) {
|
||||
return Images::Read({
|
||||
.path = path,
|
||||
.content = content,
|
||||
.maxSize = QSize(kMaxWallpaperSize, kMaxWallpaperSize),
|
||||
.gzipSvg = gzipSvg,
|
||||
}).image;
|
||||
}
|
||||
|
||||
base::binary_guard ReadImageAsync(
|
||||
not_null<Data::DocumentMedia*> media,
|
||||
FnMut<QImage(QImage)> postprocess,
|
||||
FnMut<void(QImage&&)> done) {
|
||||
auto result = base::binary_guard();
|
||||
const auto gzipSvg = media->owner()->isPatternWallPaperSVG();
|
||||
crl::async([
|
||||
gzipSvg,
|
||||
bytes = media->bytes(),
|
||||
path = media->owner()->filepath(),
|
||||
postprocess = std::move(postprocess),
|
||||
guard = result.make_guard(),
|
||||
callback = std::move(done)
|
||||
]() mutable {
|
||||
auto format = QByteArray();
|
||||
if (bytes.isEmpty()) {
|
||||
QFile f(path);
|
||||
if (f.size() <= App::kImageSizeLimit
|
||||
&& f.open(QIODevice::ReadOnly)) {
|
||||
bytes = f.readAll();
|
||||
}
|
||||
}
|
||||
auto image = bytes.isEmpty()
|
||||
? QImage()
|
||||
: App::readImage(bytes, &format, false, nullptr);
|
||||
auto image = ReadImage(path, bytes, gzipSvg);
|
||||
if (postprocess) {
|
||||
image = postprocess(std::move(image));
|
||||
}
|
||||
@@ -231,7 +236,7 @@ void ResolveDocument(
|
||||
|
||||
const auto media = document->createMediaView();
|
||||
const auto openImageInApp = [&] {
|
||||
if (document->size >= App::kImageSizeLimit) {
|
||||
if (document->size >= Images::kReadBytesLimit) {
|
||||
return false;
|
||||
}
|
||||
const auto &location = document->location(true);
|
||||
|
||||
@@ -244,6 +244,14 @@ bool Media::forwardedBecomesUnread() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Media::dropForwardedInfo() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Media::forceForwardedInfo() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString Media::errorTextForForward(not_null<PeerData*> peer) const {
|
||||
return QString();
|
||||
}
|
||||
@@ -625,6 +633,10 @@ bool MediaFile::forwardedBecomesUnread() const {
|
||||
|| _document->isVideoMessage();
|
||||
}
|
||||
|
||||
bool MediaFile::dropForwardedInfo() const {
|
||||
return _document->isSong();
|
||||
}
|
||||
|
||||
QString MediaFile::errorTextForForward(not_null<PeerData*> peer) const {
|
||||
if (const auto sticker = _document->sticker()) {
|
||||
if (const auto error = Data::RestrictionError(
|
||||
@@ -1007,7 +1019,8 @@ WebPageData *MediaWebPage::webpage() const {
|
||||
|
||||
bool MediaWebPage::hasReplyPreview() const {
|
||||
if (const auto document = MediaWebPage::document()) {
|
||||
return document->hasThumbnail() && !document->isPatternWallPaper();
|
||||
return document->hasThumbnail()
|
||||
&& !document->isPatternWallPaper();
|
||||
} else if (const auto photo = MediaWebPage::photo()) {
|
||||
return !photo->isNull();
|
||||
}
|
||||
@@ -1139,6 +1152,10 @@ QString MediaGame::errorTextForForward(not_null<PeerData*> peer) const {
|
||||
).value_or(QString());
|
||||
}
|
||||
|
||||
bool MediaGame::dropForwardedInfo() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MediaGame::consumeMessageText(const TextWithEntities &text) {
|
||||
_consumedText = text;
|
||||
return true;
|
||||
@@ -1346,6 +1363,10 @@ TextForMimeData MediaDice::clipboardText() const {
|
||||
return { notificationText() };
|
||||
}
|
||||
|
||||
bool MediaDice::forceForwardedInfo() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MediaDice::updateInlineResultMedia(const MTPMessageMedia &media) {
|
||||
return updateSentMedia(media);
|
||||
}
|
||||
|
||||
@@ -102,6 +102,8 @@ public:
|
||||
virtual bool allowsEditMedia() const;
|
||||
virtual bool allowsRevoke(TimeId now) const;
|
||||
virtual bool forwardedBecomesUnread() const;
|
||||
virtual bool dropForwardedInfo() const;
|
||||
virtual bool forceForwardedInfo() const;
|
||||
virtual QString errorTextForForward(not_null<PeerData*> peer) const;
|
||||
|
||||
[[nodiscard]] virtual bool consumeMessageText(
|
||||
@@ -191,6 +193,7 @@ public:
|
||||
bool allowsEditCaption() const override;
|
||||
bool allowsEditMedia() const override;
|
||||
bool forwardedBecomesUnread() const override;
|
||||
bool dropForwardedInfo() const override;
|
||||
QString errorTextForForward(not_null<PeerData*> peer) const override;
|
||||
|
||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||
@@ -352,6 +355,7 @@ public:
|
||||
QString pinnedTextSubstring() const override;
|
||||
TextForMimeData clipboardText() const override;
|
||||
QString errorTextForForward(not_null<PeerData*> peer) const override;
|
||||
bool dropForwardedInfo() const override;
|
||||
|
||||
bool consumeMessageText(const TextWithEntities &text) override;
|
||||
TextWithEntities consumedMessageText() const override;
|
||||
@@ -442,6 +446,8 @@ public:
|
||||
QString notificationText() const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
TextForMimeData clipboardText() const override;
|
||||
bool forceForwardedInfo() const override;
|
||||
|
||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||
std::unique_ptr<HistoryView::Media> createView(
|
||||
|
||||
@@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/file_download.h"
|
||||
#include "core/application.h"
|
||||
#include "facades.h"
|
||||
#include "app.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -32,15 +32,21 @@ constexpr auto kLegacy2DefaultBackground = 5947530738516623361;
|
||||
constexpr auto kDefaultBackground = 5778236420632084488;
|
||||
constexpr auto kIncorrectDefaultBackground = FromLegacyBackgroundId(105);
|
||||
|
||||
quint32 SerializeMaybeColor(std::optional<QColor> color) {
|
||||
return color
|
||||
? ((quint32(std::clamp(color->red(), 0, 255)) << 16)
|
||||
| (quint32(std::clamp(color->green(), 0, 255)) << 8)
|
||||
| quint32(std::clamp(color->blue(), 0, 255)))
|
||||
: quint32(-1);
|
||||
constexpr auto kVersionTag = qint32(0x7FFFFFFF);
|
||||
constexpr auto kVersion = 1;
|
||||
|
||||
[[nodiscard]] quint32 SerializeColor(const QColor &color) {
|
||||
return (quint32(std::clamp(color.red(), 0, 255)) << 16)
|
||||
| (quint32(std::clamp(color.green(), 0, 255)) << 8)
|
||||
| quint32(std::clamp(color.blue(), 0, 255));
|
||||
}
|
||||
|
||||
std::optional<QColor> MaybeColorFromSerialized(quint32 serialized) {
|
||||
[[nodiscard]] quint32 SerializeMaybeColor(std::optional<QColor> color) {
|
||||
return color ? SerializeColor(*color) : quint32(-1);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<QColor> MaybeColorFromSerialized(
|
||||
quint32 serialized) {
|
||||
return (serialized == quint32(-1))
|
||||
? std::nullopt
|
||||
: std::make_optional(QColor(
|
||||
@@ -49,7 +55,43 @@ std::optional<QColor> MaybeColorFromSerialized(quint32 serialized) {
|
||||
int(serialized & 0xFFU)));
|
||||
}
|
||||
|
||||
std::optional<QColor> ColorFromString(const QString &string) {
|
||||
[[nodiscard]] QColor DefaultBackgroundColor() {
|
||||
return QColor(213, 223, 233);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<QColor> MaybeColorFromSerialized(
|
||||
const tl::conditional<MTPint> &mtp) {
|
||||
return mtp ? MaybeColorFromSerialized(mtp->v) : std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<QColor> ColorsFromMTP(
|
||||
const MTPDwallPaperSettings &data) {
|
||||
auto result = std::vector<QColor>();
|
||||
const auto c1 = MaybeColorFromSerialized(data.vbackground_color());
|
||||
if (!c1) {
|
||||
return result;
|
||||
}
|
||||
result.reserve(4);
|
||||
result.push_back(*c1);
|
||||
const auto c2 = MaybeColorFromSerialized(data.vsecond_background_color());
|
||||
if (!c2) {
|
||||
return result;
|
||||
}
|
||||
result.push_back(*c2);
|
||||
const auto c3 = MaybeColorFromSerialized(data.vthird_background_color());
|
||||
if (!c3) {
|
||||
return result;
|
||||
}
|
||||
result.push_back(*c3);
|
||||
const auto c4 = MaybeColorFromSerialized(data.vfourth_background_color());
|
||||
if (!c4) {
|
||||
return result;
|
||||
}
|
||||
result.push_back(*c4);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<QColor> ColorFromString(QStringView string) {
|
||||
if (string.size() != 6) {
|
||||
return {};
|
||||
} else if (ranges::any_of(string, [](QChar ch) {
|
||||
@@ -59,7 +101,7 @@ std::optional<QColor> ColorFromString(const QString &string) {
|
||||
})) {
|
||||
return {};
|
||||
}
|
||||
const auto component = [](const QString &text, int index) {
|
||||
const auto component = [](QStringView text, int index) {
|
||||
const auto decimal = [](QChar hex) {
|
||||
const auto code = hex.unicode();
|
||||
return (code >= '0' && code <= '9')
|
||||
@@ -78,7 +120,30 @@ std::optional<QColor> ColorFromString(const QString &string) {
|
||||
255);
|
||||
}
|
||||
|
||||
QString StringFromColor(QColor color) {
|
||||
[[nodiscard]] std::vector<QColor> ColorsFromString(const QString &string) {
|
||||
constexpr auto kMaxColors = 4;
|
||||
const auto view = QStringView(string);
|
||||
const auto count = int(view.size() / 6);
|
||||
if (!count || count > kMaxColors || view.size() != count * 7 - 1) {
|
||||
return {};
|
||||
}
|
||||
auto result = std::vector<QColor>();
|
||||
result.reserve(count);
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
if (i + 1 < count
|
||||
&& view[i * 7 + 6] != '~'
|
||||
&& (count > 2 || view[i * 7 + 6] != '-')) {
|
||||
return {};
|
||||
} else if (const auto parsed = ColorFromString(view.mid(i * 7, 6))) {
|
||||
result.push_back(*parsed);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString StringFromColor(QColor color) {
|
||||
const auto component = [](int value) {
|
||||
const auto hex = [](int value) {
|
||||
value = std::clamp(value, 0, 15);
|
||||
@@ -93,6 +158,26 @@ QString StringFromColor(QColor color) {
|
||||
+ component(color.blue());
|
||||
}
|
||||
|
||||
[[nodiscard]] QString StringFromColors(const std::vector<QColor> &colors) {
|
||||
Expects(!colors.empty());
|
||||
|
||||
auto strings = QStringList();
|
||||
strings.reserve(colors.size());
|
||||
for (const auto &color : colors) {
|
||||
strings.push_back(StringFromColor(color));
|
||||
}
|
||||
const auto separator = (colors.size() > 2) ? '~' : '-';
|
||||
return strings.join(separator);
|
||||
}
|
||||
|
||||
[[nodiscard]] qint32 RawFromLegacyFlags(qint32 legacyFlags) {
|
||||
using Flag = WallPaperFlag;
|
||||
return ((legacyFlags & (1 << 0)) ? qint32(Flag::Creator) : 0)
|
||||
| ((legacyFlags & (1 << 1)) ? qint32(Flag::Default) : 0)
|
||||
| ((legacyFlags & (1 << 3)) ? qint32(Flag::Pattern) : 0)
|
||||
| ((legacyFlags & (1 << 4)) ? qint32(Flag::Dark) : 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WallPaper::WallPaper(WallPaperId id) : _id(id) {
|
||||
@@ -112,7 +197,13 @@ WallPaperId WallPaper::id() const {
|
||||
}
|
||||
|
||||
std::optional<QColor> WallPaper::backgroundColor() const {
|
||||
return _backgroundColor;
|
||||
return _backgroundColors.empty()
|
||||
? std::nullopt
|
||||
: std::make_optional(_backgroundColors.front());
|
||||
}
|
||||
|
||||
const std::vector<QColor> WallPaper::backgroundColors() const {
|
||||
return _backgroundColors;
|
||||
}
|
||||
|
||||
DocumentData *WallPaper::document() const {
|
||||
@@ -124,19 +215,19 @@ Image *WallPaper::localThumbnail() const {
|
||||
}
|
||||
|
||||
bool WallPaper::isPattern() const {
|
||||
return _flags & MTPDwallPaper::Flag::f_pattern;
|
||||
return _flags & WallPaperFlag::Pattern;
|
||||
}
|
||||
|
||||
bool WallPaper::isDefault() const {
|
||||
return _flags & MTPDwallPaper::Flag::f_default;
|
||||
return _flags & WallPaperFlag::Default;
|
||||
}
|
||||
|
||||
bool WallPaper::isCreator() const {
|
||||
return _flags & MTPDwallPaper::Flag::f_creator;
|
||||
return _flags & WallPaperFlag::Creator;
|
||||
}
|
||||
|
||||
bool WallPaper::isDark() const {
|
||||
return _flags & MTPDwallPaper::Flag::f_dark;
|
||||
return _flags & WallPaperFlag::Dark;
|
||||
}
|
||||
|
||||
bool WallPaper::isLocal() const {
|
||||
@@ -144,13 +235,22 @@ bool WallPaper::isLocal() const {
|
||||
}
|
||||
|
||||
bool WallPaper::isBlurred() const {
|
||||
return _settings & MTPDwallPaperSettings::Flag::f_blur;
|
||||
return _blurred;
|
||||
}
|
||||
|
||||
int WallPaper::patternIntensity() const {
|
||||
return _intensity;
|
||||
}
|
||||
|
||||
float64 WallPaper::patternOpacity() const {
|
||||
return _intensity / 100.;
|
||||
}
|
||||
|
||||
int WallPaper::gradientRotation() const {
|
||||
// In case of complex gradients rotation value is dynamic.
|
||||
return (_backgroundColors.size() < 3) ? _rotation : 0;
|
||||
}
|
||||
|
||||
bool WallPaper::hasShareUrl() const {
|
||||
return !_slug.isEmpty();
|
||||
}
|
||||
@@ -162,19 +262,20 @@ QString WallPaper::shareUrl(not_null<Main::Session*> session) const {
|
||||
const auto base = session->createInternalLinkFull("bg/" + _slug);
|
||||
auto params = QStringList();
|
||||
if (isPattern()) {
|
||||
if (_backgroundColor) {
|
||||
params.push_back("bg_color=" + StringFromColor(*_backgroundColor));
|
||||
if (!backgroundColors().empty()) {
|
||||
params.push_back(
|
||||
"bg_color=" + StringFromColors(backgroundColors()));
|
||||
}
|
||||
if (_intensity) {
|
||||
params.push_back("intensity=" + QString::number(_intensity));
|
||||
}
|
||||
}
|
||||
auto mode = QStringList();
|
||||
if (_settings & MTPDwallPaperSettings::Flag::f_blur) {
|
||||
mode.push_back("blur");
|
||||
if (_rotation && backgroundColors().size() == 2) {
|
||||
params.push_back("rotation=" + QString::number(_rotation));
|
||||
}
|
||||
if (_settings & MTPDwallPaperSettings::Flag::f_motion) {
|
||||
mode.push_back("motion");
|
||||
auto mode = QStringList();
|
||||
if (_blurred) {
|
||||
mode.push_back("blur");
|
||||
}
|
||||
if (!mode.isEmpty()) {
|
||||
params.push_back("mode=" + mode.join('+'));
|
||||
@@ -211,94 +312,110 @@ MTPInputWallPaper WallPaper::mtpInput(not_null<Main::Session*> session) const {
|
||||
}
|
||||
|
||||
MTPWallPaperSettings WallPaper::mtpSettings() const {
|
||||
const auto serializeForIndex = [&](int index) {
|
||||
return (_backgroundColors.size() > index)
|
||||
? MTP_int(SerializeColor(_backgroundColors[index]))
|
||||
: MTP_int(0);
|
||||
};
|
||||
using Flag = MTPDwallPaperSettings::Flag;
|
||||
const auto flagForIndex = [&](int index) {
|
||||
return (_backgroundColors.size() <= index)
|
||||
? Flag(0)
|
||||
: (index == 0)
|
||||
? Flag::f_background_color
|
||||
: (index == 1)
|
||||
? Flag::f_second_background_color
|
||||
: (index == 2)
|
||||
? Flag::f_third_background_color
|
||||
: Flag::f_fourth_background_color;
|
||||
};
|
||||
return MTP_wallPaperSettings(
|
||||
MTP_flags(_settings),
|
||||
(_backgroundColor
|
||||
? MTP_int(SerializeMaybeColor(_backgroundColor))
|
||||
: MTP_int(0)),
|
||||
MTP_int(0), // second_background_color
|
||||
MTP_int(0), // third_background_color
|
||||
MTP_int(0), // fourth_background_color
|
||||
MTP_flags((_blurred ? Flag::f_blur : Flag(0))
|
||||
| flagForIndex(0)
|
||||
| flagForIndex(1)
|
||||
| flagForIndex(2)
|
||||
| flagForIndex(3)),
|
||||
serializeForIndex(0),
|
||||
serializeForIndex(1),
|
||||
serializeForIndex(2),
|
||||
serializeForIndex(3),
|
||||
MTP_int(_intensity),
|
||||
MTP_int(0) // rotation
|
||||
);
|
||||
MTP_int(_rotation));
|
||||
}
|
||||
|
||||
WallPaper WallPaper::withUrlParams(
|
||||
const QMap<QString, QString> ¶ms) const {
|
||||
using Flag = MTPDwallPaperSettings::Flag;
|
||||
|
||||
auto result = *this;
|
||||
result._settings = Flag(0);
|
||||
result._backgroundColor = ColorFromString(_slug);
|
||||
result._blurred = false;
|
||||
result._backgroundColors = ColorsFromString(_slug);
|
||||
result._intensity = kDefaultIntensity;
|
||||
|
||||
if (auto mode = params.value("mode"); !mode.isEmpty()) {
|
||||
const auto list = mode.replace('+', ' ').split(' ');
|
||||
for (const auto &change : list) {
|
||||
if (change == qstr("blur")) {
|
||||
result._settings |= Flag::f_blur;
|
||||
} else if (change == qstr("motion")) {
|
||||
result._settings |= Flag::f_motion;
|
||||
result._blurred = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const auto color = ColorFromString(params.value("bg_color"))) {
|
||||
result._settings |= Flag::f_background_color;
|
||||
result._backgroundColor = color;
|
||||
if (result._backgroundColors.empty()) {
|
||||
result._backgroundColors = ColorsFromString(params.value("bg_color"));
|
||||
}
|
||||
if (result._backgroundColors.empty()) {
|
||||
result._backgroundColors = ColorsFromString(params.value("gradient"));
|
||||
}
|
||||
if (result._backgroundColors.empty()) {
|
||||
result._backgroundColors = ColorsFromString(params.value("color"));
|
||||
}
|
||||
if (result._backgroundColors.empty()) {
|
||||
result._backgroundColors = ColorsFromString(params.value("slug"));
|
||||
}
|
||||
if (const auto string = params.value("intensity"); !string.isEmpty()) {
|
||||
auto ok = false;
|
||||
const auto intensity = string.toInt(&ok);
|
||||
if (ok && base::in_range(intensity, 0, 101)) {
|
||||
result._settings |= Flag::f_intensity;
|
||||
if (ok && base::in_range(intensity, -100, 101)) {
|
||||
result._intensity = intensity;
|
||||
}
|
||||
}
|
||||
result._rotation = params.value("rotation").toInt();
|
||||
result._rotation = (std::clamp(result._rotation, 0, 315) / 45) * 45;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WallPaper WallPaper::withBlurred(bool blurred) const {
|
||||
using Flag = MTPDwallPaperSettings::Flag;
|
||||
|
||||
auto result = *this;
|
||||
if (blurred) {
|
||||
result._settings |= Flag::f_blur;
|
||||
} else {
|
||||
result._settings &= ~Flag::f_blur;
|
||||
}
|
||||
result._blurred = blurred;
|
||||
return result;
|
||||
}
|
||||
|
||||
WallPaper WallPaper::withPatternIntensity(int intensity) const {
|
||||
using Flag = MTPDwallPaperSettings::Flag;
|
||||
|
||||
auto result = *this;
|
||||
result._settings |= Flag::f_intensity;
|
||||
result._intensity = intensity;
|
||||
return result;
|
||||
}
|
||||
|
||||
WallPaper WallPaper::withBackgroundColor(QColor color) const {
|
||||
using Flag = MTPDwallPaperSettings::Flag;
|
||||
|
||||
WallPaper WallPaper::withGradientRotation(int rotation) const {
|
||||
auto result = *this;
|
||||
result._settings |= Flag::f_background_color;
|
||||
result._backgroundColor = color;
|
||||
if (ColorFromString(_slug)) {
|
||||
result._slug = StringFromColor(color);
|
||||
result._rotation = rotation;
|
||||
return result;
|
||||
}
|
||||
|
||||
WallPaper WallPaper::withBackgroundColors(std::vector<QColor> colors) const {
|
||||
auto result = *this;
|
||||
result._backgroundColors = std::move(colors);
|
||||
if (!ColorsFromString(_slug).empty()) {
|
||||
result._slug = StringFromColors(result._backgroundColors);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
WallPaper WallPaper::withParamsFrom(const WallPaper &other) const {
|
||||
auto result = *this;
|
||||
result._settings = other._settings;
|
||||
if (other._backgroundColor || !ColorFromString(_slug)) {
|
||||
result._backgroundColor = other._backgroundColor;
|
||||
if (ColorFromString(_slug)) {
|
||||
result._slug = StringFromColor(*result._backgroundColor);
|
||||
result._blurred = other._blurred;
|
||||
if (!other._backgroundColors.empty() || ColorsFromString(_slug).empty()) {
|
||||
result._backgroundColors = other._backgroundColors;
|
||||
if (!ColorsFromString(_slug).empty()) {
|
||||
result._slug = StringFromColors(result._backgroundColors);
|
||||
}
|
||||
}
|
||||
result._intensity = other._intensity;
|
||||
@@ -317,15 +434,13 @@ std::optional<WallPaper> WallPaper::Create(
|
||||
return data.match([&](const MTPDwallPaper &data) {
|
||||
return Create(session, data);
|
||||
}, [](const MTPDwallPaperNoFile &data) {
|
||||
return std::optional<WallPaper>(); // #TODO themes
|
||||
return Create(data);
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<WallPaper> WallPaper::Create(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDwallPaper &data) {
|
||||
using Flag = MTPDwallPaper::Flag;
|
||||
|
||||
const auto document = session->data().processDocument(
|
||||
data.vdocument());
|
||||
if (!document->checkWallPaperProperties()) {
|
||||
@@ -334,27 +449,41 @@ std::optional<WallPaper> WallPaper::Create(
|
||||
auto result = WallPaper(data.vid().v);
|
||||
result._accessHash = data.vaccess_hash().v;
|
||||
result._ownerId = session->userId();
|
||||
result._flags = data.vflags().v;
|
||||
result._flags = (data.is_dark() ? WallPaperFlag::Dark : WallPaperFlag(0))
|
||||
| (data.is_pattern() ? WallPaperFlag::Pattern : WallPaperFlag(0))
|
||||
| (data.is_default() ? WallPaperFlag::Default : WallPaperFlag(0))
|
||||
| (data.is_creator() ? WallPaperFlag::Creator : WallPaperFlag(0));
|
||||
result._slug = qs(data.vslug());
|
||||
result._document = document;
|
||||
if (const auto settings = data.vsettings()) {
|
||||
const auto isPattern = ((result._flags & Flag::f_pattern) != 0);
|
||||
settings->match([&](const MTPDwallPaperSettings &data) {
|
||||
using Flag = MTPDwallPaperSettings::Flag;
|
||||
|
||||
result._settings = data.vflags().v;
|
||||
const auto backgroundColor = data.vbackground_color();
|
||||
if (isPattern && backgroundColor) {
|
||||
result._backgroundColor = MaybeColorFromSerialized(
|
||||
backgroundColor->v);
|
||||
} else {
|
||||
result._settings &= ~Flag::f_background_color;
|
||||
result._blurred = data.is_blur();
|
||||
if (result.isPattern()) {
|
||||
result._backgroundColors = ColorsFromMTP(data);
|
||||
if (const auto intensity = data.vintensity()) {
|
||||
result._intensity = intensity->v;
|
||||
}
|
||||
if (const auto rotation = data.vrotation()) {
|
||||
result._rotation = rotation->v;
|
||||
}
|
||||
}
|
||||
const auto intensity = data.vintensity();
|
||||
if (isPattern && intensity) {
|
||||
result._intensity = intensity->v;
|
||||
} else {
|
||||
result._settings &= ~Flag::f_intensity;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<WallPaper> WallPaper::Create(const MTPDwallPaperNoFile &data) {
|
||||
auto result = WallPaper(data.vid().v);
|
||||
result._flags = (data.is_dark() ? WallPaperFlag::Dark : WallPaperFlag(0))
|
||||
| (data.is_default() ? WallPaperFlag::Default : WallPaperFlag(0));
|
||||
result._blurred = false;
|
||||
result._backgroundColors.clear();
|
||||
if (const auto settings = data.vsettings()) {
|
||||
settings->match([&](const MTPDwallPaperSettings &data) {
|
||||
result._blurred = data.is_blur();
|
||||
result._backgroundColors = ColorsFromMTP(data);
|
||||
if (const auto rotation = data.vrotation()) {
|
||||
result._rotation = rotation->v;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -364,30 +493,38 @@ std::optional<WallPaper> WallPaper::Create(
|
||||
QByteArray WallPaper::serialize() const {
|
||||
auto size = sizeof(quint64) // _id
|
||||
+ sizeof(quint64) // _accessHash
|
||||
+ sizeof(qint32) // version tag
|
||||
+ sizeof(qint32) // version
|
||||
+ sizeof(qint32) // _flags
|
||||
+ Serialize::stringSize(_slug)
|
||||
+ sizeof(qint32) // _settings
|
||||
+ sizeof(quint32) // _backgroundColor
|
||||
+ sizeof(qint32) // _backgroundColors.size()
|
||||
+ (_backgroundColors.size() * sizeof(quint32)) // _backgroundColors
|
||||
+ sizeof(qint32) // _intensity
|
||||
+ (2 * sizeof(qint32)); // ownerId
|
||||
+ sizeof(qint32) // _rotation
|
||||
+ sizeof(quint64); // ownerId
|
||||
|
||||
auto result = QByteArray();
|
||||
result.reserve(size);
|
||||
{
|
||||
const auto field1 = qint32(uint32(_ownerId.bare & 0xFFFFFFFF));
|
||||
const auto field2 = qint32(uint32(_ownerId.bare >> 32));
|
||||
auto stream = QDataStream(&result, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
stream
|
||||
<< quint64(_id)
|
||||
<< quint64(_accessHash)
|
||||
<< qint32(kVersionTag)
|
||||
<< qint32(kVersion)
|
||||
<< qint32(_flags)
|
||||
<< _slug
|
||||
<< qint32(_settings)
|
||||
<< SerializeMaybeColor(_backgroundColor)
|
||||
<< qint32(_blurred ? 1 : 0)
|
||||
<< qint32(_backgroundColors.size());
|
||||
for (const auto &color : _backgroundColors) {
|
||||
stream << SerializeMaybeColor(color);
|
||||
}
|
||||
stream
|
||||
<< qint32(_intensity)
|
||||
<< field1
|
||||
<< field2;
|
||||
<< qint32(_rotation)
|
||||
<< quint64(_ownerId.bare);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -400,46 +537,88 @@ std::optional<WallPaper> WallPaper::FromSerialized(
|
||||
|
||||
auto id = quint64();
|
||||
auto accessHash = quint64();
|
||||
auto ownerId = UserId();
|
||||
auto flags = qint32();
|
||||
auto slug = QString();
|
||||
auto settings = qint32();
|
||||
auto backgroundColor = quint32();
|
||||
auto intensity = qint32();
|
||||
auto versionTag = qint32();
|
||||
auto version = qint32(0);
|
||||
|
||||
auto stream = QDataStream(serialized);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
stream
|
||||
>> id
|
||||
>> accessHash
|
||||
>> flags
|
||||
>> slug
|
||||
>> settings
|
||||
>> backgroundColor
|
||||
>> intensity;
|
||||
if (!stream.atEnd()) {
|
||||
auto field1 = qint32();
|
||||
auto field2 = qint32();
|
||||
stream >> field1;
|
||||
if (!stream.atEnd()) {
|
||||
stream >> field2;
|
||||
>> versionTag;
|
||||
|
||||
auto flags = qint32();
|
||||
auto ownerId = UserId();
|
||||
auto slug = QString();
|
||||
auto blurred = qint32();
|
||||
auto backgroundColors = std::vector<QColor>();
|
||||
auto intensity = qint32();
|
||||
auto rotation = qint32();
|
||||
if (versionTag == kVersionTag) {
|
||||
auto bareOwnerId = quint64();
|
||||
auto backgroundColorsCount = qint32();
|
||||
stream
|
||||
>> version
|
||||
>> flags
|
||||
>> slug
|
||||
>> blurred
|
||||
>> backgroundColorsCount;
|
||||
if (backgroundColorsCount < 0 || backgroundColorsCount > 4) {
|
||||
return std::nullopt;
|
||||
}
|
||||
backgroundColors.reserve(backgroundColorsCount);
|
||||
for (auto i = 0; i != backgroundColorsCount; ++i) {
|
||||
auto serialized = quint32();
|
||||
stream >> serialized;
|
||||
const auto color = MaybeColorFromSerialized(serialized);
|
||||
if (!color) {
|
||||
return std::nullopt;
|
||||
}
|
||||
backgroundColors.push_back(*color);
|
||||
}
|
||||
stream
|
||||
>> intensity
|
||||
>> rotation
|
||||
>> bareOwnerId;
|
||||
ownerId = UserId(BareId(bareOwnerId));
|
||||
} else {
|
||||
auto settings = qint32();
|
||||
auto backgroundColor = quint32();
|
||||
stream
|
||||
>> slug
|
||||
>> settings
|
||||
>> backgroundColor
|
||||
>> intensity;
|
||||
if (!stream.atEnd()) {
|
||||
auto field1 = qint32();
|
||||
auto field2 = qint32();
|
||||
stream >> field1;
|
||||
if (!stream.atEnd()) {
|
||||
stream >> field2;
|
||||
}
|
||||
ownerId = UserId(
|
||||
BareId(uint32(field1)) | (BareId(uint32(field2)) << 32));
|
||||
}
|
||||
flags = RawFromLegacyFlags(versionTag);
|
||||
blurred = (settings & qint32(1U << 1)) ? 1 : 0;
|
||||
if (const auto color = MaybeColorFromSerialized(backgroundColor)) {
|
||||
backgroundColors.push_back(*color);
|
||||
}
|
||||
ownerId = UserId(
|
||||
BareId(uint32(field1)) | (BareId(uint32(field2)) << 32));
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return std::nullopt;
|
||||
} else if (intensity < 0 || intensity > 100) {
|
||||
} else if (intensity < -100 || intensity > 100) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto result = WallPaper(id);
|
||||
result._accessHash = accessHash;
|
||||
result._ownerId = ownerId;
|
||||
result._flags = MTPDwallPaper::Flags::from_raw(flags);
|
||||
result._flags = WallPaperFlags::from_raw(flags);
|
||||
result._slug = slug;
|
||||
result._settings = MTPDwallPaperSettings::Flags::from_raw(settings);
|
||||
result._backgroundColor = MaybeColorFromSerialized(backgroundColor);
|
||||
result._blurred = (blurred == 1);
|
||||
result._backgroundColors = std::move(backgroundColors);
|
||||
result._intensity = intensity;
|
||||
result._rotation = rotation;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -450,28 +629,31 @@ std::optional<WallPaper> WallPaper::FromLegacySerialized(
|
||||
QString slug) {
|
||||
auto result = WallPaper(id);
|
||||
result._accessHash = accessHash;
|
||||
result._flags = MTPDwallPaper::Flags::from_raw(flags);
|
||||
result._flags = WallPaperFlags::from_raw(RawFromLegacyFlags(flags));
|
||||
result._slug = slug;
|
||||
result._backgroundColor = ColorFromString(slug);
|
||||
if (const auto color = ColorFromString(slug)) {
|
||||
result._backgroundColors.push_back(*color);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<WallPaper> WallPaper::FromLegacyId(qint32 legacyId) {
|
||||
auto result = WallPaper(FromLegacyBackgroundId(legacyId));
|
||||
if (!IsCustomWallPaper(result)) {
|
||||
result._flags = MTPDwallPaper::Flag::f_default;
|
||||
result._flags = WallPaperFlag::Default;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<WallPaper> WallPaper::FromColorSlug(const QString &slug) {
|
||||
if (const auto color = ColorFromString(slug)) {
|
||||
auto result = CustomWallPaper();
|
||||
result._slug = slug;
|
||||
result._backgroundColor = color;
|
||||
return result;
|
||||
std::optional<WallPaper> WallPaper::FromColorsSlug(const QString &slug) {
|
||||
auto colors = ColorsFromString(slug);
|
||||
if (colors.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::nullopt;
|
||||
auto result = CustomWallPaper();
|
||||
result._slug = slug;
|
||||
result._backgroundColors = std::move(colors);
|
||||
return result;
|
||||
}
|
||||
|
||||
WallPaper ThemeWallPaper() {
|
||||
@@ -522,78 +704,54 @@ bool IsCloudWallPaper(const WallPaper &paper) {
|
||||
&& !details::IsTestingEditorWallPaper(paper);
|
||||
}
|
||||
|
||||
QColor PatternColor(QColor background) {
|
||||
const auto hue = background.hueF();
|
||||
const auto saturation = background.saturationF();
|
||||
const auto value = background.valueF();
|
||||
return QColor::fromHsvF(
|
||||
hue,
|
||||
std::min(1.0, saturation + 0.05 + 0.1 * (1. - saturation)),
|
||||
(value > 0.5
|
||||
? std::max(0., value * 0.65)
|
||||
: std::max(0., std::min(1., 1. - value * 0.65))),
|
||||
0.4
|
||||
).toRgb();
|
||||
QImage GenerateWallPaper(
|
||||
QSize size,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity,
|
||||
Fn<void(QPainter&)> drawPattern) {
|
||||
auto result = bg.empty()
|
||||
? Images::GenerateGradient(size, { DefaultBackgroundColor() })
|
||||
: Images::GenerateGradient(size, bg, gradientRotation);
|
||||
if (bg.size() > 1 && (!drawPattern || patternOpacity >= 0.)) {
|
||||
result = Images::DitherImage(std::move(result));
|
||||
}
|
||||
if (drawPattern) {
|
||||
auto p = QPainter(&result);
|
||||
if (patternOpacity >= 0.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
} else {
|
||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||
}
|
||||
drawPattern(p);
|
||||
if (patternOpacity < 0. && patternOpacity > -1.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setOpacity(1. + patternOpacity);
|
||||
p.fillRect(QRect{ QPoint(), size }, Qt::black);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
|
||||
QImage PreparePatternImage(
|
||||
QImage image,
|
||||
QColor bg,
|
||||
QColor fg,
|
||||
int intensity) {
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
image = std::move(image).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
// Similar to ColorizePattern.
|
||||
// But here we set bg to all 'alpha=0' pixels and fg to opaque ones.
|
||||
QImage pattern,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity) {
|
||||
auto result = GenerateWallPaper(
|
||||
pattern.size(),
|
||||
bg,
|
||||
gradientRotation,
|
||||
patternOpacity,
|
||||
[&](QPainter &p) {
|
||||
p.drawImage(QRect(QPoint(), pattern.size()), pattern);
|
||||
});
|
||||
|
||||
const auto width = image.width();
|
||||
const auto height = image.height();
|
||||
const auto alpha = anim::interpolate(
|
||||
0,
|
||||
255,
|
||||
fg.alphaF() * std::clamp(intensity / 100., 0., 1.));
|
||||
if (!alpha) {
|
||||
image.fill(bg);
|
||||
return image;
|
||||
}
|
||||
fg.setAlpha(255);
|
||||
const auto patternBg = anim::shifted(bg);
|
||||
const auto patternFg = anim::shifted(fg);
|
||||
|
||||
constexpr auto resultIntsPerPixel = 1;
|
||||
const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
|
||||
const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
|
||||
auto resultInts = reinterpret_cast<uint32*>(image.bits());
|
||||
Assert(resultIntsAdded >= 0);
|
||||
Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
|
||||
Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
|
||||
|
||||
const auto maskBytesPerPixel = (image.depth() >> 3);
|
||||
const auto maskBytesPerLine = image.bytesPerLine();
|
||||
const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
|
||||
|
||||
// We want to read the last byte of four available.
|
||||
// This is the difference with style::colorizeImage.
|
||||
auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
|
||||
Assert(maskBytesAdded >= 0);
|
||||
Assert(image.depth() == (maskBytesPerPixel << 3));
|
||||
for (auto y = 0; y != height; ++y) {
|
||||
for (auto x = 0; x != width; ++x) {
|
||||
const auto maskOpacity = static_cast<anim::ShiftedMultiplier>(
|
||||
*maskBytes) + 1;
|
||||
const auto fgOpacity = (maskOpacity * alpha) >> 8;
|
||||
const auto bgOpacity = 256 - fgOpacity;
|
||||
*resultInts = anim::unshifted(
|
||||
patternBg * bgOpacity + patternFg * fgOpacity);
|
||||
maskBytes += maskBytesPerPixel;
|
||||
resultInts += resultIntsPerPixel;
|
||||
}
|
||||
maskBytes += maskBytesAdded;
|
||||
resultInts += resultIntsAdded;
|
||||
}
|
||||
return image;
|
||||
pattern = QImage();
|
||||
return result;
|
||||
}
|
||||
|
||||
QImage PrepareBlurredBackground(QImage image) {
|
||||
@@ -609,6 +767,30 @@ QImage PrepareBlurredBackground(QImage image) {
|
||||
return Images::BlurLargeImage(image, kRadius);
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation) {
|
||||
constexpr auto kSize = 512;
|
||||
const auto size = QSize(kSize, kSize);
|
||||
if (colors.empty()) {
|
||||
return Images::GenerateGradient(size, { DefaultBackgroundColor() });
|
||||
}
|
||||
auto result = Images::GenerateGradient(size, colors, rotation);
|
||||
if (colors.size() > 1) {
|
||||
result = Images::DitherImage(std::move(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(const Data::WallPaper &paper) {
|
||||
if (paper.backgroundColors().empty()) {
|
||||
return GenerateDitheredGradient({ DefaultBackgroundColor() }, 0);
|
||||
}
|
||||
return GenerateDitheredGradient(
|
||||
paper.backgroundColors(),
|
||||
paper.gradientRotation());
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
WallPaper UninitializedWallPaper() {
|
||||
|
||||
@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/flags.h"
|
||||
|
||||
class Image;
|
||||
|
||||
namespace Main {
|
||||
@@ -17,6 +19,15 @@ namespace Data {
|
||||
|
||||
struct FileOrigin;
|
||||
|
||||
enum class WallPaperFlag {
|
||||
Pattern = (1 << 0),
|
||||
Default = (1 << 1),
|
||||
Creator = (1 << 2),
|
||||
Dark = (1 << 3),
|
||||
};
|
||||
inline constexpr bool is_flag_type(WallPaperFlag) { return true; };
|
||||
using WallPaperFlags = base::flags<WallPaperFlag>;
|
||||
|
||||
class WallPaper {
|
||||
public:
|
||||
explicit WallPaper(WallPaperId id);
|
||||
@@ -25,6 +36,7 @@ public:
|
||||
|
||||
[[nodiscard]] WallPaperId id() const;
|
||||
[[nodiscard]] std::optional<QColor> backgroundColor() const;
|
||||
[[nodiscard]] const std::vector<QColor> backgroundColors() const;
|
||||
[[nodiscard]] DocumentData *document() const;
|
||||
[[nodiscard]] Image *localThumbnail() const;
|
||||
[[nodiscard]] bool isPattern() const;
|
||||
@@ -34,6 +46,8 @@ public:
|
||||
[[nodiscard]] bool isLocal() const;
|
||||
[[nodiscard]] bool isBlurred() const;
|
||||
[[nodiscard]] int patternIntensity() const;
|
||||
[[nodiscard]] float64 patternOpacity() const;
|
||||
[[nodiscard]] int gradientRotation() const;
|
||||
[[nodiscard]] bool hasShareUrl() const;
|
||||
[[nodiscard]] QString shareUrl(not_null<Main::Session*> session) const;
|
||||
|
||||
@@ -50,7 +64,9 @@ public:
|
||||
const QMap<QString, QString> ¶ms) const;
|
||||
[[nodiscard]] WallPaper withBlurred(bool blurred) const;
|
||||
[[nodiscard]] WallPaper withPatternIntensity(int intensity) const;
|
||||
[[nodiscard]] WallPaper withBackgroundColor(QColor color) const;
|
||||
[[nodiscard]] WallPaper withGradientRotation(int rotation) const;
|
||||
[[nodiscard]] WallPaper withBackgroundColors(
|
||||
std::vector<QColor> colors) const;
|
||||
[[nodiscard]] WallPaper withParamsFrom(const WallPaper &other) const;
|
||||
[[nodiscard]] WallPaper withoutImageData() const;
|
||||
|
||||
@@ -60,6 +76,8 @@ public:
|
||||
[[nodiscard]] static std::optional<WallPaper> Create(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDwallPaper &data);
|
||||
[[nodiscard]] static std::optional<WallPaper> Create(
|
||||
const MTPDwallPaperNoFile &data);
|
||||
|
||||
[[nodiscard]] QByteArray serialize() const;
|
||||
[[nodiscard]] static std::optional<WallPaper> FromSerialized(
|
||||
@@ -71,21 +89,22 @@ public:
|
||||
QString slug);
|
||||
[[nodiscard]] static std::optional<WallPaper> FromLegacyId(
|
||||
qint32 legacyId);
|
||||
[[nodiscard]] static std::optional<WallPaper> FromColorSlug(
|
||||
[[nodiscard]] static std::optional<WallPaper> FromColorsSlug(
|
||||
const QString &slug);
|
||||
|
||||
private:
|
||||
static constexpr auto kDefaultIntensity = 40;
|
||||
static constexpr auto kDefaultIntensity = 50;
|
||||
|
||||
WallPaperId _id = WallPaperId();
|
||||
uint64 _accessHash = 0;
|
||||
UserId _ownerId = 0;
|
||||
MTPDwallPaper::Flags _flags;
|
||||
WallPaperFlags _flags;
|
||||
QString _slug;
|
||||
|
||||
MTPDwallPaperSettings::Flags _settings;
|
||||
std::optional<QColor> _backgroundColor;
|
||||
std::vector<QColor> _backgroundColors;
|
||||
int _rotation = 0;
|
||||
int _intensity = kDefaultIntensity;
|
||||
bool _blurred = false;
|
||||
|
||||
DocumentData *_document = nullptr;
|
||||
std::shared_ptr<Image> _thumbnail;
|
||||
@@ -103,13 +122,22 @@ private:
|
||||
[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper);
|
||||
[[nodiscard]] bool IsCloudWallPaper(const WallPaper &paper);
|
||||
|
||||
QColor PatternColor(QColor background);
|
||||
QImage PreparePatternImage(
|
||||
QImage image,
|
||||
QColor bg,
|
||||
QColor fg,
|
||||
int intensity);
|
||||
QImage PrepareBlurredBackground(QImage image);
|
||||
[[nodiscard]] QImage GenerateWallPaper(
|
||||
QSize size,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity = 1.,
|
||||
Fn<void(QPainter&)> drawPattern = nullptr);
|
||||
[[nodiscard]] QImage PreparePatternImage(
|
||||
QImage pattern,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity);
|
||||
[[nodiscard]] QImage PrepareBlurredBackground(QImage image);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(const WallPaper &paper);
|
||||
|
||||
namespace details {
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_file_origin.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "app.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
@@ -28,7 +27,7 @@ not_null<StickersSet*> StickersSetThumbnailView::owner() const {
|
||||
void StickersSetThumbnailView::set(
|
||||
not_null<Main::Session*> session,
|
||||
QByteArray content) {
|
||||
auto image = App::readImage(content, nullptr, false);
|
||||
auto image = Images::Read({ .content = content }).image;
|
||||
if (image.isNull()) {
|
||||
_content = std::move(content);
|
||||
} else {
|
||||
|
||||
@@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session_settings.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_dialogs.h" // st::dialogsTextWidthMin
|
||||
|
||||
namespace Dialogs {
|
||||
|
||||
@@ -118,10 +118,7 @@ InnerWidget::InnerWidget(
|
||||
})
|
||||
, _cancelSearchInChat(this, st::dialogsCancelSearchInPeer)
|
||||
, _cancelSearchFromUser(this, st::dialogsCancelSearchInPeer) {
|
||||
|
||||
#ifndef OS_MAC_OLD // Qt 5.3.2 build is working with glitches otherwise.
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, true);
|
||||
#endif // OS_MAC_OLD
|
||||
|
||||
_cancelSearchInChat->setClickedCallback([=] { cancelSearchInChat(); });
|
||||
_cancelSearchInChat->hide();
|
||||
|
||||
@@ -164,7 +164,7 @@ void Widget::BottomButton::paintEvent(QPaintEvent *e) {
|
||||
Widget::Widget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Window::AbstractSectionWidget(parent, controller)
|
||||
: Window::AbstractSectionWidget(parent, controller, PaintedBackground::Custom)
|
||||
, _api(&controller->session().mtp())
|
||||
, _searchControls(this)
|
||||
, _mainMenuToggle(_searchControls, st::dialogsMenuToggle)
|
||||
|
||||
@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "editor/photo_editor_layer_widget.h"
|
||||
|
||||
#include "app.h" // readImage
|
||||
#include "boxes/confirm_box.h" // InformBox
|
||||
#include "editor/photo_editor.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
@@ -80,9 +79,11 @@ void PrepareProfilePhoto(
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = result.remoteContent.isEmpty()
|
||||
? App::readImage(result.paths.front())
|
||||
: App::readImage(result.remoteContent);
|
||||
auto image = Images::Read({
|
||||
.path = result.paths.isEmpty() ? QString() : result.paths.front(),
|
||||
.content = result.remoteContent,
|
||||
.forceOpaque = true,
|
||||
}).image;
|
||||
if (image.isNull()
|
||||
|| (image.width() > (10 * image.height()))
|
||||
|| (image.height() > (10 * image.width()))) {
|
||||
|
||||
@@ -895,9 +895,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
|
||||
Painter p(this);
|
||||
|
||||
auto ms = crl::now();
|
||||
auto clip = e->rect();
|
||||
|
||||
if (_items.empty() && _upLoaded && _downLoaded) {
|
||||
paintEmpty(p);
|
||||
} else {
|
||||
@@ -914,16 +912,26 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
return this->itemTop(elem) < bottom;
|
||||
});
|
||||
if (from != end) {
|
||||
auto viewport = QRect(); // #TODO bubbles
|
||||
auto top = itemTop(from->get());
|
||||
auto context = HistoryView::PaintContext{
|
||||
.bubblesPattern = nullptr,
|
||||
.viewport = viewport.translated(0, -top),
|
||||
.clip = clip.translated(0, -top),
|
||||
.now = crl::now(),
|
||||
};
|
||||
p.translate(0, top);
|
||||
for (auto i = from; i != to; ++i) {
|
||||
const auto view = i->get();
|
||||
const auto selection = (view == _selectedItem)
|
||||
context.selection = (view == _selectedItem)
|
||||
? _selectedText
|
||||
: TextSelection();
|
||||
view->draw(p, clip.translated(0, -top), selection, ms);
|
||||
auto height = view->height();
|
||||
view->draw(p, context);
|
||||
|
||||
const auto height = view->height();
|
||||
top += height;
|
||||
context.viewport.translate(0, -height);
|
||||
context.clip.translate(0, -height);
|
||||
p.translate(0, height);
|
||||
}
|
||||
p.translate(0, -top);
|
||||
|
||||
@@ -275,11 +275,14 @@ Widget::Widget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<ChannelData*> channel)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _fixedBar(this, controller, channel)
|
||||
, _fixedBarShadow(this)
|
||||
, _whatIsThis(this, tr::lng_admin_log_about(tr::now).toUpper(), st::historyComposeButton) {
|
||||
, _whatIsThis(
|
||||
this,
|
||||
tr::lng_admin_log_about(tr::now).toUpper(),
|
||||
st::historyComposeButton) {
|
||||
_fixedBar->move(0, 0);
|
||||
_fixedBar->resizeToWidth(width());
|
||||
_fixedBar->showFilterRequests(
|
||||
@@ -303,6 +306,11 @@ Widget::Widget(
|
||||
updateAdaptiveLayout();
|
||||
}, lifetime());
|
||||
|
||||
controller->repaintBackgroundRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, controller, channel));
|
||||
_inner->showSearchSignal(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
||||
@@ -1631,7 +1631,16 @@ void History::setUnreadCount(int newUnreadCount) {
|
||||
const auto notifier = unreadStateChangeNotifier(true);
|
||||
_unreadCount = newUnreadCount;
|
||||
|
||||
if (newUnreadCount == 1) {
|
||||
const auto lastOutgoing = [&] {
|
||||
const auto last = lastMessage();
|
||||
return last
|
||||
&& IsServerMsgId(last->id)
|
||||
&& loadedAtBottom()
|
||||
&& !isEmpty()
|
||||
&& blocks.back()->messages.back()->data() == last
|
||||
&& last->out();
|
||||
}();
|
||||
if (newUnreadCount == 1 && !lastOutgoing) {
|
||||
if (loadedAtBottom()) {
|
||||
_firstUnreadView = !isEmpty()
|
||||
? blocks.back()->messages.back().get()
|
||||
|
||||
@@ -560,7 +560,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
Painter p(this);
|
||||
auto clip = e->rect();
|
||||
auto ms = crl::now();
|
||||
|
||||
const auto historyDisplayedEmpty = _history->isDisplayedEmpty()
|
||||
&& (!_migrated || _migrated->isDisplayedEmpty());
|
||||
@@ -602,6 +601,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
} else {
|
||||
seltoy += _dragSelTo->height();
|
||||
}
|
||||
const auto visibleAreaTopGlobal = mapToGlobal(
|
||||
QPoint(0, _visibleAreaTop)).y();
|
||||
|
||||
auto mtop = migratedTop();
|
||||
auto htop = historyTop();
|
||||
@@ -613,15 +614,19 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
auto view = block->messages[iItem].get();
|
||||
auto item = view->data();
|
||||
|
||||
auto y = mtop + block->y() + view->y();
|
||||
p.save();
|
||||
p.translate(0, y);
|
||||
if (clip.y() < y + view->height()) while (y < drawToY) {
|
||||
const auto selection = itemRenderSelection(
|
||||
auto top = mtop + block->y() + view->y();
|
||||
auto context = _controller->bubblesContext({
|
||||
.visibleAreaTop = _visibleAreaTop,
|
||||
.visibleAreaTopGlobal = visibleAreaTopGlobal,
|
||||
.clip = clip,
|
||||
}).translated(0, -top);
|
||||
p.translate(0, top);
|
||||
if (context.clip.y() < view->height()) while (top < drawToY) {
|
||||
context.selection = itemRenderSelection(
|
||||
view,
|
||||
selfromy - mtop,
|
||||
seltoy - mtop);
|
||||
view->draw(p, clip.translated(0, -y), selection, ms);
|
||||
view->draw(p, context);
|
||||
|
||||
if (item->hasViews()) {
|
||||
_controller->content()->scheduleViewIncrement(item);
|
||||
@@ -631,9 +636,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
_widget->enqueueMessageHighlight(view);
|
||||
}
|
||||
|
||||
int32 h = view->height();
|
||||
p.translate(0, h);
|
||||
y += h;
|
||||
const auto height = view->height();
|
||||
top += height;
|
||||
context.translate(0, -height);
|
||||
p.translate(0, height);
|
||||
|
||||
++iItem;
|
||||
if (iItem == block->messages.size()) {
|
||||
@@ -647,7 +653,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
view = block->messages[iItem].get();
|
||||
item = view->data();
|
||||
}
|
||||
p.restore();
|
||||
p.translate(0, -top);
|
||||
}
|
||||
if (htop >= 0) {
|
||||
auto iBlock = (_curHistory == _history ? _curBlock : 0);
|
||||
@@ -656,21 +662,27 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
auto view = block->messages[iItem].get();
|
||||
auto item = view->data();
|
||||
auto readTill = (HistoryItem*)nullptr;
|
||||
auto hclip = clip.intersected(QRect(0, hdrawtop, width(), clip.top() + clip.height()));
|
||||
auto y = htop + block->y() + view->y();
|
||||
p.save();
|
||||
p.translate(0, y);
|
||||
while (y < drawToY) {
|
||||
const auto h = view->height();
|
||||
if (hclip.y() < y + h && hdrawtop < y + h) {
|
||||
const auto selection = itemRenderSelection(
|
||||
auto top = htop + block->y() + view->y();
|
||||
auto context = _controller->bubblesContext({
|
||||
.visibleAreaTop = _visibleAreaTop,
|
||||
.visibleAreaTopGlobal = visibleAreaTopGlobal,
|
||||
.visibleAreaWidth = width(),
|
||||
.clip = clip.intersected(
|
||||
QRect(0, hdrawtop, width(), clip.top() + clip.height())
|
||||
),
|
||||
}).translated(0, -top);
|
||||
p.translate(0, top);
|
||||
while (top < drawToY) {
|
||||
const auto height = view->height();
|
||||
if (context.clip.y() < height && hdrawtop < top + height) {
|
||||
context.selection = itemRenderSelection(
|
||||
view,
|
||||
selfromy - htop,
|
||||
seltoy - htop);
|
||||
view->draw(p, hclip.translated(0, -y), selection, ms);
|
||||
view->draw(p, context);
|
||||
|
||||
const auto middle = y + h / 2;
|
||||
const auto bottom = y + h;
|
||||
const auto middle = top + height / 2;
|
||||
const auto bottom = top + height;
|
||||
if (_visibleAreaBottom >= bottom) {
|
||||
const auto item = view->data();
|
||||
if (!item->out() && item->unread()) {
|
||||
@@ -688,8 +700,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
p.translate(0, h);
|
||||
y += h;
|
||||
top += height;
|
||||
context.translate(0, -height);
|
||||
p.translate(0, height);
|
||||
|
||||
++iItem;
|
||||
if (iItem == block->messages.size()) {
|
||||
@@ -703,7 +716,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
view = block->messages[iItem].get();
|
||||
item = view->data();
|
||||
}
|
||||
p.restore();
|
||||
p.translate(0, -top);
|
||||
|
||||
if (readTill && _widget->doWeReadServerHistory()) {
|
||||
session().data().histories().readInboxTill(readTill);
|
||||
@@ -3168,6 +3181,10 @@ void HistoryInner::addToSelection(
|
||||
not_null<HistoryItem*> item) const {
|
||||
const auto i = toItems->find(item);
|
||||
if (i == toItems->cend()) {
|
||||
if (toItems->size() == 1
|
||||
&& toItems->begin()->second != FullSelection) {
|
||||
toItems->clear();
|
||||
}
|
||||
toItems->emplace(item, FullSelection);
|
||||
} else if (i->second != FullSelection) {
|
||||
i->second = FullSelection;
|
||||
|
||||
@@ -45,7 +45,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_chat.h"
|
||||
@@ -563,8 +562,14 @@ HistoryMessage::HistoryMessage(
|
||||
|
||||
auto config = CreateConfig();
|
||||
|
||||
if (original->Has<HistoryMessageForwarded>() || !original->history()->peer->isSelf()) {
|
||||
// Server doesn't add "fwd_from" to non-forwarded messages from chat with yourself.
|
||||
const auto originalMedia = original->media();
|
||||
const auto dropForwardInfo = (originalMedia
|
||||
&& originalMedia->dropForwardedInfo())
|
||||
|| (original->history()->peer->isSelf()
|
||||
&& !history->peer->isSelf()
|
||||
&& !original->Has<HistoryMessageForwarded>()
|
||||
&& (!originalMedia || !originalMedia->forceForwardedInfo()));
|
||||
if (!dropForwardInfo) {
|
||||
config.originalDate = original->dateOriginal();
|
||||
if (const auto info = original->hiddenForwardedInfo()) {
|
||||
config.senderNameOriginal = info->name;
|
||||
@@ -596,6 +601,14 @@ HistoryMessage::HistoryMessage(
|
||||
}
|
||||
if (const auto fwdViaBot = original->viaBot()) {
|
||||
config.viaBotId = peerToUser(fwdViaBot->id);
|
||||
} else if (originalMedia && originalMedia->game()) {
|
||||
if (const auto sender = original->senderOriginal()) {
|
||||
if (const auto user = sender->asUser()) {
|
||||
if (user->isBot()) {
|
||||
config.viaBotId = peerToUser(user->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto fwdViewsCount = original->viewsCount();
|
||||
if (fwdViewsCount > 0) {
|
||||
|
||||
@@ -88,7 +88,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/bot_keyboard.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "chat_helpers/send_context_menu.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
@@ -172,7 +171,7 @@ const auto kPsaAboutPrefix = "cloud_lng_about_psa_";
|
||||
HistoryWidget::HistoryWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Window::AbstractSectionWidget(parent, controller)
|
||||
: Window::AbstractSectionWidget(parent, controller, PaintedBackground::Section)
|
||||
, _api(&controller->session().mtp())
|
||||
, _updateEditTimeLeftDisplay([=] { updateField(); })
|
||||
, _fieldBarCancel(this, st::historyReplyCancel)
|
||||
@@ -310,6 +309,9 @@ HistoryWidget::HistoryWidget(
|
||||
|
||||
_historyDown->installEventFilter(this);
|
||||
_unreadMentions->installEventFilter(this);
|
||||
SendMenu::SetupUnreadMentionsMenu(_unreadMentions.data(), [=] {
|
||||
return _history ? _history->peer.get() : nullptr;
|
||||
});
|
||||
|
||||
InitMessageField(controller, _field);
|
||||
|
||||
@@ -430,6 +432,11 @@ HistoryWidget::HistoryWidget(
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
controller->repaintBackgroundRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
session().data().newItemAdded(
|
||||
) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
|
||||
newItemAdded(item);
|
||||
@@ -3541,15 +3548,12 @@ void HistoryWidget::chooseAttach() {
|
||||
}
|
||||
|
||||
if (!result.remoteContent.isEmpty()) {
|
||||
auto animated = false;
|
||||
auto image = App::readImage(
|
||||
result.remoteContent,
|
||||
nullptr,
|
||||
false,
|
||||
&animated);
|
||||
if (!image.isNull() && !animated) {
|
||||
auto read = Images::Read({
|
||||
.content = result.remoteContent,
|
||||
});
|
||||
if (!read.image.isNull() && !read.animated) {
|
||||
confirmSendingFiles(
|
||||
std::move(image),
|
||||
std::move(read.image),
|
||||
std::move(result.remoteContent));
|
||||
} else {
|
||||
uploadFile(result.remoteContent, SendMediaType::File);
|
||||
@@ -4494,10 +4498,7 @@ bool HistoryWidget::confirmSendingFiles(
|
||||
}
|
||||
|
||||
if (hasImage) {
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
confirmSendingFiles(
|
||||
std::move(image),
|
||||
@@ -4928,6 +4929,9 @@ void HistoryWidget::startItemRevealAnimations() {
|
||||
1.,
|
||||
HistoryView::ListWidget::kItemRevealDuration,
|
||||
anim::easeOutCirc);
|
||||
if (item->out() || _history->peer->isSelf()) {
|
||||
controller()->rotateComplexGradientBackground();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6263,11 +6267,7 @@ void HistoryWidget::updatePreview() {
|
||||
st::msgNameStyle,
|
||||
tr::lng_preview_loading(tr::now),
|
||||
Ui::NameTextOptions());
|
||||
#ifndef OS_MAC_OLD
|
||||
auto linkText = _previewLinks.splitRef(' ').at(0).toString();
|
||||
#else // OS_MAC_OLD
|
||||
auto linkText = _previewLinks.split(' ').at(0);
|
||||
#endif // OS_MAC_OLD
|
||||
_previewDescription.setText(
|
||||
st::messageTextStyle,
|
||||
TextUtilities::Clean(linkText),
|
||||
@@ -6977,6 +6977,10 @@ void HistoryWidget::synteticScrollToY(int y) {
|
||||
|
||||
HistoryWidget::~HistoryWidget() {
|
||||
if (_history) {
|
||||
// Saving a draft on account switching.
|
||||
saveFieldToHistoryLocalDraft();
|
||||
session().api().saveDraftToCloudDelayed(_history);
|
||||
|
||||
clearAllLoadRequests();
|
||||
}
|
||||
setTabbedPanel(nullptr);
|
||||
|
||||
@@ -24,6 +24,7 @@ class SessionController;
|
||||
|
||||
namespace Ui {
|
||||
class PathShiftGradient;
|
||||
struct BubblePattern;
|
||||
} // namespace Ui
|
||||
|
||||
namespace HistoryView {
|
||||
@@ -191,6 +192,30 @@ struct DateBadge : public RuntimeComponent<DateBadge, Element> {
|
||||
|
||||
};
|
||||
|
||||
struct PaintContext {
|
||||
const Ui::BubblePattern *bubblesPattern = nullptr;
|
||||
QRect viewport;
|
||||
QRect clip;
|
||||
TextSelection selection;
|
||||
crl::time now = 0;
|
||||
|
||||
void translate(int x, int y) {
|
||||
viewport.translate(x, y);
|
||||
clip.translate(x, y);
|
||||
}
|
||||
void translate(QPoint point) {
|
||||
translate(point.x(), point.y());
|
||||
}
|
||||
[[nodiscard]] PaintContext translated(int x, int y) const {
|
||||
auto result = *this;
|
||||
result.translate(x, y);
|
||||
return result;
|
||||
}
|
||||
[[nodiscard]] PaintContext translated(QPoint point) const {
|
||||
return translated(point.x(), point.y());
|
||||
}
|
||||
};
|
||||
|
||||
class Element
|
||||
: public Object
|
||||
, public RuntimeComposer<Element>
|
||||
@@ -261,11 +286,7 @@ public:
|
||||
bool displayDate() const;
|
||||
bool isInOneDayWithPrevious() const;
|
||||
|
||||
virtual void draw(
|
||||
Painter &p,
|
||||
QRect clip,
|
||||
TextSelection selection,
|
||||
crl::time ms) const = 0;
|
||||
virtual void draw(Painter &p, const PaintContext &context) const = 0;
|
||||
[[nodiscard]] virtual PointState pointState(QPoint point) const = 0;
|
||||
[[nodiscard]] virtual TextState textState(
|
||||
QPoint point,
|
||||
|
||||
@@ -1444,6 +1444,9 @@ void ListWidget::startItemRevealAnimations() {
|
||||
1.,
|
||||
kItemRevealDuration,
|
||||
anim::easeOutCirc);
|
||||
if (view->data()->out()) {
|
||||
controller()->rotateComplexGradientBackground();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1609,17 +1612,22 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
||||
return this->itemTop(elem) < bottom;
|
||||
});
|
||||
if (from != end(_items)) {
|
||||
auto viewport = QRect(); // #TODO bubbles
|
||||
auto top = itemTop(from->get());
|
||||
auto context = HistoryView::PaintContext{
|
||||
.bubblesPattern = nullptr,
|
||||
.viewport = viewport.translated(0, -top),
|
||||
.clip = clip.translated(0, -top),
|
||||
.now = crl::now(),
|
||||
};
|
||||
p.translate(0, top);
|
||||
for (auto i = from; i != to; ++i) {
|
||||
const auto view = *i;
|
||||
view->draw(
|
||||
p,
|
||||
clip.translated(0, -top),
|
||||
itemRenderSelection(view),
|
||||
ms);
|
||||
view->draw(p, context);
|
||||
const auto height = view->height();
|
||||
top += height;
|
||||
context.viewport.translate(0, -height);
|
||||
context.clip.translate(0, -height);
|
||||
p.translate(0, height);
|
||||
}
|
||||
p.translate(0, -top);
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "ui/chat/message_bubble.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/text/text_entity.h"
|
||||
@@ -149,103 +150,6 @@ QString FastReplyText() {
|
||||
return tr::lng_fast_reply(tr::now);
|
||||
}
|
||||
|
||||
void PaintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool outbg, RectPart tailSide, RectParts skip) {
|
||||
auto &bg = selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg);
|
||||
auto sh = &(selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow));
|
||||
auto cors = selected ? (outbg ? Ui::MessageOutSelectedCorners : Ui::MessageInSelectedCorners) : (outbg ? Ui::MessageOutCorners : Ui::MessageInCorners);
|
||||
auto parts = RectPart::None | RectPart::NoTopBottom;
|
||||
if (skip & RectPart::Top) {
|
||||
if (skip & RectPart::Bottom) {
|
||||
p.fillRect(rect, bg);
|
||||
return;
|
||||
}
|
||||
rect.setTop(rect.y() - st::historyMessageRadius);
|
||||
} else {
|
||||
parts |= RectPart::FullTop;
|
||||
}
|
||||
if (skip & RectPart::Bottom) {
|
||||
rect.setHeight(rect.height() + st::historyMessageRadius);
|
||||
sh = nullptr;
|
||||
tailSide = RectPart::None;
|
||||
} else {
|
||||
parts |= RectPart::Bottom;
|
||||
}
|
||||
if (tailSide == RectPart::Right) {
|
||||
parts |= RectPart::BottomLeft;
|
||||
p.fillRect(rect.x() + rect.width() - st::historyMessageRadius, rect.y() + rect.height() - st::historyMessageRadius, st::historyMessageRadius, st::historyMessageRadius, bg);
|
||||
auto &tail = selected ? st::historyBubbleTailOutRightSelected : st::historyBubbleTailOutRight;
|
||||
tail.paint(p, rect.x() + rect.width(), rect.y() + rect.height() - tail.height(), outerWidth);
|
||||
p.fillRect(rect.x() + rect.width() - st::historyMessageRadius, rect.y() + rect.height(), st::historyMessageRadius + tail.width(), st::msgShadow, *sh);
|
||||
} else if (tailSide == RectPart::Left) {
|
||||
parts |= RectPart::BottomRight;
|
||||
p.fillRect(rect.x(), rect.y() + rect.height() - st::historyMessageRadius, st::historyMessageRadius, st::historyMessageRadius, bg);
|
||||
auto &tail = selected ? (outbg ? st::historyBubbleTailOutLeftSelected : st::historyBubbleTailInLeftSelected) : (outbg ? st::historyBubbleTailOutLeft : st::historyBubbleTailInLeft);
|
||||
tail.paint(p, rect.x() - tail.width(), rect.y() + rect.height() - tail.height(), outerWidth);
|
||||
p.fillRect(rect.x() - tail.width(), rect.y() + rect.height(), st::historyMessageRadius + tail.width(), st::msgShadow, *sh);
|
||||
} else if (!(skip & RectPart::Bottom)) {
|
||||
parts |= RectPart::FullBottom;
|
||||
}
|
||||
Ui::FillRoundRect(p, rect, bg, cors, sh, parts);
|
||||
}
|
||||
|
||||
void PaintBubble(Painter &p, QRect rect, int outerWidth, bool selected, const std::vector<BubbleSelectionInterval> &selection, bool outbg, RectPart tailSide) {
|
||||
if (selection.empty()) {
|
||||
PaintBubble(
|
||||
p,
|
||||
rect,
|
||||
outerWidth,
|
||||
selected,
|
||||
outbg,
|
||||
tailSide,
|
||||
RectPart::None);
|
||||
return;
|
||||
}
|
||||
const auto left = rect.x();
|
||||
const auto width = rect.width();
|
||||
const auto top = rect.y();
|
||||
const auto bottom = top + rect.height();
|
||||
auto from = top;
|
||||
for (const auto &selected : selection) {
|
||||
if (selected.top > from) {
|
||||
const auto skip = RectPart::Bottom
|
||||
| (from > top ? RectPart::Top : RectPart::None);
|
||||
PaintBubble(
|
||||
p,
|
||||
QRect(left, from, width, selected.top - from),
|
||||
outerWidth,
|
||||
false,
|
||||
outbg,
|
||||
tailSide,
|
||||
skip);
|
||||
}
|
||||
const auto skip = ((selected.top > top)
|
||||
? RectPart::Top
|
||||
: RectPart::None)
|
||||
| ((selected.top + selected.height < bottom)
|
||||
? RectPart::Bottom
|
||||
: RectPart::None);
|
||||
PaintBubble(
|
||||
p,
|
||||
QRect(left, selected.top, width, selected.height),
|
||||
outerWidth,
|
||||
true,
|
||||
outbg,
|
||||
tailSide,
|
||||
skip);
|
||||
from = selected.top + selected.height;
|
||||
}
|
||||
if (from < bottom) {
|
||||
PaintBubble(
|
||||
p,
|
||||
QRect(left, from, width, bottom - from),
|
||||
outerWidth,
|
||||
false,
|
||||
outbg,
|
||||
tailSide,
|
||||
RectPart::Top);
|
||||
}
|
||||
}
|
||||
|
||||
style::color FromNameFg(PeerId peerId, bool selected) {
|
||||
if (selected) {
|
||||
const style::color colors[] = {
|
||||
@@ -545,11 +449,7 @@ int Message::marginBottom() const {
|
||||
return isHidden() ? 0 : st::msgMargin.bottom();
|
||||
}
|
||||
|
||||
void Message::draw(
|
||||
Painter &p,
|
||||
QRect clip,
|
||||
TextSelection selection,
|
||||
crl::time ms) const {
|
||||
void Message::draw(Painter &p, const PaintContext &context) const {
|
||||
auto g = countGeometry();
|
||||
if (g.width() < 1) {
|
||||
return;
|
||||
@@ -560,7 +460,7 @@ void Message::draw(
|
||||
|
||||
const auto outbg = hasOutLayout();
|
||||
const auto bubble = drawBubble();
|
||||
const auto selected = (selection == FullSelection);
|
||||
const auto selected = (context.selection == FullSelection);
|
||||
|
||||
auto dateh = 0;
|
||||
if (const auto date = Get<DateBadge>()) {
|
||||
@@ -568,7 +468,7 @@ void Message::draw(
|
||||
}
|
||||
if (const auto bar = Get<UnreadBar>()) {
|
||||
auto unreadbarh = bar->height();
|
||||
if (clip.intersects(QRect(0, dateh, width(), unreadbarh))) {
|
||||
if (context.clip.intersects(QRect(0, dateh, width(), unreadbarh))) {
|
||||
p.translate(0, dateh);
|
||||
bar->paint(p, 0, width(), delegate()->elementIsChatWide());
|
||||
p.translate(0, -dateh);
|
||||
@@ -587,8 +487,8 @@ void Message::draw(
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
|
||||
auto mediaSelectionIntervals = (!selected && mediaDisplayed)
|
||||
? media->getBubbleSelectionIntervals(selection)
|
||||
: std::vector<BubbleSelectionInterval>();
|
||||
? media->getBubbleSelectionIntervals(context.selection)
|
||||
: std::vector<Ui::BubbleSelectionInterval>();
|
||||
auto localMediaTop = 0;
|
||||
const auto customHighlight = mediaDisplayed && media->customHighlight();
|
||||
if (!mediaSelectionIntervals.empty() || customHighlight) {
|
||||
@@ -633,7 +533,7 @@ void Message::draw(
|
||||
g.setHeight(g.height() - keyboardHeight);
|
||||
auto keyboardPosition = QPoint(g.left(), g.top() + g.height() + st::msgBotKbButton.margin);
|
||||
p.translate(keyboardPosition);
|
||||
keyboard->paint(p, g.width(), clip.translated(-keyboardPosition));
|
||||
keyboard->paint(p, g.width(), context.clip.translated(-keyboardPosition));
|
||||
p.translate(-keyboardPosition);
|
||||
}
|
||||
|
||||
@@ -644,23 +544,30 @@ void Message::draw(
|
||||
fromNameUpdated(g.width());
|
||||
}
|
||||
|
||||
auto skipTail = isAttachedToNext()
|
||||
const auto skipTail = isAttachedToNext()
|
||||
|| (media && media->skipBubbleTail())
|
||||
|| (keyboard != nullptr)
|
||||
|| (context() == Context::Replies && data()->isDiscussionPost());
|
||||
auto displayTail = skipTail
|
||||
|| (this->context() == Context::Replies
|
||||
&& data()->isDiscussionPost());
|
||||
const auto displayTail = skipTail
|
||||
? RectPart::None
|
||||
: (outbg && !delegate()->elementIsChatWide())
|
||||
? RectPart::Right
|
||||
: RectPart::Left;
|
||||
PaintBubble(
|
||||
Ui::PaintBubble(
|
||||
p,
|
||||
g,
|
||||
width(),
|
||||
selected,
|
||||
mediaSelectionIntervals,
|
||||
outbg,
|
||||
displayTail);
|
||||
Ui::ComplexBubble{
|
||||
.simple = Ui::SimpleBubble{
|
||||
.geometry = g,
|
||||
.pattern = context.bubblesPattern,
|
||||
.patternViewport = context.viewport,
|
||||
.outerWidth = width(),
|
||||
.selected = selected,
|
||||
.outbg = outbg,
|
||||
.tailSide = displayTail,
|
||||
},
|
||||
.selection = mediaSelectionIntervals,
|
||||
});
|
||||
|
||||
auto inner = g;
|
||||
paintCommentsButton(p, inner, selected);
|
||||
@@ -680,25 +587,29 @@ void Message::draw(
|
||||
if (entry) {
|
||||
trect.setHeight(trect.height() - entry->height());
|
||||
}
|
||||
paintText(p, trect, selection);
|
||||
paintText(p, trect, context.selection);
|
||||
if (mediaDisplayed) {
|
||||
auto mediaHeight = media->height();
|
||||
auto mediaLeft = inner.left();
|
||||
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
|
||||
|
||||
p.translate(mediaLeft, mediaTop);
|
||||
media->draw(p, clip.translated(-mediaLeft, -mediaTop), skipTextSelection(selection), ms);
|
||||
auto mediaContext = context.translated(-mediaLeft, -mediaTop);
|
||||
mediaContext.selection = skipTextSelection(context.selection);
|
||||
media->draw(p, mediaContext);
|
||||
p.translate(-mediaLeft, -mediaTop);
|
||||
}
|
||||
if (entry) {
|
||||
auto entryLeft = inner.left();
|
||||
auto entryTop = trect.y() + trect.height();
|
||||
p.translate(entryLeft, entryTop);
|
||||
auto entrySelection = skipTextSelection(selection);
|
||||
auto entryContext = context.translated(-entryLeft, -entryTop);
|
||||
entryContext.selection = skipTextSelection(context.selection);
|
||||
if (mediaDisplayed) {
|
||||
entrySelection = media->skipSelection(entrySelection);
|
||||
entryContext.selection = media->skipSelection(
|
||||
entryContext.selection);
|
||||
}
|
||||
entry->draw(p, clip.translated(-entryLeft, -entryTop), entrySelection, ms);
|
||||
entry->draw(p, entryContext);
|
||||
p.translate(-entryLeft, -entryTop);
|
||||
}
|
||||
const auto needDrawInfo = entry
|
||||
@@ -734,11 +645,13 @@ void Message::draw(
|
||||
}
|
||||
|
||||
if (media) {
|
||||
media->paintBubbleFireworks(p, g, ms);
|
||||
media->paintBubbleFireworks(p, g, context.now);
|
||||
}
|
||||
} else if (media && media->isDisplayed()) {
|
||||
p.translate(g.topLeft());
|
||||
media->draw(p, clip.translated(-g.topLeft()), skipTextSelection(selection), ms);
|
||||
auto mediaContext = context.translated(-g.topLeft());
|
||||
mediaContext.selection = skipTextSelection(context.selection);
|
||||
media->draw(p, mediaContext);
|
||||
p.translate(-g.topLeft());
|
||||
}
|
||||
|
||||
@@ -2128,10 +2041,8 @@ bool Message::displayForwardedFrom() const {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const auto media = this->media();
|
||||
return !media
|
||||
|| !media->isDisplayed()
|
||||
|| !media->hideForwardedFrom();
|
||||
const auto media = item->media();
|
||||
return !media || !media->dropForwardedInfo();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -51,11 +51,7 @@ public:
|
||||
|
||||
int marginTop() const override;
|
||||
int marginBottom() const override;
|
||||
void draw(
|
||||
Painter &p,
|
||||
QRect clip,
|
||||
TextSelection selection,
|
||||
crl::time ms) const override;
|
||||
void draw(Painter &p, const PaintContext &context) const override;
|
||||
PointState pointState(QPoint point) const override;
|
||||
TextState textState(
|
||||
QPoint point,
|
||||
|
||||
@@ -44,7 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "platform/platform_specific.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "facades.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_info.h"
|
||||
@@ -91,7 +90,7 @@ PinnedWidget::PinnedWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
, _history(history->migrateToOrMe())
|
||||
, _migratedPeer(_history->peer->migrateFrom())
|
||||
, _topBar(this, controller)
|
||||
|
||||
@@ -55,10 +55,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "inline_bots/inline_bot_result.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "facades.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_info.h"
|
||||
@@ -148,7 +146,7 @@ RepliesWidget::RepliesWidget(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history,
|
||||
MsgId rootId)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
, _history(history)
|
||||
, _rootId(rootId)
|
||||
, _root(lookupRoot())
|
||||
@@ -558,15 +556,12 @@ void RepliesWidget::chooseAttach() {
|
||||
}
|
||||
|
||||
if (!result.remoteContent.isEmpty()) {
|
||||
auto animated = false;
|
||||
auto image = App::readImage(
|
||||
result.remoteContent,
|
||||
nullptr,
|
||||
false,
|
||||
&animated);
|
||||
if (!image.isNull() && !animated) {
|
||||
auto read = Images::Read({
|
||||
.content = result.remoteContent,
|
||||
});
|
||||
if (!read.image.isNull() && !read.animated) {
|
||||
confirmSendingFiles(
|
||||
std::move(image),
|
||||
std::move(read.image),
|
||||
std::move(result.remoteContent));
|
||||
} else {
|
||||
uploadFile(result.remoteContent, SendMediaType::File);
|
||||
@@ -602,10 +597,7 @@ bool RepliesWidget::confirmSendingFiles(
|
||||
}
|
||||
|
||||
if (hasImage) {
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
confirmSendingFiles(
|
||||
std::move(image),
|
||||
|
||||
@@ -48,10 +48,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "inline_bots/inline_bot_result.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "facades.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_info.h"
|
||||
@@ -92,7 +90,7 @@ ScheduledWidget::ScheduledWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
, _history(history)
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _topBar(this, controller)
|
||||
@@ -298,15 +296,12 @@ void ScheduledWidget::chooseAttach() {
|
||||
}
|
||||
|
||||
if (!result.remoteContent.isEmpty()) {
|
||||
auto animated = false;
|
||||
auto image = App::readImage(
|
||||
result.remoteContent,
|
||||
nullptr,
|
||||
false,
|
||||
&animated);
|
||||
if (!image.isNull() && !animated) {
|
||||
auto read = Images::Read({
|
||||
.content = result.remoteContent,
|
||||
});
|
||||
if (!read.image.isNull() && !read.animated) {
|
||||
confirmSendingFiles(
|
||||
std::move(image),
|
||||
std::move(read.image),
|
||||
std::move(result.remoteContent));
|
||||
} else {
|
||||
uploadFile(result.remoteContent, SendMediaType::File);
|
||||
@@ -342,10 +337,7 @@ bool ScheduledWidget::confirmSendingFiles(
|
||||
}
|
||||
|
||||
if (hasImage) {
|
||||
auto image = Platform::GetImageFromClipboard();
|
||||
if (image.isNull()) {
|
||||
image = qvariant_cast<QImage>(data->imageData());
|
||||
}
|
||||
auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
confirmSendingFiles(
|
||||
std::move(image),
|
||||
|
||||
@@ -519,11 +519,7 @@ int Service::marginBottom() const {
|
||||
return st::msgServiceMargin.bottom();
|
||||
}
|
||||
|
||||
void Service::draw(
|
||||
Painter &p,
|
||||
QRect clip,
|
||||
TextSelection selection,
|
||||
crl::time ms) const {
|
||||
void Service::draw(Painter &p, const PaintContext &context) const {
|
||||
const auto item = message();
|
||||
auto g = countGeometry();
|
||||
if (g.width() < 1) {
|
||||
@@ -533,6 +529,7 @@ void Service::draw(
|
||||
auto height = this->height() - st::msgServiceMargin.top() - st::msgServiceMargin.bottom();
|
||||
auto dateh = 0;
|
||||
auto unreadbarh = 0;
|
||||
auto clip = context.clip;
|
||||
if (auto date = Get<DateBadge>()) {
|
||||
dateh = date->height();
|
||||
p.translate(0, dateh);
|
||||
@@ -564,7 +561,9 @@ void Service::draw(
|
||||
height -= st::msgServiceMargin.top() + media->height();
|
||||
auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top();
|
||||
p.translate(left, top);
|
||||
media->draw(p, clip.translated(-left, -top), TextSelection(), ms);
|
||||
auto mediaContext = context.translated(-left, -top);
|
||||
mediaContext.selection = TextSelection();
|
||||
media->draw(p, mediaContext);
|
||||
p.translate(-left, -top);
|
||||
}
|
||||
|
||||
@@ -575,7 +574,7 @@ void Service::draw(
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(st::msgServiceFg);
|
||||
p.setFont(st::msgServiceFont);
|
||||
item->_text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignCenter, 0, -1, selection, false);
|
||||
item->_text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignCenter, 0, -1, context.selection, false);
|
||||
|
||||
p.restoreTextPalette();
|
||||
|
||||
|
||||
@@ -23,11 +23,7 @@ public:
|
||||
int marginTop() const override;
|
||||
int marginBottom() const override;
|
||||
bool isHidden() const override;
|
||||
void draw(
|
||||
Painter &p,
|
||||
QRect clip,
|
||||
TextSelection selection,
|
||||
crl::time ms) const override;
|
||||
void draw(Painter &p, const PaintContext &context) const override;
|
||||
PointState pointState(QPoint point) const override;
|
||||
TextState textState(
|
||||
QPoint point,
|
||||
@@ -50,17 +46,6 @@ private:
|
||||
|
||||
int WideChatWidth();
|
||||
|
||||
struct PaintContext {
|
||||
PaintContext(crl::time ms, const QRect &clip, TextSelection selection)
|
||||
: ms(ms)
|
||||
, clip(clip)
|
||||
, selection(selection) {
|
||||
}
|
||||
crl::time ms;
|
||||
const QRect &clip;
|
||||
TextSelection selection;
|
||||
};
|
||||
|
||||
class ServiceMessagePainter {
|
||||
public:
|
||||
static void paintDate(
|
||||
|
||||
@@ -72,12 +72,12 @@ QSize Call::countOptimalSize() {
|
||||
return { maxWidth, minHeight };
|
||||
}
|
||||
|
||||
void Call::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const {
|
||||
void Call::draw(Painter &p, const PaintContext &context) const {
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
auto paintw = width();
|
||||
|
||||
auto outbg = _parent->hasOutLayout();
|
||||
auto selected = (selection == FullSelection);
|
||||
auto selected = (context.selection == FullSelection);
|
||||
|
||||
accumulate_min(paintw, maxWidth());
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
not_null<Element*> parent,
|
||||
not_null<Data::Call*> call);
|
||||
|
||||
void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override;
|
||||
void draw(Painter &p, const PaintContext &context) const override;
|
||||
TextState textState(QPoint point, StateRequest request) const override;
|
||||
|
||||
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
|
||||
|
||||
@@ -147,27 +147,18 @@ QSize Contact::countOptimalSize() {
|
||||
accumulate_max(maxWidth, tleft + _name.maxWidth() + tright);
|
||||
accumulate_min(maxWidth, st::msgMaxWidth);
|
||||
auto minHeight = st.padding.top() + st.thumbSize + st.padding.bottom();
|
||||
if (_userId) {
|
||||
const auto msgsigned = item->Get<HistoryMessageSigned>();
|
||||
const auto views = item->Get<HistoryMessageViews>();
|
||||
if ((msgsigned && !msgsigned->isAnonymousRank)
|
||||
|| (views
|
||||
&& (views->views.count >= 0 || views->replies.count > 0))) {
|
||||
minHeight += st::msgDateFont->height - st::msgDateDelta.y();
|
||||
}
|
||||
}
|
||||
if (!isBubbleTop()) {
|
||||
minHeight -= st::msgFileTopMinus;
|
||||
}
|
||||
return { maxWidth, minHeight };
|
||||
}
|
||||
|
||||
void Contact::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const {
|
||||
void Contact::draw(Painter &p, const PaintContext &context) const {
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
auto paintw = width();
|
||||
|
||||
auto outbg = _parent->hasOutLayout();
|
||||
bool selected = (selection == FullSelection);
|
||||
bool selected = (context.selection == FullSelection);
|
||||
|
||||
accumulate_min(paintw, maxWidth());
|
||||
|
||||
@@ -177,7 +168,8 @@ void Contact::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim
|
||||
const auto nametop = st.nameTop - topMinus;
|
||||
const auto nameright = st.padding.left();
|
||||
const auto statustop = st.statusTop - topMinus;
|
||||
const auto linktop = st.linkTop - topMinus;
|
||||
const auto linkshift = st::msgDateFont->height / 2;
|
||||
const auto linktop = st.linkTop - topMinus - linkshift;
|
||||
if (_userId) {
|
||||
QRect rthumb(style::rtlrect(st.padding.left(), st.padding.top() - topMinus, st.thumbSize, st.thumbSize, paintw));
|
||||
if (_contact) {
|
||||
@@ -222,7 +214,8 @@ TextState Contact::textState(QPoint point, StateRequest request) const {
|
||||
const auto &st = _userId ? st::msgFileThumbLayout : st::msgFileLayout;
|
||||
const auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
|
||||
const auto nameleft = st.padding.left() + st.thumbSize + st.padding.right();
|
||||
const auto linktop = st.linkTop - topMinus;
|
||||
const auto linkshift = st::msgDateFont->height / 2;
|
||||
const auto linktop = st.linkTop - topMinus - linkshift;
|
||||
if (style::rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, width()).contains(point)) {
|
||||
result.link = _linkl;
|
||||
return result;
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
const QString &phone);
|
||||
~Contact();
|
||||
|
||||
void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override;
|
||||
void draw(Painter &p, const PaintContext &context) const override;
|
||||
TextState textState(QPoint point, StateRequest request) const override;
|
||||
|
||||
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
|
||||
|
||||
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/format_song_document_name.h"
|
||||
#include "ui/chat/message_bubble.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "layout/layout_selection.h" // FullSelection
|
||||
@@ -325,19 +326,14 @@ QSize Document::countCurrentSize(int newWidth) {
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
void Document::draw(
|
||||
Painter &p,
|
||||
const QRect &r,
|
||||
TextSelection selection,
|
||||
crl::time ms) const {
|
||||
draw(p, width(), selection, ms, LayoutMode::Full);
|
||||
void Document::draw(Painter &p, const PaintContext &context) const {
|
||||
draw(p, width(), context, LayoutMode::Full);
|
||||
}
|
||||
|
||||
void Document::draw(
|
||||
Painter &p,
|
||||
int width,
|
||||
TextSelection selection,
|
||||
crl::time ms,
|
||||
const PaintContext &context,
|
||||
LayoutMode mode) const {
|
||||
if (width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
|
||||
@@ -349,7 +345,7 @@ void Document::draw(
|
||||
_dataMedia->automaticLoad(_realParent->fullId(), _realParent);
|
||||
}
|
||||
bool loaded = dataLoaded(), displayLoading = _data->displayLoading();
|
||||
bool selected = (selection == FullSelection);
|
||||
bool selected = (context.selection == FullSelection);
|
||||
|
||||
int captionw = width - st::msgPadding.left() - st::msgPadding.right();
|
||||
auto outbg = _parent->hasOutLayout();
|
||||
@@ -507,19 +503,33 @@ void Document::draw(
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
if (previous && radialOpacity > 0. && radialOpacity < 1.) {
|
||||
PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner);
|
||||
|
||||
const auto paintContent = [&](Painter &q) {
|
||||
if (previous && radialOpacity > 0. && radialOpacity < 1.) {
|
||||
PaintInterpolatedIcon(q, *icon, *previous, radialOpacity, inner);
|
||||
} else {
|
||||
icon->paintInCenter(q, inner);
|
||||
}
|
||||
|
||||
if (radial && !cornerDownload) {
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg);
|
||||
_animation->radial.draw(q, rinner, st::msgFileRadialLine, fg);
|
||||
}
|
||||
};
|
||||
if (_data->isSongWithCover() || !usesBubblePattern(context)) {
|
||||
paintContent(p);
|
||||
} else {
|
||||
icon->paintInCenter(p, inner);
|
||||
Ui::PaintPatternBubblePart(
|
||||
p,
|
||||
context.viewport,
|
||||
context.bubblesPattern->pixmap,
|
||||
inner,
|
||||
paintContent,
|
||||
_iconCache);
|
||||
}
|
||||
|
||||
if (radial && !cornerDownload) {
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg);
|
||||
_animation->radial.draw(p, rinner, st::msgFileRadialLine, fg);
|
||||
}
|
||||
|
||||
drawCornerDownload(p, selected, mode);
|
||||
drawCornerDownload(p, context, mode);
|
||||
}
|
||||
auto namewidth = width - nameleft - nameright;
|
||||
auto statuswidth = namewidth;
|
||||
@@ -595,7 +605,7 @@ void Document::draw(
|
||||
|
||||
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
|
||||
captioned->_caption.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, selection);
|
||||
captioned->_caption.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, context.selection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,7 +635,10 @@ bool Document::downloadInCorner() const {
|
||||
&& IsServerMsgId(_realParent->id);
|
||||
}
|
||||
|
||||
void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) const {
|
||||
void Document::drawCornerDownload(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
LayoutMode mode) const {
|
||||
if (dataLoaded()
|
||||
|| _data->loadedInMediaCache()
|
||||
|| !downloadInCorner()) {
|
||||
@@ -633,6 +646,7 @@ void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) co
|
||||
}
|
||||
auto outbg = _parent->hasOutLayout();
|
||||
auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
|
||||
const auto selected = (context.selection == FullSelection);
|
||||
const auto thumbed = false;
|
||||
const auto &st = (mode == LayoutMode::Full)
|
||||
? (thumbed ? st::msgFileThumbLayout : st::msgFileLayout)
|
||||
@@ -640,11 +654,16 @@ void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) co
|
||||
const auto shift = st::historyAudioDownloadShift;
|
||||
const auto size = st::historyAudioDownloadSize;
|
||||
const auto inner = style::rtlrect(st.padding.left() + shift, st.padding.top() - topMinus + shift, size, size, width());
|
||||
auto pen = (selected
|
||||
? (outbg ? st::msgOutBgSelected : st::msgInBgSelected)
|
||||
: (outbg ? st::msgOutBg : st::msgInBg))->p;
|
||||
pen.setWidth(st::lineWidth);
|
||||
p.setPen(pen);
|
||||
const auto bubblePattern = usesBubblePattern(context);
|
||||
if (bubblePattern) {
|
||||
p.setPen(Qt::NoPen);
|
||||
} else {
|
||||
auto pen = (selected
|
||||
? (outbg ? st::msgOutBgSelected : st::msgInBgSelected)
|
||||
: (outbg ? st::msgOutBg : st::msgInBg))->p;
|
||||
pen.setWidth(st::lineWidth);
|
||||
p.setPen(pen);
|
||||
}
|
||||
if (selected) {
|
||||
p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected);
|
||||
} else {
|
||||
@@ -660,11 +679,34 @@ void Document::drawCornerDownload(Painter &p, bool selected, LayoutMode mode) co
|
||||
}
|
||||
return &(outbg ? (selected ? st::historyAudioOutDownloadSelected : st::historyAudioOutDownload) : (selected ? st::historyAudioInDownloadSelected : st::historyAudioInDownload));
|
||||
}();
|
||||
icon->paintInCenter(p, inner);
|
||||
if (_animation && _animation->radial.animating()) {
|
||||
const auto rinner = inner.marginsRemoved(QMargins(st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine));
|
||||
auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg);
|
||||
_animation->radial.draw(p, rinner, st::historyAudioRadialLine, fg);
|
||||
const auto paintContent = [&](Painter &q) {
|
||||
if (bubblePattern) {
|
||||
auto hq = PainterHighQualityEnabler(q);
|
||||
auto pen = st::msgOutBg->p;
|
||||
pen.setWidth(st::lineWidth);
|
||||
q.setPen(pen);
|
||||
q.setBrush(Qt::NoBrush);
|
||||
q.drawEllipse(inner);
|
||||
}
|
||||
icon->paintInCenter(q, inner);
|
||||
if (_animation && _animation->radial.animating()) {
|
||||
const auto rinner = inner.marginsRemoved(QMargins(st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine));
|
||||
auto fg = outbg ? (selected ? st::historyFileOutRadialFgSelected : st::historyFileOutRadialFg) : (selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg);
|
||||
_animation->radial.draw(q, rinner, st::historyAudioRadialLine, fg);
|
||||
}
|
||||
};
|
||||
if (bubblePattern) {
|
||||
const auto add = st::lineWidth * 2;
|
||||
const auto target = inner.marginsAdded({ add, add, add, add });
|
||||
Ui::PaintPatternBubblePart(
|
||||
p,
|
||||
context.viewport,
|
||||
context.bubblesPattern->pixmap,
|
||||
target,
|
||||
paintContent,
|
||||
_cornerDownloadCache);
|
||||
} else {
|
||||
paintContent(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,10 +992,6 @@ QMargins Document::bubbleMargins() const {
|
||||
return QMargins(padding.left(), padding.top(), padding.left(), padding.bottom());
|
||||
}
|
||||
|
||||
bool Document::hideForwardedFrom() const {
|
||||
return _data->isSong();
|
||||
}
|
||||
|
||||
QSize Document::sizeForGroupingOptimal(int maxWidth) const {
|
||||
const auto thumbed = Get<HistoryDocumentThumbed>();
|
||||
const auto &st = (thumbed ? st::msgFileThumbLayoutGrouped : st::msgFileLayoutGrouped);
|
||||
@@ -982,9 +1020,7 @@ QSize Document::sizeForGrouping(int width) const {
|
||||
|
||||
void Document::drawGrouped(
|
||||
Painter &p,
|
||||
const QRect &clip,
|
||||
TextSelection selection,
|
||||
crl::time ms,
|
||||
const PaintContext &context,
|
||||
const QRect &geometry,
|
||||
RectParts sides,
|
||||
RectParts corners,
|
||||
@@ -995,8 +1031,7 @@ void Document::drawGrouped(
|
||||
draw(
|
||||
p,
|
||||
geometry.width(),
|
||||
selection,
|
||||
ms,
|
||||
context.translated(-geometry.topLeft()),
|
||||
LayoutMode::Grouped);
|
||||
p.translate(-geometry.topLeft());
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
not_null<DocumentData*> document);
|
||||
~Document();
|
||||
|
||||
void draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const override;
|
||||
void draw(Painter &p, const PaintContext &context) const override;
|
||||
TextState textState(QPoint point, StateRequest request) const override;
|
||||
void updatePressed(QPoint point) override;
|
||||
|
||||
@@ -60,15 +60,12 @@ public:
|
||||
return false;
|
||||
}
|
||||
QMargins bubbleMargins() const override;
|
||||
bool hideForwardedFrom() const override;
|
||||
|
||||
QSize sizeForGroupingOptimal(int maxWidth) const override;
|
||||
QSize sizeForGrouping(int width) const override;
|
||||
void drawGrouped(
|
||||
Painter &p,
|
||||
const QRect &clip,
|
||||
TextSelection selection,
|
||||
crl::time ms,
|
||||
const PaintContext &context,
|
||||
const QRect &geometry,
|
||||
RectParts sides,
|
||||
RectParts corners,
|
||||
@@ -110,8 +107,7 @@ private:
|
||||
void draw(
|
||||
Painter &p,
|
||||
int width,
|
||||
TextSelection selection,
|
||||
crl::time ms,
|
||||
const PaintContext &context,
|
||||
LayoutMode mode) const;
|
||||
[[nodiscard]] TextState textState(
|
||||
QPoint point,
|
||||
@@ -132,7 +128,10 @@ private:
|
||||
bool updateStatusText() const; // returns showPause
|
||||
|
||||
[[nodiscard]] bool downloadInCorner() const;
|
||||
void drawCornerDownload(Painter &p, bool selected, LayoutMode mode) const;
|
||||
void drawCornerDownload(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
LayoutMode mode) const;
|
||||
[[nodiscard]] TextState cornerDownloadTextState(
|
||||
QPoint point,
|
||||
StateRequest request,
|
||||
@@ -140,6 +139,8 @@ private:
|
||||
|
||||
not_null<DocumentData*> _data;
|
||||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||
mutable QImage _iconCache;
|
||||
mutable QImage _cornerDownloadCache;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -58,9 +58,15 @@ void File::setLinks(
|
||||
|
||||
void File::refreshParentId(not_null<HistoryItem*> realParent) {
|
||||
const auto contextId = realParent->fullId();
|
||||
_openl->setMessageId(contextId);
|
||||
_savel->setMessageId(contextId);
|
||||
_cancell->setMessageId(contextId);
|
||||
if (_openl) {
|
||||
_openl->setMessageId(contextId);
|
||||
}
|
||||
if (_savel) {
|
||||
_savel->setMessageId(contextId);
|
||||
}
|
||||
if (_cancell) {
|
||||
_cancell->setMessageId(contextId);
|
||||
}
|
||||
}
|
||||
|
||||
void File::setStatusSize(int newSize, int fullSize, int duration, qint64 realDuration) const {
|
||||
|
||||
@@ -198,12 +198,12 @@ TextSelection Game::fromDescriptionSelection(
|
||||
return ShiftItemSelection(selection, _title);
|
||||
}
|
||||
|
||||
void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const {
|
||||
void Game::draw(Painter &p, const PaintContext &context) const {
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
auto paintw = width();
|
||||
|
||||
auto outbg = _parent->hasOutLayout();
|
||||
bool selected = (selection == FullSelection);
|
||||
bool selected = (context.selection == FullSelection);
|
||||
|
||||
auto &barfg = selected ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor);
|
||||
auto &semibold = selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg);
|
||||
@@ -227,7 +227,7 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m
|
||||
if (_title.hasSkipBlock()) {
|
||||
endskip = _parent->skipBlockWidth();
|
||||
}
|
||||
_title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleLines, style::al_left, 0, -1, endskip, false, selection);
|
||||
_title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleLines, style::al_left, 0, -1, endskip, false, context.selection);
|
||||
tshift += _titleLines * lineHeight;
|
||||
}
|
||||
if (_descriptionLines) {
|
||||
@@ -236,7 +236,7 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m
|
||||
if (_description.hasSkipBlock()) {
|
||||
endskip = _parent->skipBlockWidth();
|
||||
}
|
||||
_description.drawLeftElided(p, padding.left(), tshift, paintw, width(), _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
|
||||
_description.drawLeftElided(p, padding.left(), tshift, paintw, width(), _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(context.selection));
|
||||
tshift += _descriptionLines * lineHeight;
|
||||
}
|
||||
if (_attach) {
|
||||
@@ -247,10 +247,11 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m
|
||||
auto attachTop = tshift - bubble.top();
|
||||
if (rtl()) attachLeft = width() - attachLeft - _attach->width();
|
||||
|
||||
auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 };
|
||||
auto attachContext = context.translated(-attachLeft, -attachTop);
|
||||
attachContext.selection = selected ? FullSelection : TextSelection { 0, 0 };
|
||||
|
||||
p.translate(attachLeft, attachTop);
|
||||
_attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms);
|
||||
_attach->draw(p, attachContext);
|
||||
auto pixwidth = _attach->width();
|
||||
auto pixheight = _attach->height();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user