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

(image error) Size: 313 B

After

(image error) Size: 309 B

Binary file not shown.

Before

(image error) Size: 632 B

After

(image error) 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 "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<Ui::RoundCheckbox>(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;

View File

@ -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<MTPWallPaper> &result);
void updateWallpapers();
int32 _bgCount, _rows;
int32 _over, _overDown;
int _bgCount = 0;
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;
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;

View File

@ -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) {

View File

@ -2082,8 +2082,8 @@ void MainWidget::fillPeerMenu(PeerData *peer, base::lambda<QAction*(const QStrin
}
}
} 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_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));
}

View File

@ -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) {

View File

@ -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<RadialProgressItem*>(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 <typename UpdateCallback>
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<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) {
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<PhotoVideoCheckbox>([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<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;
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<HistoryMessageForwarded>()) {
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) {

View File

@ -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<PhotoVideoCheckbox> _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<PhotoVideoCheckbox> _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:

View File

@ -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<void()> &updateCallback, PaintRoundImage &&paintRoundImage)
RoundCheckbox::RoundCheckbox(const style::RoundCheckbox &st, const base::lambda_copy<void()> &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<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()) {
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

View File

@ -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<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 {
public:
using PaintRoundImage = base::lambda<void(Painter &p, int x, int y, int outerWidth, int size)>;
@ -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<void()> _updateCallback;
PaintRoundImage _paintRoundImage;
bool _checked = false;
QPixmap _wideCache;
FloatAnimation _selection;
std_::vector_of_moveable<Icon> _icons;
// Those pixmaps are shared among all checkboxes that have the same style.
QPixmap _wideCheckBgCache, _wideCheckFullCache;
RoundCheckbox _check;
};

View File

@ -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;