mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Split translate box to api and td_ui parts.
This commit is contained in:
@@ -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 <QLocale>
|
||||
#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<Ui::RpWidget*> parent);
|
||||
|
||||
[[nodiscard]] rpl::producer<Qt::MouseButton> clicks() const;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
LinkButton _button;
|
||||
|
||||
};
|
||||
|
||||
ShowButton::ShowButton(not_null<Ui::RpWidget*> 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<Qt::MouseButton> 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<Main::Session*> 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<SlideWrap<FlatLabel>>(
|
||||
box,
|
||||
object_ptr<FlatLabel>(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<FadeWrap<ShowButton>>(
|
||||
container.get(),
|
||||
object_ptr<ShowButton>(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<SlideWrap<FlatLabel>>(
|
||||
box,
|
||||
object_ptr<FlatLabel>(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<SlideWrap<RpWidget>>(
|
||||
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<MTPint>(1, MTP_int(msgId))
|
||||
: MTPVector<MTPint>()),
|
||||
(msgId
|
||||
? MTPVector<MTPTextWithEntities>()
|
||||
: MTP_vector<MTPTextWithEntities>(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<void(std::optional<TextWithEntities>)> done) {
|
||||
const auto callback = std::make_shared<
|
||||
Fn<void(std::optional<TextWithEntities>)>>(std::move(done));
|
||||
state->api.request(MTPmessages_TranslateText(
|
||||
MTP_flags(flags),
|
||||
msgId ? peer->input() : MTP_inputPeerEmpty(),
|
||||
(msgId
|
||||
? MTP_vector<MTPint>(1, MTP_int(msgId))
|
||||
: MTPVector<MTPint>()),
|
||||
(msgId
|
||||
? MTPVector<MTPTextWithEntities>()
|
||||
: MTP_vector<MTPTextWithEntities>(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<TextWithEntities>()
|
||||
: std::optional<TextWithEntities>(
|
||||
Api::ParseTextWithEntities(&peer->session(), list.front())));
|
||||
}).fail([=](const MTP::Error &) {
|
||||
(*callback)(std::nullopt);
|
||||
}).send();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Ui::RpWidget*> parent);
|
||||
|
||||
[[nodiscard]] rpl::producer<Qt::MouseButton> clicks() const;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
LinkButton _button;
|
||||
|
||||
};
|
||||
|
||||
ShowButton::ShowButton(not_null<Ui::RpWidget*> 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<Qt::MouseButton> ShowButton::clicks() const {
|
||||
return _button.clicks();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void TranslateBoxContent(
|
||||
not_null<GenericBox*> 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<Fn<void()>>(std::move(args.chooseTo));
|
||||
const auto request = std::make_shared<
|
||||
Fn<void(LanguageId, Fn<void(std::optional<TextWithEntities>)>)>>(
|
||||
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<SlideWrap<FlatLabel>>(
|
||||
box,
|
||||
object_ptr<FlatLabel>(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<FadeWrap<ShowButton>>(
|
||||
container.get(),
|
||||
object_ptr<ShowButton>(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<SlideWrap<FlatLabel>>(
|
||||
box,
|
||||
object_ptr<FlatLabel>(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<SlideWrap<RpWidget>>(
|
||||
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<State>();
|
||||
|
||||
const auto showText = [=](std::optional<TextWithEntities> 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<TextWithEntities> 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
|
||||
@@ -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<LanguageId> to;
|
||||
Fn<void()> chooseTo;
|
||||
Fn<void(LanguageId, Fn<void(std::optional<TextWithEntities>)>)> request;
|
||||
};
|
||||
|
||||
void TranslateBoxContent(
|
||||
not_null<GenericBox*> box,
|
||||
TranslateBoxContentArgs &&args);
|
||||
|
||||
} // namespace Ui
|
||||
Reference in New Issue
Block a user