Compare commits

...

54 Commits

Author SHA1 Message Date
John Preston
1ef0046002 Beta version 2.9.4.
- Choose one from dozens of new gorgeous animated backgrounds
in Chat Settings > Chat background.
2021-08-17 18:09:18 +03:00
John Preston
f2f19b14eb Fix reading one message if last one is outgoing. 2021-08-17 18:07:20 +03:00
John Preston
662966ba31 Support patterns with negative intensity. 2021-08-17 17:35:10 +03:00
John Preston
5383ae3d96 Fix log in after password change by email. 2021-08-17 15:51:36 +03:00
John Preston
d80b25944e Fix Snap build on GitHub Actions. 2021-08-17 15:17:58 +03:00
23rd
76813db3ad Completed ability to recover cloud password with email. 2021-08-17 15:08:57 +03:00
John Preston
c3595f2e31 Ask for a new password when recovering by email. 2021-08-17 15:07:58 +03:00
John Preston
5a882d1fdc Rotate background in ListWidget. 2021-08-17 14:55:40 +03:00
John Preston
ce6f9f580f Remove unused ColorizePattern function. 2021-08-17 13:06:28 +03:00
John Preston
52b9a1fceb Log all Qt messages as usual in debug builds. 2021-08-17 12:25:13 +03:00
John Preston
1209b2692a Generate correct next rotated gradient. 2021-08-16 17:15:02 +03:00
John Preston
2abcb51dda Filter out patterns without background colors. 2021-08-16 17:14:34 +03:00
John Preston
7a06eccaec Make complex gradients animate on outgoing messages. 2021-08-16 16:07:43 +03:00
John Preston
a1f81e4de8 Generate static complex gradients. 2021-08-16 13:24:15 +03:00
John Preston
689378ee04 Prefer 1280x720 resolution for camera video. 2021-08-16 10:26:08 +03:00
John Preston
b239506150 Fix pattern wallpaper preview on retina screens. 2021-08-16 10:26:08 +03:00
John Preston
3dadcd9352 Animated transition on pattern-on-gradient resize. 2021-08-16 10:26:08 +03:00
John Preston
b9a9520ef5 Don't blend SoftLight patterns in realtime. 2021-08-16 10:26:08 +03:00
John Preston
2b46f87d7b Cache background quickly if no buttons pressed. 2021-08-16 10:26:08 +03:00
John Preston
2667bb3568 Move background caching to Window::SessionController. 2021-08-16 10:26:08 +03:00
John Preston
1bc5277d51 Show color / gradient wallpapers in WebPage previews. 2021-08-16 10:26:08 +03:00
John Preston
436d7b9d82 Add support for linear gradients without patterns. 2021-08-16 10:26:08 +03:00
John Preston
ba7e976fe2 Fix background sample generation. 2021-08-16 10:26:08 +03:00
John Preston
c2b1187948 Start support of linear gradient wallpapers. 2021-08-16 10:26:08 +03:00
23rd
1fd28d5cfb Removed MTP* from public interface of Api::CloudPassword. 2021-08-15 13:44:43 +03:00
23rd
eec58137e9 Fixed build for Linux. 2021-08-15 13:26:43 +03:00
Ilya Fedin
e7d39e6046 Get rid of GtkIntegration::initializeSettings use 2021-08-13 15:21:19 +03:00
Ilya Fedin
63a92cb90a Log icon theme 2021-08-13 15:21:19 +03:00
John Preston
85cc3b30a0 Don't use MTP* for WallPaper flags. 2021-08-12 12:32:30 +03:00
John Preston
474a6a71d9 Move unread mentions menu to chat_helpers/send_context_menu. 2021-08-12 10:06:16 +03:00
John Preston
393173c1da Finish 'Mark mentions as read' context menu. 2021-08-12 09:46:02 +03:00
John Preston
badc27eda4 Fix build with a correct lib_ui revision. 2021-08-12 09:03:19 +03:00
test
920f3b245b Update lang.strings 2021-08-12 08:52:56 +03:00
test
f189ffc6ac Summary 2021-08-12 08:52:56 +03:00
Nicholas Guriev
840bb447ba Mention some missing includes in payments/ subdirectory 2021-08-12 08:51:45 +03:00
Ilya Fedin
414456d003 Revert "Use gtk clipboard when available to avoid https://bugreports.qt.io/browse/QTBUG-56595"
Fixed in Qt by https://codereview.qt-project.org/c/qt/qtbase/+/306771

This reverts commit 3a91003eea.
2021-08-12 08:51:10 +03:00
23rd
ea3191badf Fixed Github CI build. 2021-08-12 08:34:51 +03:00
John Preston
4452edcaad Update release asset uploading script. 2021-08-12 08:34:47 +03:00
23rd
76eb00cea9 Added drafts saving on account switching. 2021-08-12 02:36:41 +03:00
23rd
b3622b413e Added ability to set custom auto-lock timer. 2021-08-12 02:36:41 +03:00
23rd
b55383efe7 Moved time input widgets to lib_ui. 2021-08-12 02:36:41 +03:00
23rd
852e46f0c9 Added filepath removal for modified images in photo editor.
Fixed #16791.
2021-08-12 02:35:53 +03:00
John Preston
84fef1f045 Fix linking to QtSvg properly. 2021-08-11 20:22:45 +03:00
John Preston
6cadf54874 Add support for SVG patterns in wallpapers. 2021-08-11 19:56:12 +03:00
John Preston
e8fc874456 Build and link with QtSvg. 2021-08-11 19:55:47 +03:00
John Preston
c79cd0b692 Use Images::Read instead of App::readImage. 2021-08-11 18:55:08 +03:00
John Preston
b150ab8ef5 Add .tgv as a known mime type. 2021-08-11 18:23:14 +03:00
Ilya Fedin
8b7b0fa570 Remove -externalupdater flag
Having a path to executable in /etc/tdesktop/externalupdater is a way more convenient and is enough
2021-08-11 18:20:52 +03:00
Ilya Fedin
a3ee1e4ed5 Remove mentions of unused -testmode flag 2021-08-11 18:20:52 +03:00
Ilya Fedin
349446e6b0 Lock issues once a day rather than once a hour 2021-08-11 17:56:25 +03:00
Ilya Fedin
97262a99c7 Get rid of osx and linux32 special targets 2021-08-11 16:59:27 +03:00
Ilya Fedin
1d2e34f5e9 Write Qt messages only to log in debug mode 2021-08-11 15:49:31 +03:00
Ilya Fedin
bc2fc94e25 Don't check libtgvoip defines, too big queue 2021-08-11 15:46:38 +03:00
Ilya Fedin
6f0e94a04a WebKitGTK support doesn't depend on GTK integration anymore 2021-08-11 15:46:38 +03:00
184 changed files with 2393 additions and 2429 deletions

View File

@@ -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

View File

@@ -2,7 +2,7 @@ name: 'Lock Threads'
on:
schedule:
- cron: '0 * * * *'
- cron: '0 0 * * *'
jobs:
lock:

View File

@@ -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 ..

View File

@@ -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 ..

View File

@@ -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")

View File

@@ -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";

View File

@@ -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.4.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -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,4,0
PRODUCTVERSION 2,9,4,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.4.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.9.3.0"
VALUE "ProductVersion", "2.9.4.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -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,4,0
PRODUCTVERSION 2,9,4,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.4.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.9.3.0"
VALUE "ProductVersion", "2.9.4.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -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

View File

@@ -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");

View File

@@ -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"];

View File

@@ -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"\"";
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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);
};

View File

@@ -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.");

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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;
};

View File

@@ -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,

View File

@@ -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();
}
@@ -655,7 +634,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 +645,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 +662,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 +694,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 +725,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 +764,7 @@ bool BackgroundPreviewBox::Start(
not_null<Window::SessionController*> controller,
const QString &slug,
const QMap<QString, QString> &params) {
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
if (const auto paper = Data::WallPaper::FromColorsSlug(slug)) {
controller->show(Box<BackgroundPreviewBox>(
controller,
paper->withUrlParams(params)));

View File

@@ -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;

View File

@@ -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;

View File

@@ -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"

View File

@@ -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),

View File

@@ -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"

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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"

View File

@@ -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),

View File

@@ -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"

View File

@@ -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"

View File

@@ -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,

View File

@@ -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>

View File

@@ -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"

View File

@@ -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"

View File

@@ -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.

View File

@@ -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"

View File

@@ -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>

View File

@@ -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

View File

@@ -50,4 +50,8 @@ void SetupMenuAndShortcuts(
Fn<void()> silent,
Fn<void()> schedule);
void SetupUnreadMentionsMenu(
not_null<Ui::RpWidget*> button,
Fn<PeerData*()> currentPeer);
} // namespace SendMenu

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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();

View File

@@ -88,6 +88,10 @@ 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."
},
};
};

View File

@@ -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>

View File

@@ -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.");
}

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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)) {

View File

@@ -22,6 +22,7 @@ public:
TDesktopPalette,
WebP,
Tgs,
Tgv,
};
explicit MimeType(const QMimeType &type);

View File

@@ -242,9 +242,7 @@ QString FindUpdateFile() {
"tupdate|"
"tx64upd|"
"tmacupd|"
"tosxupd|"
"tlinuxupd|"
"tlinux32upd"
")\\d+(_[a-z\\d]+)?$",
QRegularExpression::CaseInsensitiveOption
).match(info.fileName()).hasMatch()) {

View File

@@ -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 = 2009004;
constexpr auto AppVersionStr = "2.9.4";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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));
}
});
});

View File

@@ -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);

View File

@@ -1007,7 +1007,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();
}

View File

@@ -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 {

View File

@@ -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,107 @@ 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> &params) 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 (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 +431,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 +446,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 +490,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 +534,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 +626,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 +701,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 +764,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() {

View File

@@ -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> &params) 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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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)

View File

@@ -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()))) {

View File

@@ -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([=] {

View File

@@ -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()

View File

@@ -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"

View File

@@ -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()) {
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);

View File

@@ -1444,6 +1444,9 @@ void ListWidget::startItemRevealAnimations() {
1.,
kItemRevealDuration,
anim::easeOutCirc);
if (view->data()->out()) {
controller()->rotateComplexGradientBackground();
}
}
}
}

View File

@@ -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)

View File

@@ -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),

View File

@@ -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),

View File

@@ -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 {

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/format_values.h"
#include "data/data_document.h"
#include "data/data_wall_paper.h"
#include "history/view/history_view_element.h"
#include "history/view/media/history_view_media_grouped.h"
#include "history/view/media/history_view_photo.h"
@@ -78,7 +79,7 @@ std::unique_ptr<Media> CreateAttach(
return std::make_unique<ThemeDocument>(
parent,
document,
webpageUrl);
ThemeDocument::ParamsFromUrl(webpageUrl));
}
return std::make_unique<Document>(parent, parent->data(), document);
} else if (photo) {
@@ -86,6 +87,8 @@ std::unique_ptr<Media> CreateAttach(
parent,
parent->data(),
photo);
} else if (const auto params = ThemeDocument::ParamsFromUrl(webpageUrl)) {
return std::make_unique<ThemeDocument>(parent, nullptr, params);
}
return nullptr;
}

View File

@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h"
#include "data/data_wall_paper.h"
#include "base/qthelp_url.h"
#include "core/local_url_handlers.h"
#include "ui/text/format_values.h"
#include "ui/cached_round_corners.h"
#include "ui/ui_utility.h"
@@ -28,19 +29,48 @@ namespace HistoryView {
ThemeDocument::ThemeDocument(
not_null<Element*> parent,
not_null<DocumentData*> document,
const QString &url)
DocumentData *document)
: ThemeDocument(parent, document, std::nullopt) {
}
ThemeDocument::ThemeDocument(
not_null<Element*> parent,
DocumentData *document,
const std::optional<Data::WallPaper> &params)
: File(parent, parent->data())
, _data(document) {
Expects(_data->hasThumbnail() || _data->isTheme());
Expects(params.has_value() || _data->hasThumbnail() || _data->isTheme());
if (_data->isWallPaper()) {
fillPatternFieldsFrom(url);
if (params) {
_background = params->backgroundColors();
_patternOpacity = params->patternOpacity();
_gradientRotation = params->gradientRotation();
}
const auto fullId = _parent->data()->fullId();
if (_data) {
_data->loadThumbnail(fullId);
setDocumentLinks(_data, parent->data());
setStatusSize(Ui::FileStatusSizeReady, _data->size, -1, 0);
} else {
class EmptyFileClickHandler final : public FileClickHandler {
public:
using FileClickHandler::FileClickHandler;
_data->loadThumbnail(_parent->data()->fullId());
setDocumentLinks(_data, parent->data());
setStatusSize(Ui::FileStatusSizeReady, _data->size, -1, 0);
private:
void onClickImpl() const override {
}
};
// We could open BackgroundPreviewBox here, but right now
// WebPage that created ThemeDocument as its attachment does it.
//
// So just provide a non-null click handler for this hack to work.
setLinks(
std::make_shared<EmptyFileClickHandler>(fullId),
nullptr,
nullptr);
}
}
ThemeDocument::~ThemeDocument() {
@@ -50,23 +80,27 @@ ThemeDocument::~ThemeDocument() {
}
}
void ThemeDocument::fillPatternFieldsFrom(const QString &url) {
const auto paramsPosition = url.indexOf('?');
std::optional<Data::WallPaper> ThemeDocument::ParamsFromUrl(
const QString &url) {
const auto local = Core::TryConvertUrlToLocal(url);
const auto paramsPosition = local.indexOf('?');
if (paramsPosition < 0) {
return;
return std::nullopt;
}
const auto paramsString = url.mid(paramsPosition + 1);
const auto paramsString = local.mid(paramsPosition + 1);
const auto params = qthelp::url_parse_params(
paramsString,
qthelp::UrlParamNameTransform::ToLower);
const auto kDefaultBackground = QColor(213, 223, 233);
const auto paper = Data::DefaultWallPaper().withUrlParams(params);
_intensity = paper.patternIntensity();
_background = paper.backgroundColor().value_or(kDefaultBackground);
auto paper = Data::DefaultWallPaper().withUrlParams(params);
return paper.backgroundColors().empty()
? std::nullopt
: std::make_optional(std::move(paper));
}
QSize ThemeDocument::countOptimalSize() {
if (_data->isTheme()) {
if (!_data) {
return { st::maxWallPaperWidth, st::maxWallPaperHeight };
} else if (_data->isTheme()) {
return st::historyThemeSize;
}
const auto &location = _data->thumbnailLocation();
@@ -87,7 +121,11 @@ QSize ThemeDocument::countOptimalSize() {
}
QSize ThemeDocument::countCurrentSize(int newWidth) {
if (_data->isTheme()) {
if (!_data) {
_pixw = st::maxWallPaperWidth;
_pixh = st::maxWallPaperHeight;
return { _pixw, _pixh };
} else if (_data->isTheme()) {
_pixw = st::historyThemeSize.width();
_pixh = st::historyThemeSize.height();
return st::historyThemeSize;
@@ -117,10 +155,12 @@ void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, cr
ensureDataMediaCreated();
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
if (_data) {
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
}
auto selected = (selection == FullSelection);
auto loaded = dataLoaded();
auto displayLoading = _data->displayLoading();
auto displayLoading = _data && _data->displayLoading();
auto paintx = 0, painty = 0, paintw = width(), painth = height();
@@ -141,79 +181,93 @@ void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, cr
Ui::FillComplexOverlayRect(p, rthumb, roundRadius, roundCorners);
}
auto statusX = paintx + st::msgDateImgDelta + st::msgDateImgPadding.x();
auto statusY = painty + st::msgDateImgDelta + st::msgDateImgPadding.y();
auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
Ui::FillRoundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners);
p.setFont(st::normalFont);
p.setPen(st::msgDateImgFg);
p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x());
if (radial || (!loaded && !_data->loading())) {
const auto radialOpacity = (radial && loaded && !_data->uploading())
? _animation->radial.opacity() :
1.;
const auto innerSize = st::msgFileLayout.thumbSize;
QRect inner(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize);
p.setPen(Qt::NoPen);
if (selected) {
p.setBrush(st::msgDateImgBgSelected);
} else if (isThumbAnimation()) {
auto over = _animation->a_thumbOver.value(1.);
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
} else {
auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _openl);
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
}
p.setOpacity(radialOpacity * p.opacity());
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(inner);
}
p.setOpacity(radialOpacity);
auto icon = ([radial, this, selected]() -> const style::icon* {
if (radial || _data->loading()) {
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
if (_data) {
auto statusX = paintx + st::msgDateImgDelta + st::msgDateImgPadding.x();
auto statusY = painty + st::msgDateImgDelta + st::msgDateImgPadding.y();
auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
Ui::FillRoundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners);
p.setFont(st::normalFont);
p.setPen(st::msgDateImgFg);
p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x());
if (radial || (!loaded && !_data->loading())) {
const auto radialOpacity = (radial && loaded && !_data->uploading())
? _animation->radial.opacity() :
1.;
const auto innerSize = st::msgFileLayout.thumbSize;
QRect inner(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize);
p.setPen(Qt::NoPen);
if (selected) {
p.setBrush(st::msgDateImgBgSelected);
} else if (isThumbAnimation()) {
auto over = _animation->a_thumbOver.value(1.);
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
} else {
auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _openl);
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
}
p.setOpacity(radialOpacity * p.opacity());
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(inner);
}
p.setOpacity(radialOpacity);
auto icon = ([radial, this, selected]() -> const style::icon* {
if (radial || _data->loading()) {
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
}
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
})();
if (icon) {
icon->paintInCenter(p, inner);
}
p.setOpacity(1);
if (radial) {
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
}
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
})();
if (icon) {
icon->paintInCenter(p, inner);
}
p.setOpacity(1);
if (radial) {
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
}
}
}
void ThemeDocument::ensureDataMediaCreated() const {
if (_dataMedia) {
if (_dataMedia || !_data) {
return;
}
_dataMedia = _data->createMediaView();
_dataMedia->goodThumbnailWanted();
if (checkGoodThumbnail()) {
_dataMedia->goodThumbnailWanted();
}
_dataMedia->thumbnailWanted(_realParent->fullId());
_parent->history()->owner().registerHeavyViewPart(_parent);
}
bool ThemeDocument::checkGoodThumbnail() const {
return _data && (!_data->hasThumbnail() || !_data->isPatternWallPaper());
}
void ThemeDocument::validateThumbnail() const {
if (_thumbnailGood > 0) {
return;
}
ensureDataMediaCreated();
if (const auto good = _dataMedia->goodThumbnail()) {
prepareThumbnailFrom(good, 1);
return;
if (checkGoodThumbnail()) {
if (_thumbnailGood > 0) {
return;
}
ensureDataMediaCreated();
if (const auto good = _dataMedia->goodThumbnail()) {
prepareThumbnailFrom(good, 1);
return;
}
}
if (_thumbnailGood >= 0) {
return;
}
if (!_data) {
generateThumbnail();
return;
}
ensureDataMediaCreated();
if (const auto normal = _dataMedia->thumbnail()) {
prepareThumbnailFrom(normal, 0);
} else if (_thumbnail.isNull()) {
@@ -223,9 +277,20 @@ void ThemeDocument::validateThumbnail() const {
}
}
void ThemeDocument::generateThumbnail() const {
_thumbnail = Ui::PixmapFromImage(Data::GenerateWallPaper(
QSize(_pixw, _pixh) * cIntRetinaFactor(),
_background,
_gradientRotation,
_patternOpacity));
_thumbnail.setDevicePixelRatio(cRetinaFactor());
_thumbnailGood = 1;
}
void ThemeDocument::prepareThumbnailFrom(
not_null<Image*> image,
int good) const {
Expects(_data != nullptr);
Expects(_thumbnailGood <= good);
const auto isTheme = _data->isTheme();
@@ -253,8 +318,9 @@ void ThemeDocument::prepareThumbnailFrom(
original = Data::PreparePatternImage(
std::move(original),
_background,
Data::PatternColor(_background),
_intensity);
_gradientRotation,
_patternOpacity);
original.setDevicePixelRatio(cRetinaFactor());
}
_thumbnail = Ui::PixmapFromImage(std::move(original));
_thumbnailGood = good;
@@ -268,7 +334,9 @@ TextState ThemeDocument::textState(QPoint point, StateRequest request) const {
}
auto paintx = 0, painty = 0, paintw = width(), painth = height();
if (QRect(paintx, painty, paintw, painth).contains(point)) {
if (_data->uploading()) {
if (!_data) {
result.link = _openl;
} else if (_data->uploading()) {
result.link = _cancell;
} else if (dataLoaded()) {
result.link = _openl;
@@ -283,22 +351,23 @@ TextState ThemeDocument::textState(QPoint point, StateRequest request) const {
float64 ThemeDocument::dataProgress() const {
ensureDataMediaCreated();
return _dataMedia->progress();
return _data ? _dataMedia->progress() : 1.;
}
bool ThemeDocument::dataFinished() const {
return !_data->loading()
&& (!_data->uploading() || _data->waitingForAlbum());
return !_data
|| (!_data->loading()
&& (!_data->uploading() || _data->waitingForAlbum()));
}
bool ThemeDocument::dataLoaded() const {
ensureDataMediaCreated();
return _dataMedia->loaded();
return !_data || _dataMedia->loaded();
}
bool ThemeDocument::isReadyForOpen() const {
ensureDataMediaCreated();
return _dataMedia->loaded();
return !_data || _dataMedia->loaded();
}
QString ThemeDocument::additionalInfoString() const {

View File

@@ -13,16 +13,18 @@ class Image;
namespace Data {
class DocumentMedia;
class WallPaper;
} // namespace Data
namespace HistoryView {
class ThemeDocument final : public File {
public:
ThemeDocument(not_null<Element*> parent, DocumentData *document);
ThemeDocument(
not_null<Element*> parent,
not_null<DocumentData*> document,
const QString &url = QString());
DocumentData *document,
const std::optional<Data::WallPaper> &params);
~ThemeDocument();
void draw(
@@ -51,6 +53,9 @@ public:
bool hasHeavyPart() const override;
void unloadHeavyPart() override;
[[nodiscard]] static std::optional<Data::WallPaper> ParamsFromUrl(
const QString &url);
protected:
float64 dataProgress() const override;
bool dataFinished() const override;
@@ -61,11 +66,13 @@ private:
QSize countCurrentSize(int newWidth) override;
void fillPatternFieldsFrom(const QString &url);
[[nodiscard]] bool checkGoodThumbnail() const;
void validateThumbnail() const;
void prepareThumbnailFrom(not_null<Image*> image, int good) const;
void generateThumbnail() const;
void ensureDataMediaCreated() const;
const not_null<DocumentData*> _data;
DocumentData *_data = nullptr;
int _pixw = 1;
int _pixh = 1;
mutable QPixmap _thumbnail;
@@ -73,8 +80,9 @@ private:
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
// For wallpaper documents.
QColor _background;
int _intensity = 0;
std::vector<QColor> _background;
float64 _patternOpacity = 0.;
int _gradientRotation = 0;
};

View File

@@ -16,12 +16,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "history/view/media/history_view_media_common.h"
#include "history/view/media/history_view_theme_document.h"
#include "ui/image/image.h"
#include "ui/text/text_options.h"
#include "ui/text/format_values.h"
#include "ui/cached_round_corners.h"
#include "layout/layout_selection.h" // FullSelection
#include "data/data_session.h"
#include "data/data_wall_paper.h"
#include "data/data_media_types.h"
#include "data/data_web_page.h"
#include "data/data_photo.h"
@@ -707,6 +709,8 @@ ClickHandlerPtr WebPage::replaceAttachLink(
} else {
return _openl;
}
} else if (ThemeDocument::ParamsFromUrl(_data->url).has_value()) {
return _openl;
}
return link;
}

View File

@@ -24,7 +24,7 @@ SectionWidget::SectionWidget(
not_null<Window::SessionController*> window,
Wrap wrap,
not_null<Memento*> memento)
: Window::SectionWidget(parent, window)
: Window::SectionWidget(parent, window, PaintedBackground::Custom)
, _content(this, window, wrap, memento) {
init();
}
@@ -34,7 +34,7 @@ SectionWidget::SectionWidget(
not_null<Window::SessionController*> window,
Wrap wrap,
not_null<MoveMemento*> memento)
: Window::SectionWidget(parent, window)
: Window::SectionWidget(parent, window, PaintedBackground::Custom)
, _content(memento->takeContent(this, wrap)) {
init();
}

View File

@@ -59,7 +59,7 @@ WrapWidget::WrapWidget(
not_null<Window::SessionController*> window,
Wrap wrap,
not_null<Memento*> memento)
: SectionWidget(parent, window)
: SectionWidget(parent, window, PaintedBackground::Custom)
, _wrap(wrap)
, _controller(createController(window, memento->content()))
, _topShadow(this) {

View File

@@ -199,11 +199,7 @@ ClickHandlerPtr ItemBase::getResultPreviewHandler() const {
}
QString ItemBase::getResultThumbLetter() const {
#ifndef OS_MAC_OLD
auto parts = _result->_url.splitRef('/');
#else // OS_MAC_OLD
auto parts = _result->_url.split('/');
#endif // OS_MAC_OLD
if (!parts.isEmpty()) {
auto domain = parts.at(0);
if (parts.size() > 2 && domain.endsWith(':') && parts.at(1).isEmpty()) { // http:// and others

View File

@@ -341,12 +341,12 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
stopCheck();
_sentRequest = 0;
const auto &d = result.c_account_password();
getData()->pwdRequest = Core::ParseCloudPasswordCheckRequest(d);
getData()->pwdState = Core::ParseCloudPasswordState(d);
if (!d.vcurrent_algo() || !d.vsrp_id() || !d.vsrp_B()) {
LOG(("API Error: No current password received on login."));
_code->setFocus();
return;
} else if (!getData()->pwdRequest) {
} else if (!getData()->pwdState.request) {
const auto callback = [=](Fn<void()> &&close) {
Core::UpdateApplication();
close();
@@ -357,9 +357,6 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
callback));
return;
}
getData()->hasRecovery = d.is_has_recovery();
getData()->pwdHint = qs(d.vhint().value_or_empty());
getData()->pwdNotEmptyPassport = d.is_has_secure_values();
goReplace<PasswordCheckWidget>(Animate::Forward);
}
@@ -381,10 +378,7 @@ void CodeWidget::submit() {
_checkRequestTimer.callEach(1000);
_sentCode = text;
getData()->pwdRequest = Core::CloudPasswordCheckRequest();
getData()->hasRecovery = false;
getData()->pwdHint = QString();
getData()->pwdNotEmptyPassport = false;
getData()->pwdState = Core::CloudPasswordState();
_sentRequest = api().request(MTPauth_SignIn(
MTP_string(getData()->phone),
MTP_bytes(getData()->phoneHash),

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h"
#include "core/core_cloud_password.h"
#include "boxes/confirm_box.h"
#include "boxes/passcode_box.h"
#include "lang/lang_keys.h"
#include "intro/intro_signup.h"
#include "ui/widgets/buttons.h"
@@ -29,16 +30,13 @@ PasswordCheckWidget::PasswordCheckWidget(
not_null<Main::Account*> account,
not_null<Data*> data)
: Step(parent, account, data)
, _request(getData()->pwdRequest)
, _hasRecovery(getData()->hasRecovery)
, _notEmptyPassport(getData()->pwdNotEmptyPassport)
, _hint(getData()->pwdHint)
, _passwordState(getData()->pwdState)
, _pwdField(this, st::introPassword, tr::lng_signin_password())
, _pwdHint(this, st::introPasswordHint)
, _codeField(this, st::introPassword, tr::lng_signin_code())
, _toRecover(this, tr::lng_signin_recover(tr::now))
, _toPassword(this, tr::lng_signin_try_password(tr::now)) {
Expects(!!_request);
Expects(!!_passwordState.request);
Lang::Updated(
) | rpl::start_with_next([=] {
@@ -53,11 +51,13 @@ PasswordCheckWidget::PasswordCheckWidget(
setTitleText(tr::lng_signin_title());
updateDescriptionText();
if (_hint.isEmpty()) {
if (_passwordState.hint.isEmpty()) {
_pwdHint->hide();
} else {
_pwdHint->setText(
tr::lng_signin_hint(tr::now, lt_password_hint, _hint));
_pwdHint->setText(tr::lng_signin_hint(
tr::now,
lt_password_hint,
_passwordState.hint));
}
_codeField->hide();
_toPassword->hide();
@@ -73,9 +73,11 @@ void PasswordCheckWidget::refreshLang() {
_toPassword->setText(
tr::lng_signin_try_password(tr::now));
}
if (!_hint.isEmpty()) {
_pwdHint->setText(
tr::lng_signin_hint(tr::now, lt_password_hint, _hint));
if (!_passwordState.hint.isEmpty()) {
_pwdHint->setText(tr::lng_signin_hint(
tr::now,
lt_password_hint,
_passwordState.hint));
}
updateControlsGeometry();
}
@@ -167,7 +169,7 @@ void PasswordCheckWidget::handleSrpIdInvalid() {
const auto now = crl::now();
if (_lastSrpIdInvalidTime > 0
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
_request.id = 0;
_passwordState.request.id = 0;
showError(rpl::single(Lang::Hard::ServerError()));
} else {
_lastSrpIdInvalidTime = now;
@@ -176,7 +178,7 @@ void PasswordCheckWidget::handleSrpIdInvalid() {
}
void PasswordCheckWidget::checkPasswordHash() {
if (_request.id) {
if (_passwordState.request.id) {
passwordChecked();
} else {
requestPasswordData();
@@ -190,12 +192,8 @@ void PasswordCheckWidget::requestPasswordData() {
).done([=](const MTPaccount_Password &result) {
_sentRequest = 0;
result.match([&](const MTPDaccount_password &data) {
auto request = Core::ParseCloudPasswordCheckRequest(data);
if (request && request.id) {
_request = std::move(request);
} else {
// Maybe the password was removed? Just submit it once again.
}
openssl::AddRandomSeed(bytes::make_span(data.vsecure_random().v));
_passwordState = Core::ParseCloudPasswordState(data);
passwordChecked();
});
}).send();
@@ -203,12 +201,12 @@ void PasswordCheckWidget::requestPasswordData() {
void PasswordCheckWidget::passwordChecked() {
const auto check = Core::ComputeCloudPasswordCheck(
_request,
_passwordState.request,
_passwordHash);
if (!check) {
return serverError();
}
_request.id = 0;
_passwordState.request.id = 0;
_sentRequest = api().request(
MTPauth_CheckPassword(check.result)
).done([=](const MTPauth_Authorization &result) {
@@ -222,6 +220,27 @@ void PasswordCheckWidget::serverError() {
showError(rpl::single(Lang::Hard::ServerError()));
}
void PasswordCheckWidget::codeSubmitDone(
const QString &code,
const MTPBool &result) {
auto fields = PasscodeBox::CloudFields::From(_passwordState);
fields.fromRecoveryCode = code;
fields.hasRecovery = false;
fields.curRequest = {};
auto box = Box<PasscodeBox>(&api().instance(), nullptr, fields);
const auto boxShared = std::make_shared<QPointer<PasscodeBox>>();
box->newAuthorization(
) | rpl::start_with_next([=](const MTPauth_Authorization &result) {
if (boxShared) {
(*boxShared)->closeBox();
}
pwdSubmitDone(true, result);
}, lifetime());
*boxShared = Ui::show(std::move(box));
}
void PasswordCheckWidget::codeSubmitFail(const MTP::Error &error) {
if (MTP::IsFloodError(error)) {
showError(tr::lng_flood_error());
@@ -269,7 +288,7 @@ void PasswordCheckWidget::recoverStartFail(const MTP::Error &error) {
}
void PasswordCheckWidget::toRecover() {
if (_hasRecovery) {
if (_passwordState.hasRecovery) {
if (_sentRequest) {
api().request(base::take(_sentRequest)).cancel();
}
@@ -339,18 +358,16 @@ void PasswordCheckWidget::submit() {
return;
}
const auto send = crl::guard(this, [=] {
_sentRequest = api().request(MTPauth_RecoverPassword(
MTP_flags(0),
MTP_string(code),
MTPaccount_PasswordInputSettings()
)).done([=](const MTPauth_Authorization &result) {
pwdSubmitDone(true, result);
_sentRequest = api().request(MTPauth_CheckRecoveryPassword(
MTP_string(code)
)).done([=](const MTPBool &result) {
codeSubmitDone(code, result);
}).fail([=](const MTP::Error &error) {
codeSubmitFail(error);
}).handleFloodErrors().send();
});
if (_notEmptyPassport) {
if (_passwordState.notEmptyPassport) {
const auto confirmed = [=](Fn<void()> &&close) {
send();
close();
@@ -367,7 +384,7 @@ void PasswordCheckWidget::submit() {
const auto password = _pwdField->getLastText().toUtf8();
_passwordHash = Core::ComputeCloudPasswordHash(
_request.algo,
_passwordState.request.algo,
bytes::make_span(password));
checkPasswordHash();
}

View File

@@ -50,6 +50,7 @@ private:
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
void pwdSubmitFail(const MTP::Error &error);
void codeSubmitDone(const QString &code, const MTPBool &result);
void codeSubmitFail(const MTP::Error &error);
void recoverStartFail(const MTP::Error &error);
@@ -62,12 +63,10 @@ private:
void passwordChecked();
void serverError();
Core::CloudPasswordCheckRequest _request;
Core::CloudPasswordState _passwordState;
crl::time _lastSrpIdInvalidTime = 0;
bytes::vector _passwordHash;
bool _hasRecovery = false;
bool _notEmptyPassport = false;
QString _hint, _emailPattern;
QString _emailPattern;
object_ptr<Ui::PasswordInput> _pwdField;
object_ptr<Ui::FlatLabel> _pwdHint;

View File

@@ -391,13 +391,12 @@ void QrWidget::sendCheckPasswordRequest() {
_requestId = api().request(MTPaccount_GetPassword(
)).done([=](const MTPaccount_Password &result) {
result.match([&](const MTPDaccount_password &data) {
getData()->pwdRequest = Core::ParseCloudPasswordCheckRequest(
data);
getData()->pwdState = Core::ParseCloudPasswordState(data);
if (!data.vcurrent_algo() || !data.vsrp_id() || !data.vsrp_B()) {
LOG(("API Error: No current password received on login."));
goReplace<QrWidget>(Animate::Forward);
return;
} else if (!getData()->pwdRequest) {
} else if (!getData()->pwdState.request) {
const auto callback = [=](Fn<void()> &&close) {
Core::UpdateApplication();
close();
@@ -408,9 +407,6 @@ void QrWidget::sendCheckPasswordRequest() {
callback));
return;
}
getData()->hasRecovery = data.is_has_recovery();
getData()->pwdHint = qs(data.vhint().value_or_empty());
getData()->pwdNotEmptyPassport = data.is_has_secure_values();
goReplace<PasswordCheckWidget>(Animate::Forward);
});
}).fail([=](const MTP::Error &error) {

View File

@@ -56,10 +56,7 @@ struct Data {
int codeLength = 5;
bool codeByTelegram = false;
Core::CloudPasswordCheckRequest pwdRequest;
bool hasRecovery = false;
QString pwdHint;
bool pwdNotEmptyPassport = false;
Core::CloudPasswordState pwdState;
Window::TermsLock termsLock;

View File

@@ -35,9 +35,7 @@ DocumentGenericPreview DocumentGenericPreview::Create(
: document->filename())
: tr::lng_message_empty(tr::now)).toLower();
auto lastDot = name.lastIndexOf('.');
const auto mime = document
? document->mimeString().toLower()
: QString();
const auto mime = document ? document->mimeString() : QString();
if (name.endsWith(qstr(".doc")) ||
name.endsWith(qstr(".docx")) ||
name.endsWith(qstr(".txt")) ||

View File

@@ -7,14 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "mainwidget.h"
#include <rpl/combine.h>
#include <rpl/merge.h>
#include <rpl/flatten_latest.h>
#include "api/api_updates.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_document_resolver.h"
#include "data/data_wall_paper.h"
#include "data/data_web_page.h"
#include "data/data_game.h"
#include "data/data_peer_values.h"
@@ -122,9 +120,6 @@ namespace {
// Send channel views each second.
constexpr auto kSendViewsTimeout = crl::time(1000);
// Cache background scaled image after 3s.
constexpr auto kCacheBackgroundTimeout = 3000;
} // namespace
enum StackItemType {
@@ -239,7 +234,6 @@ MainWidget::MainWidget(
, _dialogs(this, _controller)
, _history(this, _controller)
, _playerPlaylist(this, _controller)
, _cacheBackgroundTimer([=] { cacheBackground(); })
, _viewsIncrementTimer([=] { viewsIncrement(); })
, _changelogs(Core::Changelogs::Create(&controller->session())) {
setupConnectingWidget();
@@ -345,15 +339,6 @@ MainWidget::MainWidget(
QCoreApplication::instance()->installEventFilter(this);
using Update = Window::Theme::BackgroundUpdate;
Window::Theme::Background()->updates(
) | rpl::start_with_next([=](const Update &update) {
if (update.type == Update::Type::New
|| update.type == Update::Type::Changed) {
clearCachedBackground();
}
}, lifetime());
subscribe(Media::Player::instance()->playerWidgetOver(), [this](bool over) {
if (over) {
if (_playerPlaylist->isHidden()) {
@@ -780,49 +765,6 @@ bool MainWidget::selectingPeer() const {
return _hider ? true : false;
}
void MainWidget::cacheBackground() {
if (Window::Theme::Background()->colorForFill()) {
return;
} else if (Window::Theme::Background()->tile()) {
auto &bg = Window::Theme::Background()->pixmapForTiled();
auto result = QImage(_willCacheFor.width() * cIntRetinaFactor(), _willCacheFor.height() * cIntRetinaFactor(), QImage::Format_RGB32);
result.setDevicePixelRatio(cRetinaFactor());
{
QPainter p(&result);
auto w = bg.width() / cRetinaFactor();
auto h = bg.height() / cRetinaFactor();
auto sx = 0;
auto sy = 0;
auto cx = qCeil(_willCacheFor.width() / w);
auto cy = qCeil(_willCacheFor.height() / h);
for (int i = sx; i < cx; ++i) {
for (int j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, j * h), bg);
}
}
}
_cachedX = 0;
_cachedY = 0;
_cachedBackground = Ui::PixmapFromImage(std::move(result));
} else {
auto &bg = Window::Theme::Background()->pixmap();
QRect to, from;
Window::Theme::ComputeBackgroundRects(_willCacheFor, bg.size(), to, from);
_cachedX = to.x();
_cachedY = to.y();
_cachedBackground = Ui::PixmapFromImage(
bg.toImage().copy(from).scaled(
to.width() * cIntRetinaFactor(),
to.height() * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation));
_cachedBackground.setDevicePixelRatio(cRetinaFactor());
}
_cachedFor = _willCacheFor;
}
crl::time MainWidget::highlightStartTime(not_null<const HistoryItem*> item) const {
return _history->highlightStartTime(item);
}
@@ -1152,25 +1094,6 @@ void MainWidget::dialogsCancelled() {
_history->activate();
}
void MainWidget::clearCachedBackground() {
_cachedBackground = QPixmap();
_cacheBackgroundTimer.cancel();
update();
}
QPixmap MainWidget::cachedBackground(const QRect &forRect, int &x, int &y) {
if (!_cachedBackground.isNull() && forRect == _cachedFor) {
x = _cachedX;
y = _cachedY;
return _cachedBackground;
}
if (_willCacheFor != forRect || !_cacheBackgroundTimer.isActive()) {
_willCacheFor = forRect;
_cacheBackgroundTimer.callOnce(kCacheBackgroundTimeout);
}
return QPixmap();
}
void MainWidget::setChatBackground(
const Data::WallPaper &background,
QImage &&image) {
@@ -1213,7 +1136,7 @@ void MainWidget::setReadyChatBackground(
const auto resetToDefault = image.isNull()
&& !background.document()
&& !background.backgroundColor()
&& background.backgroundColors().empty()
&& !Data::IsLegacy1DefaultWallPaper(background);
const auto ready = resetToDefault
? Data::DefaultWallPaper()
@@ -2163,7 +2086,7 @@ void MainWidget::hideAll() {
void MainWidget::showAll() {
if (cPasswordRecovered()) {
cSetPasswordRecovered(false);
Ui::show(Box<InformBox>(tr::lng_signin_password_removed(tr::now)));
Ui::show(Box<InformBox>(tr::lng_cloud_password_updated(tr::now)));
}
if (isOneColumn()) {
_sideShadow->hide();

View File

@@ -185,8 +185,6 @@ public:
void searchMessages(const QString &query, Dialogs::Key inChat);
QPixmap cachedBackground(const QRect &forRect, int &x, int &y);
void setChatBackground(
const Data::WallPaper &background,
QImage &&image = QImage());
@@ -299,9 +297,6 @@ private:
void showAll();
void clearHider(not_null<Window::HistoryHider*> instance);
void cacheBackground();
void clearCachedBackground();
[[nodiscard]] auto floatPlayerDelegate()
-> not_null<Media::Player::FloatDelegate*>;
not_null<Ui::RpWidget*> floatPlayerWidget() override;
@@ -389,12 +384,6 @@ private:
int _exportTopBarHeight = 0;
int _contentScrollAddToY = 0;
QPixmap _cachedBackground;
QRect _cachedFor, _willCacheFor;
int _cachedX = 0;
int _cachedY = 0;
base::Timer _cacheBackgroundTimer;
PhotoData *_deletingPhoto = nullptr;
base::flat_map<not_null<PeerData*>, base::flat_set<MsgId>> _viewsIncremented;

View File

@@ -1614,16 +1614,14 @@ public:
const auto coverBytes = QByteArray(
(const char*)packet.data,
packet.size);
auto format = QByteArray();
auto animated = false;
_cover = App::readImage(
coverBytes,
&format,
true,
&animated);
if (!_cover.isNull()) {
auto read = Images::Read({
.content = coverBytes,
.forceOpaque = true,
});
if (!read.image.isNull()) {
_cover = std::move(read.image);
_coverBytes = coverBytes;
_coverFormat = format;
_coverFormat = read.format;
}
}
} else if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {

View File

@@ -70,7 +70,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_account.h"
#include "calls/calls_instance.h"
#include "facades.h"
#include "app.h"
#include "styles/style_media_view.h"
#include "styles/style_chat.h"
@@ -149,24 +148,16 @@ QWidget *PipDelegate::pipParentWidget() {
: result;
}
[[nodiscard]] QImage PrepareStaticImage(QImage image) {
if (image.width() > kMaxDisplayImageSize
|| image.height() > kMaxDisplayImageSize) {
image = image.scaled(
[[nodiscard]] QImage PrepareStaticImage(Images::ReadArgs &&args) {
auto read = Images::Read(std::move(args));
return (read.image.width() > kMaxDisplayImageSize
|| read.image.height() > kMaxDisplayImageSize)
? read.image.scaled(
kMaxDisplayImageSize,
kMaxDisplayImageSize,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
}
return image;
}
[[nodiscard]] QImage PrepareStaticImage(const QString &path) {
return PrepareStaticImage(App::readImage(path, nullptr, false));
}
[[nodiscard]] QImage PrepareStaticImage(const QByteArray &bytes) {
return PrepareStaticImage(App::readImage(bytes, nullptr, false));
Qt::SmoothTransformation)
: read.image;
}
[[nodiscard]] bool IsSemitransparent(const QImage &image) {
@@ -434,14 +425,14 @@ OverlayWidget::OverlayWidget()
updateGeometry();
updateControlsGeometry();
#if defined Q_OS_MAC && !defined OS_OSX
#ifdef Q_OS_MAC
TouchBar::SetupMediaViewTouchBar(
_widget->winId(),
static_cast<PlaybackControls::Delegate*>(this),
_touchbarTrackState.events(),
_touchbarDisplay.events(),
_touchbarFullscreenToggled.events());
#endif // Q_OS_MAC && !OS_OSX
#endif // Q_OS_MAC
using namespace rpl::mappers;
rpl::combine(
@@ -1181,7 +1172,8 @@ bool OverlayWidget::radialAnimationCallback(crl::time now) {
}
const auto ready = _document && _documentMedia->loaded();
const auto streamVideo = ready && _documentMedia->canBePlayed();
const auto tryOpenImage = ready && (_document->size < App::kImageSizeLimit);
const auto tryOpenImage = ready
&& (_document->size < Images::kReadBytesLimit);
if (ready && ((tryOpenImage && !_radial.animating()) || streamVideo)) {
_streamingStartPaused = false;
if (streamVideo) {
@@ -2415,14 +2407,16 @@ void OverlayWidget::displayDocument(
_document->saveFromDataSilent();
auto &location = _document->location(true);
if (location.accessEnable()) {
const auto &path = location.name();
if (QImageReader(path).canRead()) {
setStaticContent(PrepareStaticImage(path));
setStaticContent(PrepareStaticImage({
.path = location.name(),
}));
if (!_staticContent.isNull()) {
_touchbarDisplay.fire(TouchBarItemType::Photo);
}
} else if (!_documentMedia->bytes().isEmpty()) {
setStaticContent(
PrepareStaticImage(_documentMedia->bytes()));
} else {
setStaticContent(PrepareStaticImage({
.content = _documentMedia->bytes(),
}));
if (!_staticContent.isNull()) {
_touchbarDisplay.fire(TouchBarItemType::Photo);
}

View File

@@ -1552,11 +1552,7 @@ Link::Link(
_title = _page->title;
}
#ifndef OS_MAC_OLD
auto parts = mainUrl.splitRef('/');
#else // OS_MAC_OLD
auto parts = mainUrl.split('/');
#endif // OS_MAC_OLD
if (!parts.isEmpty()) {
auto domain = parts.at(0);
if (parts.size() > 2 && domain.endsWith(':') && parts.at(1).isEmpty()) { // http:// and others

Some files were not shown because too many files have changed in this diff Show More