| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- // This file is part of Desktop App Toolkit,
- // a set of libraries for developing nice desktop applications.
- //
- // For license and copyright information please follow this link:
- // https://github.com/desktop-app/legal/blob/master/LEGAL
- //
- #include "ui/widgets/call_button.h"
- #include "ui/effects/ripple_animation.h"
- #include "ui/painter.h"
- #include "ui/widgets/labels.h"
- #include "ui/qt_object_factory.h"
- #include "ui/ui_utility.h"
- #include "styles/style_widgets.h"
- #include "styles/palette.h"
- namespace Ui {
- namespace {
- constexpr auto kOuterBounceDuration = crl::time(100);
- } // namespace
- CallButton::CallButton(
- QWidget *parent,
- const style::CallButton &stFrom,
- const style::CallButton *stTo)
- : RippleButton(parent, stFrom.button.ripple)
- , _stFrom(&stFrom)
- , _stTo(stTo) {
- init();
- }
- void CallButton::init() {
- resize(_stFrom->button.width, _stFrom->button.height);
- const auto size = QSize(_stFrom->bgSize, _stFrom->bgSize);
- _bgMask = RippleAnimation::MaskByDrawer(size, false, [&](QPainter &p) {
- p.drawEllipse(0, 0, size.width(), size.height());
- if (_corner) {
- auto position = _corner->pos() - _stFrom->bgPosition;
- p.setCompositionMode(QPainter::CompositionMode_Source);
- p.setBrush(st::transparent);
- const auto border = _stFrom->cornerButtonBorder;
- p.drawEllipse(QRect(position, _corner->size()).marginsAdded(
- { border, border, border, border }));
- }
- });
- _bgFrom = Ui::PixmapFromImage(style::colorizeImage(_bgMask, _stFrom->bg));
- if (_stTo) {
- Assert(_stFrom->button.width == _stTo->button.width);
- Assert(_stFrom->button.height == _stTo->button.height);
- Assert(_stFrom->bgPosition == _stTo->bgPosition);
- Assert(_stFrom->bgSize == _stTo->bgSize);
- _bg = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
- _bg.setDevicePixelRatio(style::DevicePixelRatio());
- _bgTo = Ui::PixmapFromImage(style::colorizeImage(_bgMask, _stTo->bg));
- _iconMixedMask = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
- _iconMixedMask.setDevicePixelRatio(style::DevicePixelRatio());
- _iconFrom = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
- _iconFrom.setDevicePixelRatio(style::DevicePixelRatio());
- _iconFrom.fill(Qt::black);
- {
- QPainter p(&_iconFrom);
- p.drawImage(
- (_stFrom->bgSize
- - _stFrom->button.icon.width()) / 2,
- (_stFrom->bgSize
- - _stFrom->button.icon.height()) / 2,
- _stFrom->button.icon.instance(Qt::white));
- }
- _iconTo = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
- _iconTo.setDevicePixelRatio(style::DevicePixelRatio());
- _iconTo.fill(Qt::black);
- {
- QPainter p(&_iconTo);
- p.drawImage(
- (_stTo->bgSize
- - _stTo->button.icon.width()) / 2,
- (_stTo->bgSize
- - _stTo->button.icon.height()) / 2,
- _stTo->button.icon.instance(Qt::white));
- }
- _iconMixed = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
- _iconMixed.setDevicePixelRatio(style::DevicePixelRatio());
- }
- }
- void CallButton::setOuterValue(float64 value) {
- if (_outerValue != value) {
- _outerAnimation.start([this] {
- if (_progress == 0. || _progress == 1.) {
- update();
- }
- }, _outerValue, value, kOuterBounceDuration);
- _outerValue = value;
- }
- }
- void CallButton::setText(rpl::producer<QString> text) {
- _label.create(this, std::move(text), _stFrom->label);
- _label->show();
- rpl::combine(
- sizeValue(),
- _label->sizeValue()
- ) | rpl::start_with_next([=](QSize my, QSize label) {
- _label->moveToLeft(
- (my.width() - label.width()) / 2,
- my.height() - label.height(),
- my.width());
- }, _label->lifetime());
- }
- void CallButton::setProgress(float64 progress) {
- _progress = progress;
- if (_corner) {
- _corner->setProgress(progress);
- }
- update();
- }
- void CallButton::paintEvent(QPaintEvent *e) {
- QPainter p(this);
- auto bgPosition = myrtlpoint(_stFrom->bgPosition);
- auto paintFrom = (_progress == 0.) || !_stTo;
- auto paintTo = !paintFrom && (_progress == 1.);
- auto outerValue = _outerAnimation.value(_outerValue);
- if (outerValue > 0.) {
- auto outerRadius = paintFrom ? _stFrom->outerRadius : paintTo ? _stTo->outerRadius : (_stFrom->outerRadius * (1. - _progress) + _stTo->outerRadius * _progress);
- auto outerPixels = outerValue * outerRadius;
- auto outerRect = QRectF(myrtlrect(bgPosition.x(), bgPosition.y(), _stFrom->bgSize, _stFrom->bgSize));
- outerRect = outerRect.marginsAdded(QMarginsF(outerPixels, outerPixels, outerPixels, outerPixels));
- PainterHighQualityEnabler hq(p);
- if (paintFrom) {
- p.setBrush(_stFrom->outerBg);
- } else if (paintTo) {
- p.setBrush(_stTo->outerBg);
- } else {
- p.setBrush(anim::brush(_stFrom->outerBg, _stTo->outerBg, _progress));
- }
- p.setPen(Qt::NoPen);
- p.drawEllipse(outerRect);
- }
- if (_bgOverride) {
- Assert(!_corner); // Didn't support this case yet.
- const auto &s = _stFrom->bgSize;
- p.setPen(Qt::NoPen);
- p.setBrush(*_bgOverride);
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(QRect(_stFrom->bgPosition, QSize(s, s)));
- } else if (paintFrom) {
- p.drawPixmap(bgPosition, _bgFrom);
- } else if (paintTo) {
- p.drawPixmap(bgPosition, _bgTo);
- } else {
- style::colorizeImage(_bgMask, anim::color(_stFrom->bg, _stTo->bg, _progress), &_bg);
- p.drawImage(bgPosition, _bg);
- }
- auto rippleColorInterpolated = QColor();
- auto rippleColorOverride = &rippleColorInterpolated;
- if (_rippleOverride) {
- rippleColorOverride = &(*_rippleOverride);
- } else if (paintFrom) {
- rippleColorOverride = nullptr;
- } else if (paintTo) {
- rippleColorOverride = &_stTo->button.ripple.color->c;
- } else {
- rippleColorInterpolated = anim::color(_stFrom->button.ripple.color, _stTo->button.ripple.color, _progress);
- }
- paintRipple(p, _stFrom->button.rippleAreaPosition, rippleColorOverride);
- auto positionFrom = iconPosition(_stFrom);
- if (paintFrom) {
- const auto icon = &_stFrom->button.icon;
- icon->paint(p, positionFrom, width());
- } else {
- auto positionTo = iconPosition(_stTo);
- if (paintTo) {
- _stTo->button.icon.paint(p, positionTo, width());
- } else {
- mixIconMasks();
- style::colorizeImage(_iconMixedMask, st::callIconFg->c, &_iconMixed);
- p.drawImage(myrtlpoint(_stFrom->bgPosition), _iconMixed);
- }
- }
- }
- QPoint CallButton::iconPosition(not_null<const style::CallButton*> st) const {
- auto result = st->button.iconPosition;
- if (result.x() < 0) {
- result.setX((width() - st->button.icon.width()) / 2);
- }
- if (result.y() < 0) {
- result.setY((height() - st->button.icon.height()) / 2);
- }
- return result;
- }
- void CallButton::mixIconMasks() {
- _iconMixedMask.fill(Qt::black);
- Painter p(&_iconMixedMask);
- PainterHighQualityEnabler hq(p);
- auto paintIconMask = [this, &p](const QImage &mask, float64 angle) {
- auto skipFrom = _stFrom->bgSize / 2;
- p.translate(skipFrom, skipFrom);
- p.rotate(angle);
- p.translate(-skipFrom, -skipFrom);
- p.drawImage(0, 0, mask);
- };
- p.save();
- paintIconMask(_iconFrom, (_stFrom->angle - _stTo->angle) * _progress);
- p.restore();
- p.setOpacity(_progress);
- paintIconMask(_iconTo, (_stTo->angle - _stFrom->angle) * (1. - _progress));
- }
- void CallButton::onStateChanged(State was, StateChangeSource source) {
- RippleButton::onStateChanged(was, source);
- auto over = isOver();
- auto wasOver = static_cast<bool>(was & StateFlag::Over);
- if (over != wasOver) {
- update();
- }
- }
- void CallButton::setColorOverrides(rpl::producer<CallButtonColors> &&colors) {
- std::move(
- colors
- ) | rpl::start_with_next([=](const CallButtonColors &c) {
- _bgOverride = c.bg;
- _rippleOverride = c.ripple;
- update();
- }, lifetime());
- }
- void CallButton::setStyle(
- const style::CallButton &stFrom,
- const style::CallButton *stTo) {
- if (_stFrom == &stFrom && _stTo == stTo) {
- return;
- }
- _stFrom = &stFrom;
- _stTo = stTo;
- init();
- update();
- }
- not_null<CallButton*> CallButton::addCornerButton(
- const style::CallButton &stFrom,
- const style::CallButton *stTo) {
- Expects(!_corner);
- _corner = CreateChild<CallButton>(this, stFrom, stTo);
- _corner->move(_stFrom->cornerButtonPosition);
- _corner->setProgress(_progress);
- _corner->show();
- init();
- update();
- return _corner;
- }
- QPoint CallButton::prepareRippleStartPosition() const {
- return mapFromGlobal(QCursor::pos()) - _stFrom->button.rippleAreaPosition;
- }
- QImage CallButton::prepareRippleMask() const {
- return _bgMask;
- }
- } // namespace Ui
|