From 8a9409005fc24269c8e183f4cd34d88e817601cd Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 3 Mar 2026 13:03:53 +0300 Subject: [PATCH] Split translate box to api and td_ui parts. --- Telegram/SourceFiles/boxes/translate_box.cpp | 251 +++--------------- .../boxes/translate_box_content.cpp | 212 +++++++++++++++ .../SourceFiles/boxes/translate_box_content.h | 33 +++ Telegram/cmake/td_ui.cmake | 2 + 4 files changed, 289 insertions(+), 209 deletions(-) create mode 100644 Telegram/SourceFiles/boxes/translate_box_content.cpp create mode 100644 Telegram/SourceFiles/boxes/translate_box_content.h diff --git a/Telegram/SourceFiles/boxes/translate_box.cpp b/Telegram/SourceFiles/boxes/translate_box.cpp index a5b3456caa..ed103018d6 100644 --- a/Telegram/SourceFiles/boxes/translate_box.cpp +++ b/Telegram/SourceFiles/boxes/translate_box.cpp @@ -6,6 +6,7 @@ For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/translate_box.h" +#include "boxes/translate_box_content.h" #include "api/api_text_entities.h" // Api::EntitiesToMTP / EntitiesFromMTP. #include "core/application.h" @@ -20,76 +21,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/sender.h" #include "spellcheck/platform/platform_language.h" #include "ui/boxes/choose_language_box.h" -#include "ui/effects/loading_element.h" #include "ui/layers/generic_box.h" -#include "ui/text/text_utilities.h" -#include "ui/vertical_list.h" -#include "ui/painter.h" -#include "ui/power_saving.h" -#include "ui/widgets/buttons.h" -#include "ui/widgets/labels.h" #include "ui/widgets/multi_select.h" -#include "ui/wrap/fade_wrap.h" -#include "ui/wrap/slide_wrap.h" -#include "styles/style_boxes.h" -#include "styles/style_chat_helpers.h" -#include "styles/style_info.h" // inviteLinkListItem. -#include "styles/style_layers.h" - -#include +#include "ui/text/text_utilities.h" namespace Ui { namespace { constexpr auto kSkipAtLeastOneDuration = 3 * crl::time(1000); -class ShowButton final : public RpWidget { -public: - ShowButton(not_null parent); - - [[nodiscard]] rpl::producer clicks() const; - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - LinkButton _button; - -}; - -ShowButton::ShowButton(not_null parent) -: RpWidget(parent) -, _button(this, tr::lng_usernames_activate_confirm(tr::now)) { - _button.sizeValue( - ) | rpl::on_next([=](const QSize &s) { - resize( - s.width() + st::defaultEmojiSuggestions.fadeRight.width(), - s.height()); - _button.moveToRight(0, 0); - }, lifetime()); - _button.show(); -} - -void ShowButton::paintEvent(QPaintEvent *e) { - auto p = QPainter(this); - const auto clip = e->rect(); - - const auto &icon = st::defaultEmojiSuggestions.fadeRight; - const auto fade = QRect(0, 0, icon.width(), height()); - if (fade.intersects(clip)) { - icon.fill(p, fade); - } - const auto fill = clip.intersected( - { icon.width(), 0, width() - icon.width(), height() }); - if (!fill.isEmpty()) { - p.fillRect(fill, st::boxBg); - } -} - -rpl::producer ShowButton::clicks() const { - return _button.clicks(); -} - } // namespace void TranslateBox( @@ -98,10 +38,6 @@ void TranslateBox( MsgId msgId, TextWithEntities text, bool hasCopyRestriction) { - box->setWidth(st::boxWideWidth); - box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); }); - const auto container = box->verticalLayout(); - struct State { State(not_null session) : api(&session->mtp()) { } @@ -122,150 +58,47 @@ void TranslateBox( : !text.text.isEmpty() ? Flag::f_text : Flag(0); + const auto requestText = text; - const auto &stLabel = st::aboutLabel; - const auto lineHeight = stLabel.style.lineHeight; - - Ui::AddSkip(container); - // Ui::AddSubsectionTitle( - // container, - // tr::lng_translate_box_original()); - - const auto animationsPaused = [] { - using Which = FlatLabel::WhichAnimationsPaused; - const auto emoji = On(PowerSaving::kEmojiChat); - const auto spoiler = On(PowerSaving::kChatSpoiler); - return emoji - ? (spoiler ? Which::All : Which::CustomEmoji) - : (spoiler ? Which::Spoiler : Which::None); - }; - const auto original = box->addRow(object_ptr>( - box, - object_ptr(box, stLabel))); - { - if (hasCopyRestriction) { - original->entity()->setContextMenuHook([](auto&&) { - }); - } - original->entity()->setAnimationsPausedCallback(animationsPaused); - original->entity()->setMarkedText( - text, - Core::TextContext({ .session = &peer->session() })); - original->setMinimalHeight(lineHeight); - original->hide(anim::type::instant); - - const auto show = Ui::CreateChild>( - container.get(), - object_ptr(container)); - show->hide(anim::type::instant); - rpl::combine( - container->widthValue(), - original->geometryValue() - ) | rpl::on_next([=](int width, const QRect &rect) { - show->moveToLeft( - width - show->width() - st::boxRowPadding.right(), - rect.y() + std::abs(lineHeight - show->height()) / 2); - }, show->lifetime()); - original->entity()->heightValue( - ) | rpl::filter([](int height) { - return height > 0; - }) | rpl::take(1) | rpl::on_next([=](int height) { - if (height > lineHeight) { - show->show(anim::type::instant); - } - }, show->lifetime()); - show->toggleOn(show->entity()->clicks() | rpl::map_to(false)); - original->toggleOn(show->entity()->clicks() | rpl::map_to(true)); - } - Ui::AddSkip(container); - Ui::AddSkip(container); - Ui::AddDivider(container); - Ui::AddSkip(container); - - { - const auto padding = st::defaultSubsectionTitlePadding; - const auto subtitle = Ui::AddSubsectionTitle( - container, - state->to.value() | rpl::map(LanguageName)); - - // Workaround. - state->to.value() | rpl::on_next([=] { - subtitle->resizeToWidth(container->width() - - padding.left() - - padding.right()); - }, subtitle->lifetime()); - } - - const auto translated = box->addRow(object_ptr>( - box, - object_ptr(box, stLabel))); - translated->entity()->setSelectable(!hasCopyRestriction); - translated->entity()->setAnimationsPausedCallback(animationsPaused); - - constexpr auto kMaxLines = 3; - container->resizeToWidth(box->width()); - const auto loading = box->addRow(object_ptr>( - box, - CreateLoadingTextWidget( - box, - st::aboutLabel.style, - std::min(original->entity()->height() / lineHeight, kMaxLines), - state->to.value() | rpl::map([=](LanguageId id) { - return id.locale().textDirection() == Qt::RightToLeft; - })))); - - const auto showText = [=](TextWithEntities text) { - const auto label = translated->entity(); - label->setMarkedText( - text, - Core::TextContext({ .session = &peer->session() })); - translated->show(anim::type::instant); - loading->hide(anim::type::instant); - }; - - const auto send = [=](LanguageId to) { - loading->show(anim::type::instant); - translated->hide(anim::type::instant); - state->api.request(MTPmessages_TranslateText( - MTP_flags(flags), - msgId ? peer->input() : MTP_inputPeerEmpty(), - (msgId - ? MTP_vector(1, MTP_int(msgId)) - : MTPVector()), - (msgId - ? MTPVector() - : MTP_vector(1, MTP_textWithEntities( - MTP_string(text.text), - Api::EntitiesToMTP( - &peer->session(), - text.entities, - Api::ConvertOption::SkipLocal)))), - MTP_string(to.twoLetterCode()) - )).done([=](const MTPmessages_TranslatedText &result) { - const auto &data = result.data(); - const auto &list = data.vresult().v; - if (list.isEmpty()) { - showText( - tr::italic(tr::lng_translate_box_error(tr::now))); - } else { - showText(Api::ParseTextWithEntities( - &peer->session(), - list.front())); - } - }).fail([=](const MTP::Error &error) { - showText( - tr::italic(tr::lng_translate_box_error(tr::now))); - }).send(); - }; - state->to.value() | rpl::on_next(send, box->lifetime()); - - box->addLeftButton(tr::lng_settings_language(), [=] { - if (loading->toggled()) { - return; - } - box->uiShow()->showBox(ChooseTranslateToBox( - state->to.current(), - crl::guard(box, [=](LanguageId id) { state->to = id; }))); + TranslateBoxContent(box, { + .text = std::move(text), + .hasCopyRestriction = hasCopyRestriction, + .textContext = Core::TextContext({ .session = &peer->session() }), + .to = state->to.value(), + .chooseTo = [=] { + box->uiShow()->showBox(ChooseTranslateToBox( + state->to.current(), + crl::guard(box, [=](LanguageId id) { state->to = id; }))); + }, + .request = [=](LanguageId to, Fn)> done) { + const auto callback = std::make_shared< + Fn)>>(std::move(done)); + state->api.request(MTPmessages_TranslateText( + MTP_flags(flags), + msgId ? peer->input() : MTP_inputPeerEmpty(), + (msgId + ? MTP_vector(1, MTP_int(msgId)) + : MTPVector()), + (msgId + ? MTPVector() + : MTP_vector(1, MTP_textWithEntities( + MTP_string(requestText.text), + Api::EntitiesToMTP( + &peer->session(), + requestText.entities, + Api::ConvertOption::SkipLocal)))), + MTP_string(to.twoLetterCode()) + )).done([=](const MTPmessages_TranslatedText &result) { + const auto &data = result.data(); + const auto &list = data.vresult().v; + (*callback)(list.isEmpty() + ? std::optional() + : std::optional( + Api::ParseTextWithEntities(&peer->session(), list.front()))); + }).fail([=](const MTP::Error &) { + (*callback)(std::nullopt); + }).send(); + }, }); } diff --git a/Telegram/SourceFiles/boxes/translate_box_content.cpp b/Telegram/SourceFiles/boxes/translate_box_content.cpp new file mode 100644 index 0000000000..c467ad759c --- /dev/null +++ b/Telegram/SourceFiles/boxes/translate_box_content.cpp @@ -0,0 +1,212 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "boxes/translate_box_content.h" + +#include "lang/lang_keys.h" +#include "ui/boxes/choose_language_box.h" +#include "ui/effects/loading_element.h" +#include "ui/layers/generic_box.h" +#include "ui/painter.h" +#include "ui/power_saving.h" +#include "ui/vertical_list.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/fade_wrap.h" +#include "ui/wrap/slide_wrap.h" +#include "styles/style_boxes.h" +#include "styles/style_chat_helpers.h" +#include "styles/style_layers.h" + +namespace Ui { +namespace { + +class ShowButton final : public RpWidget { +public: + ShowButton(not_null parent); + + [[nodiscard]] rpl::producer clicks() const; + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + LinkButton _button; + +}; + +ShowButton::ShowButton(not_null parent) +: RpWidget(parent) +, _button(this, tr::lng_usernames_activate_confirm(tr::now)) { + _button.sizeValue( + ) | rpl::on_next([=](const QSize &s) { + resize( + s.width() + st::defaultEmojiSuggestions.fadeRight.width(), + s.height()); + _button.moveToRight(0, 0); + }, lifetime()); + _button.show(); +} + +void ShowButton::paintEvent(QPaintEvent *e) { + auto p = QPainter(this); + const auto clip = e->rect(); + + const auto &icon = st::defaultEmojiSuggestions.fadeRight; + const auto fade = QRect(0, 0, icon.width(), height()); + if (fade.intersects(clip)) { + icon.fill(p, fade); + } + const auto fill = clip.intersected( + { icon.width(), 0, width() - icon.width(), height() }); + if (!fill.isEmpty()) { + p.fillRect(fill, st::boxBg); + } +} + +rpl::producer ShowButton::clicks() const { + return _button.clicks(); +} + +} // namespace + +void TranslateBoxContent( + not_null box, + TranslateBoxContentArgs &&args) { + box->setWidth(st::boxWideWidth); + box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); }); + const auto container = box->verticalLayout(); + + const auto text = std::move(args.text); + const auto hasCopyRestriction = args.hasCopyRestriction; + const auto textContext = std::move(args.textContext); + const auto chooseTo = std::make_shared>(std::move(args.chooseTo)); + const auto request = std::make_shared< + Fn)>)>>( + std::move(args.request)); + + auto to = std::move(args.to) | rpl::start_spawning(box->lifetime()); + const auto toTitle = rpl::duplicate(to) | rpl::map(LanguageName); + const auto toDirection = rpl::duplicate(to) | rpl::map([=](LanguageId id) { + return id.locale().textDirection() == Qt::RightToLeft; + }); + + const auto &stLabel = st::aboutLabel; + const auto lineHeight = stLabel.style.lineHeight; + + Ui::AddSkip(container); + + const auto animationsPaused = [] { + using Which = FlatLabel::WhichAnimationsPaused; + const auto emoji = On(PowerSaving::kEmojiChat); + const auto spoiler = On(PowerSaving::kChatSpoiler); + return emoji + ? (spoiler ? Which::All : Which::CustomEmoji) + : (spoiler ? Which::Spoiler : Which::None); + }; + const auto original = box->addRow(object_ptr>( + box, + object_ptr(box, stLabel))); + { + if (hasCopyRestriction) { + original->entity()->setContextMenuHook([](auto&&) { + }); + } + original->entity()->setAnimationsPausedCallback(animationsPaused); + original->entity()->setMarkedText(text, textContext); + original->setMinimalHeight(lineHeight); + original->hide(anim::type::instant); + + const auto show = Ui::CreateChild>( + container.get(), + object_ptr(container)); + show->hide(anim::type::instant); + rpl::combine( + container->widthValue(), + original->geometryValue() + ) | rpl::on_next([=](int width, const QRect &rect) { + show->moveToLeft( + width - show->width() - st::boxRowPadding.right(), + rect.y() + std::abs(lineHeight - show->height()) / 2); + }, show->lifetime()); + original->entity()->heightValue( + ) | rpl::filter([](int height) { + return height > 0; + }) | rpl::take(1) | rpl::on_next([=](int height) { + if (height > lineHeight) { + show->show(anim::type::instant); + } + }, show->lifetime()); + show->toggleOn(show->entity()->clicks() | rpl::map_to(false)); + original->toggleOn(show->entity()->clicks() | rpl::map_to(true)); + } + Ui::AddSkip(container); + Ui::AddSkip(container); + Ui::AddDivider(container); + Ui::AddSkip(container); + + { + const auto padding = st::defaultSubsectionTitlePadding; + const auto subtitle = Ui::AddSubsectionTitle(container, std::move(toTitle)); + + rpl::duplicate(to) | rpl::on_next([=] { + subtitle->resizeToWidth(container->width() + - padding.left() + - padding.right()); + }, subtitle->lifetime()); + } + + const auto translated = box->addRow(object_ptr>( + box, + object_ptr(box, stLabel))); + translated->entity()->setSelectable(!hasCopyRestriction); + translated->entity()->setAnimationsPausedCallback(animationsPaused); + + constexpr auto kMaxLines = 3; + container->resizeToWidth(box->width()); + const auto loading = box->addRow(object_ptr>( + box, + CreateLoadingTextWidget( + box, + st::aboutLabel.style, + std::min(original->entity()->height() / lineHeight, kMaxLines), + std::move(toDirection)))); + + struct State { + int requestId = 0; + }; + const auto state = box->lifetime().make_state(); + + const auto showText = [=](std::optional translatedText) { + auto value = translatedText.value_or( + tr::italic(tr::lng_translate_box_error(tr::now))); + translated->entity()->setMarkedText(value, textContext); + translated->show(anim::type::instant); + loading->hide(anim::type::instant); + }; + const auto send = [=](LanguageId id) { + const auto requestId = ++state->requestId; + loading->show(anim::type::instant); + translated->hide(anim::type::instant); + (*request)(id, [=](std::optional translatedText) { + if (state->requestId != requestId) { + return; + } + showText(std::move(translatedText)); + }); + }; + std::move(to) | rpl::on_next(send, box->lifetime()); + + box->addLeftButton(tr::lng_settings_language(), [=] { + if (loading->toggled()) { + return; + } + (*chooseTo)(); + }); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/boxes/translate_box_content.h b/Telegram/SourceFiles/boxes/translate_box_content.h new file mode 100644 index 0000000000..e2d982bd8b --- /dev/null +++ b/Telegram/SourceFiles/boxes/translate_box_content.h @@ -0,0 +1,33 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "spellcheck/platform/platform_language.h" + +namespace Ui::Text { +struct MarkedContext; +} // namespace Ui::Text + +namespace Ui { + +class GenericBox; + +struct TranslateBoxContentArgs { + TextWithEntities text; + bool hasCopyRestriction = false; + Text::MarkedContext textContext; + rpl::producer to; + Fn chooseTo; + Fn)>)> request; +}; + +void TranslateBoxContent( + not_null box, + TranslateBoxContentArgs &&args); + +} // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 2aa6bbc804..9ee7210042 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -65,6 +65,8 @@ PRIVATE boxes/url_auth_box_content.cpp boxes/url_auth_box_content.h + boxes/translate_box_content.cpp + boxes/translate_box_content.h calls/group/ui/calls_group_recording_box.cpp calls/group/ui/calls_group_recording_box.h