From 2c5d990e1c1900357b7a03c4c892d0fcc40fc5fe Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 May 2023 17:46:24 +0400 Subject: [PATCH] Implement full theming of attachments in stories. --- Telegram/SourceFiles/boxes/boxes.style | 45 --- .../SourceFiles/boxes/edit_caption_box.cpp | 15 +- .../SourceFiles/boxes/premium_limits_box.cpp | 29 +- .../SourceFiles/boxes/premium_limits_box.h | 13 +- .../SourceFiles/boxes/premium_preview_box.cpp | 130 ++++---- .../SourceFiles/boxes/premium_preview_box.h | 9 + Telegram/SourceFiles/boxes/send_files_box.cpp | 156 ++++++---- Telegram/SourceFiles/boxes/send_files_box.h | 50 ++- .../calls/group/calls_group_panel.cpp | 2 +- .../chat_helpers/chat_helpers.style | 103 +++++++ .../chat_helpers/compose/compose_show.h | 2 + .../chat_helpers/emoji_list_widget.cpp | 1 + .../chat_helpers/gifs_list_widget.cpp | 1 + .../chat_helpers/stickers_list_widget.cpp | 13 +- .../SourceFiles/chat_helpers/tabbed_panel.h | 2 +- .../controllers/stickers_panel_controller.cpp | 24 +- .../controllers/stickers_panel_controller.h | 7 +- Telegram/SourceFiles/editor/photo_editor.cpp | 24 +- Telegram/SourceFiles/editor/photo_editor.h | 12 + .../editor/photo_editor_layer_widget.cpp | 7 +- .../editor/photo_editor_layer_widget.h | 10 +- .../SourceFiles/history/history_widget.cpp | 3 +- .../view/history_view_replies_section.cpp | 3 +- .../view/history_view_scheduled_section.cpp | 3 +- .../media/stories/media_stories_reply.cpp | 290 +++++++++++++++++- .../media/stories/media_stories_reply.h | 48 +++ .../SourceFiles/media/view/media_view.style | 87 +++++- .../media/view/media_view_overlay_widget.cpp | 161 +++++----- .../media/view/media_view_overlay_widget.h | 2 + .../SourceFiles/settings/settings_premium.cpp | 18 +- .../SourceFiles/settings/settings_premium.h | 7 +- .../SourceFiles/storage/localimageloader.cpp | 2 +- .../attach_abstract_single_file_preview.cpp | 25 +- .../attach_abstract_single_file_preview.h | 10 +- .../attach_abstract_single_media_preview.cpp | 14 +- .../attach_abstract_single_media_preview.h | 10 +- .../ui/chat/attach/attach_album_preview.cpp | 3 + .../ui/chat/attach/attach_album_preview.h | 6 + .../ui/chat/attach/attach_album_thumbnail.cpp | 26 +- .../ui/chat/attach/attach_album_thumbnail.h | 6 + .../ui/chat/attach/attach_controls.cpp | 2 +- .../attach_item_single_file_preview.cpp | 3 +- .../attach/attach_item_single_file_preview.h | 1 + .../attach_item_single_media_preview.cpp | 3 +- .../attach/attach_item_single_media_preview.h | 1 + .../attach/attach_single_file_preview.cpp | 3 +- .../chat/attach/attach_single_file_preview.h | 1 + .../attach/attach_single_media_preview.cpp | 5 +- .../chat/attach/attach_single_media_preview.h | 2 + Telegram/SourceFiles/ui/effects/premium.style | 27 +- .../ui/effects/premium_graphics.cpp | 19 +- .../SourceFiles/ui/effects/premium_graphics.h | 7 + .../window/window_session_controller.cpp | 13 +- 53 files changed, 1113 insertions(+), 353 deletions(-) diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 54f915a45..fb0a19ef5 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -344,11 +344,6 @@ autoDownloadLimitSlider: MediaSlider(defaultContinuousSlider) { } autoDownloadLimitPadding: margins(22px, 8px, 22px, 8px); -confirmCaptionArea: InputField(defaultInputField) { - textMargins: margins(1px, 26px, 31px, 4px); - heightMax: 158px; -} -confirmBg: windowBgOver; confirmMaxHeight: 245px; supportInfoField: InputField(defaultInputField) { @@ -391,51 +386,11 @@ sendMediaPreviewSize: 308px; sendMediaPreviewHeightMax: 1280; sendMediaRowSkip: 10px; -editMediaButtonSize: 32px; - -editMediaButtonIconFile: icon {{ "send_media/send_media_replace", menuIconFg }}; -editMediaButton: IconButton(defaultIconButton) { - width: editMediaButtonSize; - height: editMediaButtonSize; - - icon: editMediaButtonIconFile; - - rippleAreaSize: editMediaButtonSize; - ripple: defaultRippleAnimation; -} - editMediaHintLabel: FlatLabel(defaultFlatLabel) { textFg: windowSubTextFg; minWidth: sendMediaPreviewSize; } -// SendFilesBox - -sendBoxAlbumGroupEditInternalSkip: 8px; -sendBoxAlbumGroupSkipRight: 5px; -sendBoxAlbumGroupSkipTop: 5px; -sendBoxAlbumGroupRadius: 4px; -sendBoxAlbumGroupSize: size(62px, 25px); -sendBoxAlbumSmallGroupSize: size(30px, 25px); - -sendBoxFileGroupSkipTop: 2px; -sendBoxFileGroupSkipRight: 5px; -sendBoxFileGroupEditInternalSkip: -1px; - -sendBoxAlbumGroupButtonFile: IconButton(editMediaButton) { - ripple: RippleAnimation(defaultRippleAnimation) { - color: windowBgRipple; - } -} -sendBoxAlbumGroupEditButtonIconFile: editMediaButtonIconFile; -sendBoxAlbumGroupDeleteButtonIconFile: icon {{ "send_media/send_media_delete", menuIconFg }}; - -sendBoxAlbumButtonMediaEdit: icon {{ "send_media/send_media_replace", roundedFg }}; -sendBoxAlbumGroupButtonMediaEdit: icon {{ "send_media/send_media_replace", roundedFg, point(4px, 1px) }}; -sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_delete", roundedFg }}; - -// End of SendFilesBox - calendarTitleHeight: boxTitleHeight; calendarPrevious: IconButton { width: calendarTitleHeight; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 41f19a0b8..c9779b1f1 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -246,12 +246,12 @@ EditCaptionBox::EditCaptionBox( , _scroll(base::make_unique_q(this, st::boxScroll)) , _field(base::make_unique_q( this, - st::confirmCaptionArea, + st::defaultComposeFiles.caption, Ui::InputField::Mode::MultiLine, tr::lng_photo_caption())) , _emojiToggle(base::make_unique_q( this, - st::boxAttachEmoji)) + st::defaultComposeFiles.emoji)) , _initialText(std::move(text)) , _initialList(std::move(list)) , _saved(std::move(saved)) { @@ -402,6 +402,7 @@ void EditCaptionBox::rebuildPreview() { if (photo || document->isVideoFile() || document->isAnimation()) { const auto media = Ui::CreateChild( this, + st::defaultComposeControls, gifPaused, _historyItem, Ui::AttachControls::Type::EditOnly); @@ -410,6 +411,7 @@ void EditCaptionBox::rebuildPreview() { } else { _content.reset(Ui::CreateChild( this, + st::defaultComposeControls, _historyItem, Ui::AttachControls::Type::EditOnly)); } @@ -418,6 +420,7 @@ void EditCaptionBox::rebuildPreview() { const auto media = Ui::SingleMediaPreview::Create( this, + st::defaultComposeControls, gifPaused, file, Ui::AttachControls::Type::EditOnly); @@ -429,6 +432,7 @@ void EditCaptionBox::rebuildPreview() { } else { _content.reset(Ui::CreateChild( this, + st::defaultComposeControls, file, Ui::AttachControls::Type::EditOnly)); } @@ -482,7 +486,7 @@ void EditCaptionBox::setupField() { _field->setSubmitSettings( Core::App().settings().sendSubmitWay()); - _field->setMaxHeight(st::confirmCaptionArea.heightMax); + _field->setMaxHeight(st::defaultComposeFiles.caption.heightMax); connect(_field, &Ui::InputField::submitted, [=] { save(); }); connect(_field, &Ui::InputField::cancelled, [=] { closeBox(); }); @@ -596,7 +600,7 @@ void EditCaptionBox::setupPhotoEditorEventHandler() { if (!_preparedList.files.empty()) { Editor::OpenWithPreparedFile( this, - controller, + controller->uiShow(), &_preparedList.files.front(), st::sendMediaPreviewSize, [=] { rebuildPreview(); }); @@ -845,7 +849,8 @@ bool EditCaptionBox::validateLength(const QString &text) const { if (remove <= 0) { return true; } - _controller->show(Box(CaptionLimitReachedBox, session, remove)); + _controller->show( + Box(CaptionLimitReachedBox, session, remove, nullptr)); return false; } diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp index 55fbe0a35..3fbdc34c1 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp @@ -404,6 +404,7 @@ std::unique_ptr PublicsController::createRow( void SimpleLimitBox( not_null box, + const style::PremiumLimits *stOverride, not_null session, bool premiumPossible, rpl::producer title, @@ -411,6 +412,8 @@ void SimpleLimitBox( const QString &refAddition, const InfographicDescriptor &descriptor, bool fixed = false) { + const auto &st = stOverride ? *stOverride : st::defaultPremiumLimits; + box->setWidth(st::boxWideWidth); const auto top = fixed @@ -431,6 +434,7 @@ void SimpleLimitBox( if (premiumPossible) { Ui::Premium::AddLimitRow( top, + st, descriptor.premiumLimit, descriptor.phrase, 0, @@ -473,6 +477,7 @@ void SimpleLimitBox( void SimpleLimitBox( not_null box, + const style::PremiumLimits *stOverride, not_null session, rpl::producer title, rpl::producer text, @@ -481,6 +486,7 @@ void SimpleLimitBox( bool fixed = false) { SimpleLimitBox( box, + stOverride, session, session->premiumPossible(), std::move(title), @@ -524,6 +530,7 @@ void SimplePinsLimitBox( }); SimpleLimitBox( box, + nullptr, session, tr::lng_filter_pin_limit_title(), std::move(text), @@ -561,6 +568,7 @@ void ChannelsLimitBox( SimpleLimitBox( box, + nullptr, session, tr::lng_channels_limit_title(), std::move(text), @@ -650,6 +658,7 @@ void PublicLinksLimitBox( SimpleLimitBox( box, + nullptr, session, tr::lng_links_limit_title(), std::move(text), @@ -716,6 +725,7 @@ void FilterChatsLimitBox( SimpleLimitBox( box, + nullptr, session, tr::lng_filter_chats_limit_title(), std::move(text), @@ -753,6 +763,7 @@ void FilterLinksLimitBox( SimpleLimitBox( box, + nullptr, session, tr::lng_filter_links_limit_title(), std::move(text), @@ -798,6 +809,7 @@ void FiltersLimitBox( }); SimpleLimitBox( box, + nullptr, session, tr::lng_filters_limit_title(), std::move(text), @@ -836,6 +848,7 @@ void ShareableFiltersLimitBox( }); SimpleLimitBox( box, + nullptr, session, tr::lng_filter_shared_limit_title(), std::move(text), @@ -900,6 +913,7 @@ void ForumPinsLimitBox( Ui::Text::RichLangValue); SimpleLimitBox( box, + nullptr, &forum->session(), false, tr::lng_filter_pin_limit_title(), @@ -911,7 +925,8 @@ void ForumPinsLimitBox( void CaptionLimitBox( not_null box, not_null session, - int remove) { + int remove, + const style::PremiumLimits *stOverride) { const auto premium = session->premium(); const auto premiumPossible = session->premiumPossible(); @@ -943,6 +958,7 @@ void CaptionLimitBox( SimpleLimitBox( box, + stOverride, session, tr::lng_caption_limit_title(), std::move(text), @@ -953,15 +969,17 @@ void CaptionLimitBox( void CaptionLimitReachedBox( not_null box, not_null session, - int remove) { + int remove, + const style::PremiumLimits *stOverride) { Ui::ConfirmBox(box, Ui::ConfirmBoxArgs{ .text = tr::lng_caption_limit_reached(tr::now, lt_count, remove), + .labelStyle = stOverride ? &stOverride->boxLabel : nullptr, .inform = true, }); if (!session->premium()) { box->addLeftButton(tr::lng_limits_increase(), [=] { box->getDelegate()->showBox( - Box(CaptionLimitBox, session, remove), + Box(CaptionLimitBox, session, remove, stOverride), Ui::LayerOption::KeepOther, anim::type::normal); box->closeBox(); @@ -972,7 +990,8 @@ void CaptionLimitReachedBox( void FileSizeLimitBox( not_null box, not_null session, - uint64 fileSizeBytes) { + uint64 fileSizeBytes, + const style::PremiumLimits *stOverride) { const auto limits = Data::PremiumLimits(session); const auto defaultLimit = float64(limits.uploadMaxDefault()); const auto premiumLimit = float64(limits.uploadMaxPremium()); @@ -1011,6 +1030,7 @@ void FileSizeLimitBox( SimpleLimitBox( box, + stOverride, session, premiumPossible, tr::lng_file_size_limit_title(), @@ -1084,6 +1104,7 @@ void AccountsLimitBox( if (premiumPossible) { Ui::Premium::AddLimitRow( top, + st::defaultPremiumLimits, (QString::number(std::max(current, defaultLimit) + 1) + ((current + 1 == premiumLimit) ? "" : "+")), QString::number(defaultLimit)); diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.h b/Telegram/SourceFiles/boxes/premium_limits_box.h index 55be7e40f..bb9115a3e 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.h +++ b/Telegram/SourceFiles/boxes/premium_limits_box.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/generic_box.h" +namespace style { +struct PremiumLimits; +} // namespace style + namespace Data { class Forum; } // namespace Data @@ -57,15 +61,18 @@ void ForumPinsLimitBox( void CaptionLimitBox( not_null box, not_null session, - int remove); + int remove, + const style::PremiumLimits *stOverride = nullptr); void CaptionLimitReachedBox( not_null box, not_null session, - int remove); + int remove, + const style::PremiumLimits *stOverride = nullptr); void FileSizeLimitBox( not_null box, not_null session, - uint64 fileSizeBytes); + uint64 fileSizeBytes, + const style::PremiumLimits *stOverride = nullptr); void AccountsLimitBox( not_null box, not_null session); diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp index ffc5bc709..d63288a01 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp @@ -76,7 +76,7 @@ bool operator==(const Descriptor &a, const Descriptor &b) { struct Preload { Descriptor descriptor; std::shared_ptr media; - base::weak_ptr controller; + std::weak_ptr show; }; [[nodiscard]] std::vector &Preloads() { @@ -168,7 +168,7 @@ void PreloadSticker(const std::shared_ptr &media) { [[nodiscard]] not_null StickerPreview( not_null parent, - not_null controller, + std::shared_ptr show, const std::shared_ptr &media, Fn readyCallback = nullptr) { using namespace HistoryView; @@ -194,6 +194,8 @@ void PreloadSticker(const std::shared_ptr &media) { struct State { std::unique_ptr lottie; std::unique_ptr effect; + style::owned_color pathFg = style::owned_color( + QColor(255, 255, 255, 64)); std::unique_ptr pathGradient; bool readyInvoked = false; }; @@ -239,15 +241,17 @@ void PreloadSticker(const std::shared_ptr &media) { }; createLottieIfReady(); if (!state->lottie || !state->effect) { - controller->session().downloaderTaskFinished( + show->session().downloaderTaskFinished( ) | rpl::take_while([=] { createLottieIfReady(); return !state->lottie || !state->effect; }) | rpl::start(result->lifetime()); } - state->pathGradient = MakePathShiftGradient( - controller->chatStyle(), - [=] { result->update(); }); + state->pathGradient = std::make_unique( + st::shadowFg, + state->pathFg.color(), + [=] { result->update(); }, + rpl::never<>()); result->paintRequest( ) | rpl::start_with_next([=] { @@ -262,7 +266,7 @@ void PreloadSticker(const std::shared_ptr &media) { if (!state->lottie || !state->lottie->ready() || !state->effect->ready()) { - p.setBrush(controller->chatStyle()->msgServiceBg()); + p.setBrush(st::shadowFg); ChatHelpers::PaintStickerThumbnailPath( p, media.get(), @@ -302,7 +306,7 @@ void PreloadSticker(const std::shared_ptr &media) { [[nodiscard]] not_null StickersPreview( not_null parent, - not_null controller, + std::shared_ptr show, Fn readyCallback) { const auto result = Ui::CreateChild(parent.get()); result->show(); @@ -327,7 +331,7 @@ void PreloadSticker(const std::shared_ptr &media) { bool nextReady = false; int index = 0; }; - const auto premium = &controller->session().api().premium(); + const auto premium = &show->session().api().premium(); const auto state = lifetime.make_state(); const auto create = [=](std::shared_ptr media) { const auto outer = Ui::CreateChild(result); @@ -340,7 +344,7 @@ void PreloadSticker(const std::shared_ptr &media) { [[maybe_unused]] const auto sticker = StickerPreview( outer, - controller, + show, media, state->singleReadyCallback); @@ -520,7 +524,7 @@ struct VideoPreviewDocument { [[nodiscard]] not_null VideoPreview( not_null parent, - not_null controller, + std::shared_ptr show, not_null document, bool alignToBottom, Fn readyCallback) { @@ -683,7 +687,7 @@ struct VideoPreviewDocument { [[nodiscard]] not_null GenericPreview( not_null parent, - not_null controller, + std::shared_ptr show, PremiumPreview section, Fn readyCallback) { const auto result = Ui::CreateChild(parent.get()); @@ -699,7 +703,7 @@ struct VideoPreviewDocument { std::vector> medias; Ui::RpWidget *single = nullptr; }; - const auto session = &controller->session(); + const auto session = &show->session(); const auto state = lifetime.make_state(); const auto create = [=] { const auto document = LookupVideo(session, section); @@ -708,7 +712,7 @@ struct VideoPreviewDocument { } state->single = VideoPreview( result, - controller, + show, document, !VideoAlignToTop(section), readyCallback); @@ -724,14 +728,18 @@ struct VideoPreviewDocument { [[nodiscard]] not_null GenerateDefaultPreview( not_null parent, - not_null controller, + std::shared_ptr show, PremiumPreview section, Fn readyCallback) { switch (section) { case PremiumPreview::Stickers: - return StickersPreview(parent, controller, readyCallback); + return StickersPreview(parent, std::move(show), readyCallback); default: - return GenericPreview(parent, controller, section, readyCallback); + return GenericPreview( + parent, + std::move(show), + section, + readyCallback); } } @@ -792,7 +800,7 @@ struct VideoPreviewDocument { void PreviewBox( not_null box, - not_null controller, + std::shared_ptr show, const Descriptor &descriptor, const std::shared_ptr &media, const QImage &back) { @@ -825,7 +833,7 @@ void PreviewBox( }; const auto state = outer->lifetime().make_state(); state->selected = descriptor.section; - state->order = Settings::PremiumPreviewOrder(&controller->session()); + state->order = Settings::PremiumPreviewOrder(&show->session()); const auto index = [=](PremiumPreview section) { const auto it = ranges::find(state->order, section); @@ -880,7 +888,7 @@ void PreviewBox( }; state->stickersPreload = GenerateDefaultPreview( outer, - controller, + show, PremiumPreview::Stickers, ready); state->stickersPreload->hide(); @@ -890,13 +898,13 @@ void PreviewBox( switch (descriptor.section) { case PremiumPreview::Stickers: state->content = media - ? StickerPreview(outer, controller, media, state->preload) - : StickersPreview(outer, controller, state->preload); + ? StickerPreview(outer, show, media, state->preload) + : StickersPreview(outer, show, state->preload); break; default: state->content = GenericPreview( outer, - controller, + show, descriptor.section, state->preload); break; @@ -955,7 +963,7 @@ void PreviewBox( } else { state->content = GenerateDefaultPreview( outer, - controller, + show, now, state->preload); } @@ -1003,7 +1011,7 @@ void PreviewBox( state->preload(); } }; - if (descriptor.fromSettings && controller->session().premium()) { + if (descriptor.fromSettings && show->session().premium()) { box->setShowFinishedCallback(showFinished); box->addButton(tr::lng_close(), [=] { box->closeBox(); }); } else { @@ -1030,16 +1038,21 @@ void PreviewBox( auto button = descriptor.fromSettings ? object_ptr::fromRaw( Settings::CreateSubscribeButton({ - controller, - box, - computeRef, + .parent = box, + .computeRef = computeRef, + .show = show, })) : CreateUnlockButton(box, std::move(unlock)); button->resizeToWidth(width); if (!descriptor.fromSettings) { button->setClickedCallback([=] { + const auto window = show->resolveWindow( + ChatHelpers::WindowUsage::PremiumPromo); + if (!window) { + return; + } Settings::ShowPremium( - controller, + window, Settings::LookupPremiumRef(state->selected.current())); }); } @@ -1052,7 +1065,7 @@ void PreviewBox( if (descriptor.fromSettings) { Data::AmPremiumValue( - &controller->session() + &show->session() ) | rpl::skip(1) | rpl::start_with_next([=] { box->closeBox(); }, box->lifetime()); @@ -1076,25 +1089,26 @@ void PreviewBox( } void Show( - not_null controller, + std::shared_ptr show, const Descriptor &descriptor, const std::shared_ptr &media, QImage back) { - const auto box = controller->show( - Box(PreviewBox, controller, descriptor, media, back)); + auto box = Box(PreviewBox, show, descriptor, media, back); + const auto raw = box.data(); + show->showBox(std::move(box)); if (descriptor.shownCallback) { - descriptor.shownCallback(box); + descriptor.shownCallback(raw); } } -void Show(not_null controller, QImage back) { +void Show(std::shared_ptr show, QImage back) { auto &list = Preloads(); for (auto i = begin(list); i != end(list);) { - const auto already = i->controller.get(); + const auto already = i->show.lock(); if (!already) { i = list.erase(i); - } else if (already == controller) { - Show(controller, i->descriptor, i->media, back); + } else if (already == show) { + Show(std::move(show), i->descriptor, i->media, back); i = list.erase(i); return; } else { @@ -1104,21 +1118,23 @@ void Show(not_null controller, QImage back) { } void Show( - not_null controller, + std::shared_ptr show, Descriptor &&descriptor) { - if (!controller->session().premiumPossible()) { - const auto box = controller->show(Box(PremiumUnavailableBox)); + if (!show->session().premiumPossible()) { + auto box = Box(PremiumUnavailableBox); + const auto raw = box.data(); + show->showBox(std::move(box)); if (descriptor.shownCallback) { - descriptor.shownCallback(box); + descriptor.shownCallback(raw); } return; } auto &list = Preloads(); for (auto i = begin(list); i != end(list);) { - const auto already = i->controller.get(); + const auto already = i->show.lock(); if (!already) { i = list.erase(i); - } else if (already == controller) { + } else if (already == show) { if (i->descriptor == descriptor) { return; } @@ -1135,13 +1151,13 @@ void Show( } } - const auto weak = base::make_weak(controller); + const auto weak = std::weak_ptr(show); list.push_back({ .descriptor = descriptor, .media = (descriptor.requestedSticker ? descriptor.requestedSticker->createMediaView() : nullptr), - .controller = weak, + .show = weak, }); if (const auto &media = list.back().media) { PreloadSticker(media); @@ -1166,8 +1182,8 @@ void Show( Images::CornersMask(st::boxRadius), RectPart::TopLeft | RectPart::TopRight); crl::on_main([=] { - if (const auto strong = weak.get()) { - Show(strong, result); + if (auto strong = weak.lock()) { + Show(std::move(strong), result); } }); }); @@ -1178,7 +1194,7 @@ void Show( void ShowStickerPreviewBox( not_null controller, not_null document) { - Show(controller, Descriptor{ + Show(controller->uiShow(), Descriptor{ .section = PremiumPreview::Stickers, .requestedSticker = document, }); @@ -1188,7 +1204,14 @@ void ShowPremiumPreviewBox( not_null controller, PremiumPreview section, Fn)> shown) { - Show(controller, Descriptor{ + ShowPremiumPreviewBox(controller->uiShow(), section, std::move(shown)); +} + +void ShowPremiumPreviewBox( + std::shared_ptr show, + PremiumPreview section, + Fn)> shown) { + Show(std::move(show), Descriptor{ .section = section, .shownCallback = std::move(shown), }); @@ -1198,7 +1221,7 @@ void ShowPremiumPreviewToBuy( not_null controller, PremiumPreview section, Fn hiddenCallback) { - Show(controller, Descriptor{ + Show(controller->uiShow(), Descriptor{ .section = section, .fromSettings = true, .hiddenCallback = std::move(hiddenCallback), @@ -1337,7 +1360,10 @@ void DoubledLimitsPreviewBox( Main::Domain::kPremiumMaxAccounts, till, }); - Ui::Premium::ShowListBox(box, std::move(entries)); + Ui::Premium::ShowListBox( + box, + st::defaultPremiumLimits, + std::move(entries)); } object_ptr CreateUnlockButton( diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.h b/Telegram/SourceFiles/boxes/premium_preview_box.h index 0bf439d29..30fb8f866 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.h +++ b/Telegram/SourceFiles/boxes/premium_preview_box.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class DocumentData; +namespace ChatHelpers { +class Show; +} // namespace ChatHelpers + namespace Data { struct ReactionId; } // namespace Data @@ -59,6 +63,11 @@ void ShowPremiumPreviewBox( PremiumPreview section, Fn)> shown = nullptr); +void ShowPremiumPreviewBox( + std::shared_ptr show, + PremiumPreview section, + Fn)> shown = nullptr); + void ShowPremiumPreviewToBuy( not_null controller, PremiumPreview section, diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index baceecfe5..335582ceb 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -137,13 +137,19 @@ SendFilesLimits DefaultLimitsForPeer(not_null peer) { SendFilesCheck DefaultCheckForPeer( not_null controller, not_null peer) { + return DefaultCheckForPeer(controller->uiShow(), peer); +} + +SendFilesCheck DefaultCheckForPeer( + std::shared_ptr show, + not_null peer) { return [=]( const Ui::PreparedFile &file, bool compress, bool silent) { const auto error = Data::FileRestrictionError(peer, file, compress); if (error && !silent) { - controller->showToast(*error); + show->showToast(*error); } return !error.has_value(); }; @@ -151,6 +157,7 @@ SendFilesCheck DefaultCheckForPeer( SendFilesBox::Block::Block( not_null parent, + const style::ComposeControls &st, not_null*> items, int from, int till, @@ -170,20 +177,24 @@ SendFilesBox::Block::Block( if (_isAlbum) { const auto preview = Ui::CreateChild( parent.get(), + st, my, way); _preview.reset(preview); } else { const auto media = Ui::SingleMediaPreview::Create( parent, + st, gifPaused, first); if (media) { _isSingleMedia = true; _preview.reset(media); } else { - _preview.reset( - Ui::CreateChild(parent.get(), first)); + _preview.reset(Ui::CreateChild( + parent.get(), + st, + first)); } } _preview->show(); @@ -328,15 +339,32 @@ SendFilesBox::SendFilesBox( SendFilesCheck check, Api::SendType sendType, SendMenu::Type sendMenuType) -: _controller(controller) -, _sendType(sendType) +: SendFilesBox(nullptr, { + .show = controller->uiShow(), + .list = std::move(list), + .caption = caption, + .limits = limits, + .check = check, + .sendType = sendType, + .sendMenuType = sendMenuType, +}) { +} + +SendFilesBox::SendFilesBox(QWidget*, SendFilesBoxDescriptor &&descriptor) +: _show(std::move(descriptor.show)) +, _st(descriptor.stOverride + ? *descriptor.stOverride + : st::defaultComposeControls) +, _sendType(descriptor.sendType) , _titleHeight(st::boxTitleHeight) -, _list(std::move(list)) -, _limits(limits) -, _sendMenuType(sendMenuType) -, _check(std::move(check)) -, _caption(this, st::confirmCaptionArea, Ui::InputField::Mode::MultiLine) -, _prefilledCaptionText(std::move(caption)) +, _list(std::move(descriptor.list)) +, _limits(descriptor.limits) +, _sendMenuType(descriptor.sendMenuType) +, _check(std::move(descriptor.check)) +, _confirmedCallback(std::move(descriptor.confirmed)) +, _cancelledCallback(std::move(descriptor.cancelled)) +, _caption(this, _st.files.caption, Ui::InputField::Mode::MultiLine) +, _prefilledCaptionText(std::move(descriptor.caption)) , _scroll(this, st::boxScroll) , _inner( _scroll->setOwnedWidget( @@ -431,7 +459,7 @@ void SendFilesBox::setupDragArea() { const auto droppedCallback = [=](bool compress) { return [=](const QMimeData *data) { addFiles(data); - Window::ActivateWindow(_controller); + _show->activate(); }; }; areas.document->setDroppedCallback(droppedCallback(false)); @@ -479,7 +507,7 @@ void SendFilesBox::openDialogToAddFileToAlbum() { return true; }; const auto callback = [=](FileDialog::OpenResult &&result) { - const auto premium = _controller->session().premium(); + const auto premium = _show->session().premium(); FileDialogCallback( std::move(result), checkResult, @@ -563,11 +591,11 @@ void SendFilesBox::addMenuButton() { return; } - const auto top = addTopButton(st::infoTopBarMenu); + const auto top = addTopButton(_st.files.menu); top->setClickedCallback([=] { - _menu = base::make_unique_q( - top, - st::popupMenuExpandedSeparator); + const auto &tabbed = _st.tabbed; + const auto &icons = tabbed.icons; + _menu = base::make_unique_q(top, tabbed.menu); if (hasSpoilerMenu()) { const auto spoilered = allWithSpoilers(); _menu->addAction( @@ -575,9 +603,9 @@ void SendFilesBox::addMenuButton() { ? tr::lng_context_disable_spoiler(tr::now) : tr::lng_context_spoiler_effect(tr::now)), [=] { toggleSpoilers(!spoilered); }, - spoilered ? &st::menuIconSpoilerOff : &st::menuIconSpoiler); + spoilered ? &icons.menuSpoilerOff : &icons.menuSpoiler); if (hasSendMenu()) { - _menu->addSeparator(); + _menu->addSeparator(&tabbed.expandedSeparator); } } if (hasSendMenu()) { @@ -586,7 +614,8 @@ void SendFilesBox::addMenuButton() { _sendMenuType, [=] { sendSilent(); }, [=] { sendScheduled(); }, - [=] { sendWhenOnline(); }); + [=] { sendWhenOnline(); }, + &_st.tabbed.icons); } _menu->popup(QCursor::pos()); return true; @@ -711,12 +740,12 @@ void SendFilesBox::generatePreviewFrom(int fromBlock) { } void SendFilesBox::pushBlock(int from, int till) { - const auto gifPaused = [controller = _controller] { - return controller->isGifPausedAtLeastFor( - Window::GifPauseReason::Layer); + const auto gifPaused = [show = _show] { + return show->paused(Window::GifPauseReason::Layer); }; _blocks.emplace_back( _inner.data(), + _st, &_list.files, from, till, @@ -807,7 +836,7 @@ void SendFilesBox::pushBlock(int from, int till) { return checkSlowmode(list) && checkRights(list); }; const auto callback = [=](FileDialog::OpenResult &&result) { - const auto premium = _controller->session().premium(); + const auto premium = _show->session().premium(); FileDialogCallback( std::move(result), checkResult, @@ -825,15 +854,15 @@ void SendFilesBox::pushBlock(int from, int till) { const auto openedOnce = widget->lifetime().make_state(false); block.itemModifyRequest( - ) | rpl::start_with_next([=, controller = _controller](int index) { + ) | rpl::start_with_next([=, show = _show](int index) { if (!(*openedOnce)) { - controller->session().settings().incrementPhotoEditorHintShown(); - controller->session().saveSettings(); + show->session().settings().incrementPhotoEditorHintShown(); + show->session().saveSettings(); } *openedOnce = true; Editor::OpenWithPreparedFile( this, - controller, + show, &_list.files[index], st::sendMediaPreviewSize, [=] { refreshAllAfterChanges(from); }); @@ -856,12 +885,14 @@ void SendFilesBox::setupSendWayControls() { this, tr::lng_send_grouped(tr::now), groupFilesFirst, - st::defaultBoxCheckbox); + _st.files.checkbox, + _st.files.check); _sendImagesAsPhotos.create( this, tr::lng_send_compressed(tr::now), _sendWay.current().sendImagesAsPhotos(), - st::defaultBoxCheckbox); + _st.files.checkbox, + _st.files.check); _sendWay.changes( ) | rpl::start_with_next([=](SendFilesWay value) { @@ -905,7 +936,8 @@ void SendFilesBox::setupSendWayControls() { this, tr::lng_remember(tr::now), false, - st::defaultBoxCheckbox); + _st.files.checkbox, + _st.files.check); _wayRemember->hide(); rpl::combine( _groupFiles->checkedValue(), @@ -953,25 +985,32 @@ void SendFilesBox::updateSendWayControls() { : tr::lng_send_compressed_one(tr::now)); _hintLabel->setVisible( - _controller->session().settings().photoEditorHintShown() + _show->session().settings().photoEditorHintShown() ? _list.canHaveEditorHintLabel() : false); } void SendFilesBox::setupCaption() { - const auto allow = [=](const auto&) { + const auto allow = [=](const auto &) { return (_limits & SendFilesAllow::EmojiWithoutPremium); }; + const auto show = _show; InitMessageFieldHandlers( - _controller, + &show->session(), + show, _caption.data(), - Window::GifPauseReason::Layer, - allow); + [=] { return show->paused(Window::GifPauseReason::Layer); }, + allow, + &_st.files.caption); Ui::Emoji::SuggestionsController::Init( getDelegate()->outerContainer(), _caption, - &_controller->session(), - { .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow }); + &_show->session(), + { + .suggestCustomEmoji = true, + .allowCustomWithoutPremium = allow, + .st = &_st.suggestions, + }); if (!_prefilledCaptionText.text.isEmpty()) { _caption->setTextWithTags( @@ -1019,12 +1058,21 @@ void SendFilesBox::setupEmojiPanel() { using Selector = ChatHelpers::TabbedSelector; _emojiPanel = base::make_unique_q( container, - _controller, - object_ptr( - nullptr, - _controller->uiShow(), - Window::GifPauseReason::Layer, - Selector::Mode::EmojiOnly)); + ChatHelpers::TabbedPanelDescriptor{ + .ownedSelector = object_ptr( + nullptr, + ChatHelpers::TabbedSelectorDescriptor{ + .show = _show, + .st = _st.tabbed, + .level = Window::GifPauseReason::Layer, + .mode = ChatHelpers::TabbedSelector::Mode::EmojiOnly, + .features = { + .megagroupSet = false, + .stickersSettings = false, + .openStickerSets = false, + }, + }), + }); _emojiPanel->setDesiredHeightValues( 1., st::emojiPanMinHeight / 2, @@ -1041,11 +1089,9 @@ void SendFilesBox::setupEmojiPanel() { const auto info = data.document->sticker(); if (info && info->setType == Data::StickersType::Emoji - && !_controller->session().premium() + && !_show->session().premium() && !(_limits & SendFilesAllow::EmojiWithoutPremium)) { - ShowPremiumPreviewBox( - _controller, - PremiumPreview::AnimatedEmoji); + ShowPremiumPreviewBox(_show, PremiumPreview::AnimatedEmoji); } else { Data::InsertCustomEmoji(_caption.data(), data.document); } @@ -1057,7 +1103,7 @@ void SendFilesBox::setupEmojiPanel() { }; _emojiFilter.reset(base::install_event_filter(container, filterCallback)); - _emojiToggle.create(this, st::boxAttachEmoji); + _emojiToggle.create(this, _st.files.emoji); _emojiToggle->setVisible(!_caption->isHidden()); _emojiToggle->installEventFilter(_emojiPanel); _emojiToggle->addClickHandler([=] { @@ -1095,7 +1141,7 @@ bool SendFilesBox::canAddFiles(not_null data) const { } bool SendFilesBox::addFiles(not_null data) { - const auto premium = _controller->session().premium(); + const auto premium = _show->session().premium(); auto list = [&] { const auto urls = Core::ReadMimeUrls(data); auto result = CanAddUrls(urls) @@ -1242,7 +1288,7 @@ void SendFilesBox::paintEvent(QPaintEvent *e) { Painter p(this); p.setFont(st::boxTitleFont); - p.setPen(st::boxTitleFg); + p.setPen(getDelegate()->style().title.textFg); p.drawTextLeft( st::boxPhotoTitlePosition.x(), st::boxTitlePosition.y() - st::boxTopMargin, @@ -1318,7 +1364,7 @@ void SendFilesBox::saveSendWaySettings() { } bool SendFilesBox::validateLength(const QString &text) const { - const auto session = &_controller->session(); + const auto session = &_show->session(); const auto limit = Data::PremiumLimits(session).captionLengthCurrent(); const auto remove = int(text.size()) - limit; const auto way = _sendWay.current(); @@ -1328,7 +1374,8 @@ bool SendFilesBox::validateLength(const QString &text) const { way.sendImagesAsPhotos())) { return true; } - _controller->show(Box(CaptionLimitReachedBox, session, remove)); + _show->showBox( + Box(CaptionLimitReachedBox, session, remove, &_st.premium)); return false; } @@ -1385,8 +1432,7 @@ void SendFilesBox::sendScheduled() { ? SendMenu::Type::ScheduledToUser : _sendMenuType; const auto callback = [=](Api::SendOptions options) { send(options); }; - _controller->show( - HistoryView::PrepareScheduleBox(this, type, callback)); + _show->showBox(HistoryView::PrepareScheduleBox(this, type, callback)); } void SendFilesBox::sendWhenOnline() { diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index 8e93673f0..6e6c6fa82 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localimageloader.h" #include "storage/storage_media_prepare.h" +namespace style { +struct ComposeControls; +} // namespace style + namespace Window { class SessionController; } // namespace Window @@ -26,6 +30,7 @@ enum class SendType; namespace ChatHelpers { class TabbedPanel; +class Show; } // namespace ChatHelpers namespace Ui { @@ -71,6 +76,29 @@ using SendFilesCheck = Fn controller, not_null peer); +[[nodiscard]] SendFilesCheck DefaultCheckForPeer( + std::shared_ptr show, + not_null peer); + +using SendFilesConfirmed = Fn; + +struct SendFilesBoxDescriptor { + std::shared_ptr show; + Ui::PreparedList list; + TextWithTags caption; + SendFilesLimits limits = {}; + SendFilesCheck check; + Api::SendType sendType = {}; + SendMenu::Type sendMenuType = {}; + const style::ComposeControls *stOverride = nullptr; + SendFilesConfirmed confirmed; + Fn cancelled; +}; class SendFilesBox : public Ui::BoxContent { public: @@ -87,14 +115,9 @@ public: SendFilesCheck check, Api::SendType sendType, SendMenu::Type sendMenuType); + SendFilesBox(QWidget*, SendFilesBoxDescriptor &&descriptor); - void setConfirmedCallback( - Fn callback) { + void setConfirmedCallback(SendFilesConfirmed callback) { _confirmedCallback = std::move(callback); } void setCancelledCallback(Fn callback) { @@ -116,6 +139,7 @@ private: public: Block( not_null parent, + const style::ComposeControls &st, not_null*> items, int from, int till, @@ -201,7 +225,8 @@ private: void enqueueNextPrepare(); void addPreparedAsyncFile(Ui::PreparedFile &&file); - const not_null _controller; + const std::shared_ptr _show; + const style::ComposeControls &_st; const Api::SendType _sendType = Api::SendType(); QString _titleText; @@ -211,15 +236,10 @@ private: std::optional _removingIndex; SendFilesLimits _limits = {}; - SendMenu::Type _sendMenuType = SendMenu::Type(); + SendMenu::Type _sendMenuType = {}; SendFilesCheck _check; - Fn _confirmedCallback; + SendFilesConfirmed _confirmedCallback; Fn _cancelledCallback; bool _confirmed = false; diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 454d658ed..8af7d3f0d 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -124,7 +124,7 @@ void Show::showOrHideBoxOrLayer( } else if (const auto panel = _panel.get()) { panel->hideLayer(animated); } - } +} not_null Show::toastParent() const { const auto panel = _panel.get(); diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 4e03be93a..8ad939619 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -11,6 +11,7 @@ using "boxes/boxes.style"; using "ui/layers/layers.style"; using "ui/widgets/widgets.style"; using "ui/menu_icons.style"; +using "ui/effects/premium.style"; GroupCallUserpics { size: pixels; @@ -66,6 +67,8 @@ ComposeIcons { menuMute: icon; menuSchedule: icon; menuWhenOnline: icon; + menuSpoiler: icon; + menuSpoilerOff: icon; } EmojiSuggestions { @@ -107,6 +110,7 @@ EmojiPan { fadeLeft: icon; fadeRight: icon; menu: PopupMenu; + expandedSeparator: MenuSeparator; tabs: SettingsSlider; search: TabbedSearch; searchMargin: margins; @@ -162,6 +166,24 @@ RecordBar { remove: IconButton; } +ComposeFiles { + check: Check; + checkbox: Checkbox; + menu: IconButton; + caption: InputField; + emoji: EmojiButton; + confirmBg: color; + buttonFile: IconButton; + buttonFileEdit: icon; + buttonFileDelete: icon; + iconBg: color; + iconPlay: icon; + iconImage: icon; + iconDocument: icon; + nameFg: color; + statusFg: color; +} + ComposeControls { bg: color; radius: pixels; @@ -175,6 +197,8 @@ ComposeControls { tabbedHeightMin: pixels; tabbedHeightMax: pixels; record: RecordBar; + files: ComposeFiles; + premium: PremiumLimits; } switchPmButton: RoundButton(defaultBoxButton) { @@ -416,6 +440,42 @@ stickersToast: Toast(defaultToast) { stickersEmpty: icon {{ "stickers_empty", windowSubTextFg }}; emojiEmpty: icon {{ "emoji_empty", windowSubTextFg }}; +editMediaButtonSize: 32px; + +editMediaButtonIconFile: icon {{ "send_media/send_media_replace", menuIconFg }}; +editMediaButton: IconButton(defaultIconButton) { + width: editMediaButtonSize; + height: editMediaButtonSize; + + icon: editMediaButtonIconFile; + + rippleAreaSize: editMediaButtonSize; + ripple: defaultRippleAnimation; +} + +sendBoxAlbumGroupEditInternalSkip: 8px; +sendBoxAlbumGroupSkipRight: 5px; +sendBoxAlbumGroupSkipTop: 5px; +sendBoxAlbumGroupRadius: 4px; +sendBoxAlbumGroupSize: size(62px, 25px); +sendBoxAlbumSmallGroupSize: size(30px, 25px); + +sendBoxFileGroupSkipTop: 2px; +sendBoxFileGroupSkipRight: 5px; +sendBoxFileGroupEditInternalSkip: -1px; + +sendBoxAlbumGroupButtonFile: IconButton(editMediaButton) { + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgRipple; + } +} +sendBoxAlbumGroupEditButtonIconFile: editMediaButtonIconFile; +sendBoxAlbumGroupDeleteButtonIconFile: icon {{ "send_media/send_media_delete", menuIconFg }}; + +sendBoxAlbumButtonMediaEdit: icon {{ "send_media/send_media_replace", roundedFg }}; +sendBoxAlbumGroupButtonMediaEdit: icon {{ "send_media/send_media_replace", roundedFg, point(4px, 1px) }}; +sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_delete", roundedFg }}; + defaultComposeIcons: ComposeIcons { settings: icon {{ "emoji/emoji_settings", emojiIconFg }}; @@ -445,6 +505,8 @@ defaultComposeIcons: ComposeIcons { menuMute: menuIconMute; menuSchedule: menuIconSchedule; menuWhenOnline: menuIconWhenOnline; + menuSpoiler: menuIconSpoiler; + menuSpoilerOff: menuIconSpoilerOff; } defaultEmojiPan: EmojiPan { margin: margins(7px, 0px, 7px, 0px); @@ -476,6 +538,10 @@ defaultEmojiPan: EmojiPan { fadeLeft: icon {{ "fade_horizontal-flip_horizontal", emojiPanCategories }}; fadeRight: icon {{ "fade_horizontal", emojiPanCategories }}; menu: popupMenuWithIcons; + expandedSeparator: MenuSeparator(defaultMenuSeparator) { + padding: margins(0px, 4px, 0px, 4px); + width: 6px; + } tabs: emojiTabs; search: defaultTabbedSearch; searchMargin: margins(1px, 11px, 2px, 5px); @@ -974,6 +1040,41 @@ historySend: SendButton { sendDisabledFg: historyComposeIconFg; } +defaultComposeFilesMenu: IconButton(defaultIconButton) { + width: 48px; + height: 54px; + + icon: icon {{ "title_menu_dots", boxTitleCloseFg }}; + iconOver: icon {{ "title_menu_dots", boxTitleCloseFgOver }}; + iconPosition: point(18px, -1px); + + rippleAreaPosition: point(1px, 6px); + rippleAreaSize: 42px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } +} +defaultComposeFilesField: InputField(defaultInputField) { + textMargins: margins(1px, 26px, 31px, 4px); + heightMax: 158px; +} +defaultComposeFiles: ComposeFiles { + check: defaultCheck; + checkbox: defaultBoxCheckbox; + menu: defaultComposeFilesMenu; + caption: defaultComposeFilesField; + emoji: boxAttachEmoji; + confirmBg: windowBgOver; + buttonFile: sendBoxAlbumGroupButtonFile; + buttonFileEdit: sendBoxAlbumGroupEditButtonIconFile; + buttonFileDelete: sendBoxAlbumGroupDeleteButtonIconFile; + iconBg: msgFileInBg; + iconPlay: icon {{ "history_file_play", historyFileInIconFg }}; + iconImage: icon {{ "history_file_image", historyFileInIconFg }}; + iconDocument: icon {{ "history_file_document", historyFileInIconFg }}; + nameFg: historyFileNameInFg; + statusFg: mediaInFg; +} defaultComposeControls: ComposeControls { bg: historyComposeAreaBg; radius: 0px; @@ -987,4 +1088,6 @@ defaultComposeControls: ComposeControls { tabbedHeightMin: emojiPanMinHeight; tabbedHeightMax: emojiPanMaxHeight; record: defaultRecordBar; + files: defaultComposeFiles; + premium: defaultPremiumLimits; } diff --git a/Telegram/SourceFiles/chat_helpers/compose/compose_show.h b/Telegram/SourceFiles/chat_helpers/compose/compose_show.h index a1ad76e6c..4a418c042 100644 --- a/Telegram/SourceFiles/chat_helpers/compose/compose_show.h +++ b/Telegram/SourceFiles/chat_helpers/compose/compose_show.h @@ -46,6 +46,8 @@ enum class WindowUsage { class Show : public Main::SessionShow { public: + virtual void activate() = 0; + [[nodiscard]] virtual bool paused(PauseReason reason) const = 0; [[nodiscard]] virtual rpl::producer<> pauseChanged() const = 0; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 3d5f35c57..9f29463ce 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -726,6 +726,7 @@ object_ptr EmojiListWidget::createFooter() { .paused = footerPaused, .parent = this, .st = &st(), + .features = { .stickersSettings = false }, }); _footer = result; diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 48a0e9800..0ae43c3dd 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -172,6 +172,7 @@ object_ptr GifsListWidget::createFooter() { .paused = pausedMethod(), .parent = this, .st = &st(), + .features = { .stickersSettings = false }, }); _footer = result; _chosenSetId = Data::Stickers::RecentSetId; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index cf0dda5eb..afee47efd 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "chat_helpers/stickers_list_widget.h" +#include "core/application.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_session.h" @@ -221,9 +222,15 @@ StickersListWidget::StickersListWidget( } _settings->addClickHandler([=] { - using Section = StickersBox::Section; - _show->showBox( - Box(_show, Section::Installed, _isMasks)); + if (const auto window = _show->resolveWindow( + WindowUsage::PremiumPromo)) { + // While media viewer can't show StickersBox. + using Section = StickersBox::Section; + window->show( + Box(_show, Section::Installed, _isMasks)); + Core::App().hideMediaView(); + Window::ActivateWindow(window); + } }); session().downloaderTaskFinished( diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_panel.h b/Telegram/SourceFiles/chat_helpers/tabbed_panel.h index 153c19581..fcedb5efc 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_panel.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_panel.h @@ -30,7 +30,7 @@ struct TabbedPanelDescriptor { Window::SessionController *regularWindow = nullptr; object_ptr ownedSelector = { nullptr }; TabbedSelector *nonOwnedSelector = nullptr; -};; +}; class TabbedPanel : public Ui::RpWidget { public: diff --git a/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp b/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp index 66fc0f997..2c0482c33 100644 --- a/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp +++ b/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp @@ -12,21 +12,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" // Window::GifPauseReason #include "styles/style_chat_helpers.h" +#include "styles/style_media_view.h" namespace Editor { StickersPanelController::StickersPanelController( not_null panelContainer, - not_null controller) + std::shared_ptr show) : _stickersPanel( base::make_unique_q( panelContainer, - controller, - object_ptr( - nullptr, - controller->uiShow(), - Window::GifPauseReason::Layer, - ChatHelpers::TabbedSelector::Mode::MediaEditor))) { + ChatHelpers::TabbedPanelDescriptor{ + .ownedSelector = object_ptr( + nullptr, + ChatHelpers::TabbedSelectorDescriptor{ + .show = show, + .st = st::storiesComposeControls.tabbed, + .level = Window::GifPauseReason::Layer, + .mode = ChatHelpers::TabbedSelector::Mode::MediaEditor, + .features = { + .megagroupSet = false, + .stickersSettings = false, + .openStickerSets = false, + }, + }), + })) { _stickersPanel->setDesiredHeightValues( 1., st::emojiPanMinHeight / 2, diff --git a/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.h b/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.h index 5404a8adb..9514ea23b 100644 --- a/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.h +++ b/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.h @@ -11,16 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace ChatHelpers { class TabbedPanel; +class Show; } // namespace ChatHelpers namespace Ui { class RpWidget; } // namespace Ui -namespace Window { -class SessionController; -} // namespace Window - namespace Editor { class StickersPanelController final { @@ -34,7 +31,7 @@ public: StickersPanelController( not_null panelContainer, - not_null controller); + std::shared_ptr show); [[nodiscard]] auto stickerChosen() const -> rpl::producer>; diff --git a/Telegram/SourceFiles/editor/photo_editor.cpp b/Telegram/SourceFiles/editor/photo_editor.cpp index a11fe9760..801e12258 100644 --- a/Telegram/SourceFiles/editor/photo_editor.cpp +++ b/Telegram/SourceFiles/editor/photo_editor.cpp @@ -52,16 +52,34 @@ PhotoEditor::PhotoEditor( std::shared_ptr photo, PhotoModifications modifications, EditorData data) +: PhotoEditor( + parent, + controller->uiShow(), + (controller->sessionController() + ? controller->sessionController()->uiShow() + : nullptr), + std::move(photo), + std::move(modifications), + std::move(data)) { +} + +PhotoEditor::PhotoEditor( + not_null parent, + std::shared_ptr show, + std::shared_ptr sessionShow, + std::shared_ptr photo, + PhotoModifications modifications, + EditorData data) : RpWidget(parent) , _modifications(std::move(modifications)) , _controllers(std::make_shared( - controller->sessionController() + sessionShow ? std::make_unique( this, - controller->sessionController()) + std::move(sessionShow)) : nullptr, std::make_unique(), - controller->uiShow())) + std::move(show))) , _content(base::make_unique_q( this, photo, diff --git a/Telegram/SourceFiles/editor/photo_editor.h b/Telegram/SourceFiles/editor/photo_editor.h index e226b1ddb..4169339a6 100644 --- a/Telegram/SourceFiles/editor/photo_editor.h +++ b/Telegram/SourceFiles/editor/photo_editor.h @@ -15,8 +15,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { class LayerWidget; +class Show; } // namespace Ui +namespace ChatHelpers { +class Show; +} // namespace ChatHelpers + namespace Window { class Controller; } // namespace Window @@ -36,6 +41,13 @@ public: std::shared_ptr photo, PhotoModifications modifications, EditorData data = EditorData()); + PhotoEditor( + not_null parent, + std::shared_ptr show, + std::shared_ptr sessionShow, + std::shared_ptr photo, + PhotoModifications modifications, + EditorData data = EditorData()); void save(); [[nodiscard]] rpl::producer doneRequests() const; diff --git a/Telegram/SourceFiles/editor/photo_editor_layer_widget.cpp b/Telegram/SourceFiles/editor/photo_editor_layer_widget.cpp index c58fb1cc0..daf93c08d 100644 --- a/Telegram/SourceFiles/editor/photo_editor_layer_widget.cpp +++ b/Telegram/SourceFiles/editor/photo_editor_layer_widget.cpp @@ -22,7 +22,7 @@ namespace Editor { void OpenWithPreparedFile( not_null parent, - not_null controller, + std::shared_ptr show, not_null file, int previewWidth, Fn &&doneCallback) { @@ -56,13 +56,14 @@ void OpenWithPreparedFile( const auto fileImage = std::make_shared(std::move(copy)); auto editor = base::make_unique_q( parent, - &controller->window(), + show, + show, fileImage, image->modifications); const auto raw = editor.get(); auto layer = std::make_unique(parent, std::move(editor)); InitEditorLayer(layer.get(), raw, std::move(callback)); - controller->showLayer(std::move(layer), Ui::LayerOption::KeepOther); + show->showLayer(std::move(layer), Ui::LayerOption::KeepOther); } void PrepareProfilePhoto( diff --git a/Telegram/SourceFiles/editor/photo_editor_layer_widget.h b/Telegram/SourceFiles/editor/photo_editor_layer_widget.h index d7fd19a75..f1c2fd445 100644 --- a/Telegram/SourceFiles/editor/photo_editor_layer_widget.h +++ b/Telegram/SourceFiles/editor/photo_editor_layer_widget.h @@ -6,13 +6,13 @@ For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -// -//#include "ui/image/image.h" -//#include "editor/photo_editor_common.h" -//#include "base/unique_qptr.h" enum class ImageRoundRadius; +namespace ChatHelpers { +class Show; +} // namespace ChatHelpers + namespace Ui { class RpWidget; struct PreparedFile; @@ -31,7 +31,7 @@ struct EditorData; void OpenWithPreparedFile( not_null parent, - not_null controller, + std::shared_ptr show, not_null file, int previewWidth, Fn &&doneCallback); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index c6e81a086..99db1769a 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -5142,7 +5142,8 @@ bool HistoryWidget::showSendingFilesError( return false; } else if (text == u"(toolarge)"_q) { const auto fileSize = list.files.back().size; - controller()->show(Box(FileSizeLimitBox, &session(), fileSize)); + controller()->show( + Box(FileSizeLimitBox, &session(), fileSize, nullptr)); return true; } controller()->showToast(text); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 22145b376..f8ddd36e2 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1123,7 +1123,8 @@ bool RepliesWidget::showSendingFilesError( return false; } else if (text == u"(toolarge)"_q) { const auto fileSize = list.files.back().size; - controller()->show(Box(FileSizeLimitBox, &session(), fileSize)); + controller()->show( + Box(FileSizeLimitBox, &session(), fileSize, nullptr)); return true; } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 85ce03471..66255b466 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -559,7 +559,8 @@ bool ScheduledWidget::showSendingFilesError( return false; } else if (text == u"(toolarge)"_q) { const auto fileSize = list.files.back().size; - controller()->show(Box(FileSizeLimitBox, &session(), fileSize)); + controller()->show( + Box(FileSizeLimitBox, &session(), fileSize, nullptr)); return true; } diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp index 49b2e70a3..bbc28a75d 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp @@ -7,16 +7,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/stories/media_stories_reply.h" +#include "api/api_common.h" +#include "apiwrap.h" #include "base/call_delayed.h" +#include "boxes/premium_limits_box.h" +#include "boxes/send_files_box.h" #include "chat_helpers/compose/compose_show.h" #include "chat_helpers/tabbed_selector.h" +#include "core/file_utilities.h" +#include "core/mime_type.h" #include "data/data_session.h" #include "data/data_user.h" #include "history/view/controls/compose_controls_common.h" #include "history/view/controls/history_view_compose_controls.h" +#include "history/history_item_helpers.h" +#include "history/history.h" #include "inline_bots/inline_bot_result.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" #include "media/stories/media_stories_controller.h" #include "menu/menu_send.h" +#include "storage/localimageloader.h" +#include "storage/storage_media_prepare.h" +#include "ui/chat/attach/attach_prepare.h" +#include "styles/style_boxes.h" // sendMediaPreviewSize. #include "styles/style_chat_helpers.h" #include "styles/style_media_view.h" @@ -85,15 +99,276 @@ void ReplyArea::initGeometry() { } void ReplyArea::send(Api::SendOptions options) { - // #TODO stories + const auto webPageId = _controls->webPageId(); + + auto message = ApiWrap::MessageToSend(prepareSendAction(options)); + message.textWithTags = _controls->getTextWithAppliedMarkdown(); + message.webPageId = webPageId; + + const auto error = GetErrorTextForSending( + _data.user, + { + .topicRootId = MsgId(0), + .text = &message.textWithTags, + .ignoreSlowmodeCountdown = (options.scheduled != 0), + }); + if (!error.isEmpty()) { + _controller->uiShow()->showToast(error); + } + + session().api().sendMessage(std::move(message)); + + _controls->clear(); + finishSending(); } void ReplyArea::sendVoice(VoiceToSend &&data) { - // #TODO stories + auto action = prepareSendAction(data.options); + session().api().sendVoiceMessage( + data.bytes, + data.waveform, + data.duration, + std::move(action)); + + _controls->clearListenState(); + finishSending(); } -void ReplyArea::chooseAttach(std::optional overrideCompress) { - // #TODO stories +void ReplyArea::finishSending() { + _controls->hidePanelsAnimated(); + _controller->wrap()->setFocus(); +} + +void ReplyArea::uploadFile( + const QByteArray &fileContent, + SendMediaType type) { + session().api().sendFile(fileContent, type, prepareSendAction({})); +} + +bool ReplyArea::showSendingFilesError( + const Ui::PreparedList &list) const { + return showSendingFilesError(list, std::nullopt); +} + +bool ReplyArea::showSendingFilesError( + const Ui::PreparedList &list, + std::optional compress) const { + const auto text = [&] { + const auto peer = _data.user; + const auto error = Data::FileRestrictionError(peer, list, compress); + if (error) { + return *error; + } + using Error = Ui::PreparedList::Error; + switch (list.error) { + case Error::None: return QString(); + case Error::EmptyFile: + case Error::Directory: + case Error::NonLocalUrl: return tr::lng_send_image_empty( + tr::now, + lt_name, + list.errorData); + case Error::TooLargeFile: return u"(toolarge)"_q; + } + return tr::lng_forward_send_files_cant(tr::now); + }(); + if (text.isEmpty()) { + return false; + } else if (text == u"(toolarge)"_q) { + const auto fileSize = list.files.back().size; + _controller->uiShow()->showBox(Box( + FileSizeLimitBox, + &session(), + fileSize, + &st::storiesComposePremium)); + return true; + } + + _controller->uiShow()->showToast(text); + return true; +} + +Api::SendAction ReplyArea::prepareSendAction( + Api::SendOptions options) const { + Expects(_data.user != nullptr); + + const auto history = _data.user->owner().history(_data.user); + auto result = Api::SendAction(history, options); + result.options.sendAs = _controls->sendAsPeer(); + return result; +} + +void ReplyArea::chooseAttach( + std::optional overrideSendImagesAsPhotos) { + if (!_data.user) { + return; + } + const auto user = not_null(_data.user); + _choosingAttach = false; + if (const auto error = Data::AnyFileRestrictionError(user)) { + _controller->uiShow()->showToast(*error); + return; + } + + const auto filter = (overrideSendImagesAsPhotos == true) + ? FileDialog::ImagesOrAllFilter() + : FileDialog::AllOrImagesFilter(); + const auto callback = [=](FileDialog::OpenResult &&result) { + if (result.paths.isEmpty() && result.remoteContent.isEmpty()) { + return; + } + + if (!result.remoteContent.isEmpty()) { + auto read = Images::Read({ + .content = result.remoteContent, + }); + if (!read.image.isNull() && !read.animated) { + confirmSendingFiles( + std::move(read.image), + std::move(result.remoteContent), + overrideSendImagesAsPhotos); + } else { + uploadFile(result.remoteContent, SendMediaType::File); + } + } else { + const auto premium = session().premium(); + auto list = Storage::PrepareMediaList( + result.paths, + st::sendMediaPreviewSize, + premium); + list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos; + confirmSendingFiles(std::move(list)); + } + }; + FileDialog::GetOpenPaths( + _controller->wrap().get(), + tr::lng_choose_files(tr::now), + filter, + crl::guard(&_shownUserGuard, callback), + nullptr); +} + +bool ReplyArea::confirmSendingFiles( + not_null data, + std::optional overrideSendImagesAsPhotos, + const QString &insertTextOnCancel) { + const auto hasImage = data->hasImage(); + const auto premium = session().user()->isPremium(); + + if (const auto urls = Core::ReadMimeUrls(data); !urls.empty()) { + auto list = Storage::PrepareMediaList( + urls, + st::sendMediaPreviewSize, + premium); + if (list.error != Ui::PreparedList::Error::NonLocalUrl) { + if (list.error == Ui::PreparedList::Error::None + || !hasImage) { + const auto emptyTextOnCancel = QString(); + list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos; + confirmSendingFiles(std::move(list), emptyTextOnCancel); + return true; + } + } + } + + if (auto read = Core::ReadMimeImage(data)) { + confirmSendingFiles( + std::move(read.image), + std::move(read.content), + overrideSendImagesAsPhotos, + insertTextOnCancel); + return true; + } + return false; +} + +bool ReplyArea::confirmSendingFiles( + Ui::PreparedList &&list, + const QString &insertTextOnCancel) { + if (_controls->confirmMediaEdit(list)) { + return true; + } else if (showSendingFilesError(list)) { + return false; + } + + const auto show = _controller->uiShow(); + auto confirmed = [=](auto &&...args) { + sendingFilesConfirmed(std::forward(args)...); + }; + auto box = Box(SendFilesBoxDescriptor{ + .show = show, + .list = std::move(list), + .caption = _controls->getTextWithAppliedMarkdown(), + .limits = DefaultLimitsForPeer(_data.user), + .check = DefaultCheckForPeer(show, _data.user), + .sendType = Api::SendType::Normal, + .sendMenuType = SendMenu::Type::SilentOnly, + .stOverride = &st::storiesComposeControls, + .confirmed = crl::guard(this, confirmed), + .cancelled = _controls->restoreTextCallback(insertTextOnCancel), + }); + if (const auto shown = show->show(std::move(box))) { + shown->setCloseByOutsideClick(false); + } + + return true; +} + +void ReplyArea::sendingFilesConfirmed( + Ui::PreparedList &&list, + Ui::SendFilesWay way, + TextWithTags &&caption, + Api::SendOptions options, + bool ctrlShiftEnter) { + Expects(list.filesToProcess.empty()); + + if (showSendingFilesError(list, way.sendImagesAsPhotos())) { + return; + } + auto groups = DivideByGroups( + std::move(list), + way, + _data.user->slowmodeApplied()); + const auto type = way.sendImagesAsPhotos() + ? SendMediaType::Photo + : SendMediaType::File; + auto action = prepareSendAction(options); + action.clearDraft = false; + if ((groups.size() != 1 || !groups.front().sentWithCaption()) + && !caption.text.isEmpty()) { + auto message = Api::MessageToSend(action); + message.textWithTags = base::take(caption); + session().api().sendMessage(std::move(message)); + } + for (auto &group : groups) { + const auto album = (group.type != Ui::AlbumType::None) + ? std::make_shared() + : nullptr; + session().api().sendFiles( + std::move(group.list), + type, + base::take(caption), + album, + action); + } + finishSending(); +} + +bool ReplyArea::confirmSendingFiles( + QImage &&image, + QByteArray &&content, + std::optional overrideSendImagesAsPhotos, + const QString &insertTextOnCancel) { + if (image.isNull()) { + return false; + } + + auto list = Storage::PrepareMediaFromImage( + std::move(image), + std::move(content), + st::sendMediaPreviewSize); + list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos; + return confirmSendingFiles(std::move(list), insertTextOnCancel); } void ReplyArea::initActions() { @@ -180,6 +455,7 @@ void ReplyArea::show(ReplyAreaData data) { } return; } + invalidate_weak_ptrs(&_shownUserGuard); const auto user = data.user; const auto history = user ? user->owner().history(user).get() : nullptr; _controls->setHistory({ @@ -188,6 +464,12 @@ void ReplyArea::show(ReplyAreaData data) { _controls->clear(); } +Main::Session &ReplyArea::session() const { + Expects(_data.user != nullptr); + + return _data.user->session(); +} + rpl::producer ReplyArea::focusedValue() const { return _controls->focusedValue(); } diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.h b/Telegram/SourceFiles/media/stories/media_stories_reply.h index 9587e2e07..c7ea6f88c 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.h +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.h @@ -9,7 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/weak_ptr.h" +enum class SendMediaType; + namespace Api { +struct SendAction; struct SendOptions; } // namespace Api @@ -21,6 +24,15 @@ namespace HistoryView::Controls { struct VoiceToSend; } // namespace HistoryView::Controls +namespace Main { +class Session; +} // namespace Main + +namespace Ui { +struct PreparedList; +class SendFilesWay; +} // namespace Ui + namespace Media::Stories { class Controller; @@ -45,9 +57,44 @@ public: private: using VoiceToSend = HistoryView::Controls::VoiceToSend; + [[nodiscard]] Main::Session &session() const; + + bool confirmSendingFiles(const QStringList &files); + bool confirmSendingFiles(not_null data); + + void uploadFile(const QByteArray &fileContent, SendMediaType type); + bool confirmSendingFiles( + QImage &&image, + QByteArray &&content, + std::optional overrideSendImagesAsPhotos = std::nullopt, + const QString &insertTextOnCancel = QString()); + bool confirmSendingFiles( + const QStringList &files, + const QString &insertTextOnCancel); + bool confirmSendingFiles( + Ui::PreparedList &&list, + const QString &insertTextOnCancel = QString()); + bool confirmSendingFiles( + not_null data, + std::optional overrideSendImagesAsPhotos, + const QString &insertTextOnCancel = QString()); + bool showSendingFilesError(const Ui::PreparedList &list) const; + bool showSendingFilesError( + const Ui::PreparedList &list, + std::optional compress) const; + void sendingFilesConfirmed( + Ui::PreparedList &&list, + Ui::SendFilesWay way, + TextWithTags &&caption, + Api::SendOptions options, + bool ctrlShiftEnter); + void finishSending(); + void initGeometry(); void initActions(); + [[nodiscard]] Api::SendAction prepareSendAction( + Api::SendOptions options) const; void send(Api::SendOptions options); void sendVoice(VoiceToSend &&data); void chooseAttach(std::optional overrideSendImagesAsPhotos); @@ -58,6 +105,7 @@ private: const std::unique_ptr _controls; ReplyAreaData _data; + base::has_weak_ptr _shownUserGuard; bool _choosingAttach = false; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index c97645659..c822bcf0d 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -467,6 +467,9 @@ storiesRemoveSet: IconButton(stickerPanRemoveSet) { iconOver: icon {{ "simple_close", storiesComposeGrayIcon }}; ripple: storiesComposeRippleLight; } +storiesMenuSeparator: MenuSeparator(defaultMenuSeparator) { + fg: groupCallMenuBgOver; +} storiesMenu: Menu(defaultMenu) { itemBg: groupCallMenuBg; itemBgOver: groupCallMenuBgOver; @@ -477,9 +480,7 @@ storiesMenu: Menu(defaultMenu) { itemFgShortcutOver: groupCallMemberNotJoinedStatus; itemFgShortcutDisabled: groupCallMemberNotJoinedStatus; - separator: MenuSeparator(defaultMenuSeparator) { - fg: groupCallMenuBgOver; - } + separator: storiesMenuSeparator; arrow: icon {{ "menu/submenu_arrow", groupCallMemberNotJoinedStatus }}; ripple: RippleAnimation(defaultRippleAnimation) { @@ -507,6 +508,23 @@ storiesPopupMenuWithIcons: PopupMenu(storiesPopupMenu) { menu: storiesMenuWithIcons; } +storiesAttachEmojiInner: IconButton(storiesAttach) { + icon: icon {{ "chat/input_smile_face", storiesComposeGrayIcon }}; + iconOver: icon {{ "chat/input_smile_face", storiesComposeGrayIcon }}; +} +storiesAttachEmoji: EmojiButton(historyAttachEmoji) { + inner: storiesAttachEmojiInner; + bg: storiesComposeBg; + lineFg: storiesComposeGrayIcon; + lineFgOver: storiesComposeGrayIcon; +} +storiesComposePremium: PremiumLimits(defaultPremiumLimits) { + boxLabel: FlatLabel(boxLabel) { + textFg: groupCallMembersFg; + } + nonPremiumBg: storiesComposeBgOver; + nonPremiumFg: storiesComposeWhiteText; +} storiesComposeControls: ComposeControls(defaultComposeControls) { bg: storiesComposeBg; radius: storiesRadius; @@ -528,15 +546,7 @@ storiesComposeControls: ComposeControls(defaultComposeControls) { sendDisabledFg: storiesComposeGrayText; } attach: storiesAttach; - emoji: EmojiButton(historyAttachEmoji) { - inner: IconButton(storiesAttach) { - icon: icon {{ "chat/input_smile_face", storiesComposeGrayIcon }}; - iconOver: icon {{ "chat/input_smile_face", storiesComposeGrayIcon }}; - } - bg: storiesComposeBg; - lineFg: storiesComposeGrayIcon; - lineFgOver: storiesComposeGrayIcon; - } + emoji: storiesAttachEmoji; suggestions: EmojiSuggestions(defaultEmojiSuggestions) { dropdown: InnerDropdown(emojiSuggestionsDropdown) { animation: PanelAnimation(defaultPanelAnimation) { @@ -569,6 +579,10 @@ storiesComposeControls: ComposeControls(defaultComposeControls) { fadeLeft: icon {{ "fade_horizontal-flip_horizontal", storiesComposeBg }}; fadeRight: icon {{ "fade_horizontal", storiesComposeBg }}; menu: storiesPopupMenuWithIcons; + expandedSeparator: MenuSeparator(storiesMenuSeparator) { + padding: margins(0px, 4px, 0px, 4px); + width: 6px; + } tabs: SettingsSlider(emojiTabs) { barFgActive: storiesComposeBlue; labelFg: storiesComposeGrayText; @@ -638,6 +652,8 @@ storiesComposeControls: ComposeControls(defaultComposeControls) { menuMute: icon {{ "menu/mute", storiesComposeWhiteText }}; menuSchedule: icon {{ "menu/calendar", storiesComposeWhiteText }}; menuWhenOnline: icon {{ "menu/send_when_online", storiesComposeWhiteText }}; + menuSpoiler: icon {{ "menu/spoiler_on", storiesComposeWhiteText }}; + menuSpoilerOff: icon {{ "menu/spoiler_off", storiesComposeWhiteText }}; } autocompleteBottomSkip: 10px; } @@ -664,4 +680,51 @@ storiesComposeControls: ComposeControls(defaultComposeControls) { iconPosition: point(10px, 11px); } } + files: ComposeFiles(defaultComposeFiles) { + check: Check(defaultCheck) { + untoggledFg: groupCallMemberNotJoinedStatus; + toggledFg: groupCallActiveFg; + icon: icon {{ "default_checkbox_check", groupCallMembersFg, point(4px, 7px) }}; + } + checkbox: Checkbox(defaultBoxCheckbox) { + textFg: groupCallMembersFg; + textFgActive: groupCallMembersFg; + rippleBg: groupCallMembersBgRipple; + rippleBgActive: groupCallMembersBgRipple; + } + menu: IconButton(defaultComposeFilesMenu) { + icon: icon {{ "title_menu_dots", storiesComposeGrayIcon }}; + iconOver: icon {{ "title_menu_dots", storiesComposeGrayIcon }}; + ripple: storiesComposeRippleLight; + } + caption: InputField(defaultComposeFilesField) { + textFg: storiesComposeWhiteText; + textBg: storiesComposeBg; + placeholderFg: storiesComposeGrayText; + placeholderFgActive: storiesComposeBlue; + borderFg: storiesComposeGrayText; + borderFgActive: storiesComposeBlue; + menu: storiesPopupMenu; + } + emoji: EmojiButton(storiesAttachEmoji) { + inner: IconButton(storiesAttachEmojiInner) { + width: 30px; + height: 30px; + rippleAreaSize: 0px; + } + } + confirmBg: storiesComposeBgOver; + buttonFile: IconButton(sendBoxAlbumGroupButtonFile) { + ripple: storiesComposeRipple; + } + buttonFileEdit: icon {{ "send_media/send_media_replace", storiesComposeGrayIcon }}; + buttonFileDelete: icon {{ "send_media/send_media_delete", storiesComposeGrayIcon }}; + iconBg: storiesComposeBlue; + iconPlay: icon {{ "history_file_play", storiesComposeWhiteText }}; + iconImage: icon {{ "history_file_image", storiesComposeWhiteText }}; + iconDocument: icon {{ "history_file_document", storiesComposeWhiteText }}; + nameFg: storiesComposeWhiteText; + statusFg: storiesComposeGrayText; + } + premium: storiesComposePremium; } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index cd34868a0..01326c842 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -287,6 +287,87 @@ struct OverlayWidget::PipWrap { rpl::lifetime lifetime; }; +class OverlayWidget::Show final : public ChatHelpers::Show { +public: + explicit Show(not_null widget) : _widget(widget) { + } + + void activate() override { + if (!_widget->isHidden()) { + _widget->activate(); + } + } + + void showOrHideBoxOrLayer( + std::variant< + v::null_t, + object_ptr, + std::unique_ptr> &&layer, + Ui::LayerOptions options, + anim::type animated) const override { + _widget->_layerBg->uiShow()->showOrHideBoxOrLayer( + std::move(layer), + options, + anim::type::normal); + } + not_null toastParent() const override { + return _widget->_body; + } + bool valid() const override { + return _widget->_storiesSession != nullptr; + } + operator bool() const override { + return valid(); + } + + Main::Session &session() const override { + Expects(_widget->_storiesSession != nullptr); + + return *_widget->_storiesSession; + } + bool paused(ChatHelpers::PauseReason reason) const override { + if (_widget->isHidden() + || (!_widget->_fullscreen + && !_widget->_window->isActiveWindow())) { + return true; + } else if (reason < ChatHelpers::PauseReason::Layer + && _widget->_layerBg->topShownLayer() != nullptr) { + return true; + } + return false; + } + rpl::producer<> pauseChanged() const override { + return rpl::never<>(); + } + + rpl::producer adjustShadowLeft() const override { + return rpl::single(false); + } + SendMenu::Type sendMenuType() const override { + return SendMenu::Type::SilentOnly; + } + + bool showMediaPreview( + Data::FileOrigin origin, + not_null document) const override { + return false; // #TODO stories + } + bool showMediaPreview( + Data::FileOrigin origin, + not_null photo) const override { + return false; // #TODO stories + } + + void processChosenSticker( + ChatHelpers::FileChosen &&chosen) const override { + _widget->_storiesStickerOrEmojiChosen.fire(std::move(chosen)); + } + +private: + not_null _widget; + +}; + OverlayWidget::Streamed::Streamed( not_null document, Data::FileOrigin origin, @@ -3936,81 +4017,10 @@ not_null OverlayWidget::storiesWrap() { } std::shared_ptr OverlayWidget::storiesShow() { - class Show final : public ChatHelpers::Show { - public: - explicit Show(not_null widget) : _widget(widget) { - } - - void showBox( - object_ptr content, - Ui::LayerOptions options - = Ui::LayerOption::KeepOther) const override { - _widget->_layerBg->showBox( - std::move(content), - options, - anim::type::normal); - } - void hideLayer() const override { - _widget->_layerBg->hideAll(anim::type::normal); - } - not_null toastParent() const override { - return _widget->_body; - } - bool valid() const override { - return _widget->_storiesSession != nullptr; - } - operator bool() const override { - return valid(); - } - - Main::Session &session() const override { - Expects(_widget->_storiesSession != nullptr); - - return *_widget->_storiesSession; - } - bool paused(ChatHelpers::PauseReason reason) const override { - if (_widget->isHidden() - || (!_widget->_fullscreen - && !_widget->_window->isActiveWindow())) { - return true; - } else if (reason < ChatHelpers::PauseReason::Layer - && _widget->_layerBg->topShownLayer() != nullptr) { - return true; - } - return false; - } - rpl::producer<> pauseChanged() const override { - return rpl::never<>(); - } - - rpl::producer adjustShadowLeft() const override { - return rpl::single(false); - } - SendMenu::Type sendMenuType() const override { - return SendMenu::Type::SilentOnly; - } - - bool showMediaPreview( - Data::FileOrigin origin, - not_null document) const override { - return false; // #TODO stories - } - bool showMediaPreview( - Data::FileOrigin origin, - not_null photo) const override { - return false; // #TODO stories - } - - void processChosenSticker( - ChatHelpers::FileChosen &&chosen) const override { - _widget->_storiesStickerOrEmojiChosen.fire(std::move(chosen)); - } - - private: - not_null _widget; - - }; - return std::make_shared(this); + if (!_cachedShow) { + _cachedShow = std::make_shared(this); + } + return _cachedShow; } auto OverlayWidget::storiesStickerOrEmojiChosen() @@ -5753,6 +5763,7 @@ void OverlayWidget::clearBeforeHide() { _collageData = std::nullopt; clearStreaming(); setStoriesUser(nullptr); + _layerBg->hideAll(anim::type::instant); assignMediaPointer(nullptr); _preloadPhotos.clear(); _preloadDocuments.clear(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 6007e9fab..43551bf41 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -131,6 +131,7 @@ public: rpl::lifetime &lifetime(); private: + class Show; struct Streamed; struct PipWrap; class Renderer; @@ -600,6 +601,7 @@ private: bool _showAsPip = false; std::unique_ptr _stories; + std::shared_ptr _cachedShow; rpl::event_stream<> _storiesChanged; Main::Session *_storiesSession = nullptr; rpl::event_stream _storiesStickerOrEmojiChosen; diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 2c3f2eecf..055b1296e 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -1782,6 +1782,11 @@ QString LookupPremiumRef(PremiumPreview section) { not_null CreateSubscribeButton( SubscribeButtonArgs &&args) { + Expects(args.show || args.controller); + + if (!args.show && args.controller) { + args.show = args.controller->uiShow(); + } const auto result = Ui::CreateChild( args.parent.get(), args.gradientStops @@ -1789,9 +1794,14 @@ not_null CreateSubscribeButton( : Ui::Premium::ButtonGradientStops()); result->setClickedCallback([ - controller = args.controller, + show = args.show, computeRef = args.computeRef, computeBotUrl = args.computeBotUrl] { + const auto window = show->resolveWindow( + ChatHelpers::WindowUsage::PremiumPromo); + if (!window) { + return; + } const auto url = computeBotUrl ? computeBotUrl() : QString(); if (!url.isEmpty()) { const auto local = Core::TryConvertUrlToLocal(url); @@ -1801,12 +1811,12 @@ not_null CreateSubscribeButton( UrlClickHandler::Open( local, QVariant::fromValue(ClickHandlerContext{ - .sessionWindow = base::make_weak(controller), + .sessionWindow = base::make_weak(window), .botStartAutoSubmit = true, })); } else { - SendScreenAccept(controller); - StartPremiumPayment(controller, computeRef()); + SendScreenAccept(window); + StartPremiumPayment(window, computeRef()); } }); diff --git a/Telegram/SourceFiles/settings/settings_premium.h b/Telegram/SourceFiles/settings/settings_premium.h index 2ce4aba67..0ad7b036c 100644 --- a/Telegram/SourceFiles/settings/settings_premium.h +++ b/Telegram/SourceFiles/settings/settings_premium.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL enum class PremiumPreview; +namespace ChatHelpers { +class Show; +} // namespace ChatHelpers + namespace Ui { class RpWidget; class GradientButton; @@ -48,12 +52,13 @@ void StartPremiumPayment( [[nodiscard]] QString LookupPremiumRef(PremiumPreview section); struct SubscribeButtonArgs final { - not_null controller; + Window::SessionController *controller = nullptr; not_null parent; Fn computeRef; std::optional> text; std::optional gradientStops; Fn computeBotUrl; // nullable + std::shared_ptr show; }; [[nodiscard]] not_null CreateSubscribeButton( diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 1e9d03bdb..1386b2a74 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -1101,7 +1101,7 @@ void FileLoadTask::finish() { } else if (_result->filesize > kFileSizePremiumLimit || (_result->filesize > kFileSizeLimit && !premium)) { Ui::show( - Box(FileSizeLimitBox, session, _result->filesize), + Box(FileSizeLimitBox, session, _result->filesize, nullptr), Ui::LayerOption::KeepOther); removeFromAlbum(); } else { diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.cpp index 60cc5ae68..1b7a81beb 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.cpp @@ -20,14 +20,16 @@ namespace Ui { AbstractSingleFilePreview::AbstractSingleFilePreview( QWidget *parent, + const style::ComposeControls &st, AttachControls::Type type) : AbstractSinglePreview(parent) +, _st(st) , _type(type) -, _editMedia(this, st::sendBoxAlbumGroupButtonFile) -, _deleteMedia(this, st::sendBoxAlbumGroupButtonFile) { +, _editMedia(this, _st.files.buttonFile) +, _deleteMedia(this, _st.files.buttonFile) { - _editMedia->setIconOverride(&st::sendBoxAlbumGroupEditButtonIconFile); - _deleteMedia->setIconOverride(&st::sendBoxAlbumGroupDeleteButtonIconFile); + _editMedia->setIconOverride(&_st.files.buttonFileEdit); + _deleteMedia->setIconOverride(&_st.files.buttonFileDelete); if (type == AttachControls::Type::Full) { _deleteMedia->show(); @@ -100,18 +102,17 @@ void AbstractSingleFilePreview::paintEvent(QPaintEvent *e) { if (_data.fileIsAudio && !_data.fileThumb.isNull()) { p.drawPixmap(inner.topLeft(), _data.fileThumb); } else { - p.setBrush(st::msgFileInBg); + p.setBrush(_st.files.iconBg); PainterHighQualityEnabler hq(p); p.drawEllipse(inner); } - auto &icon = _data.fileIsAudio ? (_data.fileThumb.isNull() - ? st::historyFileInPlay + ? _st.files.iconPlay : st::historyFileThumbPlay) : _data.fileIsImage - ? st::historyFileInImage - : st::historyFileInDocument; + ? _st.files.iconImage + : _st.files.iconDocument; icon.paintInCenter(p, inner); } else { QRect rthumb( @@ -119,7 +120,7 @@ void AbstractSingleFilePreview::paintEvent(QPaintEvent *e) { p.drawPixmap(rthumb.topLeft(), _data.fileThumb); } p.setFont(st::semiboldFont); - p.setPen(st::historyFileNameInFg); + p.setPen(_st.files.nameFg); p.drawTextLeft( x + nameleft, y + nametop, width(), @@ -127,7 +128,7 @@ void AbstractSingleFilePreview::paintEvent(QPaintEvent *e) { _data.nameWidth); p.setFont(st::normalFont); - p.setPen(st::mediaInFg); + p.setPen(_st.files.statusFg); p.drawTextLeft( x + nameleft, y + statustop, @@ -167,7 +168,7 @@ void AbstractSingleFilePreview::updateTextWidthFor(Data &data) { - st.thumbSize - st.thumbSkip // Right buttons. - - st::sendBoxAlbumGroupButtonFile.width * buttonsCount + - _st.files.buttonFile.width * buttonsCount - st::sendBoxAlbumGroupEditInternalSkip * buttonsCount - st::sendBoxAlbumGroupSkipRight; data.nameWidth = st::semiboldFont->width(data.name); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.h index cbb0ae30a..f14992142 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.h @@ -11,13 +11,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/attach/attach_controls.h" #include "base/object_ptr.h" +namespace style { +struct ComposeControls; +} // namespace style + namespace Ui { class IconButton; class AbstractSingleFilePreview : public AbstractSinglePreview { public: - AbstractSingleFilePreview(QWidget *parent, AttachControls::Type type); + AbstractSingleFilePreview( + QWidget *parent, + const style::ComposeControls &st, + AttachControls::Type type); ~AbstractSingleFilePreview(); [[nodiscard]] rpl::producer<> deleteRequests() const override; @@ -46,6 +53,7 @@ private: void updateTextWidthFor(Data &data); + const style::ComposeControls &_st; const AttachControls::Type _type; Data _data; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp index 59790b5d6..29f38cdde 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "styles/style_boxes.h" #include "styles/style_chat.h" +#include "styles/style_chat_helpers.h" #include "styles/style_layers.h" #include "styles/style_menu_icons.h" @@ -30,8 +31,10 @@ constexpr auto kMinPreviewWidth = 20; AbstractSingleMediaPreview::AbstractSingleMediaPreview( QWidget *parent, + const style::ComposeControls &st, AttachControls::Type type) : AbstractSinglePreview(parent) +, _st(st) , _minThumbH(st::sendBoxAlbumGroupSize.height() + st::sendBoxAlbumGroupSkipTop * 2) , _controls(base::make_unique_q(this, type)) { @@ -173,7 +176,7 @@ void AbstractSingleMediaPreview::paintEvent(QPaintEvent *e) { _previewTop, _previewLeft - padding.left(), _previewHeight, - st::confirmBg); + _st.files.confirmBg); } if ((_previewLeft + _previewWidth) < (width() - padding.right())) { p.fillRect( @@ -181,7 +184,7 @@ void AbstractSingleMediaPreview::paintEvent(QPaintEvent *e) { _previewTop, width() - padding.right() - _previewLeft - _previewWidth, _previewHeight, - st::confirmBg); + _st.files.confirmBg); } if (_previewTop > 0) { p.fillRect( @@ -189,7 +192,7 @@ void AbstractSingleMediaPreview::paintEvent(QPaintEvent *e) { 0, width() - padding.right() - padding.left(), height(), - st::confirmBg); + _st.files.confirmBg); } } @@ -264,14 +267,15 @@ void AbstractSingleMediaPreview::showContextMenu(QPoint position) { } _menu = base::make_unique_q( this, - st::popupMenuWithIcons); + _st.tabbed.menu); + const auto &icons = _st.tabbed.icons; const auto spoilered = hasSpoiler(); _menu->addAction(spoilered ? tr::lng_context_disable_spoiler(tr::now) : tr::lng_context_spoiler_effect(tr::now), [=] { setSpoiler(!spoilered); - }, spoilered ? &st::menuIconSpoilerOff : &st::menuIconSpoiler); + }, spoilered ? &icons.menuSpoilerOff : &icons.menuSpoiler); if (_menu->empty()) { _menu = nullptr; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h index ef85bf0d2..3bacc6e6d 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h @@ -12,13 +12,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/attach/attach_send_files_way.h" #include "ui/abstract_button.h" +namespace style { +struct ComposeControls; +} // namespace style + namespace Ui { class PopupMenu; class AbstractSingleMediaPreview : public AbstractSinglePreview { public: - AbstractSingleMediaPreview(QWidget *parent, AttachControls::Type type); + AbstractSingleMediaPreview( + QWidget *parent, + const style::ComposeControls &st, + AttachControls::Type type); ~AbstractSingleMediaPreview(); void setSendWay(SendFilesWay way); @@ -60,6 +67,7 @@ private: void applyCursor(style::cursor cursor); void showContextMenu(QPoint position); + const style::ComposeControls &_st; SendFilesWay _sendWay; bool _animated = false; QPixmap _preview; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.cpp index 24e46d3d8..d33681b9f 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.cpp @@ -28,9 +28,11 @@ constexpr auto kDragDuration = crl::time(200); AlbumPreview::AlbumPreview( QWidget *parent, + const style::ComposeControls &st, gsl::span items, SendFilesWay way) : RpWidget(parent) +, _st(st) , _sendWay(way) , _dragTimer([=] { switchToDrag(); }) { setMouseTracking(true); @@ -135,6 +137,7 @@ void AlbumPreview::prepareThumbs(gsl::span items) { _thumbs.reserve(count); for (auto i = 0; i != count; ++i) { _thumbs.push_back(std::make_unique( + _st, items[i], layout[i], this, diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.h index ccb64970e..7811910da 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/attach/attach_send_files_way.h" #include "base/timer.h" +namespace style { +struct ComposeControls; +} // namespace style + namespace Ui { struct PreparedFile; @@ -22,6 +26,7 @@ class AlbumPreview final : public RpWidget { public: AlbumPreview( QWidget *parent, + const style::ComposeControls &st, gsl::span items, SendFilesWay way); ~AlbumPreview(); @@ -86,6 +91,7 @@ private: void showContextMenu(not_null thumb, QPoint position); + const style::ComposeControls &_st; SendFilesWay _sendWay; style::cursor _cursor = style::cur_default; std::vector _order; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp index 2e71553ab..2f847c7c0 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp @@ -26,13 +26,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { AlbumThumbnail::AlbumThumbnail( + const style::ComposeControls &st, const PreparedFile &file, const GroupMediaLayout &layout, QWidget *parent, Fn repaint, Fn editCallback, Fn deleteCallback) -: _layout(layout) +: _st(st) +, _layout(layout) , _fullPreview(file.preview) , _shrinkSize(int(std::ceil(st::roundRadiusLarge / 1.4))) , _isPhoto(file.type == PreparedFile::Type::Photo) @@ -60,8 +62,8 @@ AlbumThumbnail::AlbumThumbnail( .outer = { imageWidth, imageHeight }, })); - const auto &st = st::attachPreviewThumbLayout; - const auto idealSize = st.thumbSize * style::DevicePixelRatio(); + const auto &layoutSt = st::attachPreviewThumbLayout; + const auto idealSize = layoutSt.thumbSize * style::DevicePixelRatio(); const auto fileThumbSize = (previewWidth > previewHeight) ? QSize(previewWidth * idealSize / previewHeight, idealSize) : QSize(idealSize, previewHeight * idealSize / previewWidth); @@ -70,12 +72,12 @@ AlbumThumbnail::AlbumThumbnail( fileThumbSize, { .options = Option::RoundSmall, - .outer = { st.thumbSize, st.thumbSize }, + .outer = { layoutSt.thumbSize, layoutSt.thumbSize }, })); const auto availableFileWidth = st::sendMediaPreviewSize - - st.thumbSize - - st.thumbSkip + - layoutSt.thumbSize + - layoutSt.thumbSkip // Right buttons. - st::sendBoxAlbumGroupButtonFile.width * 2 - st::sendBoxAlbumGroupEditInternalSkip * 2 @@ -99,8 +101,8 @@ AlbumThumbnail::AlbumThumbnail( } _statusWidth = st::normalFont->width(_status); - _editMedia.create(parent, st::sendBoxAlbumGroupButtonFile); - _deleteMedia.create(parent, st::sendBoxAlbumGroupButtonFile); + _editMedia.create(parent, _st.files.buttonFile); + _deleteMedia.create(parent, _st.files.buttonFile); const auto duration = st::historyAttach.ripple.hideDuration; _editMedia->setClickedCallback([=] { @@ -108,8 +110,8 @@ AlbumThumbnail::AlbumThumbnail( }); _deleteMedia->setClickedCallback(deleteCallback); - _editMedia->setIconOverride(&st::sendBoxAlbumGroupEditButtonIconFile); - _deleteMedia->setIconOverride(&st::sendBoxAlbumGroupDeleteButtonIconFile); + _editMedia->setIconOverride(&_st.files.buttonFileEdit); + _deleteMedia->setIconOverride(&_st.files.buttonFileDelete); setSpoiler(file.spoiler); setButtonVisible(false); @@ -482,7 +484,7 @@ void AlbumThumbnail::paintFile( p.drawPixmap(left, top, _fileThumb); p.setFont(st::semiboldFont); - p.setPen(st::historyFileNameInFg); + p.setPen(_st.files.nameFg); p.drawTextLeft( textLeft, top + st.nameTop, @@ -490,7 +492,7 @@ void AlbumThumbnail::paintFile( _name, _nameWidth); p.setFont(st::normalFont); - p.setPen(st::mediaInFg); + p.setPen(_st.files.statusFg); p.drawTextLeft( textLeft, top + st.statusTop, diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.h b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.h index d85bd9c32..c3ffac584 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/round_rect.h" #include "base/object_ptr.h" +namespace style { +struct ComposeControls; +} // namespace style + namespace Ui { struct PreparedFile; @@ -23,6 +27,7 @@ class SpoilerAnimation; class AlbumThumbnail final { public: AlbumThumbnail( + const style::ComposeControls &st, const PreparedFile &file, const GroupMediaLayout &layout, QWidget *parent, @@ -78,6 +83,7 @@ private: float64 shrinkProgress); void paintPlayVideo(QPainter &p, QRect geometry); + const style::ComposeControls &_st; GroupMediaLayout _layout; std::optional _animateFromGeometry; const QImage _fullPreview; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_controls.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_controls.cpp index addfc3a9a..7ad686350 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_controls.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_controls.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/chat/attach/attach_controls.h" -#include "styles/style_boxes.h" +#include "styles/style_chat_helpers.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_file_preview.cpp index 7973f0589..1c235c96b 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_file_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_file_preview.cpp @@ -37,9 +37,10 @@ AttachControls::Type CheckControlsType( ItemSingleFilePreview::ItemSingleFilePreview( QWidget *parent, + const style::ComposeControls &st, not_null item, AttachControls::Type type) -: AbstractSingleFilePreview(parent, CheckControlsType(item, type)) { +: AbstractSingleFilePreview(parent, st, CheckControlsType(item, type)) { const auto media = item->media(); Assert(media != nullptr); const auto document = media->document(); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_file_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_file_preview.h index 8a25457fd..3e8baafcd 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_file_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_file_preview.h @@ -25,6 +25,7 @@ class ItemSingleFilePreview final : public AbstractSingleFilePreview { public: ItemSingleFilePreview( QWidget *parent, + const style::ComposeControls &st, not_null item, AttachControls::Type type); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp index a04d75e4c..eb06d363b 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp @@ -32,10 +32,11 @@ using namespace ::Media::Streaming; ItemSingleMediaPreview::ItemSingleMediaPreview( QWidget *parent, + const style::ComposeControls &st, Fn gifPaused, not_null item, AttachControls::Type type) -: AbstractSingleMediaPreview(parent, type) +: AbstractSingleMediaPreview(parent, st, type) , _gifPaused(std::move(gifPaused)) , _fullId(item->fullId()) { const auto media = item->media(); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.h index 07b93b73b..69c6604a4 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.h @@ -32,6 +32,7 @@ class ItemSingleMediaPreview final : public AbstractSingleMediaPreview { public: ItemSingleMediaPreview( QWidget *parent, + const style::ComposeControls &st, Fn gifPaused, not_null item, AttachControls::Type type); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp index afce50d72..4c50ee2dd 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp @@ -19,9 +19,10 @@ namespace Ui { SingleFilePreview::SingleFilePreview( QWidget *parent, + const style::ComposeControls &st, const PreparedFile &file, AttachControls::Type type) -: AbstractSingleFilePreview(parent, type) { +: AbstractSingleFilePreview(parent, st, type) { preparePreview(file); } diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h index f1b3b94ec..24647c917 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h @@ -17,6 +17,7 @@ class SingleFilePreview final : public AbstractSingleFilePreview { public: SingleFilePreview( QWidget *parent, + const style::ComposeControls &st, const PreparedFile &file, AttachControls::Type type = AttachControls::Type::Full); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp index 41ee35c87..ed250540c 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp @@ -16,6 +16,7 @@ namespace Ui { SingleMediaPreview *SingleMediaPreview::Create( QWidget *parent, + const style::ComposeControls &st, Fn gifPaused, const PreparedFile &file, AttachControls::Type type) { @@ -43,6 +44,7 @@ SingleMediaPreview *SingleMediaPreview::Create( } return CreateChild( parent, + st, std::move(gifPaused), preview, animated, @@ -54,6 +56,7 @@ SingleMediaPreview *SingleMediaPreview::Create( SingleMediaPreview::SingleMediaPreview( QWidget *parent, + const style::ComposeControls &st, Fn gifPaused, QImage preview, bool animated, @@ -61,7 +64,7 @@ SingleMediaPreview::SingleMediaPreview( bool spoiler, const QString &animatedPreviewPath, AttachControls::Type type) -: AbstractSingleMediaPreview(parent, type) +: AbstractSingleMediaPreview(parent, st, type) , _gifPaused(std::move(gifPaused)) , _sticker(sticker) { Expects(!preview.isNull()); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h index 546de452d..ac5d4f84f 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.h @@ -22,12 +22,14 @@ class SingleMediaPreview final : public AbstractSingleMediaPreview { public: static SingleMediaPreview *Create( QWidget *parent, + const style::ComposeControls &st, Fn gifPaused, const PreparedFile &file, AttachControls::Type type = AttachControls::Type::Full); SingleMediaPreview( QWidget *parent, + const style::ComposeControls &st, Fn gifPaused, QImage preview, bool animated, diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index 557f5ec81..2deb51c8d 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -7,7 +7,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ using "ui/basic.style"; using "ui/widgets/widgets.style"; -using "settings/settings.style"; +using "ui/layers/layers.style"; + +PremiumLimits { + boxLabel: FlatLabel; + nonPremiumBg: color; + nonPremiumFg: color; +} + +defaultPremiumBoxLabel: FlatLabel(defaultFlatLabel) { + minWidth: 220px; + align: align(topleft); + style: TextStyle(boxTextStyle) { + lineHeight: 22px; + } +} +defaultPremiumLimits: PremiumLimits { + boxLabel: defaultPremiumBoxLabel; + nonPremiumBg: windowBgOver; + nonPremiumFg: windowFg; +} // Preview. premiumPreviewBox: Box(defaultBox) { @@ -144,8 +163,10 @@ premiumGiftUserpicPadding: margins(10px, 27px, 18px, 13px); premiumGiftTitlePadding: margins(18px, 0px, 18px, 0px); premiumGiftAboutPadding: margins(18px, 5px, 18px, 23px); premiumGiftTermsPadding: margins(18px, 27px, 18px, 0px); - -premiumGiftTerms: FlatLabel(settingLocalPasscodeDescription) { +premiumGiftTerms: FlatLabel(defaultFlatLabel) { + minWidth: 256px; + align: align(top); + textFg: windowSubTextFg; style: TextStyle(defaultTextStyle) { font: font(11px); linkFont: font(11px); diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp index 29facc90e..2e9972257 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp @@ -596,12 +596,14 @@ class Line final : public Ui::RpWidget { public: Line( not_null parent, + const style::PremiumLimits &st, int max, TextFactory textFactory, int min, float64 ratio); Line( not_null parent, + const style::PremiumLimits &st, QString max, QString min, float64 ratio); @@ -614,6 +616,8 @@ protected: private: void recache(const QSize &s); + const style::PremiumLimits &_st; + int _leftWidth = 0; int _rightWidth = 0; @@ -631,12 +635,14 @@ private: Line::Line( not_null parent, + const style::PremiumLimits &st, int max, TextFactory textFactory, int min, float64 ratio) : Line( parent, + st, max ? textFactory(max) : QString(), min ? textFactory(min) : QString(), ratio) { @@ -644,10 +650,12 @@ Line::Line( Line::Line( not_null parent, + const style::PremiumLimits &st, QString max, QString min, float64 ratio) : Ui::RpWidget(parent) +, _st(st) , _leftText(st::semiboldTextStyle, tr::lng_premium_free(tr::now)) , _rightText(st::semiboldTextStyle, tr::lng_premium(tr::now)) , _rightLabel(st::semiboldTextStyle, max) @@ -689,7 +697,7 @@ void Line::paintEvent(QPaintEvent *event) { + _leftText.maxWidth() + 3 * textPadding; if (_leftWidth >= leftMinWidth) { - p.setPen(st::windowFg); + p.setPen(_st.nonPremiumFg); _leftLabel.drawRight( p, textPadding, @@ -751,7 +759,7 @@ void Line::recache(const QSize &s) { halfRect.setLeft(halfRect.center().x()); pathRect.addRect(halfRect); - p.fillPath(pathRound(_leftWidth) + pathRect, st::windowBgOver); + p.fillPath(pathRound(_leftWidth) + pathRect, _st.nonPremiumBg); _leftPixmap = std::move(leftPixmap); } @@ -811,16 +819,18 @@ void AddBubbleRow( void AddLimitRow( not_null parent, + const style::PremiumLimits &st, QString max, QString min, float64 ratio) { parent->add( - object_ptr(parent, max, min, ratio), + object_ptr(parent, st, max, min, ratio), st::boxRowPadding); } void AddLimitRow( not_null parent, + const style::PremiumLimits &st, int max, std::optional> phrase, int min, @@ -828,6 +838,7 @@ void AddLimitRow( const auto factory = ProcessTextFactory(phrase); AddLimitRow( parent, + st, max ? factory(max) : QString(), min ? factory(min) : QString(), ratio); @@ -1009,6 +1020,7 @@ QGradientStops GiftGradientStops() { void ShowListBox( not_null box, + const style::PremiumLimits &st, std::vector entries) { const auto &stLabel = st::defaultFlatLabel; @@ -1036,6 +1048,7 @@ void ShowListBox( const auto limitRow = content->add( object_ptr( content, + st, entry.rightNumber, TextFactory([=, text = ProcessTextFactory(std::nullopt)]( int n) { diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.h b/Telegram/SourceFiles/ui/effects/premium_graphics.h index de8fb6cb1..089758d93 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.h +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/round_checkbox.h" +namespace style { +struct PremiumLimits; +} // namespace style + namespace tr { template struct phrase; @@ -48,12 +52,14 @@ void AddBubbleRow( void AddLimitRow( not_null parent, + const style::PremiumLimits &st, QString max, QString min = {}, float64 ratio = kLimitRowRatio); void AddLimitRow( not_null parent, + const style::PremiumLimits &st, int max, std::optional> phrase, int min = 0, @@ -90,6 +96,7 @@ struct ListEntry final { }; void ShowListBox( not_null box, + const style::PremiumLimits &st, std::vector entries); void AddGiftOptions( diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 8ec809049..4e2563f79 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -113,6 +113,8 @@ class MainWindowShow final : public ChatHelpers::Show { public: explicit MainWindowShow(not_null controller); + void activate() override; + void showOrHideBoxOrLayer( std::variant< v::null_t, @@ -151,6 +153,12 @@ MainWindowShow::MainWindowShow(not_null controller) : _window(base::make_weak(controller)) { } +void MainWindowShow::activate() { + if (const auto window = _window.get()) { + Window::ActivateWindow(window); + } +} + void MainWindowShow::showOrHideBoxOrLayer( std::variant< v::null_t, @@ -244,10 +252,7 @@ void MainWindowShow::processChosenSticker( } // namespace void ActivateWindow(not_null controller) { - const auto window = controller->widget(); - window->raise(); - window->activateWindow(); - Ui::ActivateWindowDelayed(window); + Ui::ActivateWindow(controller->widget()); } bool IsPaused(