Photo and video overview and background gallery checkboxes improved.

This commit is contained in:
John Preston 2016-11-21 19:24:23 +03:00
parent e880c4876e
commit 9fa284a6d1
13 changed files with 491 additions and 246 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 B

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 632 B

After

Width:  |  Height:  |  Size: 623 B

View File

@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "window/window_theme.h" #include "window/window_theme.h"
#include "styles/style_overview.h" #include "styles/style_overview.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "ui/effects/round_image_checkbox.h"
BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll) BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll)
, _inner(this) { , _inner(this) {
@ -50,10 +51,8 @@ void BackgroundBox::onBackgroundChosen(int index) {
} }
BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent) BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent)
, _bgCount(0) , _check(std_::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [this] { update(); })) {
, _rows(0) _check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast);
, _over(-1)
, _overDown(-1) {
if (App::cServerBackgrounds().isEmpty()) { if (App::cServerBackgrounds().isEmpty()) {
resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
MTP::send(MTPaccount_GetWallPapers(), rpcDone(&Inner::gotWallpapers)); MTP::send(MTPaccount_GetWallPapers(), rpcDone(&Inner::gotWallpapers));
@ -157,9 +156,9 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
p.drawPixmap(x, y, pix); p.drawPixmap(x, y, pix);
if (paper.id == Window::Theme::Background()->id()) { if (paper.id == Window::Theme::Background()->id()) {
int checkPosX = x + st::backgroundSize.width() - st::overviewPhotoChecked.width(); auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
int checkPosY = y + st::backgroundSize.height() - st::overviewPhotoChecked.height(); auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
st::overviewPhotoChecked.paint(p, QPoint(checkPosX, checkPosY), width()); _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;
}

View File

@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui {
class RoundCheckbox;
} // namespace Ui
class BackgroundBox : public ItemListBox { class BackgroundBox : public ItemListBox {
Q_OBJECT Q_OBJECT
@ -43,6 +47,7 @@ class BackgroundBox::Inner : public TWidget, public RPCSender, private base::Sub
public: public:
Inner(QWidget *parent); Inner(QWidget *parent);
~Inner();
signals: signals:
void backgroundChosen(int index); void backgroundChosen(int index);
@ -52,13 +57,15 @@ protected:
void mouseMoveEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private: private:
void gotWallpapers(const MTPVector<MTPWallPaper> &result); void gotWallpapers(const MTPVector<MTPWallPaper> &result);
void updateWallpapers(); void updateWallpapers();
int32 _bgCount, _rows; int _bgCount = 0;
int32 _over, _overDown; int _rows = 0;
int _over = -1;
int _overDown = -1;
std_::unique_ptr<Ui::RoundCheckbox> _check;
}; };

View File

@ -304,11 +304,11 @@ contactsPhotoCheckbox: RoundImageCheckbox {
selectWidth: 2px; selectWidth: 2px;
selectFg: windowBgActive; selectFg: windowBgActive;
selectDuration: 150; selectDuration: 150;
checkBorder: windowBg; check: RoundCheckbox(defaultRoundCheckbox) {
checkBg: windowBgActive; size: 20px;
checkRadius: 10px; sizeSmall: 0.3;
checkSmallRadius: 3px; check: icon {{ "default_checkbox_check", windowFgActive, point(3px, 6px) }};
checkIcon: icon {{ "default_checkbox_check", windowFgActive, point(3px, 6px) }}; }
} }
contactsPhotoDisabledCheckFg: #bbbbbb; contactsPhotoDisabledCheckFg: #bbbbbb;
contactsNameCheckedFg: #2b88b8; contactsNameCheckedFg: #2b88b8;

View File

@ -1002,11 +1002,11 @@ void ContactsBox::Inner::paintDisabledCheckUserpic(Painter &p, PeerData *peer, i
auto userpicBorderPen = st::contactsPhotoDisabledCheckFg->p; auto userpicBorderPen = st::contactsPhotoDisabledCheckFg->p;
userpicBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth); 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 iconLeft = x + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter;
auto iconTop = y + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter; auto iconTop = y + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter;
auto iconEllipse = rtlrect(iconLeft, iconTop, iconDiameter, iconDiameter, outerWidth); 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); iconBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth);
peer->paintUserpicLeft(p, userpicRadius * 2, userpicLeft, userpicTop, width()); 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); 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) { void ContactsBox::Inner::paintEvent(QPaintEvent *e) {

View File

@ -2082,8 +2082,8 @@ void MainWidget::fillPeerMenu(PeerData *peer, base::lambda<QAction*(const QStrin
} }
} }
} else if (peer->isChat()) { } else if (peer->isChat()) {
callback(lang(lng_profile_clear_history), std_::move(clearHistoryHandler));
callback(lang(lng_profile_clear_and_exit), std_::move(deleteAndLeaveHandler)); 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()) { } 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)); callback(lang(peer->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel), std_::move(deleteAndLeaveHandler));
} }

View File

@ -43,21 +43,22 @@ OverviewFileLayout {
fileDateTop: pixels; fileDateTop: pixels;
} }
overviewCheckBg: #00000042; overviewCheckBg: #00000040;
overviewCheckedBg: #2fa9e2; 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; overviewPhotoSkip: 10px;
overviewPhotoBg: #F1F1F1; overviewPhotoBg: #F1F1F1;
overviewPhotoMinSize: minPhotoSize; overviewPhotoMinSize: minPhotoSize;
overviewPhotoCheck: icon { overviewPhotoSelectOverlay: #40ace333;
{ 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;
overviewVideoBg: #000000; overviewVideoBg: #000000;
@ -111,11 +112,11 @@ linksPhotoSize: 46px;
linksPhotoPadding: 12px; linksPhotoPadding: 12px;
overviewLinksCheck: icon { overviewLinksCheck: icon {
{ "overview_links_check_bg", overviewCheckBg }, { "overview_links_check_bg", overviewCheckBg },
{ "overview_links_check", #ffffff, point(4px, 5px) }, { "overview_links_check", overviewCheckFg, point(4px, 5px) },
}; };
overviewLinksChecked: icon { overviewLinksChecked: icon {
{ "overview_links_check_bg", overviewCheckedBg }, { "overview_links_check_bg", windowBgActive },
{ "overview_links_check", #ffffff, point(4px, 5px) }, { "overview_links_check", overviewCheckFgActive, point(4px, 5px) },
}; };
overviewFilter: FlatInput(defaultFlatInput) { overviewFilter: FlatInput(defaultFlatInput) {

View File

@ -35,6 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/player/media_player_instance.h" #include "media/player/media_player_instance.h"
#include "localstorage.h" #include "localstorage.h"
#include "history/history_media_types.h" #include "history/history_media_types.h"
#include "ui/effects/round_image_checkbox.h"
namespace Overview { namespace Overview {
namespace Layout { namespace Layout {
@ -101,7 +102,7 @@ void RadialProgressItem::step_radial(uint64 ms, bool timer) {
} }
} }
void RadialProgressItem::ensureRadial() const { void RadialProgressItem::ensureRadial() {
if (!_radial) { if (!_radial) {
_radial = new Ui::RadialAnimation(animation(const_cast<RadialProgressItem*>(this), &RadialProgressItem::step_radial)); _radial = new Ui::RadialAnimation(animation(const_cast<RadialProgressItem*>(this), &RadialProgressItem::step_radial));
} }
@ -118,21 +119,25 @@ RadialProgressItem::~RadialProgressItem() {
delete base::take(_radial); delete base::take(_radial);
} }
void FileBase::setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const { void StatusText::update(int newSize, int fullSize, int duration, int64 realDuration) {
_statusSize = newSize; setSize(newSize);
if (_statusSize == FileStatusSizeReady) { if (_size == FileStatusSizeReady) {
_statusText = (duration >= 0) ? formatDurationAndSizeText(duration, fullSize) : (duration < -1 ? formatGifAndSizeText(fullSize) : formatSizeText(fullSize)); _text = (duration >= 0) ? formatDurationAndSizeText(duration, fullSize) : (duration < -1 ? formatGifAndSizeText(fullSize) : formatSizeText(fullSize));
} else if (_statusSize == FileStatusSizeLoaded) { } else if (_size == FileStatusSizeLoaded) {
_statusText = (duration >= 0) ? formatDurationText(duration) : (duration < -1 ? qsl("GIF") : formatSizeText(fullSize)); _text = (duration >= 0) ? formatDurationText(duration) : (duration < -1 ? qsl("GIF") : formatSizeText(fullSize));
} else if (_statusSize == FileStatusSizeFailed) { } else if (_size == FileStatusSizeFailed) {
_statusText = lang(lng_attach_failed); _text = lang(lng_attach_failed);
} else if (_statusSize >= 0) { } else if (_size >= 0) {
_statusText = formatDownloadText(_statusSize, fullSize); _text = formatDownloadText(_size, fullSize);
} else { } 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(const QDate &date, bool month)
: _date(date) : _date(date)
, _text(month ? langMonthFull(date) : langDayOfMonthFull(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; _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))) { if (clip.intersects(QRect(0, st::linksDateMargin.top(), _width, st::normalFont->height))) {
p.setPen(st::linksDateColor); p.setPen(st::linksDateColor);
p.setFont(st::semiboldFont); 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 <typename UpdateCallback>
PhotoVideoCheckbox(UpdateCallback callback) : _updateCallback(callback), _check(st::overviewCheck, _updateCallback) {
}
void paintPhotoVideoCheck(Painter &p, int width, int height, bool selected) { void paint(Painter &p, uint64 ms, int width, int height, bool selected, bool selecting);
int checkPosX = width - st::overviewPhotoCheck.width();
int checkPosY = height - st::overviewPhotoCheck.height(); void setActive(bool active);
void setPressed(bool pressed);
private:
void startAnimation();
base::lambda_copy<void()> _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) { if (selected) {
p.fillRect(QRect(0, 0, width, height), st::overviewPhotoSelectOverlay); p.fillRect(0, 0, width, height, st::overviewPhotoSelectOverlay);
st::overviewPhotoChecked.paint(p, QPoint(checkPosX, checkPosY), width); }
} else { _check.setDisplayInactive(selecting);
st::overviewPhotoCheck.paint(p, QPoint(checkPosX, checkPosY), width); _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) Photo::Photo(PhotoData *photo, HistoryItem *parent) : ItemBase(parent)
, _data(photo) , _data(photo)
, _link(new PhotoOpenClickHandler(photo)) , _link(new PhotoOpenClickHandler(photo)) {
, _goodLoaded(false) {
} }
void Photo::initDimensions() { void Photo::initDimensions() {
@ -187,7 +229,7 @@ int32 Photo::resizeGetHeight(int32 width) {
return _height; 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); bool good = _data->loaded(), selected = (selection == FullSelection);
if (!good) { if (!good) {
_data->medium->automaticLoad(_parent); _data->medium->automaticLoad(_parent);
@ -225,9 +267,19 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const
} else { } else {
p.drawPixmap(0, 0, _pix); p.drawPixmap(0, 0, _pix);
} }
if (selected || context->selecting) { 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<PhotoVideoCheckbox>([this] {
Ui::repaintHistoryItem(_parent);
});
} }
void Photo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const { 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) , _data(video)
, _duration(formatDurationText(_data->duration())) , _duration(formatDurationText(_data->duration()))
, _thumbLoaded(false) { , _thumbLoaded(false) {
@ -254,7 +318,7 @@ int32 Video::resizeGetHeight(int32 width) {
return _height; 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(); bool selected = (selection == FullSelection), thumbLoaded = _data->thumb->loaded();
_data->automaticLoad(_parent); _data->automaticLoad(_parent);
@ -305,13 +369,13 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
if (!selected && !context->selecting && !loaded) { if (!selected && !context->selecting && !loaded) {
if (clip.intersects(QRect(0, _height - st::normalFont->height, _width, st::normalFont->height))) { 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 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(); int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
statusX = _width - statusW + statusX; statusX = _width - statusW + statusX;
p.fillRect(rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg); p.fillRect(rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg);
p.setFont(st::normalFont); p.setFont(st::normalFont);
p.setPen(st::msgDateImgColor); 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))) { 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); _radial->draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg);
} }
} }
if (selected || context->selecting) { 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<PhotoVideoCheckbox>([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; bool showPause = false;
int32 statusSize = 0; int statusSize = 0;
if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
statusSize = FileStatusSizeFailed; statusSize = FileStatusSizeFailed;
} else if (_data->status == FileUploading) { } else if (_data->status == FileUploading) {
@ -385,18 +473,18 @@ void Video::updateStatusText() const {
} else { } else {
statusSize = FileStatusSizeReady; statusSize = FileStatusSizeReady;
} }
if (statusSize != _statusSize) { if (statusSize != _status.size()) {
int32 status = statusSize, size = _data->size; int status = statusSize, size = _data->size;
if (statusSize >= 0 && statusSize < 0x7F000000) { if (statusSize >= 0 && statusSize < 0x7F000000) {
size = status; size = status;
status = FileStatusSizeReady; status = FileStatusSizeReady;
} }
setStatusSize(status, size, -1, 0); _status.update(status, size, -1, 0);
_statusSize = statusSize; _status.setSize(statusSize);
} }
} }
Voice::Voice(DocumentData *voice, HistoryItem *parent) : FileBase(parent) Voice::Voice(DocumentData *voice, HistoryItem *parent) : RadialProgressItem(parent)
, _data(voice) , _data(voice)
, _namel(new DocumentOpenClickHandler(_data)) { , _namel(new DocumentOpenClickHandler(_data)) {
AddComponents(Info::Bit()); AddComponents(Info::Bit());
@ -417,7 +505,7 @@ void Voice::initDimensions() {
_minh = st::overviewFileLayout.songPadding.top() + st::overviewFileLayout.songThumbSize + st::overviewFileLayout.songPadding.bottom() + st::lineWidth; _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); bool selected = (selection == FullSelection);
_data->automaticLoad(_parent); _data->automaticLoad(_parent);
@ -474,7 +562,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const
auto icon = ([showPause, this, selected] { auto icon = ([showPause, this, selected] {
if (showPause) { if (showPause) {
return &(selected ? st::historyFileInPauseSelected : st::historyFileInPause); 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); return &(selected ? st::historyFileInPlaySelected : st::historyFileInPlay);
} else if (_data->loading()) { } else if (_data->loading()) {
return &(selected ? st::historyFileInCancelSelected : st::historyFileInCancel); 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.setFont(st::normalFont);
p.setPen(selected ? st::mediaInFgSelected : st::mediaInFg); p.setPen(selected ? st::mediaInFgSelected : st::mediaInFg);
int32 unreadx = nameleft; int32 unreadx = nameleft;
if (_statusSize == FileStatusSizeLoaded || _statusSize == FileStatusSizeReady) { if (_status.size() == FileStatusSizeLoaded || _status.size() == FileStatusSizeReady) {
textstyleSet(&(selected ? st::mediaInStyleSelected : st::mediaInStyle)); textstyleSet(&(selected ? st::mediaInStyleSelected : st::mediaInStyle));
_details.drawLeftElided(p, nameleft, statustop, namewidth, _width); _details.drawLeftElided(p, nameleft, statustop, namewidth, _width);
textstyleRestore(); textstyleRestore();
unreadx += _details.maxWidth(); unreadx += _details.maxWidth();
} else { } else {
int32 statusw = st::normalFont->width(_statusText); int32 statusw = st::normalFont->width(_status.text());
p.drawTextLeft(nameleft, statustop, _width, _statusText, statusw); p.drawTextLeft(nameleft, statustop, _width, _status.text(), statusw);
unreadx += statusw; unreadx += statusw;
} }
if (_parent->isMediaUnread() && unreadx + st::mediaUnreadSkip + st::mediaUnreadSize <= _width) { 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 { void Voice::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
bool loaded = _data->loaded(); bool loaded = _data->loaded();
bool showPause = updateStatusText();
int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = 0; int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = 0;
nameleft = st::overviewFileLayout.songPadding.left() + st::overviewFileLayout.songThumbSize + st::overviewFileLayout.songPadding.right(); 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; return;
} }
if (rtlrect(nameleft, statustop, _width - nameleft - nameright, st::normalFont->height, _width).contains(x, y)) { 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); auto textState = _details.getStateLeft(x - nameleft, y - statustop, _width, _width);
link = textState.link; link = textState.link;
cursor = textState.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState; 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; int32 version = 0;
if (const HistoryMessageForwarded *fwd = _parent->Get<HistoryMessageForwarded>()) { if (const HistoryMessageForwarded *fwd = _parent->Get<HistoryMessageForwarded>()) {
if (_parent->fromOriginal()->isChannel()) { if (_parent->fromOriginal()->isChannel()) {
@ -561,7 +647,7 @@ void Voice::updateName() const {
_nameVersion = version; _nameVersion = version;
} }
bool Voice::updateStatusText() const { bool Voice::updateStatusText() {
bool showPause = false; bool showPause = false;
int32 statusSize = 0, realDuration = 0; int32 statusSize = 0, realDuration = 0;
if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
@ -580,13 +666,13 @@ bool Voice::updateStatusText() const {
} else { } else {
statusSize = FileStatusSizeReady; statusSize = FileStatusSizeReady;
} }
if (statusSize != _statusSize) { if (statusSize != _status.size()) {
setStatusSize(statusSize, _data->size, _data->voice()->duration, realDuration); _status.update(statusSize, _data->size, _data->voice()->duration, realDuration);
} }
return showPause; 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) , _data(document)
, _msgl(goToMessageClickHandler(parent)) , _msgl(goToMessageClickHandler(parent))
, _namel(new DocumentOpenClickHandler(_data)) , _namel(new DocumentOpenClickHandler(_data))
@ -600,7 +686,7 @@ Document::Document(DocumentData *document, HistoryItem *parent, const style::Ove
setDocumentLinks(_data); 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()) { if (withThumb()) {
_data->thumb->load(); _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); bool selected = (selection == FullSelection);
_data->automaticLoad(_parent); _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))) { if (clip.intersects(rtlrect(nameleft, statustop, availwidth, st::normalFont->height, _width))) {
p.setFont(st::normalFont); p.setFont(st::normalFont);
p.setPen(st::mediaInFg); 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))) { if (datetop >= 0 && clip.intersects(rtlrect(nameleft, datetop, _datew, st::normalFont->height, _width))) {
p.setFont(ClickHandler::showAsActive(_msgl) ? st::normalFont->underline() : st::normalFont); 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 { void Document::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
bool loaded = _data->loaded() || Local::willStickerImageLoad(_data->mediaKey()); bool loaded = _data->loaded() || Local::willStickerImageLoad(_data->mediaKey());
bool showPause = updateStatusText();
int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = 0; int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = 0;
bool wthumb = withThumb(); 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; bool showPause = false;
int32 statusSize = 0, realDuration = 0; int32 statusSize = 0, realDuration = 0;
if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
@ -880,8 +964,8 @@ bool Document::updateStatusText() const {
} else { } else {
statusSize = FileStatusSizeReady; statusSize = FileStatusSizeReady;
} }
if (statusSize != _statusSize) { if (statusSize != _status.size()) {
setStatusSize(statusSize, _data->size, _data->song() ? _data->song()->duration : -1, realDuration); _status.update(statusSize, _data->size, _data->song() ? _data->song()->duration : -1, realDuration);
} }
return showPause; return showPause;
} }
@ -1041,7 +1125,7 @@ int32 Link::resizeGetHeight(int32 width) {
return _height; 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; 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 (clip.intersects(rtlrect(0, top, st::linksPhotoSize, st::linksPhotoSize, _width))) {
if (_page && _page->photo) { if (_page && _page->photo) {

View File

@ -39,8 +39,7 @@ public:
class ItemBase; class ItemBase;
class AbstractItem : public LayoutItemBase { class AbstractItem : public LayoutItemBase {
public: public:
virtual void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) = 0;
virtual void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) const = 0;
virtual ItemBase *toMediaItem() { virtual ItemBase *toMediaItem() {
return nullptr; return nullptr;
@ -115,7 +114,7 @@ protected:
void step_iconOver(float64 ms, bool timer); void step_iconOver(float64 ms, bool timer);
void step_radial(uint64 ms, bool timer); void step_radial(uint64 ms, bool timer);
void ensureRadial() const; void ensureRadial();
void checkRadialFinished(); void checkRadialFinished();
bool isRadialAnimation(uint64 ms) const { bool isRadialAnimation(uint64 ms) const {
@ -132,28 +131,33 @@ protected:
return false; return false;
} }
mutable Ui::RadialAnimation *_radial; Ui::RadialAnimation *_radial;
anim::fvalue a_iconOver; anim::fvalue a_iconOver;
mutable Animation _a_iconOver; Animation _a_iconOver;
}; };
class FileBase : public RadialProgressItem { class StatusText {
public: 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: private:
// >= 0 will contain download / upload string, _statusSize = loaded bytes // >= 0 will contain download / upload string, _size = loaded bytes
// < 0 will contain played string, _statusSize = -(seconds + 1) played // < 0 will contain played string, _size = -(seconds + 1) played
// 0x7FFFFFF0 will contain status for not yet downloaded file // 0x7FFFFFF0 will contain status for not yet downloaded file
// 0x7FFFFFF1 will contain status for already downloaded file // 0x7FFFFFF1 will contain status for already downloaded file
// 0x7FFFFFF2 will contain status for failed to download / upload file // 0x7FFFFFF2 will contain status for failed to download / upload file
mutable int32 _statusSize; int _size = 0;
mutable QString _statusText; QString _text;
// duration = -1 - no duration, duration = -2 - "GIF" duration
void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const;
}; };
@ -166,7 +170,7 @@ public:
Date(const QDate &date, bool month); Date(const QDate &date, bool month);
void initDimensions() override; 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: private:
QDate _date; QDate _date;
@ -174,33 +178,45 @@ private:
}; };
class PhotoVideoCheckbox;
class Photo : public ItemBase { class Photo : public ItemBase {
public: public:
Photo(PhotoData *photo, HistoryItem *parent); Photo(PhotoData *photo, HistoryItem *parent);
void initDimensions() override; void initDimensions() override;
int32 resizeGetHeight(int32 width) 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 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: private:
void ensureCheckboxCreated();
std_::unique_ptr<PhotoVideoCheckbox> _check;
PhotoData *_data; PhotoData *_data;
ClickHandlerPtr _link; ClickHandlerPtr _link;
mutable QPixmap _pix; QPixmap _pix;
mutable bool _goodLoaded; bool _goodLoaded = false;
}; };
class Video : public FileBase { class Video : public RadialProgressItem {
public: public:
Video(DocumentData *video, HistoryItem *parent); Video(DocumentData *video, HistoryItem *parent);
void initDimensions() override; void initDimensions() override;
int32 resizeGetHeight(int32 width) 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 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: protected:
float64 dataProgress() const override { float64 dataProgress() const override {
return _data->progress(); return _data->progress();
@ -216,22 +232,27 @@ protected:
} }
private: private:
void ensureCheckboxCreated();
std_::unique_ptr<PhotoVideoCheckbox> _check;
DocumentData *_data; DocumentData *_data;
StatusText _status;
QString _duration; QString _duration;
mutable QPixmap _pix; QPixmap _pix;
mutable bool _thumbLoaded; bool _thumbLoaded = false;
void updateStatusText() const; void updateStatusText();
}; };
class Voice : public FileBase { class Voice : public RadialProgressItem {
public: public:
Voice(DocumentData *voice, HistoryItem *parent); Voice(DocumentData *voice, HistoryItem *parent);
void initDimensions() override; 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; void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override;
protected: protected:
@ -250,22 +271,23 @@ protected:
private: private:
DocumentData *_data; DocumentData *_data;
StatusText _status;
ClickHandlerPtr _namel; ClickHandlerPtr _namel;
mutable Text _name, _details; Text _name, _details;
mutable int32 _nameVersion; int _nameVersion;
void updateName() const; void updateName();
bool updateStatusText() const; bool updateStatusText();
}; };
class Document : public FileBase { class Document : public RadialProgressItem {
public: public:
Document(DocumentData *document, HistoryItem *parent, const style::OverviewFileLayout &st); Document(DocumentData *document, HistoryItem *parent, const style::OverviewFileLayout &st);
void initDimensions() override; 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; void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override;
virtual DocumentData *getDocument() const override { virtual DocumentData *getDocument() const override {
@ -288,12 +310,13 @@ protected:
private: private:
DocumentData *_data; DocumentData *_data;
StatusText _status;
ClickHandlerPtr _msgl, _namel; ClickHandlerPtr _msgl, _namel;
const style::OverviewFileLayout &_st; const style::OverviewFileLayout &_st;
mutable bool _thumbForLoaded = false; bool _thumbForLoaded = false;
mutable QPixmap _thumb; QPixmap _thumb;
Text _name; Text _name;
QString _date, _ext; QString _date, _ext;
@ -303,7 +326,7 @@ private:
bool withThumb() const { bool withThumb() const {
return !_data->thumb->isNull() && _data->thumb->width() && _data->thumb->height(); return !_data->thumb->isNull() && _data->thumb->width() && _data->thumb->height();
} }
bool updateStatusText() const; bool updateStatusText();
}; };
@ -313,7 +336,7 @@ public:
void initDimensions() override; void initDimensions() override;
int32 resizeGetHeight(int32 width) 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 getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override;
private: private:

View File

@ -24,23 +24,25 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui { namespace Ui {
namespace { namespace {
static constexpr int kWideScale = 4; static constexpr int kWideScale = 3;
void prepareCheckCaches(const style::RoundImageCheckbox *st, QPixmap &checkBgCache, QPixmap &checkFullCache) { void prepareCheckCaches(const style::RoundCheckbox *st, bool displayInactive, QPixmap &checkBgCache, QPixmap &checkFullCache) {
auto size = st->checkRadius * 2; auto size = st->size;
auto wideSize = size * kWideScale; auto wideSize = size * kWideScale;
auto cache = QImage(wideSize * cIntRetinaFactor(), wideSize * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); auto cache = QImage(wideSize * cIntRetinaFactor(), wideSize * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cache.setDevicePixelRatio(cRetinaFactor()); cache.setDevicePixelRatio(cRetinaFactor());
cache.fill(Qt::transparent);
{ {
Painter p(&cache); 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); p.setRenderHint(QPainter::HighQualityAntialiasing, true);
auto pen = st->checkBorder->p; if (displayInactive) {
pen.setWidth(st->selectWidth); p.setPen(Qt::NoPen);
} else {
auto pen = st->border->p;
pen.setWidth(st->width);
p.setPen(pen); p.setPen(pen);
p.setBrush(st->checkBg); }
p.setBrush(st->bgActive);
auto ellipse = QRect((wideSize - size) / 2, (wideSize - size) / 2, size, size); auto ellipse = QRect((wideSize - size) / 2, (wideSize - size) / 2, size, size);
p.drawEllipse(ellipse); p.drawEllipse(ellipse);
} }
@ -48,32 +50,183 @@ void prepareCheckCaches(const style::RoundImageCheckbox *st, QPixmap &checkBgCac
{ {
Painter p(&cacheIcon); Painter p(&cacheIcon);
auto ellipse = QRect((wideSize - size) / 2, (wideSize - size) / 2, size, size); 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 = App::pixmapFromImageInPlace(std_::move(cache));
checkBgCache.setDevicePixelRatio(cRetinaFactor());
checkFullCache = App::pixmapFromImageInPlace(std_::move(cacheIcon)); checkFullCache = App::pixmapFromImageInPlace(std_::move(cacheIcon));
checkFullCache.setDevicePixelRatio(cRetinaFactor());
} }
} // namespace } // namespace
RoundImageCheckbox::RoundImageCheckbox(const style::RoundImageCheckbox &st, const base::lambda_copy<void()> &updateCallback, PaintRoundImage &&paintRoundImage) RoundCheckbox::RoundCheckbox(const style::RoundCheckbox &st, const base::lambda_copy<void()> &updateCallback)
: _st(st) : _st(st)
, _updateCallback(updateCallback) , _updateCallback(updateCallback) {
, _paintRoundImage(std_::move(paintRoundImage)) {
prepareCheckCaches(&_st, _wideCheckBgCache, _wideCheckFullCache);
} }
void RoundImageCheckbox::paint(Painter &p, uint64 ms, int x, int y, int outerWidth) { QRect RoundCheckbox::cacheDestRect(int x, int y, float64 scale) const {
_selection.step(ms); 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) { for (auto &icon : _icons) {
icon.fadeIn.step(ms); icon.fadeIn.step(ms);
icon.fadeOut.step(ms); icon.fadeOut.step(ms);
} }
removeFadeOutedIcons(); 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<void()> &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()) { if (_selection.animating()) {
auto userpicRadius = qRound(kWideScale * (_st.imageRadius + (_st.imageSmallRadius - _st.imageRadius) * selectionLevel)); auto userpicRadius = qRound(kWideScale * (_st.imageRadius + (_st.imageSmallRadius - _st.imageRadius) * selectionLevel));
auto userpicShift = kWideScale * _st.imageRadius - userpicRadius; 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()) { if (!_wideCache.isNull()) {
_wideCache = QPixmap(); _wideCache = QPixmap();
} }
auto userpicRadius = _checked ? _st.imageSmallRadius : _st.imageRadius; auto userpicRadius = checked() ? _st.imageSmallRadius : _st.imageRadius;
auto userpicShift = _st.imageRadius - userpicRadius; auto userpicShift = _st.imageRadius - userpicRadius;
auto userpicLeft = x + userpicShift; auto userpicLeft = x + userpicShift;
auto userpicTop = y + 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::HighQualityAntialiasing, false);
} }
p.setRenderHint(QPainter::SmoothPixmapTransform, true); auto iconLeft = x + 2 * _st.imageRadius + _st.selectWidth - _st.check.size;
for (auto &icon : _icons) { auto iconTop = y + 2 * _st.imageRadius + _st.selectWidth - _st.check.size;
auto fadeIn = icon.fadeIn.current(1.); _check.paint(p, ms, iconLeft, iconTop, outerWidth);
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.);
} }
float64 RoundImageCheckbox::checkedAnimationRatio() const { 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) { void RoundImageCheckbox::setChecked(bool newChecked, SetStyle speed) {
if (_checked == checked) { auto changed = (checked() != newChecked);
_check.setChecked(newChecked, speed);
if (!changed) {
if (speed != SetStyle::Animated) { if (speed != SetStyle::Animated) {
if (!_icons.isEmpty()) {
_icons.back().fadeIn.finish();
_icons.back().fadeOut.finish();
}
_selection.finish(); _selection.finish();
} }
return; 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) { if (speed == SetStyle::Animated) {
prepareWideCache(); 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 { } else {
_selection.finish(); _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() { void RoundImageCheckbox::prepareWideCache() {
if (_wideCache.isNull()) { if (_wideCache.isNull()) {
auto size = _st.imageRadius * 2; 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 } // namespace Ui

View File

@ -24,6 +24,47 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui { namespace Ui {
class RoundCheckbox {
public:
RoundCheckbox(const style::RoundCheckbox &st, const base::lambda_copy<void()> &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<void()> _updateCallback;
bool _checked = false;
std_::vector_of_moveable<Icon> _icons;
bool _displayInactive = false;
QPixmap _inactiveCacheBg, _inactiveCacheFg;
// Those pixmaps are shared among all checkboxes that have the same style.
QPixmap _wideCheckBgCache, _wideCheckFullCache;
};
class RoundImageCheckbox { class RoundImageCheckbox {
public: public:
using PaintRoundImage = base::lambda<void(Painter &p, int x, int y, int outerWidth, int size)>; using PaintRoundImage = base::lambda<void(Painter &p, int x, int y, int outerWidth, int size)>;
@ -33,35 +74,22 @@ public:
float64 checkedAnimationRatio() const; float64 checkedAnimationRatio() const;
bool checked() const { bool checked() const {
return _checked; return _check.checked();
} }
enum class SetStyle { using SetStyle = RoundCheckbox::SetStyle;
Animated, void setChecked(bool newChecked, SetStyle speed = SetStyle::Animated);
Fast,
};
void setChecked(bool checked, SetStyle speed = SetStyle::Animated);
private: private:
struct Icon {
FloatAnimation fadeIn;
FloatAnimation fadeOut;
QPixmap wideCheckCache;
};
void removeFadeOutedIcons();
void prepareWideCache(); void prepareWideCache();
void prepareWideCheckIconCache(Icon *icon);
const style::RoundImageCheckbox &_st; const style::RoundImageCheckbox &_st;
base::lambda_copy<void()> _updateCallback; base::lambda_copy<void()> _updateCallback;
PaintRoundImage _paintRoundImage; PaintRoundImage _paintRoundImage;
bool _checked = false;
QPixmap _wideCache; QPixmap _wideCache;
FloatAnimation _selection; FloatAnimation _selection;
std_::vector_of_moveable<Icon> _icons;
// Those pixmaps are shared among all checkboxes that have the same style. RoundCheckbox _check;
QPixmap _wideCheckBgCache, _wideCheckFullCache;
}; };

View File

@ -319,17 +319,24 @@ FilledSlider {
duration: int; duration: int;
} }
RoundCheckbox {
border: color;
bgInactive: color;
bgActive: color;
width: pixels;
size: pixels;
sizeSmall: double;
duration: int;
check: icon;
}
RoundImageCheckbox { RoundImageCheckbox {
imageRadius: pixels; imageRadius: pixels;
imageSmallRadius: pixels; imageSmallRadius: pixels;
selectWidth: pixels; selectWidth: pixels;
selectFg: color; selectFg: color;
selectDuration: int; selectDuration: int;
checkBorder: color; check: RoundCheckbox;
checkBg: color;
checkRadius: pixels;
checkSmallRadius: pixels;
checkIcon: icon;
} }
MultiSelectItem { MultiSelectItem {
@ -738,6 +745,13 @@ defaultPanelAnimation: PanelAnimation {
shadow: defaultRoundShadow; shadow: defaultRoundShadow;
} }
defaultRoundCheckbox: RoundCheckbox {
border: windowBg;
bgActive: windowBgActive;
width: 2px;
duration: 150;
}
defaultMenuArrow: icon {{ "dropdown_submenu_arrow", #373737 }}; defaultMenuArrow: icon {{ "dropdown_submenu_arrow", #373737 }};
defaultMenu: Menu { defaultMenu: Menu {
skip: 0px; skip: 0px;