| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "boxes/peers/peer_short_info_box.h"
- #include "base/event_filter.h"
- #include "core/application.h"
- #include "info/profile/info_profile_text.h"
- #include "info/profile/info_profile_values.h"
- #include "lang/lang_keys.h"
- #include "media/streaming/media_streaming_instance.h"
- #include "media/streaming/media_streaming_player.h"
- #include "ui/effects/radial_animation.h"
- #include "ui/image/image_prepare.h"
- #include "ui/painter.h"
- #include "ui/text/text_utilities.h"
- #include "ui/widgets/labels.h"
- #include "ui/widgets/menu/menu_add_action_callback.h"
- #include "ui/widgets/menu/menu_add_action_callback_factory.h"
- #include "ui/widgets/popup_menu.h"
- #include "ui/widgets/scroll_area.h"
- #include "ui/wrap/slide_wrap.h"
- #include "ui/wrap/vertical_layout.h"
- #include "ui/wrap/wrap.h"
- #include "window/window_controller.h"
- #include "window/window_session_controller.h"
- #include "styles/style_boxes.h"
- #include "styles/style_info.h"
- #include "styles/style_layers.h"
- #include "styles/style_menu_icons.h"
- namespace {
- using MenuCallback = Ui::Menu::MenuCallback;
- constexpr auto kShadowMaxAlpha = 80;
- constexpr auto kInactiveBarOpacity = 0.5;
- } // namespace
- struct PeerShortInfoCover::CustomLabelStyle {
- explicit CustomLabelStyle(const style::FlatLabel &original);
- style::complex_color textFg;
- style::FlatLabel st;
- float64 opacity = 1.;
- };
- struct PeerShortInfoCover::Radial {
- explicit Radial(Fn<void()> &&callback);
- void toggle(bool visible);
- Ui::RadialAnimation radial;
- Ui::Animations::Simple shownAnimation;
- Fn<void()> callback;
- base::Timer showTimer;
- bool shown = false;
- };
- PeerShortInfoCover::Radial::Radial(Fn<void()> &&callback)
- : radial(callback)
- , callback(callback)
- , showTimer([=] { toggle(true); }) {
- }
- void PeerShortInfoCover::Radial::toggle(bool visible) {
- if (shown == visible) {
- return;
- }
- shown = visible;
- shownAnimation.start(
- callback,
- shown ? 0. : 1.,
- shown ? 1. : 0.,
- st::fadeWrapDuration);
- }
- PeerShortInfoCover::CustomLabelStyle::CustomLabelStyle(
- const style::FlatLabel &original)
- : textFg([=, c = original.textFg]{
- auto result = c->c;
- result.setAlphaF(result.alphaF() * opacity);
- return result;
- })
- , st(original) {
- st.textFg = textFg.color();
- }
- PeerShortInfoCover::PeerShortInfoCover(
- not_null<QWidget*> parent,
- const style::ShortInfoCover &st,
- rpl::producer<QString> name,
- rpl::producer<QString> status,
- rpl::producer<PeerShortInfoUserpic> userpic,
- Fn<bool()> videoPaused)
- : _st(st)
- , _owned(parent.get())
- , _widget(_owned.data())
- , _nameStyle(std::make_unique<CustomLabelStyle>(_st.name))
- , _name(_widget.get(), std::move(name), _nameStyle->st)
- , _statusStyle(std::make_unique<CustomLabelStyle>(_st.status))
- , _status(_widget.get(), std::move(status), _statusStyle->st)
- , _roundMask(Images::CornersMask(_st.radius))
- , _roundMaskRetina(
- Images::CornersMask(_st.radius / style::DevicePixelRatio()))
- , _videoPaused(std::move(videoPaused)) {
- _widget->setCursor(_cursor);
- _widget->resize(_st.size, _st.size);
- std::move(
- userpic
- ) | rpl::start_with_next([=](PeerShortInfoUserpic &&value) {
- applyUserpic(std::move(value));
- applyAdditionalStatus(value.additionalStatus);
- }, lifetime());
- style::PaletteChanged(
- ) | rpl::start_with_next([=] {
- refreshBarImages();
- }, lifetime());
- _widget->paintRequest(
- ) | rpl::start_with_next([=] {
- auto p = QPainter(_widget.get());
- paint(p);
- }, lifetime());
- base::install_event_filter(_widget.get(), [=](not_null<QEvent*> e) {
- if (e->type() != QEvent::MouseButtonPress
- && e->type() != QEvent::MouseButtonDblClick) {
- return base::EventFilterResult::Continue;
- }
- const auto mouse = static_cast<QMouseEvent*>(e.get());
- const auto x = mouse->pos().x();
- if (mouse->button() != Qt::LeftButton) {
- return base::EventFilterResult::Continue;
- } else if (/*_index > 0 && */x < _st.size / 3) {
- _moveRequests.fire(-1);
- } else if (/*_index + 1 < _count && */x >= _st.size / 3) {
- _moveRequests.fire(1);
- }
- e->accept();
- return base::EventFilterResult::Cancel;
- });
- refreshLabelsGeometry();
- _roundedTopImage = QImage(
- QSize(_st.size, _st.radius) * style::DevicePixelRatio(),
- QImage::Format_ARGB32_Premultiplied);
- _roundedTopImage.setDevicePixelRatio(style::DevicePixelRatio());
- _roundedTopImage.fill(Qt::transparent);
- }
- PeerShortInfoCover::~PeerShortInfoCover() = default;
- not_null<Ui::RpWidget*> PeerShortInfoCover::widget() const {
- return _widget;
- }
- object_ptr<Ui::RpWidget> PeerShortInfoCover::takeOwned() {
- return std::move(_owned);
- }
- gsl::span<const QImage, 4> PeerShortInfoCover::roundMask() const {
- return _roundMask;
- }
- void PeerShortInfoCover::setScrollTop(int scrollTop) {
- _scrollTop = scrollTop;
- _widget->update();
- }
- rpl::producer<int> PeerShortInfoCover::moveRequests() const {
- return _moveRequests.events();
- }
- rpl::lifetime &PeerShortInfoCover::lifetime() {
- return _widget->lifetime();
- }
- void PeerShortInfoCover::paint(QPainter &p) {
- checkStreamedIsStarted();
- auto frame = currentVideoFrame();
- auto paused = _videoPaused && _videoPaused();
- if (!frame.isNull()) {
- frame = Images::Round(
- std::move(frame),
- _roundMaskRetina,
- RectPart::TopLeft | RectPart::TopRight);
- } else if (_userpicImage.isNull()) {
- auto image = QImage(
- _widget->size() * style::DevicePixelRatio(),
- QImage::Format_ARGB32_Premultiplied);
- image.fill(Qt::black);
- _userpicImage = Images::Round(
- std::move(image),
- _roundMask,
- RectPart::TopLeft | RectPart::TopRight);
- }
- paintCoverImage(p, frame.isNull() ? _userpicImage : frame);
- paintBars(p);
- paintShadow(p);
- paintRadial(p);
- if (_videoInstance && _videoInstance->ready() && !paused) {
- _videoInstance->markFrameShown();
- }
- }
- void PeerShortInfoCover::paintCoverImage(QPainter &p, const QImage &image) {
- const auto roundedWidth = _st.size;
- const auto roundedHeight = _st.radius;
- const auto covered = (_st.size - _scrollTop);
- if (covered <= 0) {
- return;
- } else if (!_scrollTop) {
- p.drawImage(_widget->rect(), image);
- return;
- }
- const auto fill = covered - roundedHeight;
- const auto top = _widget->height() - fill;
- const auto factor = style::DevicePixelRatio();
- if (fill > 0) {
- const auto t = roundedHeight + _scrollTop;
- p.drawImage(
- QRect(0, t, roundedWidth * factor, (roundedWidth - t) * factor),
- image,
- QRect(0, t, roundedWidth * factor, (roundedWidth - t) * factor));
- }
- if (covered <= 0) {
- return;
- }
- const auto rounded = std::min(covered, roundedHeight);
- const auto from = top - rounded;
- auto q = QPainter(&_roundedTopImage);
- q.drawImage(
- QRect(0, 0, roundedWidth * factor, rounded * factor),
- image,
- QRect(0, _scrollTop, roundedWidth * factor, rounded * factor));
- q.end();
- _roundedTopImage = Images::Round(
- std::move(_roundedTopImage),
- _roundMask,
- RectPart::TopLeft | RectPart::TopRight);
- p.drawImage(
- QRect(0, from, roundedWidth, rounded),
- _roundedTopImage,
- QRect(0, 0, roundedWidth * factor, rounded * factor));
- }
- void PeerShortInfoCover::paintBars(QPainter &p) {
- const auto height = _st.linePadding * 2 + _st.line;
- const auto factor = style::DevicePixelRatio();
- if (_shadowTop.isNull()) {
- _shadowTop = Images::GenerateShadow(height, kShadowMaxAlpha, 0);
- _shadowTop = Images::Round(
- _shadowTop.scaled(QSize(_st.size, height) * factor),
- _roundMask,
- RectPart::TopLeft | RectPart::TopRight);
- }
- const auto shadowRect = QRect(0, _scrollTop, _st.size, height);
- p.drawImage(
- shadowRect,
- _shadowTop,
- QRect(0, 0, _shadowTop.width(), height * factor));
- const auto hiddenAt = _st.size - _st.namePosition.y();
- if (!_smallWidth || _scrollTop >= hiddenAt) {
- return;
- }
- const auto start = _st.linePadding;
- const auto y = _scrollTop + start;
- const auto skip = _st.lineSkip;
- const auto full = (_st.size - 2 * start - (_count - 1) * skip);
- const auto single = full / float64(_count);
- const auto masterOpacity = 1. - (_scrollTop / float64(hiddenAt));
- const auto inactiveOpacity = masterOpacity * kInactiveBarOpacity;
- for (auto i = 0; i != _count; ++i) {
- const auto left = start + i * (single + skip);
- const auto right = left + single;
- const auto x = qRound(left);
- const auto small = (qRound(right) == qRound(left) + _smallWidth);
- const auto width = small ? _smallWidth : _largeWidth;
- const auto &image = small ? _barSmall : _barLarge;
- const auto min = 2 * ((_st.line + 1) / 2);
- const auto minProgress = min / float64(width);
- const auto videoProgress = (_videoInstance && _videoDuration > 0);
- const auto progress = (i != _index)
- ? 0.
- : videoProgress
- ? std::max(_videoPosition / float64(_videoDuration), minProgress)
- : (_videoInstance ? 0. : 1.);
- if (progress == 1. && !videoProgress) {
- p.setOpacity(masterOpacity);
- p.drawImage(x, y, image);
- } else {
- p.setOpacity(inactiveOpacity);
- p.drawImage(x, y, image);
- if (progress > 0.) {
- const auto paint = qRound(progress * width);
- const auto right = paint / 2;
- const auto left = paint - right;
- p.setOpacity(masterOpacity);
- p.drawImage(
- QRect(x, y, left, _st.line),
- image,
- QRect(0, 0, left * factor, image.height()));
- p.drawImage(
- QRect(x + left, y, right, _st.line),
- image,
- QRect(left * factor, 0, right * factor, image.height()));
- }
- }
- }
- p.setOpacity(1.);
- }
- void PeerShortInfoCover::paintShadow(QPainter &p) {
- if (_shadowBottom.isNull()) {
- _shadowBottom = Images::GenerateShadow(
- _st.shadowHeight,
- 0,
- kShadowMaxAlpha);
- }
- const auto shadowTop = _st.size - _st.shadowHeight;
- if (_scrollTop >= shadowTop) {
- _name->hide();
- _status->hide();
- return;
- }
- const auto opacity = 1. - (_scrollTop / float64(shadowTop));
- _nameStyle->opacity = opacity;
- _nameStyle->textFg.refresh();
- _name->show();
- _statusStyle->opacity = opacity;
- _statusStyle->textFg.refresh();
- _status->show();
- p.setOpacity(opacity);
- const auto shadowRect = QRect(
- 0,
- shadowTop,
- _st.size,
- _st.shadowHeight);
- const auto factor = style::DevicePixelRatio();
- p.drawImage(
- shadowRect,
- _shadowBottom,
- QRect(
- 0,
- 0,
- _shadowBottom.width(),
- _st.shadowHeight * factor));
- p.setOpacity(1.);
- }
- void PeerShortInfoCover::paintRadial(QPainter &p) {
- const auto infinite = _videoInstance && _videoInstance->waitingShown();
- if (!_radial && !infinite) {
- return;
- }
- const auto radial = radialRect();
- const auto line = _st.radialAnimation.thickness;
- const auto arc = radial.marginsRemoved(
- { line, line, line, line });
- const auto infiniteOpacity = _videoInstance
- ? _videoInstance->waitingOpacity()
- : 0.;
- const auto radialState = _radial
- ? _radial->radial.computeState()
- : Ui::RadialState();
- if (_radial) {
- updateRadialState();
- }
- const auto radialOpacity = _radial
- ? (_radial->shownAnimation.value(_radial->shown ? 1. : 0.)
- * radialState.shown)
- : 0.;
- auto hq = PainterHighQualityEnabler(p);
- p.setOpacity(std::max(infiniteOpacity, radialOpacity));
- p.setPen(Qt::NoPen);
- p.setBrush(st::radialBg);
- p.drawEllipse(radial);
- if (radialOpacity > 0.) {
- p.setOpacity(radialOpacity);
- auto pen = _st.radialAnimation.color->p;
- pen.setWidth(line);
- pen.setCapStyle(Qt::RoundCap);
- p.setPen(pen);
- p.drawArc(arc, radialState.arcFrom, radialState.arcLength);
- }
- if (infinite) {
- p.setOpacity(1.);
- Ui::InfiniteRadialAnimation::Draw(
- p,
- _videoInstance->waitingState(),
- arc.topLeft(),
- arc.size(),
- _st.size,
- _st.radialAnimation.color,
- line);
- }
- }
- QImage PeerShortInfoCover::currentVideoFrame() const {
- const auto size = QSize(_st.size, _st.size);
- const auto request = Media::Streaming::FrameRequest{
- .resize = size,
- .outer = size,
- };
- return (_videoInstance
- && _videoInstance->player().ready()
- && !_videoInstance->player().videoSize().isEmpty())
- ? _videoInstance->frame(request)
- : QImage();
- }
- void PeerShortInfoCover::applyAdditionalStatus(const QString &status) {
- if (status.isEmpty()) {
- if (_additionalStatus) {
- _additionalStatus.destroy();
- refreshLabelsGeometry();
- }
- return;
- }
- if (_additionalStatus) {
- _additionalStatus->setText(status);
- } else {
- _additionalStatus.create(_widget.get(), status, _statusStyle->st);
- _additionalStatus->show();
- refreshLabelsGeometry();
- }
- }
- void PeerShortInfoCover::applyUserpic(PeerShortInfoUserpic &&value) {
- if (_index != value.index) {
- _index = value.index;
- _widget->update();
- }
- if (_count != value.count) {
- _count = value.count;
- refreshCoverCursor();
- refreshBarImages();
- _widget->update();
- }
- if (value.photo.isNull()) {
- const auto videoChanged = _videoInstance
- ? (_videoInstance->shared() != value.videoDocument)
- : (value.videoDocument != nullptr);
- auto frame = videoChanged ? currentVideoFrame() : QImage();
- if (!frame.isNull()) {
- _userpicImage = Images::Round(
- std::move(frame),
- _roundMask,
- RectPart::TopLeft | RectPart::TopRight);
- }
- } else if (_userpicImage.cacheKey() != value.photo.cacheKey()) {
- _userpicImage = std::move(value.photo);
- _widget->update();
- }
- if (!value.videoDocument) {
- clearVideo();
- } else if (!_videoInstance
- || _videoInstance->shared() != value.videoDocument) {
- using namespace Media::Streaming;
- _videoInstance = std::make_unique<Instance>(
- std::move(value.videoDocument),
- [=] { videoWaiting(); });
- _videoStartPosition = value.videoStartPosition;
- _videoInstance->lockPlayer();
- _videoInstance->player().updates(
- ) | rpl::start_with_next_error([=](Update &&update) {
- handleStreamingUpdate(std::move(update));
- }, [=](Error &&error) {
- handleStreamingError(std::move(error));
- }, _videoInstance->lifetime());
- if (_videoInstance->ready()) {
- streamingReady(base::duplicate(_videoInstance->info()));
- }
- if (!_videoInstance->valid()) {
- clearVideo();
- }
- }
- _photoLoadingProgress = value.photoLoadingProgress;
- updateRadialState();
- }
- void PeerShortInfoCover::updateRadialState() {
- const auto progress = _videoInstance ? 1. : _photoLoadingProgress;
- if (_radial) {
- _radial->radial.update(progress, (progress == 1.), crl::now());
- }
- _widget->update(radialRect());
- if (progress == 1.) {
- if (!_radial) {
- return;
- }
- _radial->showTimer.cancel();
- _radial->toggle(false);
- if (!_radial->shownAnimation.animating()) {
- _radial = nullptr;
- }
- return;
- } else if (!_radial) {
- _radial = std::make_unique<Radial>([=] { updateRadialState(); });
- _radial->radial.update(progress, false, crl::now());
- _radial->showTimer.callOnce(st::fadeWrapDuration);
- return;
- } else if (!_radial->showTimer.isActive()) {
- _radial->toggle(true);
- }
- }
- void PeerShortInfoCover::clearVideo() {
- _videoInstance = nullptr;
- _videoStartPosition = _videoPosition = _videoDuration = 0;
- }
- void PeerShortInfoCover::checkStreamedIsStarted() {
- if (!_videoInstance) {
- return;
- } else if (_videoInstance->paused()) {
- _videoInstance->resume();
- }
- if (!_videoInstance
- || _videoInstance->active()
- || _videoInstance->failed()) {
- return;
- }
- auto options = Media::Streaming::PlaybackOptions();
- options.position = _videoStartPosition;
- options.mode = Media::Streaming::Mode::Video;
- options.loop = true;
- _videoInstance->play(options);
- }
- void PeerShortInfoCover::handleStreamingUpdate(
- Media::Streaming::Update &&update) {
- using namespace Media::Streaming;
- v::match(update.data, [&](Information &update) {
- streamingReady(std::move(update));
- }, [](PreloadedVideo) {
- }, [&](UpdateVideo update) {
- _videoPosition = update.position;
- _widget->update();
- }, [](PreloadedAudio) {
- }, [](UpdateAudio) {
- }, [](WaitingForData) {
- }, [](SpeedEstimate) {
- }, [](MutedByOther) {
- }, [](Finished) {
- });
- }
- void PeerShortInfoCover::handleStreamingError(
- Media::Streaming::Error &&error) {
- //_streamedPhoto->setVideoPlaybackFailed();
- //_streamedPhoto = nullptr;
- clearVideo();
- }
- void PeerShortInfoCover::streamingReady(Media::Streaming::Information &&info) {
- _videoPosition = info.video.state.position;
- _videoDuration = info.video.state.duration;
- _widget->update();
- }
- void PeerShortInfoCover::refreshCoverCursor() {
- const auto cursor = (_count > 1)
- ? style::cur_pointer
- : style::cur_default;
- if (_cursor != cursor) {
- _cursor = cursor;
- _widget->setCursor(_cursor);
- }
- }
- void PeerShortInfoCover::refreshBarImages() {
- if (_count < 2) {
- _smallWidth = _largeWidth = 0;
- _barSmall = _barLarge = QImage();
- return;
- }
- const auto width = _st.size - 2 * _st.linePadding;
- _smallWidth = (width - (_count - 1) * _st.lineSkip) / _count;
- if (_smallWidth < _st.line) {
- _smallWidth = _largeWidth = 0;
- _barSmall = _barLarge = QImage();
- return;
- }
- _largeWidth = _smallWidth + 1;
- const auto makeBar = [&](int size) {
- const auto radius = _st.line / 2.;
- auto result = QImage(
- QSize(size, _st.line) * style::DevicePixelRatio(),
- QImage::Format_ARGB32_Premultiplied);
- result.setDevicePixelRatio(style::DevicePixelRatio());
- result.fill(Qt::transparent);
- auto p = QPainter(&result);
- auto hq = PainterHighQualityEnabler(p);
- p.setPen(Qt::NoPen);
- p.setBrush(st::groupCallVideoTextFg);
- p.drawRoundedRect(0, 0, size, _st.line, radius, radius);
- p.end();
- return result;
- };
- _barSmall = makeBar(_smallWidth);
- _barLarge = makeBar(_largeWidth);
- }
- void PeerShortInfoCover::refreshLabelsGeometry() {
- const auto statusTop = _st.size
- - _st.statusPosition.y()
- - _status->height();
- const auto diff = _st.namePosition.y()
- - _name->height()
- - _st.statusPosition.y();
- if (_additionalStatus) {
- _additionalStatus->moveToLeft(
- _status->x(),
- statusTop - diff - _additionalStatus->height());
- }
- _name->moveToLeft(
- _st.namePosition.x(),
- _st.size
- - _st.namePosition.y()
- - _name->height()
- - (_additionalStatus ? (diff + _additionalStatus->height()) : 0),
- _st.size);
- _status->moveToLeft(_st.statusPosition.x(), statusTop, _st.size);
- }
- QRect PeerShortInfoCover::radialRect() const {
- const auto cover = _widget->rect();
- const auto size = st::boxLoadingSize;
- return QRect(
- cover.x() + (cover.width() - size) / 2,
- cover.y() + (cover.height() - size) / 2,
- size,
- size);
- }
- void PeerShortInfoCover::videoWaiting() {
- if (!anim::Disabled()) {
- _widget->update(radialRect());
- }
- }
- PeerShortInfoBox::PeerShortInfoBox(
- QWidget*,
- PeerShortInfoType type,
- rpl::producer<PeerShortInfoFields> fields,
- rpl::producer<QString> status,
- rpl::producer<PeerShortInfoUserpic> userpic,
- Fn<bool()> videoPaused,
- const style::ShortInfoBox *stOverride)
- : _st(stOverride ? *stOverride : st::shortInfoBox)
- , _type(type)
- , _fields(std::move(fields))
- , _topRoundBackground(this)
- , _scroll(this, st::shortInfoScroll)
- , _rows(
- _scroll->setOwnedWidget(
- object_ptr<Ui::VerticalLayout>(
- _scroll.data())))
- , _cover(
- _rows.get(),
- st::shortInfoCover,
- nameValue(),
- std::move(status),
- std::move(userpic),
- std::move(videoPaused)) {
- _rows->add(_cover.takeOwned());
- _scroll->scrolls(
- ) | rpl::start_with_next([=] {
- _cover.setScrollTop(_scroll->scrollTop());
- }, _cover.lifetime());
- }
- PeerShortInfoBox::~PeerShortInfoBox() = default;
- rpl::producer<> PeerShortInfoBox::openRequests() const {
- return _openRequests.events();
- }
- rpl::producer<int> PeerShortInfoBox::moveRequests() const {
- return _cover.moveRequests();
- }
- void PeerShortInfoBox::prepare() {
- addButton(tr::lng_close(), [=] { closeBox(); });
- if (_type != PeerShortInfoType::Self) {
- // Perhaps a new lang key should be added for opening a group.
- addLeftButton(
- (_type == PeerShortInfoType::User)
- ? tr::lng_profile_send_message()
- : (_type == PeerShortInfoType::Group)
- ? tr::lng_view_button_group()
- : tr::lng_profile_view_channel(),
- [=] { _openRequests.fire({}); });
- }
- prepareRows();
- setNoContentMargin(true);
- _topRoundBackground->resize(st::shortInfoWidth, st::boxRadius);
- _topRoundBackground->paintRequest(
- ) | rpl::start_with_next([=] {
- if (const auto use = fillRoundedTopHeight()) {
- const auto width = _topRoundBackground->width();
- const auto top = _topRoundBackground->height() - use;
- const auto factor = style::DevicePixelRatio();
- QPainter(_topRoundBackground.data()).drawImage(
- QRect(0, top, width, use),
- _roundedTop,
- QRect(0, top * factor, width * factor, use * factor));
- }
- }, _topRoundBackground->lifetime());
- _roundedTop = QImage(
- _topRoundBackground->size() * style::DevicePixelRatio(),
- QImage::Format_ARGB32_Premultiplied);
- _roundedTop.setDevicePixelRatio(style::DevicePixelRatio());
- refreshRoundedTopImage(getDelegate()->style().bg->c);
- setCustomCornersFilling(RectPart::FullTop);
- setDimensionsToContent(st::shortInfoWidth, _rows);
- }
- void PeerShortInfoBox::prepareRows() {
- using namespace Info::Profile;
- auto addInfoLineGeneric = [&](
- rpl::producer<QString> &&label,
- rpl::producer<TextWithEntities> &&text,
- const style::FlatLabel &textSt) {
- auto line = CreateTextWithLabel(
- _rows,
- rpl::duplicate(label) | Ui::Text::ToWithEntities(),
- rpl::duplicate(text),
- _st.label,
- textSt,
- st::shortInfoLabeledPadding);
- _rows->add(object_ptr<Ui::OverrideMargins>(
- _rows.get(),
- std::move(line.wrap)));
- rpl::combine(
- std::move(label),
- std::move(text)
- ) | rpl::start_with_next([=] {
- _rows->resizeToWidth(st::shortInfoWidth);
- }, _rows->lifetime());
- //line.text->setClickHandlerFilter(infoClickFilter);
- return line.text;
- };
- auto addInfoLine = [&](
- rpl::producer<QString> &&label,
- rpl::producer<TextWithEntities> &&text,
- const style::FlatLabel &textSt) {
- return addInfoLineGeneric(
- std::move(label),
- std::move(text),
- textSt);
- };
- auto addInfoOneLine = [&](
- rpl::producer<QString> &&label,
- rpl::producer<TextWithEntities> &&text,
- const QString &contextCopyText) {
- auto result = addInfoLine(
- std::move(label),
- std::move(text),
- _st.labeledOneLine);
- result->setDoubleClickSelectsParagraph(true);
- result->setContextCopyText(contextCopyText);
- return result;
- };
- addInfoOneLine(
- tr::lng_settings_channel_label(),
- channelValue(),
- tr::lng_context_copy_link(tr::now));
- addInfoOneLine(
- tr::lng_info_link_label(),
- linkValue(),
- tr::lng_context_copy_link(tr::now));
- addInfoOneLine(
- tr::lng_info_mobile_label(),
- phoneValue() | Ui::Text::ToWithEntities(),
- tr::lng_profile_copy_phone(tr::now));
- auto label = _fields.current().isBio
- ? tr::lng_info_bio_label()
- : tr::lng_info_about_label();
- addInfoLine(std::move(label), aboutValue(), _st.labeled);
- addInfoOneLine(
- tr::lng_info_username_label(),
- usernameValue() | Ui::Text::ToWithEntities(),
- tr::lng_context_copy_mention(tr::now));
- addInfoOneLine(
- birthdayLabel(),
- birthdayValue() | Ui::Text::ToWithEntities(),
- tr::lng_mediaview_copy(tr::now));
- }
- void PeerShortInfoBox::resizeEvent(QResizeEvent *e) {
- BoxContent::resizeEvent(e);
- _rows->resizeToWidth(st::shortInfoWidth);
- _scroll->resize(st::shortInfoWidth, height());
- _scroll->move(0, 0);
- _topRoundBackground->move(0, 0);
- }
- int PeerShortInfoBox::fillRoundedTopHeight() {
- const auto roundedHeight = _topRoundBackground->height();
- const auto scrollTop = _scroll->scrollTop();
- const auto covered = (st::shortInfoWidth - scrollTop);
- if (covered >= roundedHeight) {
- return 0;
- }
- const auto &color = getDelegate()->style().bg->c;
- if (_roundedTopColor != color) {
- refreshRoundedTopImage(color);
- }
- return roundedHeight - covered;
- }
- void PeerShortInfoBox::refreshRoundedTopImage(const QColor &color) {
- _roundedTopColor = color;
- _roundedTop.fill(color);
- _roundedTop = Images::Round(
- std::move(_roundedTop),
- _cover.roundMask(),
- RectPart::TopLeft | RectPart::TopRight);
- }
- rpl::producer<MenuCallback> PeerShortInfoBox::fillMenuRequests() const {
- return _fillMenuRequests.events();
- }
- void PeerShortInfoBox::contextMenuEvent(QContextMenuEvent *e) {
- _menuHolder = nullptr;
- const auto menu = Ui::CreateChild<Ui::PopupMenu>(
- this,
- st::popupMenuWithIcons);
- _fillMenuRequests.fire(Ui::Menu::CreateAddActionCallback(menu));
- _menuHolder.reset(menu);
- if (menu->empty()) {
- _menuHolder = nullptr;
- return;
- }
- menu->popup(e->globalPos());
- }
- rpl::producer<QString> PeerShortInfoBox::nameValue() const {
- return _fields.value(
- ) | rpl::map([](const PeerShortInfoFields &fields) {
- return fields.name;
- }) | rpl::distinct_until_changed();
- }
- rpl::producer<TextWithEntities> PeerShortInfoBox::channelValue() const {
- return _fields.value(
- ) | rpl::map([](const PeerShortInfoFields &fields) {
- return Ui::Text::Link(fields.channelName, fields.channelLink);
- }) | rpl::distinct_until_changed();
- }
- rpl::producer<TextWithEntities> PeerShortInfoBox::linkValue() const {
- return _fields.value(
- ) | rpl::map([](const PeerShortInfoFields &fields) {
- return Ui::Text::Link(fields.link, fields.link);
- }) | rpl::distinct_until_changed();
- }
- rpl::producer<QString> PeerShortInfoBox::phoneValue() const {
- return _fields.value(
- ) | rpl::map([](const PeerShortInfoFields &fields) {
- return fields.phone;
- }) | rpl::distinct_until_changed();
- }
- rpl::producer<QString> PeerShortInfoBox::usernameValue() const {
- return _fields.value(
- ) | rpl::map([](const PeerShortInfoFields &fields) {
- return fields.username;
- }) | rpl::distinct_until_changed();
- }
- rpl::producer<QString> PeerShortInfoBox::birthdayLabel() const {
- return Info::Profile::BirthdayLabelText(_fields.value(
- ) | rpl::map([](const PeerShortInfoFields &fields) {
- return fields.birthday;
- }) | rpl::distinct_until_changed());
- }
- rpl::producer<QString> PeerShortInfoBox::birthdayValue() const {
- return Info::Profile::BirthdayValueText(_fields.value(
- ) | rpl::map([](const PeerShortInfoFields &fields) {
- return fields.birthday;
- }) | rpl::distinct_until_changed());
- }
- rpl::producer<TextWithEntities> PeerShortInfoBox::aboutValue() const {
- return _fields.value() | rpl::map([](const PeerShortInfoFields &fields) {
- return fields.about;
- }) | rpl::distinct_until_changed();
- }
|