From 5d1aa1076811bb973680125c4a6cb7346a57846c Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 19 Feb 2024 05:27:09 +0400 Subject: [PATCH 001/108] Remove no longer needed boost-program-options from snap Looks like cppgir has stopped to use it during some of the updates --- snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index c36f7e512b..0996a2797c 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -96,7 +96,6 @@ parts: - python3 - libasound2-dev - libavif-dev - - libboost-program-options1.74-dev - libboost-regex1.74-dev - libfmt-dev - libgirepository1.0-dev From 778ab70b72ad7eca8a6701f1a8fa00eaab45d88e Mon Sep 17 00:00:00 2001 From: Kolya <142352140+agl-1984@users.noreply.github.com> Date: Fri, 23 Feb 2024 09:07:08 +0100 Subject: [PATCH 002/108] Fix libvpx build on VS 17.8+ use with https://github.com/desktop-app/patches/pull/182 --- Telegram/build/prepare/prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index f53417ea35..d60c818e36 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -418,7 +418,7 @@ if customRunCommand: stage('patches', """ git clone https://github.com/desktop-app/patches.git cd patches - git checkout 94be868240 + git checkout bed08b53a3 """) stage('msys64', """ From ec427ad45df99365aa09f0a7b9412a4bb73ba42f Mon Sep 17 00:00:00 2001 From: Kolya <142352140+agl-1984@users.noreply.github.com> Date: Fri, 23 Feb 2024 09:12:13 +0100 Subject: [PATCH 003/108] Use TOOLCHAIN variable name --- Telegram/build/prepare/prepare.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index d60c818e36..fce761f883 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -855,9 +855,9 @@ win: SET MSYS2_PATH_TYPE=inherit if "%X8664%" equ "x64" ( - SET "TARGET=x86_64-win64-vs17" + SET "TOOLCHAIN=x86_64-win64-vs17" ) else ( - SET "TARGET=x86-win32-vs17" + SET "TOOLCHAIN=x86-win32-vs17" ) depends:patches/build_libvpx_win.sh From a8b5061003b0c2231bfcebc2ef44d9fd5c3ff3ed Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 1 Mar 2024 10:26:05 +0400 Subject: [PATCH 004/108] Fix a std::clamp assertion --- Telegram/SourceFiles/history/view/media/history_view_photo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index e5996d1bf3..2532509a7b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -232,7 +232,7 @@ QSize Photo::countCurrentSize(int newWidth) { const auto thumbMaxWidth = qMin(newWidth, st::maxMediaSize); const auto minWidth = std::clamp( _parent->minWidthForMedia(), - (_parent->hasBubble() + qMin(thumbMaxWidth, _parent->hasBubble() ? st::historyPhotoBubbleMinWidth : st::minPhotoSize), thumbMaxWidth); From da047edbc5ca747ebb9947d8943605f944a390e0 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 1 Mar 2024 00:25:21 +0000 Subject: [PATCH 005/108] Update User-Agent for DNS to Chrome 122.0.0.0. --- .../SourceFiles/mtproto/details/mtproto_domain_resolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp index b058b9bb8d..ea0a53f30e 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp +++ b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp @@ -65,7 +65,7 @@ QByteArray DnsUserAgent() { static const auto kResult = QByteArray( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/121.0.6167.85 Safari/537.36"); + "Chrome/122.0.0.0 Safari/537.36"); return kResult; } From 95b4fc021622e75146ced592e87bbd3b2f3ba73e Mon Sep 17 00:00:00 2001 From: xmdn <72883689+xmdnx@users.noreply.github.com> Date: Sun, 25 Feb 2024 15:00:35 +0300 Subject: [PATCH 006/108] use modern installer style --- Telegram/build/setup.iss | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/build/setup.iss b/Telegram/build/setup.iss index f5975b0974..8b07155816 100644 --- a/Telegram/build/setup.iss +++ b/Telegram/build/setup.iss @@ -33,6 +33,7 @@ VersionInfoVersion={#MyAppVersion}.0 CloseApplications=force DisableDirPage=no DisableProgramGroupPage=no +WizardStyle=modern #if MyBuildTarget == "win64" ArchitecturesAllowed="x64 arm64" From 50f51d074721cda4e1cd8466a31685a6122be4fb Mon Sep 17 00:00:00 2001 From: Kolya <142352140+agl-1984@users.noreply.github.com> Date: Fri, 1 Mar 2024 20:47:32 +0100 Subject: [PATCH 007/108] update new script location in qt repo --- Telegram/build/prepare/prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index fce761f883..d0fefbea53 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1302,7 +1302,7 @@ if buildQt5: stage('qt_5_15_12', """ git clone https://github.com/qt/qt5.git qt_5_15_12 cd qt_5_15_12 - perl init-repository --module-subset=qtbase,qtimageformats,qtsvg + perl init-repository.pl --module-subset=qtbase,qtimageformats,qtsvg git checkout v5.15.12-lts-lgpl git submodule update qtbase qtimageformats qtsvg depends:patches/qtbase_5.15.12/*.patch From b040b62b4eefcef173ac78d4ae9dddf3b27e51f9 Mon Sep 17 00:00:00 2001 From: Kolya <142352140+agl-1984@users.noreply.github.com> Date: Sat, 2 Mar 2024 22:07:16 +0100 Subject: [PATCH 008/108] prepare.py: simplify qt5 clone --- Telegram/build/prepare/prepare.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index d0fefbea53..fceedf2b56 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1300,11 +1300,9 @@ release: if buildQt5: stage('qt_5_15_12', """ - git clone https://github.com/qt/qt5.git qt_5_15_12 + git clone -b v5.15.12-lts-lgpl https://github.com/qt/qt5.git qt_5_15_12 cd qt_5_15_12 - perl init-repository.pl --module-subset=qtbase,qtimageformats,qtsvg - git checkout v5.15.12-lts-lgpl - git submodule update qtbase qtimageformats qtsvg + perl init-repository --module-subset=qtbase,qtimageformats,qtsvg depends:patches/qtbase_5.15.12/*.patch cd qtbase win: From 5b62d97288b03750d5570f9c59ec7c3f88f762b4 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 6 Mar 2024 20:12:31 +0400 Subject: [PATCH 009/108] Update submodules --- .gitmodules | 3 + Telegram/CMakeLists.txt | 3 - .../linux/notifications_manager_linux.cpp | 72 ++++--- .../linux/org.freedesktop.portal.Inhibit.xml | 186 ------------------ Telegram/ThirdParty/xdg-desktop-portal | 1 + Telegram/lib_base | 2 +- Telegram/lib_webview | 2 +- cmake | 2 +- 8 files changed, 41 insertions(+), 230 deletions(-) delete mode 100644 Telegram/SourceFiles/platform/linux/org.freedesktop.portal.Inhibit.xml create mode 160000 Telegram/ThirdParty/xdg-desktop-portal diff --git a/.gitmodules b/.gitmodules index 101674a909..bf3c35f423 100644 --- a/.gitmodules +++ b/.gitmodules @@ -100,3 +100,6 @@ [submodule "Telegram/ThirdParty/libprisma"] path = Telegram/ThirdParty/libprisma url = https://github.com/desktop-app/libprisma.git +[submodule "Telegram/ThirdParty/xdg-desktop-portal"] + path = Telegram/ThirdParty/xdg-desktop-portal + url = https://github.com/flatpak/xdg-desktop-portal.git diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index e1766f5e63..64409ea888 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1651,9 +1651,6 @@ else() desktop-app::external_glibmm ) - include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake) - generate_dbus(Telegram org.freedesktop.portal. XdpInhibit ${src_loc}/platform/linux/org.freedesktop.portal.Inhibit.xml) - if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION) target_link_libraries(Telegram PRIVATE diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index c94400ff19..0473edf727 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -78,24 +78,24 @@ std::unique_ptr CreateServiceWatcher() { Gio::DBus::BusType::SESSION); const auto activatable = [&] { - try { - return ranges::contains( - base::Platform::DBus::ListActivatableNames(connection), - kService, - &Glib::ustring::raw); - } catch (...) { + const auto names = base::Platform::DBus::ListActivatableNames( + connection->gobj()); + + if (!names) { // avoid service restart loop in sandboxed environments return true; } + + return ranges::contains(*names, kService); }(); return std::make_unique( - connection, + connection->gobj(), kService, [=]( - const Glib::ustring &service, - const Glib::ustring &oldOwner, - const Glib::ustring &newOwner) { + const std::string &service, + const std::string &oldOwner, + const std::string &newOwner) { Core::Sandbox::Instance().customEnterFromEventLoop([&] { if (activatable && newOwner.empty()) { Core::App().notifications().clearAll(); @@ -115,27 +115,28 @@ void StartServiceAsync(Fn callback) { const auto connection = Gio::DBus::Connection::get_sync( Gio::DBus::BusType::SESSION); - base::Platform::DBus::StartServiceByNameAsync( - connection, + namespace DBus = base::Platform::DBus; + DBus::StartServiceByNameAsync( + connection->gobj(), kService, - [=](Fn result) { + [=](Fn()> result) { Core::Sandbox::Instance().customEnterFromEventLoop([&] { Noexcept([&] { - try { - result(); // get the error if any - } catch (const Glib::Error &e) { + // get the error if any + if (const auto ret = result(); !ret) { static const auto NotSupportedErrors = { "org.freedesktop.DBus.Error.ServiceUnknown", }; - const auto errorName = - Gio::DBus::ErrorUtils::get_remote_error(e) - .raw(); - - if (!ranges::contains( + if (ranges::none_of( NotSupportedErrors, - errorName)) { - throw; + [&](const auto &error) { + return strstr( + ret.error()->what(), + error); + })) { + throw std::runtime_error( + ret.error()->what()); } } }); @@ -156,25 +157,20 @@ bool GetServiceRegistered() { const auto connection = Gio::DBus::Connection::get_sync( Gio::DBus::BusType::SESSION); - const auto hasOwner = [&] { - try { - return base::Platform::DBus::NameHasOwner( - connection, - kService); - } catch (...) { - return false; - } - }(); + const auto hasOwner = base::Platform::DBus::NameHasOwner( + connection->gobj(), + kService + ).value_or(false); static const auto activatable = [&] { - try { - return ranges::contains( - base::Platform::DBus::ListActivatableNames(connection), - kService, - &Glib::ustring::raw); - } catch (...) { + const auto names = base::Platform::DBus::ListActivatableNames( + connection->gobj()); + + if (!names) { return false; } + + return ranges::contains(*names, kService); }(); return hasOwner || activatable; diff --git a/Telegram/SourceFiles/platform/linux/org.freedesktop.portal.Inhibit.xml b/Telegram/SourceFiles/platform/linux/org.freedesktop.portal.Inhibit.xml deleted file mode 100644 index e91bd22d37..0000000000 --- a/Telegram/SourceFiles/platform/linux/org.freedesktop.portal.Inhibit.xml +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Telegram/ThirdParty/xdg-desktop-portal b/Telegram/ThirdParty/xdg-desktop-portal new file mode 160000 index 0000000000..fa8d41a2f9 --- /dev/null +++ b/Telegram/ThirdParty/xdg-desktop-portal @@ -0,0 +1 @@ +Subproject commit fa8d41a2f9a5d30a1e41568b6fb53b046dce14dc diff --git a/Telegram/lib_base b/Telegram/lib_base index 888a19075b..cee9211bd5 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 888a19075b569eda3d18a977543320823b984ae0 +Subproject commit cee9211bd58e054f24ad5e7f122037f71a44b237 diff --git a/Telegram/lib_webview b/Telegram/lib_webview index 4fce8b1971..27af88195b 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit 4fce8b1971721da739619acf36da0fe79d614a23 +Subproject commit 27af88195bca687e9d2a52b4fcd4e83ef5476be9 diff --git a/cmake b/cmake index a46279fcfe..b699c232d5 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit a46279fcfe69ebcc806bb31679ccece5f7c07508 +Subproject commit b699c232d57d50070a7b1b861809e206624f48d4 From a66b886c513d3554dc035a0aeff7b85da92576d3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 6 Mar 2024 20:13:17 +0400 Subject: [PATCH 010/108] Initialize Linux lock screen monitor fully asynchronously --- .../platform/linux/integration_linux.cpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/integration_linux.cpp b/Telegram/SourceFiles/platform/linux/integration_linux.cpp index 1087e09fbe..6613f7382a 100644 --- a/Telegram/SourceFiles/platform/linux/integration_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/integration_linux.cpp @@ -180,7 +180,7 @@ gi::ref_ptr MakeApplication() { return result; } -class LinuxIntegration final : public Integration { +class LinuxIntegration final : public Integration, public base::has_weak_ptr { public: LinuxIntegration(); @@ -200,13 +200,6 @@ private: LinuxIntegration::LinuxIntegration() : _application(MakeApplication()) -, _inhibitProxy( - XdpInhibit::InhibitProxy::new_for_bus_sync( - Gio::BusType::SESSION_, - Gio::DBusProxyFlags::DO_NOT_AUTO_START_AT_CONSTRUCTION_, - base::Platform::XDP::kService, - base::Platform::XDP::kObjectPath, - nullptr)) , _darkModeWatcher( "org.freedesktop.appearance", "color-scheme", @@ -230,7 +223,18 @@ LinuxIntegration::LinuxIntegration() } void LinuxIntegration::init() { - initInhibit(); + XdpInhibit::InhibitProxy::new_for_bus( + Gio::BusType::SESSION_, + Gio::DBusProxyFlags::NONE_, + base::Platform::XDP::kService, + base::Platform::XDP::kObjectPath, + crl::guard(this, [=](GObject::Object, Gio::AsyncResult res) { + _inhibitProxy = XdpInhibit::InhibitProxy::new_for_bus_finish( + res, + nullptr); + + initInhibit(); + })); } void LinuxIntegration::initInhibit() { From 1e9b7e2726add6b87bd54d8532783b30cc22c553 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 7 Mar 2024 01:24:50 +0400 Subject: [PATCH 011/108] Use XDP::kObjectPath for session and request paths --- Telegram/SourceFiles/platform/linux/integration_linux.cpp | 3 ++- .../SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp | 4 ++-- Telegram/SourceFiles/platform/linux/specific_linux.cpp | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/integration_linux.cpp b/Telegram/SourceFiles/platform/linux/integration_linux.cpp index 6613f7382a..fef646b203 100644 --- a/Telegram/SourceFiles/platform/linux/integration_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/integration_linux.cpp @@ -252,7 +252,8 @@ void LinuxIntegration::initInhibit() { const auto sessionHandleToken = "tdesktop" + std::to_string(base::RandomValue()); - const auto sessionHandle = "/org/freedesktop/portal/desktop/session/" + const auto sessionHandle = base::Platform::XDP::kObjectPath + + std::string("/session/") + uniqueName + '/' + sessionHandleToken; diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp index e75bcb4210..b01d5abbe3 100644 --- a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp @@ -66,8 +66,8 @@ bool ShowXDPOpenWithDialog(const QString &filepath) { uniqueName.erase(0, 1); uniqueName.replace(uniqueName.find('.'), 1, 1, '_'); - const auto requestPath = Glib::ustring( - "/org/freedesktop/portal/desktop/request/") + const auto requestPath = base::Platform::XDP::kObjectPath + + Glib::ustring("/request/") + uniqueName + '/' + handleToken; diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index a63c54bc7d..c0cbed82e8 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -105,8 +105,8 @@ void PortalAutostart(bool enabled, Fn done) { uniqueName.erase(0, 1); uniqueName.replace(uniqueName.find('.'), 1, 1, '_'); - const auto requestPath = Glib::ustring( - "/org/freedesktop/portal/desktop/request/") + const auto requestPath = base::Platform::XDP::kObjectPath + + Glib::ustring("/request/") + uniqueName + '/' + handleToken; From 7b8cdb43c49d6d91e406c459f004fbf9a8658981 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 7 Mar 2024 02:21:09 +0400 Subject: [PATCH 012/108] Port linux_xdp_open_with_dialog to cppgir --- .../linux/linux_xdp_open_with_dialog.cpp | 205 ++++++++---------- 1 file changed, 95 insertions(+), 110 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp index b01d5abbe3..bd82cb17fc 100644 --- a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp @@ -13,130 +13,115 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/random.h" #include -#include -#include +#include +#include namespace Platform { namespace File { namespace internal { namespace { -constexpr auto kXDPOpenURIInterface = "org.freedesktop.portal.OpenURI"; -constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"; +using namespace gi::repository; using base::Platform::XdgActivationToken; } // namespace bool ShowXDPOpenWithDialog(const QString &filepath) { - try { - const auto connection = Gio::DBus::Connection::get_sync( - Gio::DBus::BusType::SESSION); + auto proxy = XdpOpenURI::OpenURIProxy::new_for_bus_sync( + Gio::BusType::SESSION_, + Gio::DBusProxyFlags::NONE_, + base::Platform::XDP::kService, + base::Platform::XDP::kObjectPath, + nullptr); - const auto version = connection->call_sync( - base::Platform::XDP::kObjectPath, - kPropertiesInterface, - "Get", - Glib::create_variant(std::tuple{ - Glib::ustring(kXDPOpenURIInterface), - Glib::ustring("version"), - }), - base::Platform::XDP::kService - ).get_child(0).get_dynamic>().get(); - - if (version < 3) { - return false; - } - - const auto filepathUtf8 = filepath.toUtf8(); - - const auto fd = open( - filepathUtf8.constData(), - O_RDONLY); - - if (fd == -1) { - return false; - } - - const auto fdGuard = gsl::finally([&] { ::close(fd); }); - - const auto handleToken = Glib::ustring("tdesktop") - + std::to_string(base::RandomValue()); - - auto uniqueName = connection->get_unique_name(); - uniqueName.erase(0, 1); - uniqueName.replace(uniqueName.find('.'), 1, 1, '_'); - - const auto requestPath = base::Platform::XDP::kObjectPath - + Glib::ustring("/request/") - + uniqueName - + '/' - + handleToken; - - const auto loop = Glib::MainLoop::create(); - - const auto signalId = connection->signal_subscribe( - [&]( - const Glib::RefPtr &connection, - const Glib::ustring &sender_name, - const Glib::ustring &object_path, - const Glib::ustring &interface_name, - const Glib::ustring &signal_name, - const Glib::VariantContainerBase ¶meters) { - loop->quit(); - }, - base::Platform::XDP::kService, - base::Platform::XDP::kRequestInterface, - "Response", - requestPath); - - const auto signalGuard = gsl::finally([&] { - if (signalId != 0) { - connection->signal_unsubscribe(signalId); - } - }); - - auto outFdList = Glib::RefPtr(); - - connection->call_sync( - base::Platform::XDP::kObjectPath, - kXDPOpenURIInterface, - "OpenFile", - Glib::create_variant(std::tuple{ - base::Platform::XDP::ParentWindowID(), - Glib::DBusHandle(), - std::map{ - { - "handle_token", - Glib::create_variant(handleToken) - }, - { - "activation_token", - Glib::create_variant( - Glib::ustring(XdgActivationToken().toStdString())) - }, - { - "ask", - Glib::create_variant(true) - }, - }, - }), - Gio::UnixFDList::create(std::vector{ fd }), - outFdList, - base::Platform::XDP::kService); - - if (signalId != 0) { - QWidget window; - window.setAttribute(Qt::WA_DontShowOnScreen); - window.setWindowModality(Qt::ApplicationModal); - window.show(); - loop->run(); - } - - return true; - } catch (...) { + if (!proxy) { + return false; } - return false; + auto interface = XdpOpenURI::OpenURI(proxy); + if (interface.get_version() < 3) { + return false; + } + + const auto fd = open( + QFile::encodeName(filepath).constData(), + O_RDONLY); + + if (fd == -1) { + return false; + } + + const auto fdGuard = gsl::finally([&] { close(fd); }); + + const auto handleToken = "tdesktop" + + std::to_string(base::RandomValue()); + + std::string uniqueName = proxy.get_connection().get_unique_name(); + uniqueName.erase(0, 1); + uniqueName.replace(uniqueName.find('.'), 1, 1, '_'); + + auto request = XdpRequest::Request( + XdpRequest::RequestProxy::new_sync( + proxy.get_connection(), + Gio::DBusProxyFlags::NONE_, + base::Platform::XDP::kService, + base::Platform::XDP::kObjectPath + + std::string("/request/") + + uniqueName + + '/' + + handleToken, + nullptr, + nullptr)); + + if (!request) { + return false; + } + + auto loop = GLib::MainLoop::new_(); + + const auto signalId = request.signal_response().connect([=]( + XdpRequest::Request, + guint, + GLib::Variant) mutable { + loop.quit(); + }); + + const auto signalGuard = gsl::finally([&] { + request.disconnect(signalId); + }); + + auto result = interface.call_open_file_sync( + std::string(base::Platform::XDP::ParentWindowID()), + GLib::Variant::new_handle(0), + GLib::Variant::new_array({ + GLib::Variant::new_dict_entry( + GLib::Variant::new_string("handle_token"), + GLib::Variant::new_variant( + GLib::Variant::new_string(handleToken))), + GLib::Variant::new_dict_entry( + GLib::Variant::new_string("activation_token"), + GLib::Variant::new_variant( + GLib::Variant::new_string( + XdgActivationToken().toStdString()))), + GLib::Variant::new_dict_entry( + GLib::Variant::new_string("ask"), + GLib::Variant::new_variant( + GLib::Variant::new_boolean(true))), + }), + Gio::UnixFDList::new_from_array((std::array{ fd }).data(), 1), + nullptr); + + if (!result) { + return false; + } + + QWidget window; + window.setAttribute(Qt::WA_DontShowOnScreen); + window.setWindowModality(Qt::ApplicationModal); + window.show(); + loop.run(); + + return true; } } // namespace internal From 41481129f7775faeb51f889937d4d6de0d18ddd1 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 7 Mar 2024 02:57:38 +0400 Subject: [PATCH 013/108] Port main_window_linux to cppgir --- .../platform/linux/main_window_linux.cpp | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index f4c400f32e..e6a3b68a6b 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -43,8 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include -#include -#include +#include namespace Platform { namespace { @@ -236,6 +235,8 @@ void MainWindow::updateUnityCounter() { #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) qApp->setBadgeNumber(Core::App().unreadBadge()); #else // Qt >= 6.6.0 + using namespace gi::repository; + static const auto djbStringHash = [](const std::string &string) { uint hash = 5381; for (const auto &curChar : string) { @@ -244,40 +245,36 @@ void MainWindow::updateUnityCounter() { return hash; }; - const auto launcherUrl = Glib::ustring( - "application://" - + QGuiApplication::desktopFileName().toStdString() - + ".desktop"); + const auto launcherUrl = "application://" + + QGuiApplication::desktopFileName().toStdString() + + ".desktop"; + const auto counterSlice = std::min(Core::App().unreadBadge(), 9999); - std::map dbusUnityProperties; - if (counterSlice > 0) { - // According to the spec, it should be of 'x' D-Bus signature, - // which corresponds to signed 64-bit integer - // https://wiki.ubuntu.com/Unity/LauncherAPI#Low_level_DBus_API:_com.canonical.Unity.LauncherEntry - dbusUnityProperties["count"] = Glib::create_variant( - int64(counterSlice)); - dbusUnityProperties["count-visible"] = Glib::create_variant(true); - } else { - dbusUnityProperties["count-visible"] = Glib::create_variant(false); + auto connection = Gio::bus_get_sync(Gio::BusType::SESSION_, nullptr); + if (!connection) { + return; } - try { - const auto connection = Gio::DBus::Connection::get_sync( - Gio::DBus::BusType::SESSION); - - connection->emit_signal( - "/com/canonical/unity/launcherentry/" - + std::to_string(djbStringHash(launcherUrl)), - "com.canonical.Unity.LauncherEntry", - "Update", - {}, - Glib::create_variant(std::tuple{ - launcherUrl, - dbusUnityProperties, - })); - } catch (...) { - } + connection.emit_signal( + {}, + "/com/canonical/unity/launcherentry/" + + std::to_string(djbStringHash(launcherUrl)), + "com.canonical.Unity.LauncherEntry", + "Update", + GLib::Variant::new_tuple({ + GLib::Variant::new_string(launcherUrl), + GLib::Variant::new_array({ + GLib::Variant::new_dict_entry( + GLib::Variant::new_string("count"), + GLib::Variant::new_variant( + GLib::Variant::new_int64(counterSlice))), + GLib::Variant::new_dict_entry( + GLib::Variant::new_string("count-visible"), + GLib::Variant::new_variant( + GLib::Variant::new_boolean(counterSlice))), + }), + })); #endif // Qt < 6.6.0 } From 5971aefb8384d8743d616699c4f6def1489f6949 Mon Sep 17 00:00:00 2001 From: mrbesen Date: Tue, 28 Nov 2023 19:42:17 +0100 Subject: [PATCH 014/108] add bash shebang --- Telegram/build/prepare/linux.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Telegram/build/prepare/linux.sh b/Telegram/build/prepare/linux.sh index d3ee5b5be8..e4d2f920e4 100755 --- a/Telegram/build/prepare/linux.sh +++ b/Telegram/build/prepare/linux.sh @@ -1,3 +1,5 @@ +#!/bin/bash + set -e FullExecPath=$PWD pushd `dirname $0` > /dev/null From c26982be3e31c99a6125b0f10e49e91038494322 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 18 Feb 2024 20:48:05 +0300 Subject: [PATCH 015/108] Added support for AVIF, HEIF and JPEG XL on macOS. --- .github/workflows/mac.yml | 2 +- Telegram/build/prepare/prepare.py | 234 +++++++++++++++++++++--------- docs/building-mac.md | 2 +- 3 files changed, 169 insertions(+), 69 deletions(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 8e99d7162d..68bab389ca 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -64,7 +64,7 @@ jobs: - name: First set up. run: | sudo chown -R `whoami`:admin /usr/local/share - brew install automake ninja pkg-config + brew install automake ninja pkg-config nasm meson # Disable spotlight. sudo mdutil -a -i off diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index fceedf2b56..30d5084f4f 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -202,6 +202,11 @@ def removeDir(folder): return 'if exist ' + folder + ' rmdir /Q /S ' + folder + '\nif exist ' + folder + ' exit /b 1' return 'rm -rf ' + folder +def setVar(key, value): + if win: + return 'SET ' + key + '="' + value + '"'; + return key + '="' + value + '"'; + def filterByPlatform(commands): commands = commands.split('\n') result = '' @@ -690,10 +695,9 @@ mac: """) stage('dav1d', """ -win: git clone -b 1.2.1 --depth 1 https://code.videolan.org/videolan/dav1d.git cd dav1d - +win: if "%X8664%" equ "x64" ( SET "TARGET=x86_64" ) else ( @@ -709,7 +713,7 @@ win: echo system = 'windows' >> %FILE% echo cpu_family = '%TARGET%' >> %FILE% echo cpu = '%TARGET%' >> %FILE% - echo endian = 'little'>> %FILE% + echo endian = 'little' >> %FILE% depends:python/Scripts/activate.bat %THIRDPARTY_DIR%\\python\\Scripts\\activate.bat @@ -723,12 +727,55 @@ release: win: copy %LIBS_DIR%\\local\\lib\\libdav1d.a %LIBS_DIR%\\local\\lib\\dav1d.lib deactivate +mac: + buildOneArch() { + arch=$1 + folder=`pwd`/$2 + + TARGET="\'${arch}\'" + MIN="\'${MIN_VER}\'" + FILE=cross-file.txt + echo "[binaries]" > $FILE + echo "c = ['clang', '-arch', ${TARGET}]" >> $FILE + echo "cpp = ['clang++', '-arch', ${TARGET}]" >> $FILE + echo "ar = 'ar'" >> $FILE + echo "strip = 'strip'" >> $FILE + echo "[built-in options]" >> $FILE + echo "c_args = [${MIN}]" >> $FILE + echo "cpp_args = [${MIN}]" >> $FILE + echo "c_link_args = [${MIN}]" >> $FILE + echo "cpp_link_args = [${MIN}]" >> $FILE + echo "[host_machine]" >> $FILE + echo "system = 'darwin'" >> $FILE + echo "subsystem = 'macos'" >> $FILE + echo "cpu_family = ${TARGET}" >> $FILE + echo "cpu = ${TARGET}" >> $FILE + echo "endian = 'little'" >> $FILE + + meson setup \\ + --cross-file $FILE \\ + --prefix ${USED_PREFIX} \\ + --default-library=static \\ + --buildtype=minsize \\ + -Denable_tools=false \\ + -Denable_tests=false \\ + ${folder} + meson compile -C ${folder} + meson install -C ${folder} + + mv ${USED_PREFIX}/lib/libdav1d.a ${folder}/libdav1d.a + } + + buildOneArch arm64 build.arm64 + buildOneArch x86_64 build + + lipo -create build.arm64/libdav1d.a build/libdav1d.a -output ${USED_PREFIX}/lib/libdav1d.a """) stage('libavif', """ -win: git clone -b v0.11.1 --depth 1 https://github.com/AOMediaCodec/libavif.git cd libavif +win: cmake . ^ -A %WIN32X64% ^ -DCMAKE_INSTALL_PREFIX=%LIBS_DIR%/local ^ @@ -743,12 +790,22 @@ win: release: cmake --build . --config Release cmake --install . --config Release +mac: + cmake . \\ + -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" \\ + -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\ + -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\ + -D BUILD_SHARED_LIBS=OFF \\ + -D AVIF_ENABLE_WERROR=OFF \\ + -D AVIF_CODEC_DAV1D=ON + cmake --build . --config MinSizeRel $MAKE_THREADS_CNT + cmake --install . --config MinSizeRel """) stage('libde265', """ -win: git clone --depth 1 -b v1.0.12 https://github.com/strukturag/libde265.git cd libde265 +win: cmake . ^ -A %WIN32X64% ^ -DCMAKE_INSTALL_PREFIX=%LIBS_DIR%/local ^ @@ -768,12 +825,63 @@ win: release: cmake --build . --config Release cmake --install . --config Release +mac: + cmake . \\ + -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" \\ + -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\ + -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\ + -D DISABLE_SSE=ON \\ + -D BUILD_SHARED_LIBS=OFF \\ + -D ENABLE_DECODER=ON \\ + -D ENABLE_ENCODER=OFF + cmake --build . --config MinSizeRel $MAKE_THREADS_CNT + cmake --install . --config MinSizeRel +""") + +stage('libwebp', """ + git clone -b v1.3.2 https://github.com/webmproject/libwebp.git + cd libwebp +win: + nmake /f Makefile.vc CFG=debug-static OBJDIR=out RTLIBCFG=static all + nmake /f Makefile.vc CFG=release-static OBJDIR=out RTLIBCFG=static all + copy out\\release-static\\$X8664\\lib\\libwebp.lib out\\release-static\\$X8664\\lib\\webp.lib + copy out\\release-static\\$X8664\\lib\\libwebpdemux.lib out\\release-static\\$X8664\\lib\\webpdemux.lib + copy out\\release-static\\$X8664\\lib\\libwebpmux.lib out\\release-static\\$X8664\\lib\\webpmux.lib +mac: + buildOneArch() { + arch=$1 + folder=$2 + + CFLAGS=$UNGUARDED cmake -B $folder -G Ninja . \\ + -D CMAKE_BUILD_TYPE=Release \\ + -D CMAKE_INSTALL_PREFIX=$USED_PREFIX \\ + -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\ + -D CMAKE_OSX_ARCHITECTURES=$arch \\ + -D WEBP_BUILD_ANIM_UTILS=OFF \\ + -D WEBP_BUILD_CWEBP=OFF \\ + -D WEBP_BUILD_DWEBP=OFF \\ + -D WEBP_BUILD_GIF2WEBP=OFF \\ + -D WEBP_BUILD_IMG2WEBP=OFF \\ + -D WEBP_BUILD_VWEBP=OFF \\ + -D WEBP_BUILD_WEBPMUX=OFF \\ + -D WEBP_BUILD_WEBPINFO=OFF \\ + -D WEBP_BUILD_EXTRAS=OFF + cmake --build $folder $MAKE_THREADS_CNT + } + buildOneArch arm64 build.arm64 + buildOneArch x86_64 build + + lipo -create build.arm64/libsharpyuv.a build/libsharpyuv.a -output build/libsharpyuv.a + lipo -create build.arm64/libwebp.a build/libwebp.a -output build/libwebp.a + lipo -create build.arm64/libwebpdemux.a build/libwebpdemux.a -output build/libwebpdemux.a + lipo -create build.arm64/libwebpmux.a build/libwebpmux.a -output build/libwebpmux.a + cmake --install build """) stage('libheif', """ -win: git clone --depth 1 -b v1.16.2 https://github.com/strukturag/libheif.git cd libheif +win: %THIRDPARTY_DIR%\\msys64\\usr\\bin\\sed.exe -i 's/LIBHEIF_EXPORTS/LIBDE265_STATIC_BUILD/g' libheif/CMakeLists.txt %THIRDPARTY_DIR%\\msys64\\usr\\bin\\sed.exe -i 's/HAVE_VISIBILITY/LIBHEIF_STATIC_BUILD/g' libheif/CMakeLists.txt cmake . ^ @@ -797,12 +905,55 @@ win: release: cmake --build . --config Release cmake --install . --config Release +mac: + cmake . \\ + -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" \\ + -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\ + -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\ + -D BUILD_SHARED_LIBS=OFF \\ + -D ENABLE_PLUGIN_LOADING=OFF \\ + -D WITH_AOM_ENCODER=OFF \\ + -D WITH_AOM_DECODER=OFF \\ + -D WITH_X265=OFF \\ + -D WITH_SvtEnc=OFF \\ + -D WITH_RAV1E=OFF \\ + -D WITH_DAV1D=ON \\ + -D WITH_LIBDE265=ON \\ + -D LIBDE265_INCLUDE_DIR=$USED_PREFIX/include/ \\ + -D LIBDE265_LIBRARY=$USED_PREFIX/lib/libde265.a \\ + -D LIBSHARPYUV_INCLUDE_DIR=$USED_PREFIX/include/webp/ \\ + -D LIBSHARPYUV_LIBRARY=$USED_PREFIX/lib/libsharpyuv.a \\ + -D WITH_EXAMPLES=OFF + cmake --build . --config MinSizeRel $MAKE_THREADS_CNT + cmake --install . --config MinSizeRel """) stage('libjxl', """ -win: git clone -b v0.8.2 --depth 1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git cd libjxl +""" + setVar("cmake_defines", ' '.join(""" + -DBUILD_SHARED_LIBS=OFF + -DBUILD_TESTING=OFF + -DJPEGXL_ENABLE_FUZZERS=OFF + -DJPEGXL_ENABLE_DEVTOOLS=OFF + -DJPEGXL_ENABLE_TOOLS=OFF + -DJPEGXL_ENABLE_DOXYGEN=OFF + -DJPEGXL_ENABLE_MANPAGES=OFF + -DJPEGXL_ENABLE_EXAMPLES=OFF + -DJPEGXL_ENABLE_JNI=OFF + -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF + -DJPEGXL_ENABLE_SJPEG=OFF + -DJPEGXL_ENABLE_OPENEXR=OFF + -DJPEGXL_ENABLE_SKCMS=ON + -DJPEGXL_BUNDLE_SKCMS=ON + -DJPEGXL_ENABLE_VIEWERS=OFF + -DJPEGXL_ENABLE_TCMALLOC=OFF + -DJPEGXL_ENABLE_PLUGINS=OFF + -DJPEGXL_ENABLE_COVERAGE=OFF + -DJPEGXL_ENABLE_PROFILER=OFF + -DJPEGXL_WARNINGS_AS_ERRORS=OFF +""".replace('\n', '').split())) + """ +win: cmake . ^ -A %WIN32X64% ^ -DCMAKE_INSTALL_PREFIX=%LIBS_DIR%/local ^ @@ -813,31 +964,20 @@ win: -DCMAKE_CXX_FLAGS_DEBUG="/MTd /Zi /Ob0 /Od /RTC1" ^ -DCMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG" ^ -DCMAKE_CXX_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG" ^ - -DBUILD_SHARED_LIBS=OFF ^ - -DBUILD_TESTING=OFF ^ - -DJPEGXL_ENABLE_FUZZERS=OFF ^ - -DJPEGXL_ENABLE_DEVTOOLS=OFF ^ - -DJPEGXL_ENABLE_TOOLS=OFF ^ - -DJPEGXL_ENABLE_DOXYGEN=OFF ^ - -DJPEGXL_ENABLE_MANPAGES=OFF ^ - -DJPEGXL_ENABLE_EXAMPLES=OFF ^ - -DJPEGXL_ENABLE_JNI=OFF ^ - -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF ^ - -DJPEGXL_ENABLE_SJPEG=OFF ^ - -DJPEGXL_ENABLE_OPENEXR=OFF ^ - -DJPEGXL_ENABLE_SKCMS=ON ^ - -DJPEGXL_BUNDLE_SKCMS=ON ^ - -DJPEGXL_ENABLE_VIEWERS=OFF ^ - -DJPEGXL_ENABLE_TCMALLOC=OFF ^ - -DJPEGXL_ENABLE_PLUGINS=OFF ^ - -DJPEGXL_ENABLE_COVERAGE=OFF ^ - -DJPEGXL_ENABLE_PROFILER=OFF ^ - -DJPEGXL_WARNINGS_AS_ERRORS=OFF + %cmake_defines% cmake --build . --config Debug cmake --install . --config Debug release: cmake --build . --config Release cmake --install . --config Release +mac: + cmake . \\ + -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" \\ + -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\ + -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\ + ${cmake_defines} + cmake --build . --config MinSizeRel $MAKE_THREADS_CNT + cmake --install . --config MinSizeRel """) stage('libvpx', """ @@ -906,46 +1046,6 @@ depends:yasm/yasm make install """) -stage('libwebp', """ - git clone -b v1.3.2 https://github.com/webmproject/libwebp.git - cd libwebp -win: - nmake /f Makefile.vc CFG=debug-static OBJDIR=out RTLIBCFG=static all - nmake /f Makefile.vc CFG=release-static OBJDIR=out RTLIBCFG=static all - copy out\\release-static\\$X8664\\lib\\libwebp.lib out\\release-static\\$X8664\\lib\\webp.lib - copy out\\release-static\\$X8664\\lib\\libwebpdemux.lib out\\release-static\\$X8664\\lib\\webpdemux.lib - copy out\\release-static\\$X8664\\lib\\libwebpmux.lib out\\release-static\\$X8664\\lib\\webpmux.lib -mac: - buildOneArch() { - arch=$1 - folder=$2 - - CFLAGS=$UNGUARDED cmake -B $folder -G Ninja . \\ - -D CMAKE_BUILD_TYPE=Release \\ - -D CMAKE_INSTALL_PREFIX=$USED_PREFIX \\ - -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\ - -D CMAKE_OSX_ARCHITECTURES=$arch \\ - -D WEBP_BUILD_ANIM_UTILS=OFF \\ - -D WEBP_BUILD_CWEBP=OFF \\ - -D WEBP_BUILD_DWEBP=OFF \\ - -D WEBP_BUILD_GIF2WEBP=OFF \\ - -D WEBP_BUILD_IMG2WEBP=OFF \\ - -D WEBP_BUILD_VWEBP=OFF \\ - -D WEBP_BUILD_WEBPMUX=OFF \\ - -D WEBP_BUILD_WEBPINFO=OFF \\ - -D WEBP_BUILD_EXTRAS=OFF - cmake --build $folder $MAKE_THREADS_CNT - } - buildOneArch arm64 build.arm64 - buildOneArch x86_64 build - - lipo -create build.arm64/libsharpyuv.a build/libsharpyuv.a -output build/libsharpyuv.a - lipo -create build.arm64/libwebp.a build/libwebp.a -output build/libwebp.a - lipo -create build.arm64/libwebpdemux.a build/libwebpdemux.a -output build/libwebpdemux.a - lipo -create build.arm64/libwebpmux.a build/libwebpmux.a -output build/libwebpmux.a - cmake --install build -""") - stage('nv-codec-headers', """ win: git clone https://github.com/FFmpeg/nv-codec-headers.git diff --git a/docs/building-mac.md b/docs/building-mac.md index d357c66c1c..11435a8dd2 100644 --- a/docs/building-mac.md +++ b/docs/building-mac.md @@ -13,7 +13,7 @@ You will require **api_id** and **api_hash** to access the Telegram API servers. Go to ***BuildPath*** and run ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" - brew install git automake cmake wget pkg-config gnu-tar ninja + brew install git automake cmake wget pkg-config gnu-tar ninja nasm meson sudo xcode-select -s /Applications/Xcode.app/Contents/Developer From de1bd6ef28ed625bcadbc4ef089dc3342a768fc0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 19 Feb 2024 18:55:56 +0300 Subject: [PATCH 016/108] Slightly simplified creation of universal variables in prepare.py. --- Telegram/build/prepare/prepare.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 30d5084f4f..c3a5667968 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -202,10 +202,11 @@ def removeDir(folder): return 'if exist ' + folder + ' rmdir /Q /S ' + folder + '\nif exist ' + folder + ' exit /b 1' return 'rm -rf ' + folder -def setVar(key, value): +def setVar(key, multilineValue): + singlelineValue = ' '.join(multilineValue.replace('\n', '').split()); if win: - return 'SET ' + key + '="' + value + '"'; - return key + '="' + value + '"'; + return 'SET ' + key + '="' + singlelineValue + '"'; + return key + '="' + singlelineValue + '"'; def filterByPlatform(commands): commands = commands.split('\n') @@ -931,7 +932,7 @@ mac: stage('libjxl', """ git clone -b v0.8.2 --depth 1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git cd libjxl -""" + setVar("cmake_defines", ' '.join(""" +""" + setVar("cmake_defines", """ -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DJPEGXL_ENABLE_FUZZERS=OFF @@ -952,7 +953,7 @@ stage('libjxl', """ -DJPEGXL_ENABLE_COVERAGE=OFF -DJPEGXL_ENABLE_PROFILER=OFF -DJPEGXL_WARNINGS_AS_ERRORS=OFF -""".replace('\n', '').split())) + """ +""") + """ win: cmake . ^ -A %WIN32X64% ^ From 52c779bffa8dde3c5c09826add2607328fae0924 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 21 Feb 2024 20:47:11 +0300 Subject: [PATCH 017/108] Added support of inline markup reply to HTML export. --- Telegram/Resources/export_html/css/style.css | 23 +++++++++ Telegram/Resources/export_html/js/script.js | 6 +++ .../export/data/export_data_types.cpp | 24 +++++++++ .../export/data/export_data_types.h | 2 + .../export/output/export_output_html.cpp | 49 +++++++++++++++++++ .../export/output/export_output_json.cpp | 26 +--------- 6 files changed, 106 insertions(+), 24 deletions(-) diff --git a/Telegram/Resources/export_html/css/style.css b/Telegram/Resources/export_html/css/style.css index 79b680cc2c..102f5f3a5d 100644 --- a/Telegram/Resources/export_html/css/style.css +++ b/Telegram/Resources/export_html/css/style.css @@ -559,3 +559,26 @@ div.toast_shown { opacity: 0; user-select: none; } + +.bot_buttons_table { + border-spacing: 0px 2px; + width: 100%; +} +.bot_button { + border-radius: 8px; + text-align: center; + vertical-align: middle; + background-color: #168acd40; +} +.bot_button_row { + display: table; + table-layout: fixed; + padding: 0px; + width:100%; +} +.bot_button_row div { + display: table-cell; +} +.bot_button_column_separator { + width: 2px +} diff --git a/Telegram/Resources/export_html/js/script.js b/Telegram/Resources/export_html/js/script.js index 8d25f53022..2842322020 100644 --- a/Telegram/Resources/export_html/js/script.js +++ b/Telegram/Resources/export_html/js/script.js @@ -62,6 +62,12 @@ function ShowNotAvailableEmoji() { return false; } +function ShowTextCopied(content) { + navigator.clipboard.writeText(content); + ShowToast("Text copied to clipboard."); + return false; +} + function ShowSpoiler(target) { if (target.classList.contains("hidden")) { target.classList.toggle("hidden"); diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index f7928f006b..1f56f9ec19 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -160,6 +160,30 @@ std::vector> ButtonRowsFromTL( } // namespace +QByteArray HistoryMessageMarkupButton::TypeToString( + const HistoryMessageMarkupButton &button) { + using Type = HistoryMessageMarkupButton::Type; + switch (button.type) { + case Type::Default: return "default"; + case Type::Url: return "url"; + case Type::Callback: return "callback"; + case Type::CallbackWithPassword: return "callback_with_password"; + case Type::RequestPhone: return "request_phone"; + case Type::RequestLocation: return "request_location"; + case Type::RequestPoll: return "request_poll"; + case Type::RequestPeer: return "request_peer"; + case Type::SwitchInline: return "switch_inline"; + case Type::SwitchInlineSame: return "switch_inline_same"; + case Type::Game: return "game"; + case Type::Buy: return "buy"; + case Type::Auth: return "auth"; + case Type::UserProfile: return "user_profile"; + case Type::WebView: return "web_view"; + case Type::SimpleWebView: return "simple_web_view"; + } + Unexpected("Type in HistoryMessageMarkupButton::Type."); +} + uint8 PeerColorIndex(BareId bareId) { const uint8 map[] = { 0, 7, 4, 1, 6, 3, 5 }; return map[bareId % base::array_size(map)]; diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 5f2c2da393..76585991ca 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -690,6 +690,8 @@ struct HistoryMessageMarkupButton { SimpleWebView, }; + static QByteArray TypeToString(const HistoryMessageMarkupButton &); + Type type; QString text; QByteArray data; diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index 3953152278..9df6bcbb3f 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -1443,6 +1443,55 @@ auto HtmlWriter::Wrap::pushMessage( block.append(text); block.append(popTag()); } + if (!message.inlineButtonRows.empty()) { + using Type = HistoryMessageMarkupButton::Type; + const auto endline = u" | "_q; + block.append(pushTag("table", { { "class", "bot_buttons_table" } })); + block.append(pushTag("tbody")); + for (const auto &row : message.inlineButtonRows) { + block.append(pushTag("tr")); + block.append(pushTag("td", { { "class", "bot_button_row" } })); + for (const auto &button : row) { + using Attribute = std::pair; + const auto content = (!button.data.isEmpty() + ? (u"Data: "_q + button.data + endline) + : QString()) + + (!button.forwardText.isEmpty() + ? (u"Forward text: "_q + button.forwardText + endline) + : QString()) + + (u"Type: "_q + + HistoryMessageMarkupButton::TypeToString(button)); + const auto link = (button.type == Type::Url) + ? button.data + : QByteArray(); + const auto onclick = (button.type != Type::Url) + ? ("return ShowTextCopied('" + content + "');").toUtf8() + : QByteArray(); + block.append(pushTag("div", { { "class", "bot_button" } })); + block.append(pushTag("a", { + link.isEmpty() ? Attribute() : Attribute{ "href", link }, + onclick.isEmpty() + ? Attribute() + : Attribute{ "onclick", onclick }, + })); + block.append(pushTag("div")); + block.append(button.text.toUtf8()); + block.append(popTag()); + block.append(popTag()); + block.append(popTag()); + + if (&button != &row.back()) { + block.append(pushTag("div", { + { "class", "bot_button_column_separator" } + })); + block.append(popTag()); + } + } + block.append(popTag()); + block.append(popTag()); + } + block.append(popTag()); + } if (!message.signature.isEmpty()) { block.append(pushDiv("signature details")); block.append(SerializeString(message.signature)); diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index dde36a5a38..5bd7f2c376 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -784,29 +784,6 @@ QByteArray SerializeMessage( pushBare("text_entities", SerializeText(context, message.text, true)); if (!message.inlineButtonRows.empty()) { - const auto typeString = []( - const HistoryMessageMarkupButton &entry) -> QByteArray { - using Type = HistoryMessageMarkupButton::Type; - switch (entry.type) { - case Type::Default: return "default"; - case Type::Url: return "url"; - case Type::Callback: return "callback"; - case Type::CallbackWithPassword: return "callback_with_password"; - case Type::RequestPhone: return "request_phone"; - case Type::RequestLocation: return "request_location"; - case Type::RequestPoll: return "request_poll"; - case Type::RequestPeer: return "request_peer"; - case Type::SwitchInline: return "switch_inline"; - case Type::SwitchInlineSame: return "switch_inline_same"; - case Type::Game: return "game"; - case Type::Buy: return "buy"; - case Type::Auth: return "auth"; - case Type::UserProfile: return "user_profile"; - case Type::WebView: return "web_view"; - case Type::SimpleWebView: return "simple_web_view"; - } - Unexpected("Type in HistoryMessageMarkupButton::Type."); - }; const auto serializeRow = [&]( const std::vector &row) { context.nesting.push_back(Context::kArray); @@ -817,7 +794,8 @@ QByteArray SerializeMessage( auto pairs = std::vector>(); pairs.push_back({ "type", - SerializeString(typeString(entry)), + SerializeString( + HistoryMessageMarkupButton::TypeToString(entry)), }); if (!entry.text.isEmpty()) { pairs.push_back({ From 93d1a187ca4b10a6f26418664a046fadb84829cf Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 22 Feb 2024 05:04:35 +0300 Subject: [PATCH 018/108] Improved view style of contacts. --- Telegram/Resources/langs/lang.strings | 3 + .../SourceFiles/data/data_media_types.cpp | 2 +- .../view/media/history_view_contact.cpp | 487 ++++++++++++++---- .../history/view/media/history_view_contact.h | 52 +- 4 files changed, 425 insertions(+), 119 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6369098637..ea6c5f2e8b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4724,6 +4724,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boosts_prepaid_giveaway_status#one" = "{count} subscription {duration}"; "lng_boosts_prepaid_giveaway_status#other" = "{count} subscriptions {duration}"; +"lng_contact_add" = "Add"; +"lng_contact_send_message" = "message"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 7361373df3..3e43cd47af 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -1253,7 +1253,7 @@ const SharedContact *MediaContact::sharedContact() const { } TextWithEntities MediaContact::notificationText() const { - return tr::lng_in_dlg_contact(tr::now, Ui::Text::WithEntities); + return Ui::Text::Colorized(tr::lng_in_dlg_contact(tr::now)); } QString MediaContact::pinnedTextSubstring() const { diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp index 7b0ac35b95..908bf1c0c2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp @@ -7,28 +7,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/view/media/history_view_contact.h" -#include "core/click_handler_types.h" // ClickHandlerContext -#include "lang/lang_keys.h" -#include "layout/layout_selection.h" -#include "mainwindow.h" #include "boxes/add_contact_box.h" -#include "history/history_item_components.h" -#include "history/history_item.h" -#include "history/history.h" -#include "history/view/history_view_element.h" -#include "history/view/history_view_cursor_state.h" -#include "window/window_session_controller.h" -#include "ui/empty_userpic.h" -#include "ui/chat/chat_style.h" -#include "ui/text/format_values.h" // Ui::FormatPhone -#include "ui/text/text_options.h" -#include "ui/painter.h" +#include "core/click_handler_types.h" // ClickHandlerContext #include "data/data_session.h" #include "data/data_user.h" -#include "data/data_media_types.h" -#include "data/data_cloud_file.h" +#include "history/history.h" +#include "history/history_item_components.h" +#include "history/view/history_view_cursor_state.h" +#include "history/view/history_view_reply.h" +#include "history/view/media/history_view_media_common.h" +#include "lang/lang_keys.h" #include "main/main_session.h" +#include "styles/style_boxes.h" #include "styles/style_chat.h" +#include "ui/chat/chat_style.h" +#include "ui/empty_userpic.h" +#include "ui/painter.h" +#include "ui/power_saving.h" +#include "ui/rect.h" +#include "ui/text/format_values.h" // Ui::FormatPhone +#include "ui/text/text_options.h" +#include "window/window_session_controller.h" namespace HistoryView { namespace { @@ -81,17 +80,32 @@ Contact::Contact( const QString &last, const QString &phone) : Media(parent) -, _userId(userId) -, _fname(first) -, _lname(last) -, _phone(Ui::FormatPhone(phone)) { +, _st(st::historyPagePreview) +, _pixh(st::contactsPhotoSize) +, _userId(userId) { history()->owner().registerContactView(userId, parent); - _name.setText( - st::semiboldTextStyle, - tr::lng_full_name(tr::now, lt_first_name, first, lt_last_name, last).trimmed(), - Ui::NameTextOptions()); - _phonew = st::normalFont->width(_phone); + _nameLine.setText( + st::webPageTitleStyle, + tr::lng_full_name( + tr::now, + lt_first_name, + first, + lt_last_name, + last).trimmed(), + Ui::WebpageTextTitleOptions()); + + _phoneLine.setText( + st::webPageDescriptionStyle, + Ui::FormatPhone(QString(phone).replace(QChar('+'), QString())), + Ui::WebpageTextTitleOptions()); + +#if 0 // No info. + _infoLine.setText( + st::webPageDescriptionStyle, + phone, + Ui::WebpageTextTitleOptions()); +#endif } Contact::~Contact() { @@ -111,121 +125,300 @@ void Contact::updateSharedContactUserId(UserId userId) { } QSize Contact::countOptimalSize() { - const auto item = _parent->data(); - auto maxWidth = st::msgFileMinWidth; - _contact = _userId - ? item->history()->owner().userLoaded(_userId) + ? _parent->data()->history()->owner().userLoaded(_userId) : nullptr; if (_contact) { _contact->loadUserpic(); } else { - const auto full = _name.toString(); + const auto full = _nameLine.toString(); _photoEmpty = std::make_unique( Ui::EmptyUserpic::UserpicColor(Data::DecideColorIndex(_userId ? peerFromUser(_userId) : Data::FakePeerIdForJustName(full))), full); } - if (_contact && _contact->isContact()) { - _linkl = SendMessageClickHandler(_contact); - _link = tr::lng_profile_send_message(tr::now).toUpper(); - } else if (_userId) { - _linkl = AddContactClickHandler(_parent->data()); - _link = tr::lng_profile_add_contact(tr::now).toUpper(); - } - _linkw = _link.isEmpty() ? 0 : st::semiboldFont->width(_link); - const auto &st = _userId ? st::msgFileThumbLayout : st::msgFileLayout; - - const auto tleft = st.padding.left() + st.thumbSize + st.thumbSkip; - const auto tright = st.padding.right(); - if (_userId) { - accumulate_max(maxWidth, tleft + _phonew + tright); + _buttons.clear(); + if (_contact) { + const auto message = tr::lng_contact_send_message(tr::now).toUpper(); + _buttons.push_back({ + message, + st::semiboldFont->width(message), + SendMessageClickHandler(_contact), + }); + if (!_contact->isContact()) { + const auto add = tr::lng_contact_add(tr::now).toUpper(); + _buttons.push_back({ + add, + st::semiboldFont->width(add), + AddContactClickHandler(_parent->data()), + }); + } + _mainButton.link = _buttons.front().link; } else { - accumulate_max(maxWidth, tleft + _phonew + _parent->skipBlockWidth() + st::msgPadding.right()); +#if 0 // Can't view contact. + const auto view = tr::lng_profile_add_contact(tr::now).toUpper(); + _buttons.push_back({ + view, + st::semiboldFont->width(view), + AddContactClickHandler(_parent->data()), + }); +#endif + _mainButton.link = nullptr; } - accumulate_max(maxWidth, tleft + _name.maxWidth() + tright); - accumulate_min(maxWidth, st::msgMaxWidth); - auto minHeight = st.padding.top() + st.thumbSize + st.padding.bottom(); - if (_parent->bottomInfoIsWide()) { - minHeight += st::msgDateFont->height - st::msgDateDelta.y(); + const auto padding = inBubblePadding() + innerMargin(); + const auto full = Rect(currentSize()); + const auto outer = full - inBubblePadding(); + const auto inner = outer - innerMargin(); + const auto lineLeft = inner.left() + _pixh + inner.left() - outer.left(); + const auto lineHeight = UnitedLineHeight(); + + auto maxWidth = _parent->skipBlockWidth(); + auto minHeight = 0; + + auto textMinHeight = 0; + if (!_nameLine.isEmpty()) { + accumulate_max(maxWidth, lineLeft + _nameLine.maxWidth()); + textMinHeight += 1 * lineHeight; } - if (!isBubbleTop()) { - minHeight -= st::msgFileTopMinus; + if (!_phoneLine.isEmpty()) { + accumulate_max(maxWidth, lineLeft + _phoneLine.maxWidth()); + textMinHeight += 1 * lineHeight; } + if (!_infoLine.isEmpty()) { + accumulate_max(maxWidth, lineLeft + _infoLine.maxWidth()); + textMinHeight += std::min(_infoLine.minHeight(), 1 * lineHeight); + } + minHeight = std::max(textMinHeight, st::contactsPhotoSize); + + if (!_buttons.empty()) { + auto buttonsWidth = rect::m::sum::h(st::historyPageButtonPadding); + for (const auto &button : _buttons) { + buttonsWidth += button.width; + } + accumulate_max(maxWidth, buttonsWidth); + } + maxWidth += rect::m::sum::h(padding); + minHeight += rect::m::sum::v(padding); + return { maxWidth, minHeight }; } void Contact::draw(Painter &p, const PaintContext &context) const { - if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; - auto paintw = width(); + if (width() < rect::m::sum::h(st::msgPadding) + 1) { + return; + } + const auto st = context.st; + const auto sti = context.imageStyle(); const auto stm = context.messageStyle(); - accumulate_min(paintw, maxWidth()); + const auto bubble = st::msgPadding; + const auto full = Rect(currentSize()); + const auto outer = full - inBubblePadding(); + const auto inner = outer - innerMargin(); + auto tshift = inner.top(); - const auto &st = _userId ? st::msgFileThumbLayout : st::msgFileLayout; - const auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; - const auto nameleft = st.padding.left() + st.thumbSize + st.thumbSkip; - const auto nametop = st.nameTop - topMinus; - const auto nameright = st.padding.right(); - const auto statustop = st.statusTop - topMinus; - const auto linkshift = st::msgDateFont->height / 2; - const auto linktop = st.linkTop - topMinus - linkshift; - if (_userId) { - QRect rthumb(style::rtlrect(st.padding.left(), st.padding.top() - topMinus, st.thumbSize, st.thumbSize, paintw)); - if (_contact) { - const auto was = !_userpic.null(); - _contact->paintUserpic(p, _userpic, rthumb.x(), rthumb.y(), st.thumbSize); - if (!was && !_userpic.null()) { - history()->owner().registerHeavyViewPart(_parent); + const auto selected = context.selected(); + const auto view = parent(); + const auto colorIndex = _contact + ? _contact->colorIndex() + : Data::DecideColorIndex( + Data::FakePeerIdForJustName(_nameLine.toString())); + const auto cache = context.outbg + ? stm->replyCache[st->colorPatternIndex(colorIndex)].get() + : st->coloredReplyCache(selected, colorIndex).get(); + const auto backgroundEmojiId = _contact + ? _contact->backgroundEmojiId() + : DocumentId(); + const auto backgroundEmoji = backgroundEmojiId + ? st->backgroundEmojiData(backgroundEmojiId).get() + : nullptr; + const auto backgroundEmojiCache = backgroundEmoji + ? &backgroundEmoji->caches[Ui::BackgroundEmojiData::CacheIndex( + selected, + context.outbg, + true, + colorIndex + 1)] + : nullptr; + Ui::Text::ValidateQuotePaintCache(*cache, _st); + Ui::Text::FillQuotePaint(p, outer, *cache, _st); + if (backgroundEmoji) { + ValidateBackgroundEmoji( + backgroundEmojiId, + backgroundEmoji, + backgroundEmojiCache, + cache, + view); + if (!backgroundEmojiCache->frames[0].isNull()) { + const auto end = rect::bottom(inner) + _st.padding.bottom(); + const auto r = outer + - QMargins(0, 0, 0, rect::bottom(outer) - end); + FillBackgroundEmoji(p, r, false, *backgroundEmojiCache); + } + } + + if (_mainButton.ripple) { + _mainButton.ripple->paint( + p, + outer.x(), + outer.y(), + width(), + &cache->bg); + if (_mainButton.ripple->empty()) { + _mainButton.ripple = nullptr; + } + } + + { + const auto left = inner.left(); + const auto top = tshift; + if (_userId) { + if (_contact) { + const auto was = !_userpic.null(); + _contact->paintUserpic(p, _userpic, left, top, _pixh); + if (!was && !_userpic.null()) { + history()->owner().registerHeavyViewPart(_parent); + } + } else { + _photoEmpty->paintCircle(p, left, top, _pixh, _pixh); } } else { - _photoEmpty->paintCircle(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize); + _photoEmpty->paintCircle(p, left, top, _pixh, _pixh); } if (context.selected()) { - PainterHighQualityEnabler hq(p); + auto hq = PainterHighQualityEnabler(p); p.setBrush(p.textPalette().selectOverlay); p.setPen(Qt::NoPen); - p.drawEllipse(rthumb); + p.drawEllipse(left, top, _pixh, _pixh); } - - bool over = ClickHandler::showAsActive(_linkl); - p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont); - p.setPen(stm->msgFileThumbLinkFg); - p.drawTextLeft(nameleft, linktop, paintw, _link, _linkw); - } else { - _photoEmpty->paintCircle(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize); } - const auto namewidth = paintw - nameleft - nameright; - p.setFont(st::semiboldFont); - p.setPen(stm->historyFileNameFg); - _name.drawLeftElided(p, nameleft, nametop, namewidth, paintw); + const auto lineHeight = UnitedLineHeight(); + const auto lineLeft = inner.left() + _pixh + inner.left() - outer.left(); + const auto lineWidth = rect::right(inner) - lineLeft; - p.setFont(st::normalFont); - p.setPen(stm->mediaFg); - p.drawTextLeft(nameleft, statustop, paintw, _phone); + { + p.setPen(cache->icon); + p.setTextPalette(context.outbg + ? stm->semiboldPalette + : st->coloredTextPalette(selected, colorIndex)); + + const auto endskip = _nameLine.hasSkipBlock() + ? _parent->skipBlockWidth() + : 0; + _nameLine.drawLeftElided( + p, + lineLeft, + tshift, + lineWidth, + width(), + 1, + style::al_left, + 0, + -1, + endskip, + false, + context.selection); + tshift += lineHeight; + + p.setTextPalette(stm->textPalette); + } + p.setPen(stm->historyTextFg); + { + tshift += st::lineWidth * 3; // Additional skip. + const auto endskip = _phoneLine.hasSkipBlock() + ? _parent->skipBlockWidth() + : 0; + _phoneLine.drawLeftElided( + p, + lineLeft, + tshift, + lineWidth, + width(), + 1, + style::al_left, + 0, + -1, + endskip, + false, + toTitleSelection(context.selection)); + tshift += 1 * lineHeight; + } + if (!_infoLine.isEmpty()) { + tshift += st::lineWidth * 3; // Additional skip. + const auto endskip = _infoLine.hasSkipBlock() + ? _parent->skipBlockWidth() + : 0; + _parent->prepareCustomEmojiPaint(p, context, _infoLine); + _infoLine.draw(p, { + .position = { lineLeft, tshift }, + .outerWidth = width(), + .availableWidth = lineWidth, + .spoiler = Ui::Text::DefaultSpoilerCache(), + .now = context.now, + .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), + .pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler), + .selection = toDescriptionSelection(context.selection), + .elisionHeight = (1 * lineHeight), + .elisionRemoveFromEnd = endskip, + }); + tshift += (1 * lineHeight); + } + + if (!_buttons.empty()) { + p.setFont(st::semiboldFont); + p.setPen(cache->icon); + const auto end = rect::bottom(inner) + _st.padding.bottom(); + const auto line = st::historyPageButtonLine; + auto color = cache->icon; + color.setAlphaF(color.alphaF() * 0.3); + const auto top = end + st::historyPageButtonPadding.top(); + const auto buttonWidth = inner.width() / float64(_buttons.size()); + p.fillRect(inner.x(), end, inner.width(), line, color); + for (auto i = 0; i < _buttons.size(); i++) { + const auto &button = _buttons[i]; + const auto left = inner.x() + i * buttonWidth; + if (button.ripple) { + button.ripple->paint(p, left, end, buttonWidth, &cache->bg); + if (button.ripple->empty()) { + _buttons[i].ripple = nullptr; + } + } + p.drawText( + left + (buttonWidth - button.width) / 2, + top + st::semiboldFont->ascent, + button.text); + } + } } TextState Contact::textState(QPoint point, StateRequest request) const { auto result = TextState(_parent); - if (_userId) { - const auto &st = _userId ? st::msgFileThumbLayout : st::msgFileLayout; - const auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; - const auto nameleft = st.padding.left() + st.thumbSize + st.thumbSkip; - const auto linkshift = st::msgDateFont->height / 2; - const auto linktop = st.linkTop - topMinus - linkshift; - if (style::rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, width()).contains(point)) { - result.link = _linkl; - return result; + const auto full = Rect(currentSize()); + const auto outer = full - inBubblePadding(); + const auto inner = outer - innerMargin(); + + _lastPoint = point; + + if (_buttons.size() > 1) { + const auto end = rect::bottom(inner) + _st.padding.bottom(); + const auto line = st::historyPageButtonLine; + const auto bWidth = inner.width() / float64(_buttons.size()); + const auto bHeight = rect::bottom(outer) - end; + for (auto i = 0; i < _buttons.size(); i++) { + const auto left = inner.x() + i * bWidth; + if (QRectF(left, end, bWidth, bHeight).contains(point)) { + result.link = _buttons[i].link; + return result; + } } } - if (QRect(0, 0, width(), height()).contains(point) && _contact) { - result.link = _contact->openLink(); + if (outer.contains(point)) { + result.link = _mainButton.link; return result; } return result; @@ -239,4 +432,100 @@ bool Contact::hasHeavyPart() const { return !_userpic.null(); } +void Contact::clickHandlerPressedChanged( + const ClickHandlerPtr &p, + bool pressed) { + const auto full = Rect(currentSize()); + const auto outer = full - inBubblePadding(); + const auto inner = outer - innerMargin(); + const auto end = rect::bottom(inner) + _st.padding.bottom(); + if ((_lastPoint.y() < end) || (_buttons.size() <= 1)) { + if (p != _mainButton.link) { + return; + } + if (pressed) { + if (!_mainButton.ripple) { + const auto owner = &parent()->history()->owner(); + _mainButton.ripple = std::make_unique( + st::defaultRippleAnimation, + Ui::RippleAnimation::RoundRectMask( + outer.size(), + _st.radius), + [=] { owner->requestViewRepaint(parent()); }); + } + _mainButton.ripple->add(_lastPoint - outer.topLeft()); + } else if (_mainButton.ripple) { + _mainButton.ripple->lastStop(); + } + return; + } else if (_buttons.empty()) { + return; + } + const auto bWidth = inner.width() / float64(_buttons.size()); + const auto bHeight = rect::bottom(outer) - end; + for (auto i = 0; i < _buttons.size(); i++) { + const auto &button = _buttons[i]; + if (p != button.link) { + continue; + } + if (pressed) { + if (!button.ripple) { + const auto owner = &parent()->history()->owner(); + + _buttons[i].ripple = std::make_unique( + st::defaultRippleAnimation, + Ui::RippleAnimation::MaskByDrawer( + QSize(bWidth, bHeight), + false, + [=](QPainter &p) { + p.drawRect(0, 0, bWidth, bHeight); + }), + [=] { owner->requestViewRepaint(parent()); }); + } + button.ripple->add(_lastPoint + - QPoint(inner.x() + i * bWidth, end)); + } else if (button.ripple) { + button.ripple->lastStop(); + } + } +} + +QMargins Contact::inBubblePadding() const { + return { + st::msgPadding.left(), + isBubbleTop() ? st::msgPadding.left() : 0, + st::msgPadding.right(), + isBubbleBottom() ? (st::msgPadding.left() + bottomInfoPadding()) : 0 + }; +} + +QMargins Contact::innerMargin() const { + const auto button = _buttons.empty() ? 0 : st::historyPageButtonHeight; + return _st.padding + QMargins(0, 0, 0, button); +} + +int Contact::bottomInfoPadding() const { + if (!isBubbleBottom()) { + return 0; + } + + auto result = st::msgDateFont->height; + + // We use padding greater than st::msgPadding.bottom() in the + // bottom of the bubble so that the left line looks pretty. + // but if we have bottom skip because of the info display + // we don't need that additional padding so we replace it + // back with st::msgPadding.bottom() instead of left(). + result += st::msgPadding.bottom() - st::msgPadding.left(); + return result; +} + +TextSelection Contact::toTitleSelection(TextSelection selection) const { + return UnshiftItemSelection(selection, _nameLine); +} + +TextSelection Contact::toDescriptionSelection(TextSelection selection) const { + return UnshiftItemSelection(toTitleSelection(selection), _phoneLine); +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.h b/Telegram/SourceFiles/history/view/media/history_view_contact.h index ecca595f2e..2dd665b946 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_contact.h +++ b/Telegram/SourceFiles/history/view/media/history_view_contact.h @@ -12,11 +12,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { class EmptyUserpic; +class RippleAnimation; } // namespace Ui namespace HistoryView { -class Contact : public Media { +class Contact final : public Media { public: Contact( not_null parent, @@ -29,7 +30,8 @@ public: void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; - bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { + bool toggleSelectionByHandlerClick( + const ClickHandlerPtr &p) const override { return true; } bool dragItemByHandler(const ClickHandlerPtr &p) const override { @@ -43,16 +45,6 @@ public: return false; } - const QString &fname() const { - return _fname; - } - const QString &lname() const { - return _lname; - } - const QString &phone() const { - return _phone; - } - // Should be called only by Data::Session. void updateSharedContactUserId(UserId userId) override; @@ -62,18 +54,40 @@ public: private: QSize countOptimalSize() override; + void clickHandlerPressedChanged( + const ClickHandlerPtr &p, bool pressed) override; + + [[nodiscard]] QMargins inBubblePadding() const; + [[nodiscard]] QMargins innerMargin() const; + [[nodiscard]] int bottomInfoPadding() const; + + [[nodiscard]] TextSelection toTitleSelection( + TextSelection selection) const; + [[nodiscard]] TextSelection toDescriptionSelection( + TextSelection selection) const; + + const style::QuoteStyle &_st; + const int _pixh; + UserId _userId = 0; UserData *_contact = nullptr; - int _phonew = 0; - QString _fname, _lname, _phone; - Ui::Text::String _name; + Ui::Text::String _nameLine; + Ui::Text::String _phoneLine; + Ui::Text::String _infoLine; + + struct Button { + QString text; + int width = 0; + ClickHandlerPtr link; + mutable std::unique_ptr ripple; + }; + std::vector