Implement full theming of attachments in stories.

This commit is contained in:
John Preston 2023-05-18 17:46:24 +04:00
parent ae4d660c38
commit 2c5d990e1c
53 changed files with 1113 additions and 353 deletions

View File

@ -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;

View File

@ -246,12 +246,12 @@ EditCaptionBox::EditCaptionBox(
, _scroll(base::make_unique_q<Ui::ScrollArea>(this, st::boxScroll))
, _field(base::make_unique_q<Ui::InputField>(
this,
st::confirmCaptionArea,
st::defaultComposeFiles.caption,
Ui::InputField::Mode::MultiLine,
tr::lng_photo_caption()))
, _emojiToggle(base::make_unique_q<Ui::EmojiButton>(
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<Ui::ItemSingleMediaPreview>(
this,
st::defaultComposeControls,
gifPaused,
_historyItem,
Ui::AttachControls::Type::EditOnly);
@ -410,6 +411,7 @@ void EditCaptionBox::rebuildPreview() {
} else {
_content.reset(Ui::CreateChild<Ui::ItemSingleFilePreview>(
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<Ui::SingleFilePreview>(
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;
}

View File

@ -404,6 +404,7 @@ std::unique_ptr<PeerListRow> PublicsController::createRow(
void SimpleLimitBox(
not_null<Ui::GenericBox*> box,
const style::PremiumLimits *stOverride,
not_null<Main::Session*> session,
bool premiumPossible,
rpl::producer<QString> 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<Ui::GenericBox*> box,
const style::PremiumLimits *stOverride,
not_null<Main::Session*> session,
rpl::producer<QString> title,
rpl::producer<TextWithEntities> 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<Ui::GenericBox*> box,
not_null<Main::Session*> 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<Ui::GenericBox*> box,
not_null<Main::Session*> 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<Ui::GenericBox*> box,
not_null<Main::Session*> 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));

View File

@ -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<Ui::GenericBox*> box,
not_null<Main::Session*> session,
int remove);
int remove,
const style::PremiumLimits *stOverride = nullptr);
void CaptionLimitReachedBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session,
int remove);
int remove,
const style::PremiumLimits *stOverride = nullptr);
void FileSizeLimitBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session,
uint64 fileSizeBytes);
uint64 fileSizeBytes,
const style::PremiumLimits *stOverride = nullptr);
void AccountsLimitBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session);

View File

@ -76,7 +76,7 @@ bool operator==(const Descriptor &a, const Descriptor &b) {
struct Preload {
Descriptor descriptor;
std::shared_ptr<Data::DocumentMedia> media;
base::weak_ptr<Window::SessionController> controller;
std::weak_ptr<ChatHelpers::Show> show;
};
[[nodiscard]] std::vector<Preload> &Preloads() {
@ -168,7 +168,7 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
[[nodiscard]] not_null<Ui::RpWidget*> StickerPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
const std::shared_ptr<Data::DocumentMedia> &media,
Fn<void()> readyCallback = nullptr) {
using namespace HistoryView;
@ -194,6 +194,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
struct State {
std::unique_ptr<Lottie::SinglePlayer> lottie;
std::unique_ptr<Lottie::SinglePlayer> effect;
style::owned_color pathFg = style::owned_color(
QColor(255, 255, 255, 64));
std::unique_ptr<Ui::PathShiftGradient> pathGradient;
bool readyInvoked = false;
};
@ -239,15 +241,17 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &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<Ui::PathShiftGradient>(
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<Data::DocumentMedia> &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<Data::DocumentMedia> &media) {
[[nodiscard]] not_null<Ui::RpWidget*> StickersPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
Fn<void()> readyCallback) {
const auto result = Ui::CreateChild<Ui::RpWidget>(parent.get());
result->show();
@ -327,7 +331,7 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &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<State>();
const auto create = [=](std::shared_ptr<Data::DocumentMedia> media) {
const auto outer = Ui::CreateChild<Ui::RpWidget>(result);
@ -340,7 +344,7 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
[[maybe_unused]] const auto sticker = StickerPreview(
outer,
controller,
show,
media,
state->singleReadyCallback);
@ -520,7 +524,7 @@ struct VideoPreviewDocument {
[[nodiscard]] not_null<Ui::RpWidget*> VideoPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
not_null<DocumentData*> document,
bool alignToBottom,
Fn<void()> readyCallback) {
@ -683,7 +687,7 @@ struct VideoPreviewDocument {
[[nodiscard]] not_null<Ui::RpWidget*> GenericPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
PremiumPreview section,
Fn<void()> readyCallback) {
const auto result = Ui::CreateChild<Ui::RpWidget>(parent.get());
@ -699,7 +703,7 @@ struct VideoPreviewDocument {
std::vector<std::shared_ptr<Data::DocumentMedia>> medias;
Ui::RpWidget *single = nullptr;
};
const auto session = &controller->session();
const auto session = &show->session();
const auto state = lifetime.make_state<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<Ui::RpWidget*> GenerateDefaultPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
PremiumPreview section,
Fn<void()> 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<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
const Descriptor &descriptor,
const std::shared_ptr<Data::DocumentMedia> &media,
const QImage &back) {
@ -825,7 +833,7 @@ void PreviewBox(
};
const auto state = outer->lifetime().make_state<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<Ui::GradientButton>::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<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
const Descriptor &descriptor,
const std::shared_ptr<Data::DocumentMedia> &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<Window::SessionController*> controller, QImage back) {
void Show(std::shared_ptr<ChatHelpers::Show> 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<Window::SessionController*> controller, QImage back) {
}
void Show(
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> 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<Window::SessionController*> controller,
not_null<DocumentData*> document) {
Show(controller, Descriptor{
Show(controller->uiShow(), Descriptor{
.section = PremiumPreview::Stickers,
.requestedSticker = document,
});
@ -1188,7 +1204,14 @@ void ShowPremiumPreviewBox(
not_null<Window::SessionController*> controller,
PremiumPreview section,
Fn<void(not_null<Ui::BoxContent*>)> shown) {
Show(controller, Descriptor{
ShowPremiumPreviewBox(controller->uiShow(), section, std::move(shown));
}
void ShowPremiumPreviewBox(
std::shared_ptr<ChatHelpers::Show> show,
PremiumPreview section,
Fn<void(not_null<Ui::BoxContent*>)> shown) {
Show(std::move(show), Descriptor{
.section = section,
.shownCallback = std::move(shown),
});
@ -1198,7 +1221,7 @@ void ShowPremiumPreviewToBuy(
not_null<Window::SessionController*> controller,
PremiumPreview section,
Fn<void()> 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<Ui::GradientButton> CreateUnlockButton(

View File

@ -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<void(not_null<Ui::BoxContent*>)> shown = nullptr);
void ShowPremiumPreviewBox(
std::shared_ptr<ChatHelpers::Show> show,
PremiumPreview section,
Fn<void(not_null<Ui::BoxContent*>)> shown = nullptr);
void ShowPremiumPreviewToBuy(
not_null<Window::SessionController*> controller,
PremiumPreview section,

View File

@ -137,13 +137,19 @@ SendFilesLimits DefaultLimitsForPeer(not_null<PeerData*> peer) {
SendFilesCheck DefaultCheckForPeer(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer) {
return DefaultCheckForPeer(controller->uiShow(), peer);
}
SendFilesCheck DefaultCheckForPeer(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> 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<QWidget*> parent,
const style::ComposeControls &st,
not_null<std::vector<Ui::PreparedFile>*> items,
int from,
int till,
@ -170,20 +177,24 @@ SendFilesBox::Block::Block(
if (_isAlbum) {
const auto preview = Ui::CreateChild<Ui::AlbumPreview>(
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<Ui::SingleFilePreview>(parent.get(), first));
_preview.reset(Ui::CreateChild<Ui::SingleFilePreview>(
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<Ui::PopupMenu>(
top,
st::popupMenuExpandedSeparator);
const auto &tabbed = _st.tabbed;
const auto &icons = tabbed.icons;
_menu = base::make_unique_q<Ui::PopupMenu>(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<bool>(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<ChatHelpers::TabbedPanel>(
container,
_controller,
object_ptr<Selector>(
nullptr,
_controller->uiShow(),
Window::GifPauseReason::Layer,
Selector::Mode::EmojiOnly));
ChatHelpers::TabbedPanelDescriptor{
.ownedSelector = object_ptr<Selector>(
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<const QMimeData*> data) const {
}
bool SendFilesBox::addFiles(not_null<const QMimeData*> 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() {

View File

@ -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<bool(
[[nodiscard]] SendFilesCheck DefaultCheckForPeer(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer);
[[nodiscard]] SendFilesCheck DefaultCheckForPeer(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer);
using SendFilesConfirmed = Fn<void(
Ui::PreparedList &&list,
Ui::SendFilesWay way,
TextWithTags &&caption,
Api::SendOptions options,
bool ctrlShiftEnter)>;
struct SendFilesBoxDescriptor {
std::shared_ptr<ChatHelpers::Show> show;
Ui::PreparedList list;
TextWithTags caption;
SendFilesLimits limits = {};
SendFilesCheck check;
Api::SendType sendType = {};
SendMenu::Type sendMenuType = {};
const style::ComposeControls *stOverride = nullptr;
SendFilesConfirmed confirmed;
Fn<void()> 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<void(
Ui::PreparedList &&list,
Ui::SendFilesWay way,
TextWithTags &&caption,
Api::SendOptions options,
bool ctrlShiftEnter)> callback) {
void setConfirmedCallback(SendFilesConfirmed callback) {
_confirmedCallback = std::move(callback);
}
void setCancelledCallback(Fn<void()> callback) {
@ -116,6 +139,7 @@ private:
public:
Block(
not_null<QWidget*> parent,
const style::ComposeControls &st,
not_null<std::vector<Ui::PreparedFile>*> items,
int from,
int till,
@ -201,7 +225,8 @@ private:
void enqueueNextPrepare();
void addPreparedAsyncFile(Ui::PreparedFile &&file);
const not_null<Window::SessionController*> _controller;
const std::shared_ptr<ChatHelpers::Show> _show;
const style::ComposeControls &_st;
const Api::SendType _sendType = Api::SendType();
QString _titleText;
@ -211,15 +236,10 @@ private:
std::optional<int> _removingIndex;
SendFilesLimits _limits = {};
SendMenu::Type _sendMenuType = SendMenu::Type();
SendMenu::Type _sendMenuType = {};
SendFilesCheck _check;
Fn<void(
Ui::PreparedList &&list,
Ui::SendFilesWay way,
TextWithTags &&caption,
Api::SendOptions options,
bool ctrlShiftEnter)> _confirmedCallback;
SendFilesConfirmed _confirmedCallback;
Fn<void()> _cancelledCallback;
bool _confirmed = false;

View File

@ -124,7 +124,7 @@ void Show::showOrHideBoxOrLayer(
} else if (const auto panel = _panel.get()) {
panel->hideLayer(animated);
}
}
}
not_null<QWidget*> Show::toastParent() const {
const auto panel = _panel.get();

View File

@ -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;
}

View File

@ -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;

View File

@ -726,6 +726,7 @@ object_ptr<TabbedSelector::InnerFooter> EmojiListWidget::createFooter() {
.paused = footerPaused,
.parent = this,
.st = &st(),
.features = { .stickersSettings = false },
});
_footer = result;

View File

@ -172,6 +172,7 @@ object_ptr<TabbedSelector::InnerFooter> GifsListWidget::createFooter() {
.paused = pausedMethod(),
.parent = this,
.st = &st(),
.features = { .stickersSettings = false },
});
_footer = result;
_chosenSetId = Data::Stickers::RecentSetId;

View File

@ -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<StickersBox>(_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<StickersBox>(_show, Section::Installed, _isMasks));
Core::App().hideMediaView();
Window::ActivateWindow(window);
}
});
session().downloaderTaskFinished(

View File

@ -30,7 +30,7 @@ struct TabbedPanelDescriptor {
Window::SessionController *regularWindow = nullptr;
object_ptr<TabbedSelector> ownedSelector = { nullptr };
TabbedSelector *nonOwnedSelector = nullptr;
};;
};
class TabbedPanel : public Ui::RpWidget {
public:

View File

@ -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<Ui::RpWidget*> panelContainer,
not_null<Window::SessionController*> controller)
std::shared_ptr<ChatHelpers::Show> show)
: _stickersPanel(
base::make_unique_q<ChatHelpers::TabbedPanel>(
panelContainer,
controller,
object_ptr<ChatHelpers::TabbedSelector>(
nullptr,
controller->uiShow(),
Window::GifPauseReason::Layer,
ChatHelpers::TabbedSelector::Mode::MediaEditor))) {
ChatHelpers::TabbedPanelDescriptor{
.ownedSelector = object_ptr<ChatHelpers::TabbedSelector>(
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,

View File

@ -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<Ui::RpWidget*> panelContainer,
not_null<Window::SessionController*> controller);
std::shared_ptr<ChatHelpers::Show> show);
[[nodiscard]] auto stickerChosen() const
-> rpl::producer<not_null<DocumentData*>>;

View File

@ -52,16 +52,34 @@ PhotoEditor::PhotoEditor(
std::shared_ptr<Image> 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<QWidget*> parent,
std::shared_ptr<Ui::Show> show,
std::shared_ptr<ChatHelpers::Show> sessionShow,
std::shared_ptr<Image> photo,
PhotoModifications modifications,
EditorData data)
: RpWidget(parent)
, _modifications(std::move(modifications))
, _controllers(std::make_shared<Controllers>(
controller->sessionController()
sessionShow
? std::make_unique<StickersPanelController>(
this,
controller->sessionController())
std::move(sessionShow))
: nullptr,
std::make_unique<UndoController>(),
controller->uiShow()))
std::move(show)))
, _content(base::make_unique_q<PhotoEditorContent>(
this,
photo,

View File

@ -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<Image> photo,
PhotoModifications modifications,
EditorData data = EditorData());
PhotoEditor(
not_null<QWidget*> parent,
std::shared_ptr<Ui::Show> show,
std::shared_ptr<ChatHelpers::Show> sessionShow,
std::shared_ptr<Image> photo,
PhotoModifications modifications,
EditorData data = EditorData());
void save();
[[nodiscard]] rpl::producer<PhotoModifications> doneRequests() const;

View File

@ -22,7 +22,7 @@ namespace Editor {
void OpenWithPreparedFile(
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::PreparedFile*> file,
int previewWidth,
Fn<void()> &&doneCallback) {
@ -56,13 +56,14 @@ void OpenWithPreparedFile(
const auto fileImage = std::make_shared<Image>(std::move(copy));
auto editor = base::make_unique_q<PhotoEditor>(
parent,
&controller->window(),
show,
show,
fileImage,
image->modifications);
const auto raw = editor.get();
auto layer = std::make_unique<LayerWidget>(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(

View File

@ -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<QWidget*> parent,
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::PreparedFile*> file,
int previewWidth,
Fn<void()> &&doneCallback);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<bool> 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<bool> 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<bool> 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<const QMimeData*> data,
std::optional<bool> 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<decltype(args)>(args)...);
};
auto box = Box<SendFilesBox>(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<SendingAlbum>()
: nullptr;
session().api().sendFiles(
std::move(group.list),
type,
base::take(caption),
album,
action);
}
finishSending();
}
bool ReplyArea::confirmSendingFiles(
QImage &&image,
QByteArray &&content,
std::optional<bool> 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<bool> ReplyArea::focusedValue() const {
return _controls->focusedValue();
}

View File

@ -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<const QMimeData*> data);
void uploadFile(const QByteArray &fileContent, SendMediaType type);
bool confirmSendingFiles(
QImage &&image,
QByteArray &&content,
std::optional<bool> 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<const QMimeData*> data,
std::optional<bool> overrideSendImagesAsPhotos,
const QString &insertTextOnCancel = QString());
bool showSendingFilesError(const Ui::PreparedList &list) const;
bool showSendingFilesError(
const Ui::PreparedList &list,
std::optional<bool> 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<bool> overrideSendImagesAsPhotos);
@ -58,6 +105,7 @@ private:
const std::unique_ptr<HistoryView::ComposeControls> _controls;
ReplyAreaData _data;
base::has_weak_ptr _shownUserGuard;
bool _choosingAttach = false;
rpl::lifetime _lifetime;

View File

@ -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;
}

View File

@ -287,6 +287,87 @@ struct OverlayWidget::PipWrap {
rpl::lifetime lifetime;
};
class OverlayWidget::Show final : public ChatHelpers::Show {
public:
explicit Show(not_null<OverlayWidget*> widget) : _widget(widget) {
}
void activate() override {
if (!_widget->isHidden()) {
_widget->activate();
}
}
void showOrHideBoxOrLayer(
std::variant<
v::null_t,
object_ptr<Ui::BoxContent>,
std::unique_ptr<Ui::LayerWidget>> &&layer,
Ui::LayerOptions options,
anim::type animated) const override {
_widget->_layerBg->uiShow()->showOrHideBoxOrLayer(
std::move(layer),
options,
anim::type::normal);
}
not_null<QWidget*> 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<bool> adjustShadowLeft() const override {
return rpl::single(false);
}
SendMenu::Type sendMenuType() const override {
return SendMenu::Type::SilentOnly;
}
bool showMediaPreview(
Data::FileOrigin origin,
not_null<DocumentData*> document) const override {
return false; // #TODO stories
}
bool showMediaPreview(
Data::FileOrigin origin,
not_null<PhotoData*> photo) const override {
return false; // #TODO stories
}
void processChosenSticker(
ChatHelpers::FileChosen &&chosen) const override {
_widget->_storiesStickerOrEmojiChosen.fire(std::move(chosen));
}
private:
not_null<OverlayWidget*> _widget;
};
OverlayWidget::Streamed::Streamed(
not_null<DocumentData*> document,
Data::FileOrigin origin,
@ -3936,81 +4017,10 @@ not_null<Ui::RpWidget*> OverlayWidget::storiesWrap() {
}
std::shared_ptr<ChatHelpers::Show> OverlayWidget::storiesShow() {
class Show final : public ChatHelpers::Show {
public:
explicit Show(not_null<OverlayWidget*> widget) : _widget(widget) {
}
void showBox(
object_ptr<Ui::BoxContent> 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<QWidget*> 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<bool> adjustShadowLeft() const override {
return rpl::single(false);
}
SendMenu::Type sendMenuType() const override {
return SendMenu::Type::SilentOnly;
}
bool showMediaPreview(
Data::FileOrigin origin,
not_null<DocumentData*> document) const override {
return false; // #TODO stories
}
bool showMediaPreview(
Data::FileOrigin origin,
not_null<PhotoData*> photo) const override {
return false; // #TODO stories
}
void processChosenSticker(
ChatHelpers::FileChosen &&chosen) const override {
_widget->_storiesStickerOrEmojiChosen.fire(std::move(chosen));
}
private:
not_null<OverlayWidget*> _widget;
};
return std::make_shared<Show>(this);
if (!_cachedShow) {
_cachedShow = std::make_shared<Show>(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();

View File

@ -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::View> _stories;
std::shared_ptr<Show> _cachedShow;
rpl::event_stream<> _storiesChanged;
Main::Session *_storiesSession = nullptr;
rpl::event_stream<ChatHelpers::FileChosen> _storiesStickerOrEmojiChosen;

View File

@ -1782,6 +1782,11 @@ QString LookupPremiumRef(PremiumPreview section) {
not_null<Ui::GradientButton*> CreateSubscribeButton(
SubscribeButtonArgs &&args) {
Expects(args.show || args.controller);
if (!args.show && args.controller) {
args.show = args.controller->uiShow();
}
const auto result = Ui::CreateChild<Ui::GradientButton>(
args.parent.get(),
args.gradientStops
@ -1789,9 +1794,14 @@ not_null<Ui::GradientButton*> 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<Ui::GradientButton*> 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());
}
});

View File

@ -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<Window::SessionController*> controller;
Window::SessionController *controller = nullptr;
not_null<Ui::RpWidget*> parent;
Fn<QString()> computeRef;
std::optional<rpl::producer<QString>> text;
std::optional<QGradientStops> gradientStops;
Fn<QString()> computeBotUrl; // nullable
std::shared_ptr<ChatHelpers::Show> show;
};
[[nodiscard]] not_null<Ui::GradientButton*> CreateSubscribeButton(

View File

@ -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 {

View File

@ -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);

View File

@ -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;

View File

@ -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<AttachControlsWidget>(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<Ui::PopupMenu>(
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;

View File

@ -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;

View File

@ -28,9 +28,11 @@ constexpr auto kDragDuration = crl::time(200);
AlbumPreview::AlbumPreview(
QWidget *parent,
const style::ComposeControls &st,
gsl::span<Ui::PreparedFile> items,
SendFilesWay way)
: RpWidget(parent)
, _st(st)
, _sendWay(way)
, _dragTimer([=] { switchToDrag(); }) {
setMouseTracking(true);
@ -135,6 +137,7 @@ void AlbumPreview::prepareThumbs(gsl::span<Ui::PreparedFile> items) {
_thumbs.reserve(count);
for (auto i = 0; i != count; ++i) {
_thumbs.push_back(std::make_unique<AlbumThumbnail>(
_st,
items[i],
layout[i],
this,

View File

@ -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<Ui::PreparedFile> items,
SendFilesWay way);
~AlbumPreview();
@ -86,6 +91,7 @@ private:
void showContextMenu(not_null<AlbumThumbnail*> thumb, QPoint position);
const style::ComposeControls &_st;
SendFilesWay _sendWay;
style::cursor _cursor = style::cur_default;
std::vector<int> _order;

View File

@ -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<void()> repaint,
Fn<void()> editCallback,
Fn<void()> 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,

View File

@ -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<QRect> _animateFromGeometry;
const QImage _fullPreview;

View File

@ -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 {

View File

@ -37,9 +37,10 @@ AttachControls::Type CheckControlsType(
ItemSingleFilePreview::ItemSingleFilePreview(
QWidget *parent,
const style::ComposeControls &st,
not_null<HistoryItem*> 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();

View File

@ -25,6 +25,7 @@ class ItemSingleFilePreview final : public AbstractSingleFilePreview {
public:
ItemSingleFilePreview(
QWidget *parent,
const style::ComposeControls &st,
not_null<HistoryItem*> item,
AttachControls::Type type);

View File

@ -32,10 +32,11 @@ using namespace ::Media::Streaming;
ItemSingleMediaPreview::ItemSingleMediaPreview(
QWidget *parent,
const style::ComposeControls &st,
Fn<bool()> gifPaused,
not_null<HistoryItem*> item,
AttachControls::Type type)
: AbstractSingleMediaPreview(parent, type)
: AbstractSingleMediaPreview(parent, st, type)
, _gifPaused(std::move(gifPaused))
, _fullId(item->fullId()) {
const auto media = item->media();

View File

@ -32,6 +32,7 @@ class ItemSingleMediaPreview final : public AbstractSingleMediaPreview {
public:
ItemSingleMediaPreview(
QWidget *parent,
const style::ComposeControls &st,
Fn<bool()> gifPaused,
not_null<HistoryItem*> item,
AttachControls::Type type);

View File

@ -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);
}

View File

@ -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);

View File

@ -16,6 +16,7 @@ namespace Ui {
SingleMediaPreview *SingleMediaPreview::Create(
QWidget *parent,
const style::ComposeControls &st,
Fn<bool()> gifPaused,
const PreparedFile &file,
AttachControls::Type type) {
@ -43,6 +44,7 @@ SingleMediaPreview *SingleMediaPreview::Create(
}
return CreateChild<SingleMediaPreview>(
parent,
st,
std::move(gifPaused),
preview,
animated,
@ -54,6 +56,7 @@ SingleMediaPreview *SingleMediaPreview::Create(
SingleMediaPreview::SingleMediaPreview(
QWidget *parent,
const style::ComposeControls &st,
Fn<bool()> 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());

View File

@ -22,12 +22,14 @@ class SingleMediaPreview final : public AbstractSingleMediaPreview {
public:
static SingleMediaPreview *Create(
QWidget *parent,
const style::ComposeControls &st,
Fn<bool()> gifPaused,
const PreparedFile &file,
AttachControls::Type type = AttachControls::Type::Full);
SingleMediaPreview(
QWidget *parent,
const style::ComposeControls &st,
Fn<bool()> gifPaused,
QImage preview,
bool animated,

View File

@ -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);

View File

@ -596,12 +596,14 @@ class Line final : public Ui::RpWidget {
public:
Line(
not_null<Ui::RpWidget*> parent,
const style::PremiumLimits &st,
int max,
TextFactory textFactory,
int min,
float64 ratio);
Line(
not_null<Ui::RpWidget*> 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<Ui::RpWidget*> 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<Ui::RpWidget*> 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<Ui::VerticalLayout*> parent,
const style::PremiumLimits &st,
QString max,
QString min,
float64 ratio) {
parent->add(
object_ptr<Line>(parent, max, min, ratio),
object_ptr<Line>(parent, st, max, min, ratio),
st::boxRowPadding);
}
void AddLimitRow(
not_null<Ui::VerticalLayout*> parent,
const style::PremiumLimits &st,
int max,
std::optional<tr::phrase<lngtag_count>> 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<Ui::GenericBox*> box,
const style::PremiumLimits &st,
std::vector<ListEntry> entries) {
const auto &stLabel = st::defaultFlatLabel;
@ -1036,6 +1048,7 @@ void ShowListBox(
const auto limitRow = content->add(
object_ptr<Line>(
content,
st,
entry.rightNumber,
TextFactory([=, text = ProcessTextFactory(std::nullopt)](
int n) {

View File

@ -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 <typename ...>
struct phrase;
@ -48,12 +52,14 @@ void AddBubbleRow(
void AddLimitRow(
not_null<Ui::VerticalLayout*> parent,
const style::PremiumLimits &st,
QString max,
QString min = {},
float64 ratio = kLimitRowRatio);
void AddLimitRow(
not_null<Ui::VerticalLayout*> parent,
const style::PremiumLimits &st,
int max,
std::optional<tr::phrase<lngtag_count>> phrase,
int min = 0,
@ -90,6 +96,7 @@ struct ListEntry final {
};
void ShowListBox(
not_null<Ui::GenericBox*> box,
const style::PremiumLimits &st,
std::vector<ListEntry> entries);
void AddGiftOptions(

View File

@ -113,6 +113,8 @@ class MainWindowShow final : public ChatHelpers::Show {
public:
explicit MainWindowShow(not_null<SessionController*> controller);
void activate() override;
void showOrHideBoxOrLayer(
std::variant<
v::null_t,
@ -151,6 +153,12 @@ MainWindowShow::MainWindowShow(not_null<SessionController*> 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<SessionController*> controller) {
const auto window = controller->widget();
window->raise();
window->activateWindow();
Ui::ActivateWindowDelayed(window);
Ui::ActivateWindow(controller->widget());
}
bool IsPaused(