diff --git a/Telegram/Resources/icons/overview_photo_check.png b/Telegram/Resources/icons/overview_photo_check.png index 6d6591b70..44347c6f7 100644 Binary files a/Telegram/Resources/icons/overview_photo_check.png and b/Telegram/Resources/icons/overview_photo_check.png differ diff --git a/Telegram/Resources/icons/overview_photo_check@2x.png b/Telegram/Resources/icons/overview_photo_check@2x.png index 20601fcb8..d5ad0a834 100644 Binary files a/Telegram/Resources/icons/overview_photo_check@2x.png and b/Telegram/Resources/icons/overview_photo_check@2x.png differ diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index 35984d703..1795d49b3 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/window_theme.h" #include "styles/style_overview.h" #include "styles/style_boxes.h" +#include "ui/effects/round_image_checkbox.h" BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll) , _inner(this) { @@ -50,10 +51,8 @@ void BackgroundBox::onBackgroundChosen(int index) { } BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent) -, _bgCount(0) -, _rows(0) -, _over(-1) -, _overDown(-1) { +, _check(std_::make_unique(st::overviewCheck, [this] { update(); })) { + _check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast); if (App::cServerBackgrounds().isEmpty()) { resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); MTP::send(MTPaccount_GetWallPapers(), rpcDone(&Inner::gotWallpapers)); @@ -157,9 +156,9 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) { p.drawPixmap(x, y, pix); if (paper.id == Window::Theme::Background()->id()) { - int checkPosX = x + st::backgroundSize.width() - st::overviewPhotoChecked.width(); - int checkPosY = y + st::backgroundSize.height() - st::overviewPhotoChecked.height(); - st::overviewPhotoChecked.paint(p, QPoint(checkPosX, checkPosY), width()); + 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()); } } } @@ -198,5 +197,4 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) { } } -void BackgroundBox::Inner::resizeEvent(QResizeEvent *e) { -} +BackgroundBox::Inner::~Inner() = default; diff --git a/Telegram/SourceFiles/boxes/backgroundbox.h b/Telegram/SourceFiles/boxes/backgroundbox.h index bfd8a6dc3..b5136ba9f 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.h +++ b/Telegram/SourceFiles/boxes/backgroundbox.h @@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/abstractbox.h" +namespace Ui { +class RoundCheckbox; +} // namespace Ui + class BackgroundBox : public ItemListBox { Q_OBJECT @@ -43,6 +47,7 @@ class BackgroundBox::Inner : public TWidget, public RPCSender, private base::Sub public: Inner(QWidget *parent); + ~Inner(); signals: void backgroundChosen(int index); @@ -52,13 +57,15 @@ protected: void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; private: void gotWallpapers(const MTPVector &result); void updateWallpapers(); - int32 _bgCount, _rows; - int32 _over, _overDown; + int _bgCount = 0; + int _rows = 0; + int _over = -1; + int _overDown = -1; + std_::unique_ptr _check; }; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index dea1fece3..b22e687f4 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -304,11 +304,11 @@ contactsPhotoCheckbox: RoundImageCheckbox { selectWidth: 2px; selectFg: windowBgActive; selectDuration: 150; - checkBorder: windowBg; - checkBg: windowBgActive; - checkRadius: 10px; - checkSmallRadius: 3px; - checkIcon: icon {{ "default_checkbox_check", windowFgActive, point(3px, 6px) }}; + check: RoundCheckbox(defaultRoundCheckbox) { + size: 20px; + sizeSmall: 0.3; + check: icon {{ "default_checkbox_check", windowFgActive, point(3px, 6px) }}; + } } contactsPhotoDisabledCheckFg: #bbbbbb; contactsNameCheckedFg: #2b88b8; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 5363b0ff6..6ae34786c 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -1002,11 +1002,11 @@ void ContactsBox::Inner::paintDisabledCheckUserpic(Painter &p, PeerData *peer, i auto userpicBorderPen = st::contactsPhotoDisabledCheckFg->p; userpicBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth); - auto iconDiameter = 2 * st::contactsPhotoCheckbox.checkRadius; + auto iconDiameter = st::contactsPhotoCheckbox.check.size; auto iconLeft = x + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter; auto iconTop = y + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter; auto iconEllipse = rtlrect(iconLeft, iconTop, iconDiameter, iconDiameter, outerWidth); - auto iconBorderPen = st::contactsPhotoCheckbox.checkBorder->p; + auto iconBorderPen = st::contactsPhotoCheckbox.check.border->p; iconBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth); peer->paintUserpicLeft(p, userpicRadius * 2, userpicLeft, userpicTop, width()); @@ -1023,7 +1023,7 @@ void ContactsBox::Inner::paintDisabledCheckUserpic(Painter &p, PeerData *peer, i p.setRenderHint(QPainter::HighQualityAntialiasing, false); - st::contactsPhotoCheckbox.checkIcon.paint(p, iconEllipse.topLeft(), outerWidth); + st::contactsPhotoCheckbox.check.check.paint(p, iconEllipse.topLeft(), outerWidth); } void ContactsBox::Inner::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 7d10e5c98..46c7b2ae9 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2082,8 +2082,8 @@ void MainWidget::fillPeerMenu(PeerData *peer, base::lambdaisChat()) { - callback(lang(lng_profile_clear_history), std_::move(clearHistoryHandler)); callback(lang(lng_profile_clear_and_exit), std_::move(deleteAndLeaveHandler)); + callback(lang(lng_profile_clear_history), std_::move(clearHistoryHandler)); } else if (peer->isChannel() && peer->asChannel()->amIn() && !peer->asChannel()->amCreator()) { callback(lang(peer->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel), std_::move(deleteAndLeaveHandler)); } diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index 117d3deda..c53500f9f 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -43,21 +43,22 @@ OverviewFileLayout { fileDateTop: pixels; } -overviewCheckBg: #00000042; -overviewCheckedBg: #2fa9e2; +overviewCheckBg: #00000040; +overviewCheckFg: windowBg; +overviewCheckFgActive: windowBg; +overviewCheckPressedSize: 0.8; +overviewCheck: RoundCheckbox(defaultRoundCheckbox) { + bgInactive: overviewCheckBg; + size: 29px; + sizeSmall: 0.3; + check: icon {{ "overview_photo_check", overviewCheckFgActive, point(4px, 8px) }}; +} +overviewCheckSkip: 5px; overviewPhotoSkip: 10px; overviewPhotoBg: #F1F1F1; overviewPhotoMinSize: minPhotoSize; -overviewPhotoCheck: icon { - { size(31px, 31px), overviewCheckBg }, - { "overview_photo_check", #ffffff, point(4px, 8px) }, -}; -overviewPhotoChecked: icon { - { size(31px, 31px), overviewCheckedBg }, - { "overview_photo_check", #ffffff, point(4px, 8px) }, -}; -overviewPhotoSelectOverlay: #0a7bb03f; +overviewPhotoSelectOverlay: #40ace333; overviewVideoBg: #000000; @@ -111,11 +112,11 @@ linksPhotoSize: 46px; linksPhotoPadding: 12px; overviewLinksCheck: icon { { "overview_links_check_bg", overviewCheckBg }, - { "overview_links_check", #ffffff, point(4px, 5px) }, + { "overview_links_check", overviewCheckFg, point(4px, 5px) }, }; overviewLinksChecked: icon { - { "overview_links_check_bg", overviewCheckedBg }, - { "overview_links_check", #ffffff, point(4px, 5px) }, + { "overview_links_check_bg", windowBgActive }, + { "overview_links_check", overviewCheckFgActive, point(4px, 5px) }, }; overviewFilter: FlatInput(defaultFlatInput) { diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index c9ed59b78..68599e0af 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -35,6 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/player/media_player_instance.h" #include "localstorage.h" #include "history/history_media_types.h" +#include "ui/effects/round_image_checkbox.h" namespace Overview { namespace Layout { @@ -101,7 +102,7 @@ void RadialProgressItem::step_radial(uint64 ms, bool timer) { } } -void RadialProgressItem::ensureRadial() const { +void RadialProgressItem::ensureRadial() { if (!_radial) { _radial = new Ui::RadialAnimation(animation(const_cast(this), &RadialProgressItem::step_radial)); } @@ -118,21 +119,25 @@ RadialProgressItem::~RadialProgressItem() { delete base::take(_radial); } -void FileBase::setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const { - _statusSize = newSize; - if (_statusSize == FileStatusSizeReady) { - _statusText = (duration >= 0) ? formatDurationAndSizeText(duration, fullSize) : (duration < -1 ? formatGifAndSizeText(fullSize) : formatSizeText(fullSize)); - } else if (_statusSize == FileStatusSizeLoaded) { - _statusText = (duration >= 0) ? formatDurationText(duration) : (duration < -1 ? qsl("GIF") : formatSizeText(fullSize)); - } else if (_statusSize == FileStatusSizeFailed) { - _statusText = lang(lng_attach_failed); - } else if (_statusSize >= 0) { - _statusText = formatDownloadText(_statusSize, fullSize); +void StatusText::update(int newSize, int fullSize, int duration, int64 realDuration) { + setSize(newSize); + if (_size == FileStatusSizeReady) { + _text = (duration >= 0) ? formatDurationAndSizeText(duration, fullSize) : (duration < -1 ? formatGifAndSizeText(fullSize) : formatSizeText(fullSize)); + } else if (_size == FileStatusSizeLoaded) { + _text = (duration >= 0) ? formatDurationText(duration) : (duration < -1 ? qsl("GIF") : formatSizeText(fullSize)); + } else if (_size == FileStatusSizeFailed) { + _text = lang(lng_attach_failed); + } else if (_size >= 0) { + _text = formatDownloadText(_size, fullSize); } else { - _statusText = formatPlayedText(-_statusSize - 1, realDuration); + _text = formatPlayedText(-_size - 1, realDuration); } } +void StatusText::setSize(int newSize) { + _size = newSize; +} + Date::Date(const QDate &date, bool month) : _date(date) , _text(month ? langMonthFull(date) : langDayOfMonthFull(date)) { @@ -144,7 +149,7 @@ void Date::initDimensions() { _minh = st::linksDateMargin.top() + st::normalFont->height + st::linksDateMargin.bottom() + st::linksBorder; } -void Date::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const { +void Date::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { if (clip.intersects(QRect(0, st::linksDateMargin.top(), _width, st::normalFont->height))) { p.setPen(st::linksDateColor); p.setFont(st::semiboldFont); @@ -152,25 +157,62 @@ void Date::paint(Painter &p, const QRect &clip, TextSelection selection, const P } } -namespace { +class PhotoVideoCheckbox { +public: + template + PhotoVideoCheckbox(UpdateCallback callback) : _updateCallback(callback), _check(st::overviewCheck, _updateCallback) { + } -void paintPhotoVideoCheck(Painter &p, int width, int height, bool selected) { - int checkPosX = width - st::overviewPhotoCheck.width(); - int checkPosY = height - st::overviewPhotoCheck.height(); + void paint(Painter &p, uint64 ms, int width, int height, bool selected, bool selecting); + + void setActive(bool active); + void setPressed(bool pressed); + +private: + void startAnimation(); + + base::lambda_copy _updateCallback; + Ui::RoundCheckbox _check; + + FloatAnimation _pression; + bool _active = false; + bool _pressed = false; + +}; + +void PhotoVideoCheckbox::paint(Painter &p, uint64 ms, int width, int height, bool selected, bool selecting) { if (selected) { - p.fillRect(QRect(0, 0, width, height), st::overviewPhotoSelectOverlay); - st::overviewPhotoChecked.paint(p, QPoint(checkPosX, checkPosY), width); - } else { - st::overviewPhotoCheck.paint(p, QPoint(checkPosX, checkPosY), width); + p.fillRect(0, 0, width, height, st::overviewPhotoSelectOverlay); + } + _check.setDisplayInactive(selecting); + _check.setChecked(selected); + auto pression = _pression.current(ms, (_active && _pressed) ? 1. : 0.); + auto masterScale = 1. - (1. - st::overviewCheckPressedSize) * pression; + _check.paint(p, ms, width - st::overviewCheckSkip - st::overviewCheck.size, height - st::overviewCheckSkip - st::overviewCheck.size, width, masterScale); +} + +void PhotoVideoCheckbox::setActive(bool active) { + _active = active; + if (_pressed) { + startAnimation(); } } -} // namespace +void PhotoVideoCheckbox::setPressed(bool pressed) { + _pressed = pressed; + if (_active) { + startAnimation(); + } +} + +void PhotoVideoCheckbox::startAnimation() { + auto showPressed = (_pressed && _active); + _pression.start(_updateCallback, showPressed ? 0. : 1., showPressed ? 1. : 0., st::overviewCheck.duration); +} Photo::Photo(PhotoData *photo, HistoryItem *parent) : ItemBase(parent) , _data(photo) -, _link(new PhotoOpenClickHandler(photo)) -, _goodLoaded(false) { +, _link(new PhotoOpenClickHandler(photo)) { } void Photo::initDimensions() { @@ -187,7 +229,7 @@ int32 Photo::resizeGetHeight(int32 width) { return _height; } -void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const { +void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { bool good = _data->loaded(), selected = (selection == FullSelection); if (!good) { _data->medium->automaticLoad(_parent); @@ -225,9 +267,19 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const } else { p.drawPixmap(0, 0, _pix); } + if (selected || context->selecting) { - paintPhotoVideoCheck(p, _width, _height, selected); + ensureCheckboxCreated(); } + if (_check) { + _check->paint(p, context->ms, _width, _height, selected, context->selecting); + } +} + +void Photo::ensureCheckboxCreated() { + if (!_check) _check = std_::make_unique([this] { + Ui::repaintHistoryItem(_parent); + }); } void Photo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const { @@ -236,7 +288,19 @@ void Photo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, i } } -Video::Video(DocumentData *video, HistoryItem *parent) : FileBase(parent) +void Photo::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { + if (_check) { + _check->setActive(active); + } +} + +void Photo::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) { + if (_check) { + _check->setPressed(pressed); + } +} + +Video::Video(DocumentData *video, HistoryItem *parent) : RadialProgressItem(parent) , _data(video) , _duration(formatDurationText(_data->duration())) , _thumbLoaded(false) { @@ -254,7 +318,7 @@ int32 Video::resizeGetHeight(int32 width) { return _height; } -void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const { +void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { bool selected = (selection == FullSelection), thumbLoaded = _data->thumb->loaded(); _data->automaticLoad(_parent); @@ -305,13 +369,13 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const if (!selected && !context->selecting && !loaded) { if (clip.intersects(QRect(0, _height - st::normalFont->height, _width, st::normalFont->height))) { int32 statusX = st::msgDateImgPadding.x(), statusY = _height - st::normalFont->height - st::msgDateImgPadding.y(); - int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); + int32 statusW = st::normalFont->width(_status.text()) + 2 * st::msgDateImgPadding.x(); int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); statusX = _width - statusW + statusX; p.fillRect(rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg); p.setFont(st::normalFont); p.setPen(st::msgDateImgColor); - p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x()); + p.drawTextLeft(statusX, statusY, _width, _status.text(), statusW - 2 * st::msgDateImgPadding.x()); } } if (clip.intersects(QRect(0, 0, _width, st::normalFont->height))) { @@ -358,8 +422,32 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const _radial->draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg); } } + if (selected || context->selecting) { - paintPhotoVideoCheck(p, _width, _height, selected); + ensureCheckboxCreated(); + } + if (_check) { + _check->paint(p, context->ms, _width, _height, selected, context->selecting); + } +} + +void Video::ensureCheckboxCreated() { + if (!_check) _check = std_::make_unique([this] { + Ui::repaintHistoryItem(_parent); + }); +} + +void Video::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { + RadialProgressItem::clickHandlerActiveChanged(action, active); + if (_check) { + _check->setActive(active); + } +} + +void Video::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) { + RadialProgressItem::clickHandlerPressedChanged(action, pressed); + if (_check) { + _check->setPressed(pressed); } } @@ -371,9 +459,9 @@ void Video::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, i } } -void Video::updateStatusText() const { +void Video::updateStatusText() { bool showPause = false; - int32 statusSize = 0; + int statusSize = 0; if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { statusSize = FileStatusSizeFailed; } else if (_data->status == FileUploading) { @@ -385,18 +473,18 @@ void Video::updateStatusText() const { } else { statusSize = FileStatusSizeReady; } - if (statusSize != _statusSize) { - int32 status = statusSize, size = _data->size; + if (statusSize != _status.size()) { + int status = statusSize, size = _data->size; if (statusSize >= 0 && statusSize < 0x7F000000) { size = status; status = FileStatusSizeReady; } - setStatusSize(status, size, -1, 0); - _statusSize = statusSize; + _status.update(status, size, -1, 0); + _status.setSize(statusSize); } } -Voice::Voice(DocumentData *voice, HistoryItem *parent) : FileBase(parent) +Voice::Voice(DocumentData *voice, HistoryItem *parent) : RadialProgressItem(parent) , _data(voice) , _namel(new DocumentOpenClickHandler(_data)) { AddComponents(Info::Bit()); @@ -417,7 +505,7 @@ void Voice::initDimensions() { _minh = st::overviewFileLayout.songPadding.top() + st::overviewFileLayout.songThumbSize + st::overviewFileLayout.songPadding.bottom() + st::lineWidth; } -void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const { +void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { bool selected = (selection == FullSelection); _data->automaticLoad(_parent); @@ -474,7 +562,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const auto icon = ([showPause, this, selected] { if (showPause) { return &(selected ? st::historyFileInPauseSelected : st::historyFileInPause); - } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { + } else if (_status.size() < 0 || _status.size() == FileStatusSizeLoaded) { return &(selected ? st::historyFileInPlaySelected : st::historyFileInPlay); } else if (_data->loading()) { return &(selected ? st::historyFileInCancelSelected : st::historyFileInCancel); @@ -495,14 +583,14 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const p.setFont(st::normalFont); p.setPen(selected ? st::mediaInFgSelected : st::mediaInFg); int32 unreadx = nameleft; - if (_statusSize == FileStatusSizeLoaded || _statusSize == FileStatusSizeReady) { + if (_status.size() == FileStatusSizeLoaded || _status.size() == FileStatusSizeReady) { textstyleSet(&(selected ? st::mediaInStyleSelected : st::mediaInStyle)); _details.drawLeftElided(p, nameleft, statustop, namewidth, _width); textstyleRestore(); unreadx += _details.maxWidth(); } else { - int32 statusw = st::normalFont->width(_statusText); - p.drawTextLeft(nameleft, statustop, _width, _statusText, statusw); + int32 statusw = st::normalFont->width(_status.text()); + p.drawTextLeft(nameleft, statustop, _width, _status.text(), statusw); unreadx += statusw; } if (_parent->isMediaUnread() && unreadx + st::mediaUnreadSkip + st::mediaUnreadSize <= _width) { @@ -519,8 +607,6 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const void Voice::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const { bool loaded = _data->loaded(); - bool showPause = updateStatusText(); - int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = 0; nameleft = st::overviewFileLayout.songPadding.left() + st::overviewFileLayout.songThumbSize + st::overviewFileLayout.songPadding.right(); @@ -534,7 +620,7 @@ void Voice::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, i return; } if (rtlrect(nameleft, statustop, _width - nameleft - nameright, st::normalFont->height, _width).contains(x, y)) { - if (_statusSize == FileStatusSizeLoaded || _statusSize == FileStatusSizeReady) { + if (_status.size() == FileStatusSizeLoaded || _status.size() == FileStatusSizeReady) { auto textState = _details.getStateLeft(x - nameleft, y - statustop, _width, _width); link = textState.link; cursor = textState.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState; @@ -546,7 +632,7 @@ void Voice::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, i } } -void Voice::updateName() const { +void Voice::updateName() { int32 version = 0; if (const HistoryMessageForwarded *fwd = _parent->Get()) { if (_parent->fromOriginal()->isChannel()) { @@ -561,7 +647,7 @@ void Voice::updateName() const { _nameVersion = version; } -bool Voice::updateStatusText() const { +bool Voice::updateStatusText() { bool showPause = false; int32 statusSize = 0, realDuration = 0; if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { @@ -580,13 +666,13 @@ bool Voice::updateStatusText() const { } else { statusSize = FileStatusSizeReady; } - if (statusSize != _statusSize) { - setStatusSize(statusSize, _data->size, _data->voice()->duration, realDuration); + if (statusSize != _status.size()) { + _status.update(statusSize, _data->size, _data->voice()->duration, realDuration); } return showPause; } -Document::Document(DocumentData *document, HistoryItem *parent, const style::OverviewFileLayout &st) : FileBase(parent) +Document::Document(DocumentData *document, HistoryItem *parent, const style::OverviewFileLayout &st) : RadialProgressItem(parent) , _data(document) , _msgl(goToMessageClickHandler(parent)) , _namel(new DocumentOpenClickHandler(_data)) @@ -600,7 +686,7 @@ Document::Document(DocumentData *document, HistoryItem *parent, const style::Ove setDocumentLinks(_data); - setStatusSize(FileStatusSizeReady, _data->size, _data->song() ? _data->song()->duration : -1, 0); + _status.update(FileStatusSizeReady, _data->size, _data->song() ? _data->song()->duration : -1, 0); if (withThumb()) { _data->thumb->load(); @@ -630,7 +716,7 @@ void Document::initDimensions() { } } -void Document::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const { +void Document::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { bool selected = (selection == FullSelection); _data->automaticLoad(_parent); @@ -786,7 +872,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con if (clip.intersects(rtlrect(nameleft, statustop, availwidth, st::normalFont->height, _width))) { p.setFont(st::normalFont); p.setPen(st::mediaInFg); - p.drawTextLeft(nameleft, statustop, _width, _statusText); + p.drawTextLeft(nameleft, statustop, _width, _status.text()); } if (datetop >= 0 && clip.intersects(rtlrect(nameleft, datetop, _datew, st::normalFont->height, _width))) { p.setFont(ClickHandler::showAsActive(_msgl) ? st::normalFont->underline() : st::normalFont); @@ -798,8 +884,6 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con void Document::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const { bool loaded = _data->loaded() || Local::willStickerImageLoad(_data->mediaKey()); - bool showPause = updateStatusText(); - int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = 0; bool wthumb = withThumb(); @@ -850,7 +934,7 @@ void Document::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x } } -bool Document::updateStatusText() const { +bool Document::updateStatusText() { bool showPause = false; int32 statusSize = 0, realDuration = 0; if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { @@ -880,8 +964,8 @@ bool Document::updateStatusText() const { } else { statusSize = FileStatusSizeReady; } - if (statusSize != _statusSize) { - setStatusSize(statusSize, _data->size, _data->song() ? _data->song()->duration : -1, realDuration); + if (statusSize != _status.size()) { + _status.update(statusSize, _data->size, _data->song() ? _data->song()->duration : -1, realDuration); } return showPause; } @@ -1041,7 +1125,7 @@ int32 Link::resizeGetHeight(int32 width) { return _height; } -void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const { +void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { int32 left = st::linksPhotoSize + st::linksPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left; if (clip.intersects(rtlrect(0, top, st::linksPhotoSize, st::linksPhotoSize, _width))) { if (_page && _page->photo) { diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index 3909487a6..608fdfdd4 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -39,8 +39,7 @@ public: class ItemBase; class AbstractItem : public LayoutItemBase { public: - - virtual void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const = 0; + virtual void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) = 0; virtual ItemBase *toMediaItem() { return nullptr; @@ -115,7 +114,7 @@ protected: void step_iconOver(float64 ms, bool timer); void step_radial(uint64 ms, bool timer); - void ensureRadial() const; + void ensureRadial(); void checkRadialFinished(); bool isRadialAnimation(uint64 ms) const { @@ -132,28 +131,33 @@ protected: return false; } - mutable Ui::RadialAnimation *_radial; + Ui::RadialAnimation *_radial; anim::fvalue a_iconOver; - mutable Animation _a_iconOver; + Animation _a_iconOver; }; -class FileBase : public RadialProgressItem { +class StatusText { public: - FileBase(HistoryItem *parent) : RadialProgressItem(parent) { + // duration = -1 - no duration, duration = -2 - "GIF" duration + void update(int newSize, int fullSize, int duration, int64 realDuration); + void setSize(int newSize); + + int size() const { + return _size; + } + QString text() const { + return _text; } -protected: - // >= 0 will contain download / upload string, _statusSize = loaded bytes - // < 0 will contain played string, _statusSize = -(seconds + 1) played +private: + // >= 0 will contain download / upload string, _size = loaded bytes + // < 0 will contain played string, _size = -(seconds + 1) played // 0x7FFFFFF0 will contain status for not yet downloaded file // 0x7FFFFFF1 will contain status for already downloaded file // 0x7FFFFFF2 will contain status for failed to download / upload file - mutable int32 _statusSize; - mutable QString _statusText; - - // duration = -1 - no duration, duration = -2 - "GIF" duration - void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const; + int _size = 0; + QString _text; }; @@ -166,7 +170,7 @@ public: Date(const QDate &date, bool month); void initDimensions() override; - void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const override; + void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; private: QDate _date; @@ -174,33 +178,45 @@ private: }; +class PhotoVideoCheckbox; + class Photo : public ItemBase { public: Photo(PhotoData *photo, HistoryItem *parent); void initDimensions() override; int32 resizeGetHeight(int32 width) override; - void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const override; + void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override; + void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) override; + private: + void ensureCheckboxCreated(); + + std_::unique_ptr _check; + PhotoData *_data; ClickHandlerPtr _link; - mutable QPixmap _pix; - mutable bool _goodLoaded; + QPixmap _pix; + bool _goodLoaded = false; }; -class Video : public FileBase { +class Video : public RadialProgressItem { public: Video(DocumentData *video, HistoryItem *parent); void initDimensions() override; int32 resizeGetHeight(int32 width) override; - void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const override; + void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override; + void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) override; + protected: float64 dataProgress() const override { return _data->progress(); @@ -216,22 +232,27 @@ protected: } private: + void ensureCheckboxCreated(); + + std_::unique_ptr _check; + DocumentData *_data; + StatusText _status; QString _duration; - mutable QPixmap _pix; - mutable bool _thumbLoaded; + QPixmap _pix; + bool _thumbLoaded = false; - void updateStatusText() const; + void updateStatusText(); }; -class Voice : public FileBase { +class Voice : public RadialProgressItem { public: Voice(DocumentData *voice, HistoryItem *parent); void initDimensions() override; - void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const override; + void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override; protected: @@ -250,22 +271,23 @@ protected: private: DocumentData *_data; + StatusText _status; ClickHandlerPtr _namel; - mutable Text _name, _details; - mutable int32 _nameVersion; + Text _name, _details; + int _nameVersion; - void updateName() const; - bool updateStatusText() const; + void updateName(); + bool updateStatusText(); }; -class Document : public FileBase { +class Document : public RadialProgressItem { public: Document(DocumentData *document, HistoryItem *parent, const style::OverviewFileLayout &st); void initDimensions() override; - void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const override; + void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override; virtual DocumentData *getDocument() const override { @@ -288,12 +310,13 @@ protected: private: DocumentData *_data; + StatusText _status; ClickHandlerPtr _msgl, _namel; const style::OverviewFileLayout &_st; - mutable bool _thumbForLoaded = false; - mutable QPixmap _thumb; + bool _thumbForLoaded = false; + QPixmap _thumb; Text _name; QString _date, _ext; @@ -303,7 +326,7 @@ private: bool withThumb() const { return !_data->thumb->isNull() && _data->thumb->width() && _data->thumb->height(); } - bool updateStatusText() const; + bool updateStatusText(); }; @@ -313,7 +336,7 @@ public: void initDimensions() override; int32 resizeGetHeight(int32 width) override; - void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const override; + void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override; private: diff --git a/Telegram/SourceFiles/ui/effects/round_image_checkbox.cpp b/Telegram/SourceFiles/ui/effects/round_image_checkbox.cpp index 28b67a083..b7cc84f9a 100644 --- a/Telegram/SourceFiles/ui/effects/round_image_checkbox.cpp +++ b/Telegram/SourceFiles/ui/effects/round_image_checkbox.cpp @@ -24,23 +24,25 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { namespace { -static constexpr int kWideScale = 4; +static constexpr int kWideScale = 3; -void prepareCheckCaches(const style::RoundImageCheckbox *st, QPixmap &checkBgCache, QPixmap &checkFullCache) { - auto size = st->checkRadius * 2; +void prepareCheckCaches(const style::RoundCheckbox *st, bool displayInactive, QPixmap &checkBgCache, QPixmap &checkFullCache) { + auto size = st->size; auto wideSize = size * kWideScale; auto cache = QImage(wideSize * cIntRetinaFactor(), wideSize * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); cache.setDevicePixelRatio(cRetinaFactor()); + cache.fill(Qt::transparent); { Painter p(&cache); - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(0, 0, wideSize, wideSize, Qt::transparent); - p.setCompositionMode(QPainter::CompositionMode_SourceOver); p.setRenderHint(QPainter::HighQualityAntialiasing, true); - auto pen = st->checkBorder->p; - pen.setWidth(st->selectWidth); - p.setPen(pen); - p.setBrush(st->checkBg); + if (displayInactive) { + p.setPen(Qt::NoPen); + } else { + auto pen = st->border->p; + pen.setWidth(st->width); + p.setPen(pen); + } + p.setBrush(st->bgActive); auto ellipse = QRect((wideSize - size) / 2, (wideSize - size) / 2, size, size); p.drawEllipse(ellipse); } @@ -48,32 +50,183 @@ void prepareCheckCaches(const style::RoundImageCheckbox *st, QPixmap &checkBgCac { Painter p(&cacheIcon); auto ellipse = QRect((wideSize - size) / 2, (wideSize - size) / 2, size, size); - st->checkIcon.paint(p, ellipse.topLeft(), wideSize); + st->check.paint(p, ellipse.topLeft(), wideSize); } checkBgCache = App::pixmapFromImageInPlace(std_::move(cache)); - checkBgCache.setDevicePixelRatio(cRetinaFactor()); checkFullCache = App::pixmapFromImageInPlace(std_::move(cacheIcon)); - checkFullCache.setDevicePixelRatio(cRetinaFactor()); } } // namespace -RoundImageCheckbox::RoundImageCheckbox(const style::RoundImageCheckbox &st, const base::lambda_copy &updateCallback, PaintRoundImage &&paintRoundImage) +RoundCheckbox::RoundCheckbox(const style::RoundCheckbox &st, const base::lambda_copy &updateCallback) : _st(st) -, _updateCallback(updateCallback) -, _paintRoundImage(std_::move(paintRoundImage)) { - prepareCheckCaches(&_st, _wideCheckBgCache, _wideCheckFullCache); +, _updateCallback(updateCallback) { } -void RoundImageCheckbox::paint(Painter &p, uint64 ms, int x, int y, int outerWidth) { - _selection.step(ms); +QRect RoundCheckbox::cacheDestRect(int x, int y, float64 scale) const { + auto iconSizeFull = kWideScale * _st.size; + auto iconSize = qRound(iconSizeFull * scale); + if (iconSize % 2 != iconSizeFull % 2) { + ++iconSize; + } + auto iconShift = (iconSizeFull - iconSize) / 2; + auto iconLeft = x - (kWideScale - 1) * _st.size / 2 + iconShift; + auto iconTop = y - (kWideScale - 1) * _st.size / 2 + iconShift; + return QRect(iconLeft, iconTop, iconSize, iconSize); +} + +void RoundCheckbox::paint(Painter &p, uint64 ms, int x, int y, int outerWidth, float64 masterScale) { for (auto &icon : _icons) { icon.fadeIn.step(ms); icon.fadeOut.step(ms); } removeFadeOutedIcons(); - auto selectionLevel = _selection.current(_checked ? 1. : 0.); + auto cacheSize = kWideScale * _st.size * cIntRetinaFactor(); + auto cacheFrom = QRect(0, 0, cacheSize, cacheSize); + auto displayInactive = !_inactiveCacheBg.isNull(); + auto inactiveTo = cacheDestRect(x, y, masterScale); + p.setRenderHint(QPainter::SmoothPixmapTransform, true); + if (!_inactiveCacheBg.isNull()) { + p.drawPixmap(inactiveTo, _inactiveCacheBg, cacheFrom); + } + for (auto &icon : _icons) { + auto fadeIn = icon.fadeIn.current(1.); + auto fadeOut = icon.fadeOut.current(1.); + auto to = cacheDestRect(x, y, (1. - (1. - _st.sizeSmall) * (1. - fadeOut)) * masterScale); + p.setOpacity(fadeIn * fadeOut); + if (fadeOut < 1.) { + p.drawPixmapLeft(to, outerWidth, icon.wideCheckCache, cacheFrom); + } else { + auto realDivider = ((kWideScale - 1) * _st.size / 2 + qMax(fadeIn - 0.5, 0.) * 2. * _st.size); + auto divider = qRound(realDivider * masterScale); + auto cacheDivider = qRound(realDivider) * cIntRetinaFactor(); + p.drawPixmapLeft(QRect(to.x(), to.y(), divider, to.height()), outerWidth, _wideCheckFullCache, QRect(0, 0, cacheDivider, cacheFrom.height())); + p.drawPixmapLeft(QRect(to.x() + divider, to.y(), to.width() - divider, to.height()), outerWidth, _wideCheckBgCache, QRect(cacheDivider, 0, cacheFrom.width() - cacheDivider, _wideCheckBgCache.height())); + } + } + p.setOpacity(1.); + if (!_inactiveCacheFg.isNull()) { + p.drawPixmap(inactiveTo, _inactiveCacheFg, cacheFrom); + } + p.setRenderHint(QPainter::SmoothPixmapTransform, false); +} + +void RoundCheckbox::setChecked(bool newChecked, SetStyle speed) { + if (_checked == newChecked) { + if (speed != SetStyle::Animated && !_icons.isEmpty()) { + _icons.back().fadeIn.finish(); + _icons.back().fadeOut.finish(); + } + return; + } + _checked = newChecked; + if (_checked) { + if (_wideCheckBgCache.isNull()) { + prepareCheckCaches(&_st, _displayInactive, _wideCheckBgCache, _wideCheckFullCache); + } + _icons.push_back(Icon()); + _icons.back().fadeIn.start(_updateCallback, 0, 1, _st.duration); + if (speed != SetStyle::Animated) { + _icons.back().fadeIn.finish(); + } + } else { + if (speed == SetStyle::Animated) { + prepareWideCheckIconCache(&_icons.back()); + } + _icons.back().fadeOut.start(_updateCallback, 1, 0, _st.duration); + if (speed != SetStyle::Animated) { + _icons.back().fadeOut.finish(); + } + } +} + +void RoundCheckbox::setDisplayInactive(bool displayInactive) { + if (_displayInactive != displayInactive) { + _displayInactive = displayInactive; + if (_displayInactive) { + prepareInactiveCache(); + } else { + _inactiveCacheBg = _inactiveCacheFg = QPixmap(); + } + if (!_wideCheckBgCache.isNull()) { + prepareCheckCaches(&_st, _displayInactive, _wideCheckBgCache, _wideCheckFullCache); + } + for (auto &icon : _icons) { + if (!icon.wideCheckCache.isNull()) { + prepareWideCheckIconCache(&icon); + } + } + } +} + +void RoundCheckbox::removeFadeOutedIcons() { + while (!_icons.empty() && !_icons.front().fadeIn.animating() && !_icons.front().fadeOut.animating()) { + if (_icons.size() > 1 || !_checked) { + _icons.erase(_icons.begin()); + } else { + break; + } + } +} + +void RoundCheckbox::prepareWideCheckIconCache(Icon *icon) { + auto cacheWidth = _wideCheckBgCache.width() / _wideCheckBgCache.devicePixelRatio(); + auto cacheHeight = _wideCheckBgCache.height() / _wideCheckBgCache.devicePixelRatio(); + auto wideCache = QImage(cacheWidth * cIntRetinaFactor(), cacheHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + wideCache.setDevicePixelRatio(cRetinaFactor()); + { + Painter p(&wideCache); + p.setCompositionMode(QPainter::CompositionMode_Source); + auto iconSize = kWideScale * _st.size; + auto divider = qRound((kWideScale - 1) * _st.size / 2 + qMax(icon->fadeIn.current(1.) - 0.5, 0.) * 2. * _st.size); + p.drawPixmapLeft(QRect(0, 0, divider, iconSize), cacheWidth, _wideCheckFullCache, QRect(0, 0, divider * cIntRetinaFactor(), _wideCheckFullCache.height())); + p.drawPixmapLeft(QRect(divider, 0, iconSize - divider, iconSize), cacheWidth, _wideCheckBgCache, QRect(divider * cIntRetinaFactor(), 0, _wideCheckBgCache.width() - divider * cIntRetinaFactor(), _wideCheckBgCache.height())); + } + icon->wideCheckCache = App::pixmapFromImageInPlace(std_::move(wideCache)); + icon->wideCheckCache.setDevicePixelRatio(cRetinaFactor()); +} + +void RoundCheckbox::prepareInactiveCache() { + auto wideSize = _st.size * kWideScale; + auto ellipse = QRect((wideSize - _st.size) / 2, (wideSize - _st.size) / 2, _st.size, _st.size); + + auto cacheBg = QImage(wideSize * cIntRetinaFactor(), wideSize * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + cacheBg.setDevicePixelRatio(cRetinaFactor()); + cacheBg.fill(Qt::transparent); + auto cacheFg = cacheBg; + if (_st.bgInactive) { + Painter p(&cacheBg); + p.setRenderHint(QPainter::HighQualityAntialiasing, true); + p.setPen(Qt::NoPen); + p.setBrush(_st.bgInactive); + p.drawEllipse(ellipse); + } + _inactiveCacheBg = App::pixmapFromImageInPlace(std_::move(cacheBg)); + + { + Painter p(&cacheFg); + p.setRenderHint(QPainter::HighQualityAntialiasing, true); + auto pen = _st.border->p; + pen.setWidth(_st.width); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + p.drawEllipse(ellipse); + } + _inactiveCacheFg = App::pixmapFromImageInPlace(std_::move(cacheFg)); +} + +RoundImageCheckbox::RoundImageCheckbox(const style::RoundImageCheckbox &st, const base::lambda_copy &updateCallback, PaintRoundImage &&paintRoundImage) +: _st(st) +, _updateCallback(updateCallback) +, _paintRoundImage(std_::move(paintRoundImage)) +, _check(_st.check, _updateCallback) { +} + +void RoundImageCheckbox::paint(Painter &p, uint64 ms, int x, int y, int outerWidth) { + _selection.step(ms); + + auto selectionLevel = _selection.current(checked() ? 1. : 0.); if (_selection.animating()) { auto userpicRadius = qRound(kWideScale * (_st.imageRadius + (_st.imageSmallRadius - _st.imageRadius) * selectionLevel)); auto userpicShift = kWideScale * _st.imageRadius - userpicRadius; @@ -89,7 +242,7 @@ void RoundImageCheckbox::paint(Painter &p, uint64 ms, int x, int y, int outerWid if (!_wideCache.isNull()) { _wideCache = QPixmap(); } - auto userpicRadius = _checked ? _st.imageSmallRadius : _st.imageRadius; + auto userpicRadius = checked() ? _st.imageSmallRadius : _st.imageRadius; auto userpicShift = _st.imageRadius - userpicRadius; auto userpicLeft = x + userpicShift; auto userpicTop = y + userpicShift; @@ -108,78 +261,32 @@ void RoundImageCheckbox::paint(Painter &p, uint64 ms, int x, int y, int outerWid p.setRenderHint(QPainter::HighQualityAntialiasing, false); } - p.setRenderHint(QPainter::SmoothPixmapTransform, true); - for (auto &icon : _icons) { - auto fadeIn = icon.fadeIn.current(1.); - auto fadeOut = icon.fadeOut.current(1.); - auto iconRadius = qRound(kWideScale * (_st.checkSmallRadius + fadeOut * (_st.checkRadius - _st.checkSmallRadius))); - auto iconShift = kWideScale * _st.checkRadius - iconRadius; - auto iconLeft = x + 2 * _st.imageRadius + _st.selectWidth - 2 * _st.checkRadius - (kWideScale - 1) * _st.checkRadius + iconShift; - auto iconTop = y + 2 * _st.imageRadius + _st.selectWidth - 2 * _st.checkRadius - (kWideScale - 1) * _st.checkRadius + iconShift; - auto to = QRect(iconLeft, iconTop, iconRadius * 2, iconRadius * 2); - auto from = QRect(QPoint(0, 0), _wideCheckFullCache.size()); - auto opacity = fadeIn * fadeOut; - p.setOpacity(opacity); - if (fadeOut < 1.) { - p.drawPixmapLeft(to, outerWidth, icon.wideCheckCache, from); - } else { - auto divider = qRound((kWideScale - 2) * _st.checkRadius + fadeIn * 3 * _st.checkRadius); - p.drawPixmapLeft(QRect(iconLeft, iconTop, divider, iconRadius * 2), outerWidth, _wideCheckFullCache, QRect(0, 0, divider * cIntRetinaFactor(), _wideCheckFullCache.height())); - p.drawPixmapLeft(QRect(iconLeft + divider, iconTop, iconRadius * 2 - divider, iconRadius * 2), outerWidth, _wideCheckBgCache, QRect(divider * cIntRetinaFactor(), 0, _wideCheckBgCache.width() - divider * cIntRetinaFactor(), _wideCheckBgCache.height())); - } - } - p.setRenderHint(QPainter::SmoothPixmapTransform, false); - p.setOpacity(1.); + auto iconLeft = x + 2 * _st.imageRadius + _st.selectWidth - _st.check.size; + auto iconTop = y + 2 * _st.imageRadius + _st.selectWidth - _st.check.size; + _check.paint(p, ms, iconLeft, iconTop, outerWidth); } float64 RoundImageCheckbox::checkedAnimationRatio() const { - return snap(_selection.current(_checked ? 1. : 0.), 0., 1.); + return snap(_selection.current(checked() ? 1. : 0.), 0., 1.); } -void RoundImageCheckbox::setChecked(bool checked, SetStyle speed) { - if (_checked == checked) { +void RoundImageCheckbox::setChecked(bool newChecked, SetStyle speed) { + auto changed = (checked() != newChecked); + _check.setChecked(newChecked, speed); + if (!changed) { if (speed != SetStyle::Animated) { - if (!_icons.isEmpty()) { - _icons.back().fadeIn.finish(); - _icons.back().fadeOut.finish(); - } _selection.finish(); } return; } - _checked = checked; - if (_checked) { - _icons.push_back(Icon()); - _icons.back().fadeIn.start(_updateCallback, 0, 1, _st.selectDuration); - if (speed != SetStyle::Animated) { - _icons.back().fadeIn.finish(); - } - } else { - _icons.back().fadeOut.start(_updateCallback, 1, 0, _st.selectDuration); - if (speed == SetStyle::Animated) { - prepareWideCheckIconCache(&_icons.back()); - } else { - _icons.back().fadeOut.finish(); - } - } if (speed == SetStyle::Animated) { prepareWideCache(); - _selection.start(_updateCallback, _checked ? 0 : 1, _checked ? 1 : 0, _st.selectDuration, anim::bumpy(1.25)); + _selection.start(_updateCallback, checked() ? 0 : 1, checked() ? 1 : 0, _st.selectDuration, anim::bumpy(1.25)); } else { _selection.finish(); } } -void RoundImageCheckbox::removeFadeOutedIcons() { - while (!_icons.empty() && !_icons.front().fadeIn.animating() && !_icons.front().fadeOut.animating()) { - if (_icons.size() > 1 || !_checked) { - _icons.erase(_icons.begin()); - } else { - break; - } - } -} - void RoundImageCheckbox::prepareWideCache() { if (_wideCache.isNull()) { auto size = _st.imageRadius * 2; @@ -197,21 +304,4 @@ void RoundImageCheckbox::prepareWideCache() { } } -void RoundImageCheckbox::prepareWideCheckIconCache(Icon *icon) { - auto cacheWidth = _wideCheckBgCache.width() / _wideCheckBgCache.devicePixelRatio(); - auto cacheHeight = _wideCheckBgCache.height() / _wideCheckBgCache.devicePixelRatio(); - auto wideCache = QImage(cacheWidth * cIntRetinaFactor(), cacheHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - wideCache.setDevicePixelRatio(cRetinaFactor()); - { - Painter p(&wideCache); - p.setCompositionMode(QPainter::CompositionMode_Source); - auto iconRadius = kWideScale * _st.checkRadius; - auto divider = qRound((kWideScale - 2) * _st.checkRadius + icon->fadeIn.current(1.) * (kWideScale - 1) * _st.checkRadius); - p.drawPixmapLeft(QRect(0, 0, divider, iconRadius * 2), cacheWidth, _wideCheckFullCache, QRect(0, 0, divider * cIntRetinaFactor(), _wideCheckFullCache.height())); - p.drawPixmapLeft(QRect(divider, 0, iconRadius * 2 - divider, iconRadius * 2), cacheWidth, _wideCheckBgCache, QRect(divider * cIntRetinaFactor(), 0, _wideCheckBgCache.width() - divider * cIntRetinaFactor(), _wideCheckBgCache.height())); - } - icon->wideCheckCache = App::pixmapFromImageInPlace(std_::move(wideCache)); - icon->wideCheckCache.setDevicePixelRatio(cRetinaFactor()); -} - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/round_image_checkbox.h b/Telegram/SourceFiles/ui/effects/round_image_checkbox.h index 67b5d91f8..b528bced8 100644 --- a/Telegram/SourceFiles/ui/effects/round_image_checkbox.h +++ b/Telegram/SourceFiles/ui/effects/round_image_checkbox.h @@ -24,6 +24,47 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { +class RoundCheckbox { +public: + RoundCheckbox(const style::RoundCheckbox &st, const base::lambda_copy &updateCallback); + + void paint(Painter &p, uint64 ms, int x, int y, int outerWidth, float64 masterScale = 1.); + + void setDisplayInactive(bool displayInactive); + bool checked() const { + return _checked; + } + enum class SetStyle { + Animated, + Fast, + }; + void setChecked(bool newChecked, SetStyle speed = SetStyle::Animated); + +private: + struct Icon { + FloatAnimation fadeIn; + FloatAnimation fadeOut; + QPixmap wideCheckCache; + }; + void removeFadeOutedIcons(); + void prepareWideCheckIconCache(Icon *icon); + void prepareInactiveCache(); + QRect cacheDestRect(int x, int y, float64 scale) const; + + const style::RoundCheckbox &_st; + base::lambda_copy _updateCallback; + + bool _checked = false; + std_::vector_of_moveable _icons; + + bool _displayInactive = false; + QPixmap _inactiveCacheBg, _inactiveCacheFg; + + // Those pixmaps are shared among all checkboxes that have the same style. + QPixmap _wideCheckBgCache, _wideCheckFullCache; + +}; + class RoundImageCheckbox { public: using PaintRoundImage = base::lambda; @@ -33,35 +74,22 @@ public: float64 checkedAnimationRatio() const; bool checked() const { - return _checked; + return _check.checked(); } - enum class SetStyle { - Animated, - Fast, - }; - void setChecked(bool checked, SetStyle speed = SetStyle::Animated); + using SetStyle = RoundCheckbox::SetStyle; + void setChecked(bool newChecked, SetStyle speed = SetStyle::Animated); private: - struct Icon { - FloatAnimation fadeIn; - FloatAnimation fadeOut; - QPixmap wideCheckCache; - }; - void removeFadeOutedIcons(); void prepareWideCache(); - void prepareWideCheckIconCache(Icon *icon); const style::RoundImageCheckbox &_st; base::lambda_copy _updateCallback; PaintRoundImage _paintRoundImage; - bool _checked = false; QPixmap _wideCache; FloatAnimation _selection; - std_::vector_of_moveable _icons; - // Those pixmaps are shared among all checkboxes that have the same style. - QPixmap _wideCheckBgCache, _wideCheckFullCache; + RoundCheckbox _check; }; diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index df6840fd3..9e8680571 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -319,17 +319,24 @@ FilledSlider { duration: int; } +RoundCheckbox { + border: color; + bgInactive: color; + bgActive: color; + width: pixels; + size: pixels; + sizeSmall: double; + duration: int; + check: icon; +} + RoundImageCheckbox { imageRadius: pixels; imageSmallRadius: pixels; selectWidth: pixels; selectFg: color; selectDuration: int; - checkBorder: color; - checkBg: color; - checkRadius: pixels; - checkSmallRadius: pixels; - checkIcon: icon; + check: RoundCheckbox; } MultiSelectItem { @@ -738,6 +745,13 @@ defaultPanelAnimation: PanelAnimation { shadow: defaultRoundShadow; } +defaultRoundCheckbox: RoundCheckbox { + border: windowBg; + bgActive: windowBgActive; + width: 2px; + duration: 150; +} + defaultMenuArrow: icon {{ "dropdown_submenu_arrow", #373737 }}; defaultMenu: Menu { skip: 0px;