/* 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 "ui/effects/loading_element.h" #include "base/object_ptr.h" #include "base/random.h" #include "styles/palette.h" #include "ui/effects/glare.h" #include "ui/painter.h" #include "ui/rect.h" #include "ui/rp_widget.h" #include "styles/style_basic.h" #include "styles/style_widgets.h" namespace Ui { namespace { class LoadingElement { public: LoadingElement() = default; [[nodiscard]] virtual int height() const = 0; virtual void paint(QPainter &p, int width) = 0; }; class LoadingText final : public LoadingElement { public: LoadingText(const style::FlatLabel &st); [[nodiscard]] int height() const override; void paint(QPainter &p, int width) override; private: const style::FlatLabel &_st; }; LoadingText::LoadingText(const style::FlatLabel &st) : _st(st) { } int LoadingText::height() const { return _st.style.lineHeight; } void LoadingText::paint(QPainter &p, int width) { auto hq = PainterHighQualityEnabler(p); p.setPen(Qt::NoPen); p.setBrush(st::windowBgOver); const auto h = _st.style.font->ascent; p.drawRoundedRect( 0, height() - h - (height() - _st.style.font->height), width, h, h / 2, h / 2); } class LoadingPeerListItem final : public LoadingElement { public: LoadingPeerListItem(const style::PeerListItem &st) : _st(st) { } [[nodiscard]] int height() const override { return _st.height; } void paint(QPainter &p, int width) override { auto hq = PainterHighQualityEnabler(p); const auto &style = _st.nameStyle; const auto offset = -style.font->ascent - (style.lineHeight - style.font->height); p.setPen(Qt::NoPen); p.setBrush(st::windowBgOver); p.drawEllipse( _st.photoPosition.x(), _st.photoPosition.y(), _st.photoSize, _st.photoSize); constexpr auto kNameWidth = 60; constexpr auto kStatusWidth = 100; const auto h1 = st::semiboldTextStyle.font->ascent; p.drawRoundedRect( _st.namePosition.x(), _st.namePosition.y() + offset, kNameWidth, h1, h1 / 2, h1 / 2); { const auto h2 = st::defaultTextStyle.font->ascent; const auto radius = h2 / 2; const auto rect = QRect( _st.statusPosition.x(), _st.statusPosition.y() + offset, kStatusWidth, h2); if (rect::bottom(rect) < height()) { p.drawRoundedRect(rect, radius, radius); } } } private: const style::PeerListItem &_st; }; template object_ptr CreateLoadingElementWidget( not_null parent, int lines, rpl::producer rtl, ElementArgs &&...args) { auto widget = object_ptr(parent); const auto raw = widget.data(); struct State { GlareEffect glare; Ui::Animations::Simple animation; int lastLineWidth = 0; rpl::variable rtl; }; const auto state = widget->lifetime().make_state(); state->rtl = std::move(rtl); state->rtl.value( ) | rpl::start_with_next([=] { raw->update(); }, raw->lifetime()); raw->resize( raw->width(), Element(std::forward(args)...).height() * lines); const auto draw = [=](QPainter &p) { auto loading = Element(std::forward(args)...); const auto countRows = lines; for (auto i = 0; i < countRows; i++) { const auto w = (i == countRows - 1) ? state->lastLineWidth : raw->width(); loading.paint(p, w); p.translate(0, loading.height()); } p.resetTransform(); auto &_glare = state->glare; if (_glare.glare.birthTime) { const auto progress = _glare.progress(crl::now()); const auto x = (-_glare.width) + (raw->width() + _glare.width * 2) * progress; const auto h = raw->height(); p.drawTiledPixmap(x, 0, _glare.width, h, _glare.pixmap, 0, 0); } }; widget->paintRequest( ) | rpl::start_with_next([=](const QRect &r) { auto p = QPainter(raw); if (state->rtl.current()) { const auto r = raw->rect(); p.translate(r.center()); p.scale(-1., 1.); p.translate(-r.center()); } draw(p); }, widget->lifetime()); constexpr auto kTimeout = crl::time(1000); constexpr auto kDuration = crl::time(1000); widget->widthValue( ) | rpl::start_with_next([=](int width) { state->glare.width = width; state->glare.validate( st::dialogsBg->c, [=] { raw->update(); }, kTimeout, kDuration); if (width) { state->lastLineWidth = (width / 4) + base::RandomIndex(width / 2); } }, widget->lifetime()); return widget; } } // namespace object_ptr CreateLoadingTextWidget( not_null parent, const style::FlatLabel &st, int lines, rpl::producer rtl) { return CreateLoadingElementWidget( parent, lines, std::move(rtl), st); } object_ptr CreateLoadingPeerListItemWidget( not_null parent, const style::PeerListItem &st, int lines) { return CreateLoadingElementWidget( parent, lines, rpl::single(false), st); } } // namespace Ui