| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171 |
- /*
- 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/background_preview_box.h"
- #include "base/unixtime.h"
- #include "boxes/peers/edit_peer_color_box.h"
- #include "boxes/premium_preview_box.h"
- #include "lang/lang_keys.h"
- #include "mainwidget.h"
- #include "window/themes/window_theme.h"
- #include "ui/boxes/confirm_box.h"
- #include "ui/boxes/boost_box.h"
- #include "ui/controls/chat_service_checkbox.h"
- #include "ui/chat/chat_theme.h"
- #include "ui/chat/chat_style.h"
- #include "ui/toast/toast.h"
- #include "ui/image/image.h"
- #include "ui/widgets/checkbox.h"
- #include "ui/widgets/continuous_sliders.h"
- #include "ui/wrap/fade_wrap.h"
- #include "ui/wrap/slide_wrap.h"
- #include "ui/painter.h"
- #include "ui/vertical_list.h"
- #include "ui/ui_utility.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "history/history_item_helpers.h"
- #include "history/view/history_view_message.h"
- #include "main/main_session.h"
- #include "apiwrap.h"
- #include "data/data_session.h"
- #include "data/data_user.h"
- #include "data/data_document.h"
- #include "data/data_document_media.h"
- #include "data/data_document_resolver.h"
- #include "data/data_file_origin.h"
- #include "data/data_peer_values.h"
- #include "data/data_premium_limits.h"
- #include "settings/settings_premium.h"
- #include "storage/file_upload.h"
- #include "storage/localimageloader.h"
- #include "window/window_session_controller.h"
- #include "window/themes/window_themes_embedded.h"
- #include "styles/style_chat.h"
- #include "styles/style_layers.h"
- #include "styles/style_boxes.h"
- #include <QtGui/QClipboard>
- #include <QtGui/QGuiApplication>
- namespace {
- constexpr auto kMaxWallPaperSlugLength = 255;
- [[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
- if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
- return false;
- }
- return ranges::none_of(slug, [](QChar ch) {
- return (ch != '.')
- && (ch != '_')
- && (ch != '-')
- && (ch < '0' || ch > '9')
- && (ch < 'a' || ch > 'z')
- && (ch < 'A' || ch > 'Z');
- });
- }
- [[nodiscard]] AdminLog::OwnedItem GenerateServiceItem(
- not_null<HistoryView::ElementDelegate*> delegate,
- not_null<History*> history,
- const QString &text,
- bool out) {
- Expects(history->peer->isUser());
- const auto flags = MessageFlag::FakeHistoryItem
- | MessageFlag::HasFromId
- | (out ? MessageFlag::Outgoing : MessageFlag(0));
- const auto item = history->makeMessage({
- .id = history->owner().nextLocalMessageId(),
- .flags = flags,
- .date = base::unixtime::now(),
- }, PreparedServiceText{ { text } });
- return AdminLog::OwnedItem(delegate, item);
- }
- [[nodiscard]] AdminLog::OwnedItem GenerateTextItem(
- not_null<HistoryView::ElementDelegate*> delegate,
- not_null<History*> history,
- const QString &text,
- bool out) {
- Expects(history->peer->isUser());
- const auto item = history->makeMessage({
- .id = history->nextNonHistoryEntryId(),
- .flags = (MessageFlag::FakeHistoryItem
- | MessageFlag::HasFromId
- | (out ? MessageFlag::Outgoing : MessageFlag(0))),
- .from = (out
- ? history->session().userId()
- : peerToUser(history->peer->id)),
- .date = base::unixtime::now(),
- }, TextWithEntities{ text }, MTP_messageMediaEmpty());
- return AdminLog::OwnedItem(delegate, item);
- }
- [[nodiscard]] QImage PrepareScaledNonPattern(
- const QImage &image,
- Images::Option blur) {
- const auto size = st::boxWideWidth;
- const auto width = std::max(image.width(), 1);
- const auto height = std::max(image.height(), 1);
- const auto takeWidth = (width > height)
- ? (width * size / height)
- : size;
- const auto takeHeight = (width > height)
- ? size
- : (height * size / width);
- const auto ratio = style::DevicePixelRatio();
- return Images::Prepare(image, QSize(takeWidth, takeHeight) * ratio, {
- .options = Images::Option::TransparentBackground | blur,
- .outer = { size, size },
- });
- }
- [[nodiscard]] QImage PrepareScaledFromFull(
- const QImage &image,
- bool isPattern,
- const std::vector<QColor> &background,
- int gradientRotation,
- float64 patternOpacity,
- Images::Option blur = Images::Option(0)) {
- auto result = PrepareScaledNonPattern(image, blur);
- if (isPattern) {
- result = Ui::PreparePatternImage(
- std::move(result),
- background,
- gradientRotation,
- patternOpacity);
- }
- return std::move(result).convertToFormat(
- QImage::Format_ARGB32_Premultiplied);
- }
- [[nodiscard]] QImage BlackImage(QSize size) {
- auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
- result.fill(Qt::black);
- return result;
- }
- [[nodiscard]] Data::WallPaper Resolve(
- not_null<Main::Session*> session,
- const Data::WallPaper &paper,
- bool dark) {
- if (paper.emojiId().isEmpty()) {
- return paper;
- }
- const auto &themes = session->data().cloudThemes();
- if (const auto theme = themes.themeForEmoji(paper.emojiId())) {
- using Type = Data::CloudThemeType;
- const auto type = dark ? Type::Dark : Type::Light;
- const auto i = theme->settings.find(type);
- if (i != end(theme->settings) && i->second.paper) {
- return *i->second.paper;
- }
- }
- return paper;
- }
- } // namespace
- struct BackgroundPreviewBox::OverridenStyle {
- style::Box box;
- style::IconButton toggle;
- style::MediaSlider slider;
- style::FlatLabel subtitle;
- };
- BackgroundPreviewBox::BackgroundPreviewBox(
- QWidget*,
- not_null<Window::SessionController*> controller,
- const Data::WallPaper &paper,
- BackgroundPreviewArgs args)
- : SimpleElementDelegate(controller, [=] { update(); })
- , _controller(controller)
- , _forPeer(args.forPeer)
- , _fromMessageId(args.fromMessageId)
- , _chatStyle(std::make_unique<Ui::ChatStyle>(
- controller->session().colorIndicesValue()))
- , _serviceHistory(_controller->session().data().history(
- PeerData::kServiceNotificationsId))
- , _service(nullptr)
- , _text1(GenerateTextItem(
- delegate(),
- _serviceHistory,
- (_forPeer
- ? tr::lng_background_apply1(tr::now)
- : tr::lng_background_text1(tr::now)),
- false))
- , _text2(GenerateTextItem(
- delegate(),
- _serviceHistory,
- (_forPeer
- ? tr::lng_background_apply2(tr::now)
- : tr::lng_background_text2(tr::now)),
- true))
- , _paperEmojiId(paper.emojiId())
- , _paper(
- Resolve(&controller->session(), paper, Window::Theme::IsNightMode()))
- , _media(_paper.document() ? _paper.document()->createMediaView() : nullptr)
- , _radial([=](crl::time now) { radialAnimationCallback(now); })
- , _appNightMode(Window::Theme::IsNightModeValue())
- , _boxDarkMode(_appNightMode.current())
- , _dimmingIntensity(std::clamp(_paper.patternIntensity(), 0, 100))
- , _dimmed(_forPeer
- && (_paper.document() || _paper.localThumbnail())
- && !_paper.isPattern()) {
- if (_media) {
- _media->thumbnailWanted(_paper.fileOrigin());
- }
- generateBackground();
- _controller->session().downloaderTaskFinished(
- ) | rpl::start_with_next([=] {
- update();
- }, lifetime());
- _appNightMode.changes(
- ) | rpl::start_with_next([=](bool night) {
- _boxDarkMode = night;
- update();
- }, lifetime());
- _boxDarkMode.changes(
- ) | rpl::start_with_next([=](bool dark) {
- applyDarkMode(dark);
- }, lifetime());
- const auto prepare = [=](bool dark, auto pointer) {
- const auto weak = Ui::MakeWeak(this);
- crl::async([=] {
- auto result = std::make_unique<style::palette>();
- Window::Theme::PreparePaletteCallback(dark, {})(*result);
- crl::on_main([=, result = std::move(result)]() mutable {
- if (const auto strong = weak.data()) {
- strong->*pointer = std::move(result);
- strong->paletteReady();
- }
- });
- });
- };
- prepare(false, &BackgroundPreviewBox::_lightPalette);
- prepare(true, &BackgroundPreviewBox::_darkPalette);
- }
- BackgroundPreviewBox::~BackgroundPreviewBox() = default;
- void BackgroundPreviewBox::recreate(bool dark) {
- _paper = Resolve(
- &_controller->session(),
- Data::WallPaper::FromEmojiId(_paperEmojiId),
- dark);
- _media = _paper.document()
- ? _paper.document()->createMediaView()
- : nullptr;
- if (_media) {
- _media->thumbnailWanted(_paper.fileOrigin());
- }
- _full = QImage();
- _generated = _scaled = _blurred = _fadeOutThumbnail = QPixmap();
- _generating = {};
- generateBackground();
- _paper.loadDocument();
- if (const auto document = _paper.document()) {
- if (document->loading()) {
- _radial.start(_media->progress());
- }
- }
- checkLoadedDocument();
- updateServiceBg(_paper.backgroundColors());
- update();
- }
- void BackgroundPreviewBox::applyDarkMode(bool dark) {
- if (!_paperEmojiId.isEmpty()) {
- recreate(dark);
- }
- const auto equals = (dark == Window::Theme::IsNightMode());
- const auto &palette = (dark ? _darkPalette : _lightPalette);
- if (!equals && !palette) {
- _waitingForPalette = true;
- return;
- }
- _waitingForPalette = false;
- if (equals) {
- setStyle(st::defaultBox);
- _chatStyle->applyCustomPalette(nullptr);
- _paletteServiceBg = rpl::single(
- rpl::empty
- ) | rpl::then(
- style::PaletteChanged()
- ) | rpl::map([=] {
- return st::msgServiceBg->c;
- });
- } else {
- setStyle(overridenStyle(dark));
- _chatStyle->applyCustomPalette(palette.get());
- _paletteServiceBg = palette->msgServiceBg()->c;
- }
- resetTitle();
- rebuildButtons(dark);
- update();
- if (const auto parent = parentWidget()) {
- parent->update();
- }
- if (_dimmed) {
- createDimmingSlider(dark);
- }
- }
- void BackgroundPreviewBox::createDimmingSlider(bool dark) {
- const auto created = !_dimmingWrap;
- if (created) {
- _dimmingWrap.create(this, object_ptr<Ui::RpWidget>(this));
- _dimmingContent = _dimmingWrap->entity();
- }
- _dimmingSlider = nullptr;
- for (const auto &child : _dimmingContent->children()) {
- if (child->isWidgetType()) {
- static_cast<QWidget*>(child)->hide();
- child->deleteLater();
- }
- }
- const auto equals = (dark == Window::Theme::IsNightMode());
- const auto inner = Ui::CreateChild<Ui::VerticalLayout>(_dimmingContent);
- inner->show();
- Ui::AddSubsectionTitle(
- inner,
- tr::lng_background_dimming(),
- style::margins(0, st::defaultVerticalListSkip, 0, 0),
- equals ? nullptr : dark ? &_dark->subtitle : &_light->subtitle);
- _dimmingSlider = inner->add(
- object_ptr<Ui::MediaSlider>(
- inner,
- (equals
- ? st::defaultContinuousSlider
- : dark
- ? _dark->slider
- : _light->slider)),
- st::localStorageLimitMargin);
- _dimmingSlider->setValue(_dimmingIntensity / 100.);
- _dimmingSlider->setAlwaysDisplayMarker(true);
- _dimmingSlider->resize(st::defaultContinuousSlider.seekSize);
- const auto handle = [=](float64 value) {
- const auto intensity = std::clamp(
- int(base::SafeRound(value * 100)),
- 0,
- 100);
- _paper = _paper.withPatternIntensity(intensity);
- _dimmingIntensity = intensity;
- update();
- };
- _dimmingSlider->setChangeProgressCallback(handle);
- _dimmingSlider->setChangeFinishedCallback(handle);
- inner->resizeToWidth(st::boxWideWidth);
- Ui::SendPendingMoveResizeEvents(inner);
- inner->move(0, 0);
- _dimmingContent->resize(inner->size());
- _dimmingContent->paintRequest(
- ) | rpl::start_with_next([=](QRect clip) {
- auto p = QPainter(_dimmingContent);
- const auto palette = (dark ? _darkPalette : _lightPalette).get();
- p.fillRect(clip, equals ? st::boxBg : palette->boxBg());
- }, _dimmingContent->lifetime());
- _dimmingToggleScheduled = true;
- if (created) {
- rpl::combine(
- heightValue(),
- _dimmingWrap->heightValue(),
- rpl::mappers::_1 - rpl::mappers::_2
- ) | rpl::start_with_next([=](int top) {
- _dimmingWrap->move(0, top);
- }, _dimmingWrap->lifetime());
- _dimmingWrap->toggle(dark, anim::type::instant);
- _dimmingHeight = _dimmingWrap->heightValue();
- _dimmingHeight.changes() | rpl::start_with_next([=] {
- update();
- }, _dimmingWrap->lifetime());
- }
- }
- void BackgroundPreviewBox::paletteReady() {
- if (_waitingForPalette) {
- applyDarkMode(_boxDarkMode.current());
- }
- }
- const style::Box &BackgroundPreviewBox::overridenStyle(bool dark) {
- auto &st = dark ? _dark : _light;
- if (!st) {
- st = std::make_unique<OverridenStyle>(prepareOverridenStyle(dark));
- }
- return st->box;
- }
- auto BackgroundPreviewBox::prepareOverridenStyle(bool dark)
- -> OverridenStyle {
- const auto p = (dark ? _darkPalette : _lightPalette).get();
- Assert(p != nullptr);
- const auto &toggle = dark
- ? st::backgroundSwitchToLight
- : st::backgroundSwitchToDark;
- auto result = OverridenStyle{
- .box = st::defaultBox,
- .toggle = toggle,
- .slider = st::defaultContinuousSlider,
- .subtitle = st::defaultSubsectionTitle,
- };
- result.box.button.textFg = p->lightButtonFg();
- result.box.button.textFgOver = p->lightButtonFgOver();
- result.box.button.numbersTextFg = p->lightButtonFg();
- result.box.button.numbersTextFgOver = p->lightButtonFgOver();
- result.box.button.textBg = p->lightButtonBg();
- result.box.button.textBgOver = p->lightButtonBgOver();
- result.box.button.ripple.color = p->lightButtonBgRipple();
- result.box.title.textFg = p->boxTitleFg();
- result.box.bg = p->boxBg();
- result.box.titleAdditionalFg = p->boxTitleAdditionalFg();
- result.toggle.ripple.color = p->windowBgOver();
- result.toggle.icon = toggle.icon.withPalette(*p);
- result.toggle.iconOver = toggle.iconOver.withPalette(*p);
- result.slider.activeFg = p->mediaPlayerActiveFg();
- result.slider.inactiveFg = p->mediaPlayerInactiveFg();
- result.slider.activeFgOver = p->mediaPlayerActiveFg();
- result.slider.inactiveFgOver = p->mediaPlayerInactiveFg();
- result.slider.activeFgDisabled = p->mediaPlayerInactiveFg();
- result.slider.inactiveFgDisabled = p->windowBg();
- result.slider.receivedTillFg = p->mediaPlayerInactiveFg();
- result.subtitle.textFg = p->windowActiveTextFg();
- return result;
- }
- bool BackgroundPreviewBox::forChannel() const {
- return _forPeer && _forPeer->isChannel();
- }
- bool BackgroundPreviewBox::forGroup() const {
- return forChannel() && _forPeer->isMegagroup();
- }
- void BackgroundPreviewBox::generateBackground() {
- if (_paper.backgroundColors().empty()) {
- return;
- }
- const auto size = QSize(st::boxWideWidth, st::boxWideWidth)
- * style::DevicePixelRatio();
- _generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.)
- ? Ui::GenerateBackgroundImage(
- size,
- _paper.backgroundColors(),
- _paper.gradientRotation())
- : BlackImage(size));
- _generated.setDevicePixelRatio(style::DevicePixelRatio());
- }
- not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
- return static_cast<HistoryView::ElementDelegate*>(this);
- }
- void BackgroundPreviewBox::resetTitle() {
- setTitle(tr::lng_background_header());
- }
- void BackgroundPreviewBox::rebuildButtons(bool dark) {
- clearButtons();
- addButton(forGroup()
- ? tr::lng_background_apply_group()
- : forChannel()
- ? tr::lng_background_apply_channel()
- : _forPeer
- ? tr::lng_background_apply_button()
- : tr::lng_settings_apply(), [=] { apply(); });
- addButton(tr::lng_cancel(), [=] { closeBox(); });
- if (!_forPeer && _paper.hasShareUrl()) {
- addLeftButton(tr::lng_background_share(), [=] { share(); });
- }
- const auto equals = (dark == Window::Theme::IsNightMode());
- auto toggle = object_ptr<Ui::IconButton>(this, equals
- ? (dark ? st::backgroundSwitchToLight : st::backgroundSwitchToDark)
- : dark ? _dark->toggle : _light->toggle);
- toggle->setClickedCallback([=] {
- _boxDarkMode = !_boxDarkMode.current();
- });
- addTopButton(std::move(toggle));
- }
- void BackgroundPreviewBox::prepare() {
- applyDarkMode(Window::Theme::IsNightMode());
- _paper.loadDocument();
- if (const auto document = _paper.document()) {
- if (document->loading()) {
- _radial.start(_media->progress());
- }
- }
- updateServiceBg(_paper.backgroundColors());
- setScaledFromThumb();
- checkLoadedDocument();
- _text1->setDisplayDate(false);
- _text1->initDimensions();
- _text1->resizeGetHeight(st::boxWideWidth);
- _text2->initDimensions();
- _text2->resizeGetHeight(st::boxWideWidth);
- setDimensions(st::boxWideWidth, st::boxWideWidth);
- }
- void BackgroundPreviewBox::recreateBlurCheckbox() {
- const auto document = _paper.document();
- if (_paper.isPattern()
- || (!_paper.localThumbnail()
- && (!document || !document->hasThumbnail()))) {
- return;
- }
- const auto blurred = _blur ? _blur->checked() : _paper.isBlurred();
- _blur = Ui::MakeChatServiceCheckbox(
- this,
- tr::lng_background_blur(tr::now),
- st::backgroundCheckbox,
- st::backgroundCheck,
- blurred,
- [=] { return _serviceBg.value_or(QColor(255, 255, 255, 0)); });
- _blur->show();
- rpl::combine(
- sizeValue(),
- _blur->sizeValue(),
- _dimmingHeight.value()
- ) | rpl::start_with_next([=](QSize outer, QSize inner, int dimming) {
- const auto bottom = st::historyPaddingBottom;
- _blur->move(
- (outer.width() - inner.width()) / 2,
- outer.height() - dimming - bottom - inner.height());
- }, _blur->lifetime());
- _blur->checkedChanges(
- ) | rpl::start_with_next([=](bool checked) {
- checkBlurAnimationStart();
- update();
- }, _blur->lifetime());
- _blur->setDisabled(_paper.document() && _full.isNull());
- if (_forBothOverlay) {
- _forBothOverlay->raise();
- }
- }
- void BackgroundPreviewBox::apply() {
- if (_forPeer) {
- applyForPeer();
- } else {
- applyForEveryone();
- }
- }
- void BackgroundPreviewBox::uploadForPeer(bool both) {
- Expects(_forPeer != nullptr);
- if (_uploadId) {
- return;
- }
- const auto session = &_controller->session();
- const auto ready = Window::Theme::PrepareWallPaper(
- session->mainDcId(),
- _paper.localThumbnail()->original());
- const auto documentId = ready->id;
- _uploadId = FullMsgId(
- session->userPeerId(),
- session->data().nextLocalMessageId());
- session->uploader().upload(_uploadId, ready);
- if (_uploadLifetime) {
- return;
- }
- const auto document = session->data().document(documentId);
- document->uploadingData = std::make_unique<Data::UploadState>(
- document->size);
- session->uploader().documentProgress(
- ) | rpl::start_with_next([=](const FullMsgId &fullId) {
- if (fullId != _uploadId) {
- return;
- }
- _uploadProgress = document->uploading()
- ? ((document->uploadingData->offset * 100)
- / document->uploadingData->size)
- : 0.;
- update(radialRect());
- }, _uploadLifetime);
- session->uploader().documentReady(
- ) | rpl::start_with_next([=](const Storage::UploadedMedia &data) {
- if (data.fullId != _uploadId) {
- return;
- }
- _uploadProgress = 1.;
- _uploadLifetime.destroy();
- update(radialRect());
- session->api().request(MTPaccount_UploadWallPaper(
- MTP_flags(MTPaccount_UploadWallPaper::Flag::f_for_chat),
- data.info.file,
- MTP_string("image/jpeg"),
- _paper.mtpSettings()
- )).done([=](const MTPWallPaper &result) {
- result.match([&](const MTPDwallPaper &data) {
- session->data().documentConvert(
- session->data().document(documentId),
- data.vdocument());
- }, [&](const MTPDwallPaperNoFile &data) {
- LOG(("API Error: "
- "Got wallPaperNoFile after account.UploadWallPaper."));
- });
- if (const auto paper = Data::WallPaper::Create(session, result)) {
- setExistingForPeer(*paper, both);
- }
- }).send();
- }, _uploadLifetime);
- _uploadProgress = 0.;
- _radial.start(_uploadProgress);
- }
- void BackgroundPreviewBox::setExistingForPeer(
- const Data::WallPaper &paper,
- bool both) {
- Expects(_forPeer != nullptr);
- if (const auto already = _forPeer->wallPaper()) {
- if (already->equals(paper)) {
- _controller->finishChatThemeEdit(_forPeer);
- return;
- }
- }
- const auto api = &_controller->session().api();
- using Flag = MTPmessages_SetChatWallPaper::Flag;
- api->request(MTPmessages_SetChatWallPaper(
- MTP_flags((_fromMessageId ? Flag::f_id : Flag())
- | (_fromMessageId ? Flag() : Flag::f_wallpaper)
- | (both ? Flag::f_for_both : Flag())
- | Flag::f_settings),
- _forPeer->input,
- paper.mtpInput(&_controller->session()),
- paper.mtpSettings(),
- MTP_int(_fromMessageId.msg)
- )).done([=](const MTPUpdates &result) {
- api->applyUpdates(result);
- }).send();
- _forPeer->setWallPaper(paper);
- _controller->finishChatThemeEdit(_forPeer);
- }
- void BackgroundPreviewBox::checkLevelForChannel() {
- Expects(forChannel());
- const auto show = _controller->uiShow();
- _forPeerLevelCheck = true;
- const auto weak = Ui::MakeWeak(this);
- CheckBoostLevel(show, _forPeer, [=](int level) {
- if (!weak) {
- return std::optional<Ui::AskBoostReason>();
- }
- const auto limits = Data::LevelLimits(&_forPeer->session());
- const auto required = _paperEmojiId.isEmpty()
- ? limits.channelCustomWallpaperLevelMin()
- : limits.channelWallpaperLevelMin();
- if (level >= required) {
- applyForPeer(false);
- return std::optional<Ui::AskBoostReason>();
- }
- return std::make_optional(Ui::AskBoostReason{
- Ui::AskBoostWallpaper{ required, _forPeer->isMegagroup()}
- });
- }, [=] { _forPeerLevelCheck = false; });
- }
- void BackgroundPreviewBox::applyForPeer() {
- Expects(_forPeer != nullptr);
- if (!Data::IsCustomWallPaper(_paper)) {
- if (const auto already = _forPeer->wallPaper()) {
- if (already->equals(_paper)) {
- _controller->finishChatThemeEdit(_forPeer);
- return;
- }
- }
- }
- if (forChannel()) {
- checkLevelForChannel();
- return;
- } else if (_fromMessageId || !_forPeer->session().premiumPossible()) {
- applyForPeer(false);
- return;
- } else if (_forBothOverlay) {
- return;
- }
- const auto size = this->size() * style::DevicePixelRatio();
- const auto bg = Images::DitherImage(
- Images::BlurLargeImage(
- Ui::GrabWidgetToImage(this).scaled(
- size / style::ConvertScale(4),
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation),
- 24).scaled(
- size,
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation));
- _forBothOverlay = std::make_unique<Ui::FadeWrap<>>(
- this,
- object_ptr<Ui::RpWidget>(this));
- const auto overlay = _forBothOverlay->entity();
- sizeValue() | rpl::start_with_next([=](QSize size) {
- _forBothOverlay->setGeometry({ QPoint(), size });
- overlay->setGeometry({ QPoint(), size });
- }, _forBothOverlay->lifetime());
- overlay->paintRequest(
- ) | rpl::start_with_next([=](QRect clip) {
- auto p = QPainter(overlay);
- p.drawImage(0, 0, bg);
- p.fillRect(clip, QColor(0, 0, 0, 64));
- }, overlay->lifetime());
- using namespace Ui;
- const auto forMe = CreateChild<RoundButton>(
- overlay,
- tr::lng_background_apply_me(),
- st::backgroundConfirm);
- forMe->setClickedCallback([=] {
- applyForPeer(false);
- });
- using namespace rpl::mappers;
- const auto forBoth = ::Settings::CreateLockedButton(
- overlay,
- tr::lng_background_apply_both(
- lt_user,
- rpl::single(_forPeer->shortName())),
- st::backgroundConfirm,
- Data::AmPremiumValue(&_forPeer->session()) | rpl::map(!_1));
- forBoth->setClickedCallback([=] {
- if (_forPeer->session().premium()) {
- applyForPeer(true);
- } else {
- ShowPremiumPreviewBox(
- _controller->uiShow(),
- PremiumFeature::Wallpapers);
- }
- });
- const auto cancel = CreateChild<RoundButton>(
- overlay,
- tr::lng_cancel(),
- st::backgroundConfirmCancel);
- cancel->setClickedCallback([=] {
- const auto raw = _forBothOverlay.release();
- raw->shownValue() | rpl::filter(
- !rpl::mappers::_1
- ) | rpl::take(1) | rpl::start_with_next(crl::guard(raw, [=] {
- delete raw;
- }), raw->lifetime());
- raw->toggle(false, anim::type::normal);
- });
- forMe->setTextTransform(RoundButton::TextTransform::NoTransform);
- forBoth->setTextTransform(RoundButton::TextTransform::NoTransform);
- cancel->setTextTransform(RoundButton::TextTransform::NoTransform);
- overlay->sizeValue(
- ) | rpl::start_with_next([=](QSize size) {
- const auto padding = st::backgroundConfirmPadding;
- const auto width = size.width()
- - padding.left()
- - padding.right();
- const auto height = cancel->height();
- auto top = size.height() - padding.bottom() - height;
- cancel->setGeometry(padding.left(), top, width, height);
- top -= height + padding.top();
- forBoth->setGeometry(padding.left(), top, width, height);
- top -= height + padding.top();
- forMe->setGeometry(padding.left(), top, width, height);
- }, _forBothOverlay->lifetime());
- _forBothOverlay->hide(anim::type::instant);
- _forBothOverlay->show(anim::type::normal);
- }
- void BackgroundPreviewBox::applyForPeer(bool both) {
- using namespace Data;
- if (forChannel() && !_paperEmojiId.isEmpty()) {
- setExistingForPeer(WallPaper::FromEmojiId(_paperEmojiId), both);
- } else if (IsCustomWallPaper(_paper)) {
- uploadForPeer(both);
- } else {
- setExistingForPeer(_paper, both);
- }
- }
- void BackgroundPreviewBox::applyForEveryone() {
- const auto install = (_paper.id() != Window::Theme::Background()->id())
- && Data::IsCloudWallPaper(_paper);
- _controller->content()->setChatBackground(_paper, std::move(_full));
- if (install) {
- _controller->session().api().request(MTPaccount_InstallWallPaper(
- _paper.mtpInput(&_controller->session()),
- _paper.mtpSettings()
- )).send();
- }
- closeBox();
- }
- void BackgroundPreviewBox::share() {
- QGuiApplication::clipboard()->setText(
- _paper.shareUrl(&_controller->session()));
- showToast(tr::lng_background_link_copied(tr::now));
- }
- void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
- Painter p(this);
- const auto ms = crl::now();
- if (_scaled.isNull()) {
- setScaledFromThumb();
- }
- if (!_generated.isNull()
- && (_scaled.isNull()
- || (_fadeOutThumbnail.isNull() && _fadeIn.animating()))) {
- p.drawPixmap(0, 0, _generated);
- }
- if (!_scaled.isNull()) {
- paintImage(p);
- const auto dimming = (_dimmed && _boxDarkMode.current())
- ? _dimmingIntensity
- : 0;
- if (dimming > 0) {
- const auto alpha = 255 * dimming / 100;
- p.fillRect(e->rect(), QColor(0, 0, 0, alpha));
- }
- paintRadial(p);
- } else if (_generated.isNull()) {
- p.fillRect(e->rect(), st::boxBg);
- return;
- } else {
- // Progress of pattern loading.
- paintRadial(p);
- }
- paintTexts(p, ms);
- if (_dimmingToggleScheduled) {
- crl::on_main(this, [=] {
- if (!_dimmingToggleScheduled) {
- return;
- }
- _dimmingToggleScheduled = false;
- _dimmingWrap->toggle(_boxDarkMode.current(), anim::type::normal);
- });
- }
- }
- void BackgroundPreviewBox::paintImage(Painter &p) {
- Expects(!_scaled.isNull());
- const auto factor = style::DevicePixelRatio();
- const auto size = st::boxWideWidth;
- const auto from = QRect(
- 0,
- (size - height()) / 2 * factor,
- size * factor,
- height() * factor);
- const auto guard = gsl::finally([&] { p.setOpacity(1.); });
- const auto fade = _fadeIn.value(1.);
- if (fade < 1. && !_fadeOutThumbnail.isNull()) {
- p.drawPixmap(rect(), _fadeOutThumbnail, from);
- }
- const auto &pixmap = (!_blurred.isNull() && _paper.isBlurred())
- ? _blurred
- : _scaled;
- p.setOpacity(fade);
- p.drawPixmap(rect(), pixmap, from);
- checkBlurAnimationStart();
- }
- void BackgroundPreviewBox::paintRadial(Painter &p) {
- const auto radial = _radial.animating();
- const auto radialOpacity = radial ? _radial.opacity() : 0.;
- if (!radial) {
- return;
- }
- auto inner = radialRect();
- p.setPen(Qt::NoPen);
- p.setOpacity(radialOpacity);
- p.setBrush(st::radialBg);
- {
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(inner);
- }
- p.setOpacity(1);
- QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine)));
- _radial.draw(p, arc, st::radialLine, st::radialFg);
- }
- int BackgroundPreviewBox::textsTop() const {
- const auto bottom = _blur
- ? _blur->y()
- : (height() - _dimmingHeight.current());
- return bottom
- - st::historyPaddingBottom
- - (_service ? _service->height() : 0)
- - _text1->height()
- - (forChannel() ? 0 : _text2->height());
- }
- QRect BackgroundPreviewBox::radialRect() const {
- const auto available = textsTop() - st::historyPaddingBottom;
- return QRect(
- QPoint(
- (width() - st::radialSize.width()) / 2,
- (available - st::radialSize.height()) / 2),
- st::radialSize);
- }
- void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
- const auto heights = _service ? _service->height() : 0;
- const auto height1 = _text1->height();
- const auto height2 = _text2->height();
- auto context = _controller->defaultChatTheme()->preparePaintContext(
- _chatStyle.get(),
- rect(),
- rect(),
- _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer));
- p.translate(0, textsTop());
- if (_service) {
- _service->draw(p, context);
- p.translate(0, heights);
- }
- context.outbg = _text1->hasOutLayout();
- _text1->draw(p, context);
- p.translate(0, height1);
- if (!forChannel()) {
- context.outbg = _text2->hasOutLayout();
- _text2->draw(p, context);
- p.translate(0, height2);
- }
- }
- void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
- const auto document = _paper.document();
- const auto wasAnimating = _radial.animating();
- const auto updated = _uploadId
- ? _radial.update(_uploadProgress, !_uploadLifetime, now)
- : _radial.update(_media->progress(), !document->loading(), now);
- if ((wasAnimating || _radial.animating())
- && (!anim::Disabled() || updated)) {
- update(radialRect());
- }
- checkLoadedDocument();
- }
- void BackgroundPreviewBox::setScaledFromThumb() {
- if (!_scaled.isNull()) {
- return;
- }
- const auto localThumbnail = _paper.localThumbnail();
- const auto thumbnail = localThumbnail
- ? localThumbnail
- : _media
- ? _media->thumbnail()
- : nullptr;
- if (!thumbnail) {
- return;
- } else if (_paper.isPattern() && _paper.document() != nullptr) {
- return;
- }
- auto scaled = PrepareScaledFromFull(
- thumbnail->original(),
- _paper.isPattern(),
- _paper.backgroundColors(),
- _paper.gradientRotation(),
- _paper.patternOpacity(),
- _paper.document() ? Images::Option::Blur : Images::Option());
- auto blurred = (_paper.document() || _paper.isPattern())
- ? QImage()
- : PrepareScaledNonPattern(
- Ui::PrepareBlurredBackground(thumbnail->original()),
- Images::Option(0));
- setScaledFromImage(std::move(scaled), std::move(blurred));
- }
- void BackgroundPreviewBox::setScaledFromImage(
- QImage &&image,
- QImage &&blurred) {
- updateServiceBg({ Ui::CountAverageColor(image) });
- if (!_full.isNull()) {
- startFadeInFrom(std::move(_scaled));
- }
- _scaled = Ui::PixmapFromImage(std::move(image));
- _blurred = Ui::PixmapFromImage(std::move(blurred));
- if (_blur) {
- _blur->setDisabled(_paper.document() && _full.isNull());
- }
- }
- void BackgroundPreviewBox::startFadeInFrom(QPixmap previous) {
- _fadeOutThumbnail = std::move(previous);
- _fadeIn.start([=] { update(); }, 0., 1., st::backgroundCheck.duration);
- }
- void BackgroundPreviewBox::checkBlurAnimationStart() {
- if (_fadeIn.animating()
- || _blurred.isNull()
- || !_blur
- || _paper.isBlurred() == _blur->checked()) {
- return;
- }
- _paper = _paper.withBlurred(_blur->checked());
- startFadeInFrom(_paper.isBlurred() ? _scaled : _blurred);
- }
- void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
- const auto count = int(bg.size());
- if (!count) {
- return;
- }
- auto red = 0LL, green = 0LL, blue = 0LL;
- for (const auto &color : bg) {
- red += color.red();
- green += color.green();
- blue += color.blue();
- }
- _serviceBgLifetime = _paletteServiceBg.value(
- ) | rpl::start_with_next([=](QColor color) {
- _serviceBg = Ui::ThemeAdjustedColor(
- color,
- QColor(red / count, green / count, blue / count));
- _chatStyle->applyAdjustedServiceBg(*_serviceBg);
- recreateBlurCheckbox();
- });
- _service = GenerateServiceItem(
- delegate(),
- _serviceHistory,
- (forGroup()
- ? tr::lng_background_other_group(tr::now)
- : forChannel()
- ? tr::lng_background_other_channel(tr::now)
- : (_forPeer
- && !_fromMessageId
- && !_forPeer->starsPerMessageChecked())
- ? tr::lng_background_other_info(
- tr::now,
- lt_user,
- _forPeer->shortName())
- : ItemDateText(_text1->data(), false)),
- false);
- _service->initDimensions();
- _service->resizeGetHeight(st::boxWideWidth);
- }
- void BackgroundPreviewBox::checkLoadedDocument() {
- const auto document = _paper.document();
- if (!_full.isNull()
- || !document
- || !_media->loaded(true)
- || _generating) {
- return;
- }
- const auto generateCallback = [=](QImage &&image) {
- if (image.isNull()) {
- return;
- }
- crl::async([
- this,
- image = std::move(image),
- isPattern = _paper.isPattern(),
- background = _paper.backgroundColors(),
- gradientRotation = _paper.gradientRotation(),
- patternOpacity = _paper.patternOpacity(),
- guard = _generating.make_guard()
- ]() mutable {
- auto scaled = PrepareScaledFromFull(
- image,
- isPattern,
- background,
- gradientRotation,
- patternOpacity);
- auto blurred = !isPattern
- ? PrepareScaledNonPattern(
- Ui::PrepareBlurredBackground(image),
- Images::Option(0))
- : QImage();
- crl::on_main(std::move(guard), [
- this,
- image = std::move(image),
- scaled = std::move(scaled),
- blurred = std::move(blurred)
- ]() mutable {
- _full = std::move(image);
- setScaledFromImage(std::move(scaled), std::move(blurred));
- update();
- });
- });
- };
- _generating = Data::ReadBackgroundImageAsync(
- _media.get(),
- Ui::PreprocessBackgroundImage,
- generateCallback);
- }
- bool BackgroundPreviewBox::Start(
- not_null<Window::SessionController*> controller,
- const QString &slug,
- const QMap<QString, QString> ¶ms) {
- if (const auto paper = Data::WallPaper::FromColorsSlug(slug)) {
- controller->show(Box<BackgroundPreviewBox>(
- controller,
- paper->withUrlParams(params)));
- return true;
- }
- if (!IsValidWallPaperSlug(slug)) {
- controller->show(Ui::MakeInformBox(tr::lng_background_bad_link()));
- return false;
- }
- controller->session().api().requestWallPaper(slug, crl::guard(controller, [=](
- const Data::WallPaper &result) {
- controller->show(Box<BackgroundPreviewBox>(
- controller,
- result.withUrlParams(params)));
- }), crl::guard(controller, [=] {
- controller->show(Ui::MakeInformBox(tr::lng_background_bad_link()));
- }));
- return true;
- }
- HistoryView::Context BackgroundPreviewBox::elementContext() {
- return HistoryView::Context::ContactPreview;
- }
|