/* 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/background_box.h" #include "lang/lang_keys.h" #include "mainwidget.h" #include "mainwindow.h" #include "window/themes/window_theme.h" #include "ui/effects/round_checkbox.h" #include "ui/image/image.h" #include "auth_session.h" #include "data/data_session.h" #include "styles/style_overview.h" #include "styles/style_boxes.h" namespace { constexpr auto kBackgroundsInRow = 3; } // namespace class BackgroundBox::Inner : public Ui::RpWidget , private MTP::Sender , private base::Subscriber { public: Inner(QWidget *parent); void setBackgroundChosenCallback(Fn callback) { _backgroundChosenCallback = std::move(callback); } ~Inner(); protected: void paintEvent(QPaintEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; private: void updateWallpapers(); Fn _backgroundChosenCallback; int _over = -1; int _overDown = -1; std::unique_ptr _check; // this is not a widget }; BackgroundBox::BackgroundBox(QWidget*) { } void BackgroundBox::prepare() { setTitle(langFactory(lng_backgrounds_header)); addButton(langFactory(lng_close), [=] { closeBox(); }); setDimensions(st::boxWideWidth, st::boxMaxListHeight); _inner = setInnerWidget(object_ptr(this), st::backgroundScroll); _inner->setBackgroundChosenCallback([=](int index) { backgroundChosen(index); }); } void BackgroundBox::backgroundChosen(int index) { const auto &papers = Auth().data().wallpapers(); if (index >= 0 && index < papers.size()) { App::main()->setChatBackground(papers[index]); } closeBox(); } BackgroundBox::Inner::Inner(QWidget *parent) : RpWidget(parent) , _check(std::make_unique(st::overviewCheck, [=] { update(); })) { _check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast); if (Auth().data().wallpapers().empty()) { resize(kBackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); } else { updateWallpapers(); } request(MTPaccount_GetWallPapers( MTP_int(Auth().data().wallpapersHash()) )).done([=](const MTPaccount_WallPapers &result) { if (Auth().data().updateWallpapers(result)) { updateWallpapers(); } }).send(); subscribe(Auth().downloaderTaskFinished(), [=] { update(); }); subscribe(Window::Theme::Background(), [=](const Window::Theme::BackgroundUpdate &update) { if (update.paletteChanged()) { _check->invalidateCache(); } }); setMouseTracking(true); } void BackgroundBox::Inner::updateWallpapers() { const auto &papers = Auth().data().wallpapers(); const auto count = papers.size(); const auto rows = (count / kBackgroundsInRow) + (count % kBackgroundsInRow ? 1 : 0); resize(kBackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, rows * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); const auto preload = kBackgroundsInRow * 3; for (const auto &paper : papers | ranges::view::take(preload)) { paper.thumb->load(Data::FileOrigin()); } } void BackgroundBox::Inner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); Painter p(this); const auto &papers = Auth().data().wallpapers(); if (papers.empty()) { p.setFont(st::noContactsFont); p.setPen(st::noContactsColor); p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); return; } auto row = 0; auto column = 0; for (const auto &paper : papers) { const auto increment = gsl::finally([&] { ++column; if (column == kBackgroundsInRow) { column = 0; ++row; } }); if ((st::backgroundSize.height() + st::backgroundPadding) * (row + 1) <= r.top()) { continue; } paper.thumb->load(Data::FileOrigin()); int x = st::backgroundPadding + column * (st::backgroundSize.width() + st::backgroundPadding); int y = st::backgroundPadding + row * (st::backgroundSize.height() + st::backgroundPadding); const auto &pix = paper.thumb->pix( Data::FileOrigin(), st::backgroundSize.width(), st::backgroundSize.height()); p.drawPixmap(x, y, pix); if (paper.id == Window::Theme::Background()->id()) { auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size; auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size; _check->paint(p, getms(), checkLeft, checkTop, width()); } } } void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) { const auto newOver = [&] { const auto x = e->pos().x(); const auto y = e->pos().y(); const auto width = st::backgroundSize.width(); const auto height = st::backgroundSize.height(); const auto skip = st::backgroundPadding; const auto row = int((y - skip) / (height + skip)); const auto column = int((x - skip) / (width + skip)); if (y - row * (height + skip) > skip + height) { return -1; } else if (x - column * (width + skip) > skip + width) { return -1; } const auto result = row * kBackgroundsInRow + column; return (result < Auth().data().wallpapers().size()) ? result : -1; }(); if (_over != newOver) { _over = newOver; setCursor((_over >= 0 || _overDown >= 0) ? style::cur_pointer : style::cur_default); } } void BackgroundBox::Inner::mousePressEvent(QMouseEvent *e) { _overDown = _over; } void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) { if (_overDown == _over && _over >= 0) { if (_backgroundChosenCallback) { _backgroundChosenCallback(_over); } } else if (_over < 0) { setCursor(style::cur_default); } } BackgroundBox::Inner::~Inner() = default;