| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "chat_helpers/ttl_media_layer_widget.h"
- #include "base/event_filter.h"
- #include "data/data_document.h"
- #include "data/data_session.h"
- #include "editor/editor_layer_widget.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "history/view/history_view_element.h"
- #include "history/view/media/history_view_document.h"
- #include "lang/lang_keys.h"
- #include "main/main_session.h"
- #include "mainwidget.h"
- #include "media/audio/media_audio.h"
- #include "media/player/media_player_instance.h"
- #include "ui/chat/chat_style.h"
- #include "ui/chat/chat_theme.h"
- #include "ui/effects/path_shift_gradient.h"
- #include "ui/painter.h"
- #include "ui/rect.h"
- #include "ui/text/text_utilities.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/labels.h"
- #include "ui/widgets/tooltip.h"
- #include "ui/ui_utility.h"
- #include "window/section_widget.h" // Window::ChatThemeValueFromPeer.
- #include "window/themes/window_theme.h"
- #include "window/window_controller.h"
- #include "window/window_session_controller.h"
- #include "styles/style_chat.h"
- #include "styles/style_chat_helpers.h"
- #include "styles/style_dialogs.h"
- namespace ChatHelpers {
- namespace {
- class PreviewDelegate final : public HistoryView::DefaultElementDelegate {
- public:
- PreviewDelegate(
- not_null<QWidget*> parent,
- not_null<Ui::ChatStyle*> st,
- rpl::producer<bool> chatWideValue,
- Fn<void()> update);
- bool elementAnimationsPaused() override;
- not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
- HistoryView::Context elementContext() override;
- bool elementIsChatWide() override;
- private:
- const not_null<QWidget*> _parent;
- const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
- rpl::variable<bool> _chatWide;
- };
- PreviewDelegate::PreviewDelegate(
- not_null<QWidget*> parent,
- not_null<Ui::ChatStyle*> st,
- rpl::producer<bool> chatWideValue,
- Fn<void()> update)
- : _parent(parent)
- , _pathGradient(HistoryView::MakePathShiftGradient(st, update))
- , _chatWide(std::move(chatWideValue)) {
- }
- bool PreviewDelegate::elementAnimationsPaused() {
- return _parent->window()->isActiveWindow();
- }
- not_null<Ui::PathShiftGradient*> PreviewDelegate::elementPathShiftGradient() {
- return _pathGradient.get();
- }
- HistoryView::Context PreviewDelegate::elementContext() {
- return HistoryView::Context::TTLViewer;
- }
- bool PreviewDelegate::elementIsChatWide() {
- return _chatWide.current();
- }
- class PreviewWrap final : public Ui::RpWidget {
- public:
- PreviewWrap(
- not_null<Ui::RpWidget*> parent,
- not_null<HistoryItem*> item,
- rpl::producer<QRect> viewportValue,
- rpl::producer<bool> chatWideValue,
- rpl::producer<std::shared_ptr<Ui::ChatTheme>> theme);
- ~PreviewWrap();
- [[nodiscard]] rpl::producer<> closeRequests() const;
- private:
- void paintEvent(QPaintEvent *e) override;
- void createView();
- [[nodiscard]] bool goodItem() const;
- void clear();
- const not_null<HistoryItem*> _item;
- const std::unique_ptr<Ui::ChatStyle> _style;
- const std::unique_ptr<PreviewDelegate> _delegate;
- rpl::variable<QRect> _globalViewport;
- rpl::variable<bool> _chatWide;
- std::shared_ptr<Ui::ChatTheme> _theme;
- std::unique_ptr<HistoryView::Element> _element;
- QRect _viewport;
- QRect _elementGeometry;
- rpl::variable<QRect> _elementInner;
- rpl::lifetime _elementLifetime;
- QImage _lastFrameCache;
- rpl::event_stream<> _closeRequests;
- };
- PreviewWrap::PreviewWrap(
- not_null<Ui::RpWidget*> parent,
- not_null<HistoryItem*> item,
- rpl::producer<QRect> viewportValue,
- rpl::producer<bool> chatWideValue,
- rpl::producer<std::shared_ptr<Ui::ChatTheme>> theme)
- : RpWidget(parent)
- , _item(item)
- , _style(std::make_unique<Ui::ChatStyle>(
- item->history()->session().colorIndicesValue()))
- , _delegate(std::make_unique<PreviewDelegate>(
- parent,
- _style.get(),
- std::move(chatWideValue),
- [=] { update(_elementGeometry); }))
- , _globalViewport(std::move(viewportValue)) {
- const auto closeCallback = [=] { _closeRequests.fire({}); };
- HistoryView::TTLVoiceStops(
- item->fullId()
- ) | rpl::start_with_next([=] {
- _lastFrameCache = Ui::GrabWidgetToImage(this, _elementGeometry);
- closeCallback();
- }, lifetime());
- const auto isRound = _item
- && _item->media()
- && _item->media()->document()
- && _item->media()->document()->isVideoMessage();
- std::move(
- theme
- ) | rpl::start_with_next([=](std::shared_ptr<Ui::ChatTheme> theme) {
- _theme = std::move(theme);
- _style->apply(_theme.get());
- }, lifetime());
- const auto session = &_item->history()->session();
- session->data().viewRepaintRequest(
- ) | rpl::start_with_next([=](not_null<const HistoryView::Element*> view) {
- if (view == _element.get()) {
- update(_elementGeometry);
- }
- }, lifetime());
- session->data().itemViewRefreshRequest(
- ) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
- if (item == _item) {
- if (goodItem()) {
- createView();
- update();
- } else {
- clear();
- _closeRequests.fire({});
- }
- }
- }, lifetime());
- session->data().itemDataChanges(
- ) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
- if (item == _item) {
- _element->itemDataChanged();
- }
- }, lifetime());
- session->data().itemRemoved(
- ) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
- if (item == _item) {
- _closeRequests.fire({});
- }
- }, lifetime());
- {
- const auto close = Ui::CreateChild<Ui::RoundButton>(
- this,
- item->out()
- ? tr::lng_close()
- : tr::lng_ttl_voice_close_in(),
- st::ttlMediaButton);
- close->setFullRadius(true);
- close->setClickedCallback(closeCallback);
- close->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
- rpl::combine(
- sizeValue(),
- _elementInner.value()
- ) | rpl::start_with_next([=](QSize size, QRect inner) {
- close->moveToLeft(
- inner.x() + (inner.width() - close->width()) / 2,
- (size.height()
- - close->height()
- - st::ttlMediaButtonBottomSkip));
- }, close->lifetime());
- }
- QWidget::setAttribute(Qt::WA_OpaquePaintEvent, false);
- createView();
- {
- auto text = item->out()
- ? (isRound
- ? tr::lng_ttl_round_tooltip_out
- : tr::lng_ttl_voice_tooltip_out)(
- lt_user,
- rpl::single(
- item->history()->peer->shortName()
- ) | Ui::Text::ToRichLangValue(),
- Ui::Text::RichLangValue)
- : (isRound
- ? tr::lng_ttl_round_tooltip_in
- : tr::lng_ttl_voice_tooltip_in)(Ui::Text::RichLangValue);
- const auto tooltip = Ui::CreateChild<Ui::ImportantTooltip>(
- this,
- object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
- this,
- Ui::MakeNiceTooltipLabel(
- parent,
- std::move(text),
- st::dialogsStoriesTooltipMaxWidth,
- st::ttlMediaImportantTooltipLabel),
- st::defaultImportantTooltip.padding),
- st::dialogsStoriesTooltip);
- tooltip->toggleFast(true);
- _elementInner.value(
- ) | rpl::filter([](const QRect &inner) {
- return !inner.isEmpty();
- }) | rpl::start_with_next([=](const QRect &inner) {
- tooltip->pointAt(inner, RectPart::Top, [=](QSize size) {
- return QPoint{
- inner.x() + (inner.width() - size.width()) / 2,
- (inner.y()
- - st::normalFont->height
- - size.height()
- - st::defaultImportantTooltip.padding.top()),
- };
- });
- }, tooltip->lifetime());
- }
- }
- rpl::producer<> PreviewWrap::closeRequests() const {
- return _closeRequests.events();
- }
- bool PreviewWrap::goodItem() const {
- const auto media = _item->media();
- if (!media || !media->ttlSeconds()) {
- return false;
- }
- const auto document = media->document();
- return document
- && (document->isVoiceMessage() || document->isVideoMessage());
- }
- void PreviewWrap::createView() {
- clear();
- _element = _item->createView(_delegate.get());
- _element->initDimensions();
- rpl::combine(
- sizeValue(),
- _globalViewport.value()
- ) | rpl::start_with_next([=](QSize outer, QRect globalViewport) {
- _viewport = globalViewport.isEmpty()
- ? rect()
- : mapFromGlobal(globalViewport);
- if (_viewport.width() < st::msgMinWidth) {
- return;
- }
- _element->resizeGetHeight(_viewport.width());
- _elementGeometry = QRect(
- (_viewport.width() - _element->width()) / 2,
- (_viewport.height() - _element->height()) / 2,
- _element->width(),
- _element->height()
- ).translated(_viewport.topLeft());
- _elementInner = _element->innerGeometry().translated(
- _elementGeometry.topLeft());
- update();
- }, _elementLifetime);
- }
- void PreviewWrap::clear() {
- _elementLifetime.destroy();
- _element = nullptr;
- }
- PreviewWrap::~PreviewWrap() {
- clear();
- }
- void PreviewWrap::paintEvent(QPaintEvent *e) {
- if (!_element || _elementGeometry.isEmpty()) {
- return;
- }
- auto p = Painter(this);
- p.translate(_elementGeometry.topLeft());
- if (!_lastFrameCache.isNull()) {
- p.drawImage(0, 0, _lastFrameCache);
- } else {
- auto context = _theme->preparePaintContext(
- _style.get(),
- Rect(_element->currentSize()),
- Rect(_element->currentSize()),
- !window()->isActiveWindow());
- context.outbg = _element->hasOutLayout();
- _element->draw(p, context);
- }
- }
- rpl::producer<QRect> GlobalViewportForWindow(
- not_null<Window::SessionController*> controller) {
- const auto delegate = controller->window().floatPlayerDelegate();
- return rpl::single(rpl::empty) | rpl::then(
- delegate->floatPlayerAreaUpdates()
- ) | rpl::map([=] {
- auto section = (Media::Player::FloatSectionDelegate*)nullptr;
- delegate->floatPlayerEnumerateSections([&](
- not_null<Media::Player::FloatSectionDelegate*> check,
- Window::Column column) {
- if ((column == Window::Column::First && !section)
- || column == Window::Column::Second) {
- section = check;
- }
- });
- if (section) {
- const auto rect = section->floatPlayerAvailableRect();
- if (rect.width() >= st::msgMinWidth) {
- return rect;
- }
- }
- return QRect();
- });
- }
- } // namespace
- void ShowTTLMediaLayerWidget(
- not_null<Window::SessionController*> controller,
- not_null<HistoryItem*> item) {
- const auto parent = controller->content();
- const auto show = controller->uiShow();
- auto preview = base::make_unique_q<PreviewWrap>(
- parent,
- item,
- GlobalViewportForWindow(controller),
- controller->adaptive().chatWideValue(),
- Window::ChatThemeValueFromPeer(
- controller,
- item->history()->peer));
- preview->closeRequests(
- ) | rpl::start_with_next([=] {
- show->hideLayer();
- }, preview->lifetime());
- auto layer = std::make_unique<Editor::LayerWidget>(
- parent,
- std::move(preview));
- layer->lifetime().add([] { ::Media::Player::instance()->stop(); });
- base::install_event_filter(layer.get(), [=](not_null<QEvent*> e) {
- if (e->type() == QEvent::KeyPress) {
- const auto k = static_cast<QKeyEvent*>(e.get());
- if (k->key() == Qt::Key_Escape) {
- show->hideLayer();
- }
- return base::EventFilterResult::Cancel;
- }
- return base::EventFilterResult::Continue;
- });
- controller->showLayer(std::move(layer), Ui::LayerOption::KeepOther);
- }
- } // namespace ChatHelpers
|