Closed beta 10019008: Some more ripple animations added.

This commit is contained in:
John Preston 2016-11-16 19:04:25 +03:00
parent cdef9fa14f
commit 07689476a6
66 changed files with 845 additions and 680 deletions

View File

@ -53,6 +53,11 @@ lightButtonBgRipple: #c9e4f6;
lightButtonFg: #2b99d5; lightButtonFg: #2b99d5;
lightButtonFgOver: lightButtonFg; lightButtonFgOver: lightButtonFg;
attentionButtonFg: #d14e4e;
attentionButtonFgOver: #d14e4e;
attentionButtonBgOver: #fcdfde;
attentionButtonBgRipple: #f4c3c2;
menuBg: windowBg; menuBg: windowBg;
menuBgOver: windowBgOver; menuBgOver: windowBgOver;
menuBgRipple: windowBgRipple; menuBgRipple: windowBgRipple;
@ -99,11 +104,6 @@ boxBlockTitleAdditionalFg: #808080;
boxBlockTitleCloseFg: cancelIconFg; boxBlockTitleCloseFg: cancelIconFg;
boxBlockTitleCloseFgOver: cancelIconFgOver; boxBlockTitleCloseFgOver: cancelIconFgOver;
attentionBoxButtonFg: #d14e4e;
attentionBoxButtonFgOver: #d14e4e;
attentionBoxButtonBgOver: #fcdfde;
attentionBoxButtonBgRipple: #f4c3c2;
membersAboutLimitFg: windowSubTextFgOver; membersAboutLimitFg: windowSubTextFgOver;
contactsBg: windowBg; contactsBg: windowBg;
@ -228,6 +228,7 @@ mediaviewFileExtFg: activeButtonFg;
mediaviewMenuBg: #383838; mediaviewMenuBg: #383838;
mediaviewMenuBgOver: #505050; mediaviewMenuBgOver: #505050;
mediaviewMenuBgRipple: #676767;
mediaviewMenuFg: #ffffff; mediaviewMenuFg: #ffffff;
mediaviewBg: #222222eb; mediaviewBg: #222222eb;

View File

@ -49,6 +49,10 @@ lightButtonBgOver: #e3f1fa;
lightButtonBgRipple: #c9e4f6; lightButtonBgRipple: #c9e4f6;
lightButtonFg: #2b99d5; lightButtonFg: #2b99d5;
lightButtonFgOver: lightButtonFg; lightButtonFgOver: lightButtonFg;
attentionButtonFg: #d14e4e;
attentionButtonFgOver: #d14e4e;
attentionButtonBgOver: #fcdfde;
attentionButtonBgRipple: #f4c3c2;
menuBg: windowBg; menuBg: windowBg;
menuBgOver: windowBgOver; menuBgOver: windowBgOver;
menuBgRipple: windowBgRipple; menuBgRipple: windowBgRipple;
@ -84,10 +88,6 @@ boxBlockTitleFg: boxTitleFg;
boxBlockTitleAdditionalFg: #808080; boxBlockTitleAdditionalFg: #808080;
boxBlockTitleCloseFg: cancelIconFg; boxBlockTitleCloseFg: cancelIconFg;
boxBlockTitleCloseFgOver: cancelIconFgOver; boxBlockTitleCloseFgOver: cancelIconFgOver;
attentionBoxButtonFg: #d14e4e;
attentionBoxButtonFgOver: #d14e4e;
attentionBoxButtonBgOver: #fcdfde;
attentionBoxButtonBgRipple: #f4c3c2;
membersAboutLimitFg: windowSubTextFgOver; membersAboutLimitFg: windowSubTextFgOver;
contactsBg: windowBg; contactsBg: windowBg;
contactsBgOver: windowBgOver; contactsBgOver: windowBgOver;
@ -189,6 +189,7 @@ mediaviewFileBlueCornerFg: #599dcf;
mediaviewFileExtFg: activeButtonFg; mediaviewFileExtFg: activeButtonFg;
mediaviewMenuBg: #383838; mediaviewMenuBg: #383838;
mediaviewMenuBgOver: #505050; mediaviewMenuBgOver: #505050;
mediaviewMenuBgRipple: #676767;
mediaviewMenuFg: #ffffff; mediaviewMenuFg: #ffffff;
mediaviewBg: #222222eb; mediaviewBg: #222222eb;
mediaviewVideoBg: #000000; mediaviewVideoBg: #000000;

View File

@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,10,19,7 FILEVERSION 0,10,19,8
PRODUCTVERSION 0,10,19,7 PRODUCTVERSION 0,10,19,8
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -51,10 +51,10 @@ BEGIN
BLOCK "040904b0" BLOCK "040904b0"
BEGIN BEGIN
VALUE "CompanyName", "Telegram Messenger LLP" VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileVersion", "0.10.19.7" VALUE "FileVersion", "0.10.19.8"
VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.10.19.7" VALUE "ProductVersion", "0.10.19.8"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,10,19,7 FILEVERSION 0,10,19,8
PRODUCTVERSION 0,10,19,7 PRODUCTVERSION 0,10,19,8
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -43,10 +43,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Telegram Messenger LLP" VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Updater" VALUE "FileDescription", "Telegram Updater"
VALUE "FileVersion", "0.10.19.7" VALUE "FileVersion", "0.10.19.8"
VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.10.19.7" VALUE "ProductVersion", "0.10.19.8"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class Radiobutton; class Radiobutton;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
#include "core/lambda_wrap.h" #include "core/lambda_wrap.h"
class BackgroundBox : public ItemListBox { class BackgroundBox : public ItemListBox {

View File

@ -37,7 +37,6 @@ defaultBoxButton: RoundButton {
padding: margins(0px, 0px, 0px, 0px); padding: margins(0px, 0px, 0px, 0px);
textTop: 8px; textTop: 8px;
downTextTop: 8px;
font: boxButtonFont; font: boxButtonFont;
@ -51,12 +50,12 @@ cancelBoxButton: RoundButton(defaultBoxButton) {
} }
attentionBoxButton: RoundButton(defaultBoxButton) { attentionBoxButton: RoundButton(defaultBoxButton) {
textFg: attentionBoxButtonFg; textFg: attentionButtonFg;
textFgOver: attentionBoxButtonFgOver; textFgOver: attentionButtonFgOver;
textBgOver: attentionBoxButtonBgOver; textBgOver: attentionButtonBgOver;
ripple: RippleAnimation(defaultRippleAnimation) { ripple: RippleAnimation(defaultRippleAnimation) {
color: attentionBoxButtonBgRipple; color: attentionButtonBgRipple;
} }
} }
@ -198,12 +197,21 @@ contactUserIcon: icon {{ "add_contact_user", #999999 }};
contactPhoneIcon: icon {{ "add_contact_phone", #999999 }}; contactPhoneIcon: icon {{ "add_contact_phone", #999999 }};
contactIconTop: 10px; contactIconTop: 10px;
contactsAdd: IconButton { contactsAddIconAbove: icon {{ "contacts_add", activeButtonFg, point(18px, 18px) }};
contactsAdd: TwoIconButton {
width: 52px; width: 52px;
height: 52px; height: 52px;
icon: contactsAddIcon; iconBelow: contactsAddIconBelow;
iconOver: contactsAddIconOver; iconBelowOver: contactsAddIconBelowOver;
iconAbove: contactsAddIconAbove;
iconAboveOver: contactsAddIconAbove;
rippleAreaPosition: point(5px, 5px);
rippleAreaSize: 42px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: activeButtonBgRipple;
}
} }
contactsAddPosition: point(14px, 8px); contactsAddPosition: point(14px, 8px);
@ -289,9 +297,7 @@ contactsMultiSelect: MultiSelect {
icon: boxSearchCancelIcon; icon: boxSearchCancelIcon;
iconOver: boxSearchCancelIconOver; iconOver: boxSearchCancelIconOver;
iconPosition: point(8px, 18px); iconPosition: point(8px, 18px);
iconPositionDown: point(8px, 18px);
} }
fieldCancelSkip: 34px; fieldCancelSkip: 34px;
} }
@ -370,9 +376,7 @@ sessionTerminate: IconButton {
icon: simpleCloseIcon; icon: simpleCloseIcon;
iconOver: simpleCloseIconOver; iconOver: simpleCloseIconOver;
iconPosition: point(3px, 3px); iconPosition: point(3px, 3px);
iconPositionDown: point(3px, 4px);
} }
sessionTerminateAllButton: LinkButton(boxLinkButton) { sessionTerminateAllButton: LinkButton(boxLinkButton) {
color: #d15948; color: #d15948;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class InputField; class InputField;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
#include "core/single_timer.h" #include "core/single_timer.h"
#include "ui/effects/round_image_checkbox.h" #include "ui/effects/round_image_checkbox.h"
#include "boxes/members_box.h" #include "boxes/members_box.h"
@ -93,7 +93,7 @@ private:
class Inner; class Inner;
ChildWidget<Inner> _inner; ChildWidget<Inner> _inner;
ChildWidget<Ui::WidgetSlideWrap<Ui::MultiSelect>> _select; ChildWidget<Ui::WidgetSlideWrap<Ui::MultiSelect>> _select;
ChildWidget<Ui::IconButton> _add = { nullptr }; ChildWidget<MembersAddButton> _add = { nullptr };
ChildWidget<Ui::RoundButton> _next; ChildWidget<Ui::RoundButton> _next;
ChildWidget<Ui::RoundButton> _cancel; ChildWidget<Ui::RoundButton> _cancel;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
#include "core/observer.h" #include "core/observer.h"
namespace Ui { namespace Ui {

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
class EmojiBox : public AbstractBox { class EmojiBox : public AbstractBox {
Q_OBJECT Q_OBJECT

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class Radiobutton; class Radiobutton;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class RoundButton; class RoundButton;

View File

@ -30,8 +30,36 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/effects/ripple_animation.h"
#include "observer_peer.h" #include "observer_peer.h"
MembersAddButton::MembersAddButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
, _st(st) {
resize(_st.width, _st.height);
setCursor(style::cur_pointer);
}
void MembersAddButton::paintEvent(QPaintEvent *e) {
Painter p(this);
auto ms = getms();
auto over = (_state & StateOver);
auto down = (_state & StateDown);
((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width());
paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms);
((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width());
}
QImage MembersAddButton::prepareRippleMask() const {
return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
}
QPoint MembersAddButton::prepareRippleStartPosition() const {
return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
}
MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll) MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll)
, _inner(this, channel, filter) { , _inner(this, channel, filter) {
ItemListBox::init(_inner); ItemListBox::init(_inner);

View File

@ -20,9 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
#include "core/single_timer.h" #include "core/single_timer.h"
#include "ui/effects/round_image_checkbox.h" #include "ui/effects/round_image_checkbox.h"
#include "ui/widgets/buttons.h"
class ContactsBox; class ContactsBox;
class ConfirmBox; class ConfirmBox;
@ -33,6 +34,21 @@ enum class MembersFilter {
}; };
using MembersAlreadyIn = OrderedSet<UserData*>; using MembersAlreadyIn = OrderedSet<UserData*>;
class MembersAddButton : public Ui::RippleButton {
public:
MembersAddButton(QWidget *parent, const style::TwoIconButton &st);
protected:
void paintEvent(QPaintEvent *e) override;
QImage prepareRippleMask() const override;
QPoint prepareRippleStartPosition() const override;
private:
const style::TwoIconButton &_st;
};
class MembersBox : public ItemListBox { class MembersBox : public ItemListBox {
Q_OBJECT Q_OBJECT
@ -54,7 +70,7 @@ private:
class Inner; class Inner;
ChildWidget<Inner> _inner; ChildWidget<Inner> _inner;
ChildWidget<Ui::IconButton> _add = { nullptr }; ChildWidget<MembersAddButton> _add = { nullptr };
ContactsBox *_addBox = nullptr; ContactsBox *_addBox = nullptr;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class RoundButton; class RoundButton;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class InputField; class InputField;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class RoundButton; class RoundButton;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
#include "localimageloader.h" #include "localimageloader.h"
namespace Ui { namespace Ui {

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class Radiobutton; class Radiobutton;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
#include "core/single_timer.h" #include "core/single_timer.h"
class ConfirmBox; class ConfirmBox;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
#include "core/lambda_wrap.h" #include "core/lambda_wrap.h"
#include "core/observer.h" #include "core/observer.h"
#include "core/vector_of_moveable.h" #include "core/vector_of_moveable.h"

View File

@ -33,6 +33,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_stickers.h" #include "styles/style_stickers.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/effects/ripple_animation.h"
namespace { namespace {
@ -563,6 +564,7 @@ void StickersBox::Inner::paintEvent(QPaintEvent *e) {
_a_shifting.step(); _a_shifting.step();
auto ms = getms();
p.fillRect(r, st::boxBg); p.fillRect(r, st::boxBg);
p.setClipRect(r); p.setClipRect(r);
@ -591,19 +593,26 @@ void StickersBox::Inner::paintEvent(QPaintEvent *e) {
p.translate(0, from * _rowHeight); p.translate(0, from * _rowHeight);
for (int32 i = from; i < to; ++i) { for (int32 i = from; i < to; ++i) {
if (i != _above) { if (i != _above) {
paintRow(p, i); paintRow(p, i, ms);
} }
p.translate(0, _rowHeight); p.translate(0, _rowHeight);
} }
if (from <= _above && _above < to) { if (from <= _above && _above < to) {
p.translate(0, (_above - to) * _rowHeight); p.translate(0, (_above - to) * _rowHeight);
paintRow(p, _above); paintRow(p, _above, ms);
} }
} }
} }
void StickersBox::Inner::paintRow(Painter &p, int32 index) { QRect StickersBox::Inner::relativeAddButtonRect() const {
const StickerSetRow *s(_rows.at(index)); int addw = st::stickersAddSize.width();
int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw;
int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2;
return QRect(addx, addy, addw, st::stickersAddSize.height());
}
void StickersBox::Inner::paintRow(Painter &p, int32 index, uint64 ms) {
auto s = _rows.at(index);
int32 xadd = 0, yadd = s->yadd.current(); int32 xadd = 0, yadd = s->yadd.current();
if (xadd || yadd) p.translate(xadd, yadd); if (xadd || yadd) p.translate(xadd, yadd);
@ -639,17 +648,23 @@ void StickersBox::Inner::paintRow(Painter &p, int32 index) {
int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (addw + st::stickersFeaturedInstalled.width()) / 2); int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (addw + st::stickersFeaturedInstalled.width()) / 2);
int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2; int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2;
st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width()); st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width());
if (s->ripple) {
s->ripple.reset();
}
} else { } else {
int addw = st::stickersAddSize.width(); auto relativeAdd = relativeAddButtonRect();
int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; auto add = myrtlrect(relativeAdd);
int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2;
QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height()));
auto &textBg = (_actionSel == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg; auto &textBg = (_actionSel == index || _actionDown == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg;
App::roundRect(p, add, textBg, ImageRoundRadius::Small); App::roundRect(p, add, textBg, ImageRoundRadius::Small);
int iconx = addx + (st::stickersAddSize.width() - st::stickersAddIcon.width()) / 2; if (s->ripple) {
int icony = addy + (st::stickersAddSize.height() - st::stickersAddIcon.height()) / 2; s->ripple->paint(p, relativeAdd.x(), relativeAdd.y(), width(), ms);
icony += (_actionSel == index && _actionDown == index) ? (st::defaultActiveButton.downTextTop - st::defaultActiveButton.textTop) : 0; if (s->ripple->empty()) {
s->ripple.reset();
}
}
int iconx = relativeAdd.x() + (st::stickersAddSize.width() - st::stickersAddIcon.width()) / 2;
int icony = relativeAdd.y() + (st::stickersAddSize.height() - st::stickersAddIcon.height()) / 2;
st::stickersAddIcon.paint(p, QPoint(iconx, icony), width()); st::stickersAddIcon.paint(p, QPoint(iconx, icony), width());
} }
@ -696,7 +711,7 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) {
_pressed = _selected; _pressed = _selected;
if (_actionSel >= 0) { if (_actionSel >= 0) {
_actionDown = _actionSel; setActionDown(_actionSel);
update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
} else if (_selected >= 0 && _section == Section::Installed && !_rows.at(_selected)->recent) { } else if (_selected >= 0 && _section == Section::Installed && !_rows.at(_selected)->recent) {
_above = _dragging = _started = _selected; _above = _dragging = _started = _selected;
@ -704,6 +719,33 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) {
} }
} }
void StickersBox::Inner::setActionDown(int newActionDown) {
if (_actionDown == newActionDown) {
return;
}
if (_actionDown >= 0 && _actionDown < _rows.size()) {
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
auto set = _rows[_actionDown];
if (set->ripple) {
set->ripple->lastStop();
}
}
_actionDown = newActionDown;
if (_actionDown >= 0 && _actionDown < _rows.size()) {
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
auto set = _rows[_actionDown];
if (!set->ripple) {
auto mask = Ui::RippleAnimation::roundRectMask(st::stickersAddSize, st::buttonRadius);
set->ripple = MakeShared<Ui::RippleAnimation>(st::defaultActiveButton.ripple, std_::move(mask), [this, index = _actionDown] {
update(0, _itemsTop + index * _rowHeight, width(), _rowHeight);
});
}
auto relativeAdd = relativeAddButtonRect();
auto add = myrtlrect(relativeAdd);
set->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(add.x(), _itemsTop + _actionDown * _rowHeight + add.y()));
}
}
void StickersBox::Inner::mouseMoveEvent(QMouseEvent *e) { void StickersBox::Inner::mouseMoveEvent(QMouseEvent *e) {
if (_saving) return; if (_saving) return;
_mouse = e->globalPos(); _mouse = e->globalPos();
@ -762,10 +804,8 @@ void StickersBox::Inner::onUpdateSelected() {
} else if (_rows.at(selected)->installed && !_rows.at(selected)->disabled) { } else if (_rows.at(selected)->installed && !_rows.at(selected)->disabled) {
actionSel = -1; actionSel = -1;
} else { } else {
int addw = st::stickersAddSize.width(); auto relativeAdd = relativeAddButtonRect();
int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; auto add = myrtlrect(relativeAdd);
int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2;
QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height()));
actionSel = add.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; actionSel = add.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1;
} }
} else if (_hasFeaturedButton && QRect(0, st::membersPadding.top(), width(), _buttonHeight).contains(local)) { } else if (_hasFeaturedButton && QRect(0, st::membersPadding.top(), width(), _buttonHeight).contains(local)) {
@ -878,10 +918,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
} }
} }
} }
if (_actionDown >= 0) { setActionDown(-1);
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
_actionDown = -1;
}
} }
void StickersBox::Inner::leaveEvent(QEvent *e) { void StickersBox::Inner::leaveEvent(QEvent *e) {

View File

@ -20,13 +20,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
class ConfirmBox; class ConfirmBox;
namespace Ui { namespace Ui {
class PlainShadow; class PlainShadow;
class RoundButton; class RoundButton;
class RippleAnimation;
} // namespace Ui } // namespace Ui
class StickersBox : public ItemListBox, public RPCSender { class StickersBox : public ItemListBox, public RPCSender {
@ -150,11 +151,13 @@ private slots:
void onImageLoaded(); void onImageLoaded();
private: private:
void setActionDown(int newActionDown);
void setup(); void setup();
QRect relativeAddButtonRect() const;
void paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const; void paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const;
void step_shifting(uint64 ms, bool timer); void step_shifting(uint64 ms, bool timer);
void paintRow(Painter &p, int32 index); void paintRow(Painter &p, int32 index, uint64 ms);
void clear(); void clear();
void setActionSel(int32 actionSel); void setActionSel(int32 actionSel);
float64 aboveShadowOpacity() const; float64 aboveShadowOpacity() const;
@ -192,6 +195,7 @@ private:
bool installed, official, unread, disabled, recent; bool installed, official, unread, disabled, recent;
int32 pixw, pixh; int32 pixw, pixh;
anim::ivalue yadd; anim::ivalue yadd;
QSharedPointer<Ui::RippleAnimation> ripple;
}; };
using StickerSetRows = QList<StickerSetRow*>; using StickerSetRows = QList<StickerSetRow*>;
@ -236,5 +240,6 @@ private:
Ui::RectShadow _aboveShadow; Ui::RectShadow _aboveShadow;
int32 _scrollbar = 0; int _scrollbar = 0;
}; };

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
#include "core/vector_of_moveable.h" #include "core/vector_of_moveable.h"
class ConfirmBox; class ConfirmBox;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "abstractbox.h" #include "boxes/abstractbox.h"
namespace Ui { namespace Ui {
class UsernameInput; class UsernameInput;

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "core/utils.h" #include "core/utils.h"
#define BETA_VERSION_MACRO (10019007ULL) #define BETA_VERSION_MACRO (10019008ULL)
constexpr int AppVersion = 10020; constexpr int AppVersion = 10020;
constexpr str_const AppVersionStr = "0.10.20"; constexpr str_const AppVersionStr = "0.10.20";

View File

@ -84,7 +84,6 @@ dialogsMenuToggle: IconButton {
icon: icon {{ "dialogs_menu", dialogsMenuIconFg }}; icon: icon {{ "dialogs_menu", dialogsMenuIconFg }};
iconOver: icon {{ "dialogs_menu", dialogsMenuIconFgOver }}; iconOver: icon {{ "dialogs_menu", dialogsMenuIconFgOver }};
iconPosition: point(10px, 10px); iconPosition: point(10px, 10px);
iconPositionDown: point(10px, 10px);
rippleAreaPosition: point(0px, 0px); rippleAreaPosition: point(0px, 0px);
rippleAreaSize: 40px; rippleAreaSize: 40px;
@ -112,18 +111,14 @@ dialogsFilter: FlatInput(defaultFlatInput) {
dialogsCancelSearchInPeer: IconButton(dialogsMenuToggle) { dialogsCancelSearchInPeer: IconButton(dialogsMenuToggle) {
icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg }}; icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg }};
iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver }}; iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver }};
iconPosition: point(11px, 11px); iconPosition: point(11px, 11px);
iconPositionDown: point(11px, 11px);
} }
dialogsCancelSearch: IconButton(dialogsCancelSearchInPeer) { dialogsCancelSearch: IconButton(dialogsCancelSearchInPeer) {
width: 32px; width: 32px;
height: 32px; height: 32px;
iconPosition: point(7px, 7px); iconPosition: point(7px, 7px);
iconPositionDown: point(7px, 7px);
rippleAreaSize: 0px; ripple: emptyRippleAnimation;
} }
dialogsChatTypeSkip: 22px; dialogsChatTypeSkip: 22px;
@ -177,8 +172,6 @@ dialogsUpdateButton: FlatButton {
height: 46px; height: 46px;
textTop: 14px; textTop: 14px;
overTextTop: 14px;
downTextTop: 15px;
font: semiboldFont; font: semiboldFont;
overFont: semiboldFont; overFont: semiboldFont;
@ -197,8 +190,6 @@ dialogsForwardCancel: IconButton {
icon: dialogsForwardCancelIcon; icon: dialogsForwardCancelIcon;
iconOver: dialogsForwardCancelIcon; iconOver: dialogsForwardCancelIcon;
iconPosition: point(12px, 11px); iconPosition: point(12px, 11px);
iconPositionDown: point(12px, 11px);
} }
dialogsForwardFont: semiboldFont; dialogsForwardFont: semiboldFont;

View File

@ -22,14 +22,6 @@ using "basic.style";
using "dialogs/dialogs.style"; using "dialogs/dialogs.style";
using "ui/widgets/widgets.style"; using "ui/widgets/widgets.style";
BotKeyboardButton {
margin: pixels;
padding: pixels;
height: pixels;
textTop: pixels;
downTextTop: pixels;
}
historyScroll: FlatScroll(defaultFlatScroll) { historyScroll: FlatScroll(defaultFlatScroll) {
barColor: #89a0b47a; barColor: #89a0b47a;
bgColor: #89a0b44c; bgColor: #89a0b44c;
@ -47,13 +39,28 @@ historyScroll: FlatScroll(defaultFlatScroll) {
bottomsh: -1px; bottomsh: -1px;
} }
historyPaddingBottom: 10px; historyPaddingBottom: 8px;
historyToDownPosition: point(12px, 10px); historyToDownPosition: point(12px, 10px);
historyToDownArrow: icon { historyToDownAbove: icon {{ "history_down_arrow", #b9b9b9, point(17px, 23px) }};
{ "history_down_arrow", #b9b9b9, point(17px, 23px) }, historyToDownAboveOver: icon {{ "history_down_arrow", #a3a3a3, point(17px, 23px) }};
};
historyToDownPaddingTop: 10px; historyToDownPaddingTop: 10px;
historyToDown: TwoIconButton {
width: 52px;
height: 62px;
iconBelow: historyToDownBelow;
iconBelowOver: historyToDownBelowOver;
iconAbove: historyToDownAbove;
iconAboveOver: historyToDownAboveOver;
iconPosition: point(0px, historyToDownPaddingTop);
rippleAreaPosition: point(5px, 15px);
rippleAreaSize: 42px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgRipple;
}
}
historyToDownBadgeFont: semiboldFont; historyToDownBadgeFont: semiboldFont;
historyToDownBadgeSize: 22px; historyToDownBadgeSize: 22px;
@ -196,8 +203,6 @@ historyComposeButton: FlatButton {
height: 46px; height: 46px;
textTop: 14px; textTop: 14px;
overTextTop: 14px;
downTextTop: 14px;
font: semiboldFont; font: semiboldFont;
overFont: semiboldFont; overFont: semiboldFont;
@ -217,9 +222,7 @@ historySend: IconButton {
icon: icon {{ "send_control_send", historySendIconFg }}; icon: icon {{ "send_control_send", historySendIconFg }};
iconOver: icon {{ "send_control_send", historySendIconFgOver }}; iconOver: icon {{ "send_control_send", historySendIconFgOver }};
iconPosition: point(11px, 11px); iconPosition: point(11px, 11px);
iconPositionDown: point(11px, 11px);
} }
historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(3px, 7px) }}; historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(3px, 7px) }};
historyEditSaveIconOver: icon {{ "send_control_save", historySendIconFgOver, point(3px, 7px) }}; historyEditSaveIconOver: icon {{ "send_control_save", historySendIconFgOver, point(3px, 7px) }};
@ -242,11 +245,9 @@ historyAttachPhotoIconOver: icon {{ "media_type_photo", historyComposeIconFgOver
historyAttachEmoji: IconButton(historyAttach) { historyAttachEmoji: IconButton(historyAttach) {
icon: icon {{ "send_control_emoji", historyComposeIconFg }}; icon: icon {{ "send_control_emoji", historyComposeIconFg }};
iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }}; iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }};
iconPosition: point(15px, 15px); iconPosition: point(15px, 15px);
iconPositionDown: point(15px, 15px);
rippleAreaSize: 0px; ripple: emptyRippleAnimation;
} }
historyEmojiCircle: size(20px, 20px); historyEmojiCircle: size(20px, 20px);
historyEmojiCirclePeriod: 1500; historyEmojiCirclePeriod: 1500;
@ -263,9 +264,7 @@ historyBotKeyboardShow: IconButton(historySend) {
historyBotKeyboardHide: IconButton(historySend) { historyBotKeyboardHide: IconButton(historySend) {
icon: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFg }}; icon: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFg }};
iconOver: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFgOver }}; iconOver: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFgOver }};
iconPosition: point(11px, 16px); iconPosition: point(11px, 16px);
iconPositionDown: point(11px, 16px);
} }
historyBotCommandStart: IconButton(historySend) { historyBotCommandStart: IconButton(historySend) {
icon: icon {{ "send_control_bot_command", historyComposeIconFg }}; icon: icon {{ "send_control_bot_command", historyComposeIconFg }};
@ -319,9 +318,7 @@ historyReplyCancel: IconButton {
icon: historyReplyCancelIcon; icon: historyReplyCancelIcon;
iconOver: historyReplyCancelIconOver; iconOver: historyReplyCancelIconOver;
iconPosition: point(-1px, -1px); iconPosition: point(-1px, -1px);
iconPositionDown: point(-1px, -1px);
rippleAreaPosition: point(4px, 4px); rippleAreaPosition: point(4px, 4px);
rippleAreaSize: 40px; rippleAreaSize: 40px;
@ -349,8 +346,6 @@ reportSpamHide: FlatButton {
height: 46px; height: 46px;
textTop: 15px; textTop: 15px;
overTextTop: 15px;
downTextTop: 16px;
font: font(fsize); font: font(fsize);
overFont: font(fsize underline); overFont: font(fsize underline);
@ -361,7 +356,7 @@ reportSpamFg: #000000;
msgBotKbDuration: 200; msgBotKbDuration: 200;
msgBotKbFont: semiboldFont; msgBotKbFont: semiboldFont;
msgBotKbOverBg: #ffffff1a; msgBotKbOverBg: #ffffff20;
msgBotKbIconPadding: 2px; msgBotKbIconPadding: 2px;
msgBotKbUrlIcon: icon {{ "inline_button_url", #ffffff }}; msgBotKbUrlIcon: icon {{ "inline_button_url", #ffffff }};
msgBotKbSwitchPmIcon: icon {{ "inline_button_switch", #ffffff }}; msgBotKbSwitchPmIcon: icon {{ "inline_button_switch", #ffffff }};
@ -370,28 +365,30 @@ msgBotKbButton: BotKeyboardButton {
padding: 10px; padding: 10px;
height: 36px; height: 36px;
textTop: 8px; textTop: 8px;
downTextTop: 9px; ripple: RippleAnimation(defaultRippleAnimation) {
color: #00000020;
}
} }
botKbDuration: 200; botKbDuration: 200;
botKbBg: #edf1f5; botKbBg: menuBgOver;
botKbOverBg: #d8e2ec; botKbOverBg: menuBgOver;
botKbDownBg: #d8e2ec; botKbDownBg: menuBgRipple;
botKbColor: #4b565f; botKbColor: windowBoldFgOver;
botKbFont: font(15px semibold); botKbFont: font(15px semibold);
botKbButton: BotKeyboardButton { botKbButton: BotKeyboardButton {
margin: 10px; margin: 10px;
padding: 10px; padding: 10px;
height: 38px; height: 38px;
textTop: 9px; textTop: 9px;
downTextTop: 9px; ripple: defaultRippleAnimation;
} }
botKbTinyButton: BotKeyboardButton { botKbTinyButton: BotKeyboardButton {
margin: 4px; margin: 4px;
padding: 3px; padding: 3px;
height: 25px; height: 25px;
textTop: 2px; textTop: 2px;
downTextTop: 2px; ripple: defaultRippleAnimation;
} }
botKbScroll: FlatScroll(defaultSolidScroll) { botKbScroll: FlatScroll(defaultSolidScroll) {
deltax: 3px; deltax: 3px;

View File

@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/media_clip_reader.h" #include "media/media_clip_reader.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
#include "styles/style_history.h" #include "styles/style_history.h"
#include "ui/effects/ripple_animation.h"
#include "fileuploader.h" #include "fileuploader.h"
namespace { namespace {
@ -221,7 +222,7 @@ int ReplyKeyboard::naturalHeight() const {
return (_rows.size() - 1) * _st->buttonSkip() + _rows.size() * _st->buttonHeight(); return (_rows.size() - 1) * _st->buttonSkip() + _rows.size() * _st->buttonHeight();
} }
void ReplyKeyboard::paint(Painter &p, int outerWidth, const QRect &clip) const { void ReplyKeyboard::paint(Painter &p, int outerWidth, const QRect &clip, uint64 ms) const {
t_assert(_st != nullptr); t_assert(_st != nullptr);
t_assert(_width > 0); t_assert(_width > 0);
@ -235,7 +236,7 @@ void ReplyKeyboard::paint(Painter &p, int outerWidth, const QRect &clip) const {
// just ignore the buttons that didn't layout well // just ignore the buttons that didn't layout well
if (rect.x() + rect.width() > _width) break; if (rect.x() + rect.width() > _width) break;
_st->paintButton(p, outerWidth, button); _st->paintButton(p, outerWidth, button, ms);
} }
} }
} }
@ -251,6 +252,7 @@ ClickHandlerPtr ReplyKeyboard::getState(int x, int y) const {
if (rect.x() + rect.width() > _width) break; if (rect.x() + rect.width() > _width) break;
if (rect.contains(x, y)) { if (rect.contains(x, y)) {
_savedCoords = QPoint(x, y);
return button.link; return button.link;
} }
} }
@ -261,34 +263,63 @@ ClickHandlerPtr ReplyKeyboard::getState(int x, int y) const {
void ReplyKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { void ReplyKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (!p) return; if (!p) return;
bool startAnimation = false; _savedActive = active ? p : ClickHandlerPtr();
auto coords = findButtonCoordsByClickHandler(p);
if (coords.i >= 0 && _savedPressed != p) {
startAnimation(coords.i, coords.j, active ? 1 : -1);
}
}
ReplyKeyboard::ButtonCoords ReplyKeyboard::findButtonCoordsByClickHandler(const ClickHandlerPtr &p) {
for (int i = 0, rows = _rows.size(); i != rows; ++i) { for (int i = 0, rows = _rows.size(); i != rows; ++i) {
auto &row = _rows.at(i); auto &row = _rows[i];
for (int j = 0, cols = row.size(); j != cols; ++j) { for (int j = 0, cols = row.size(); j != cols; ++j) {
if (row.at(j).link == p) { if (row[j].link == p) {
bool startAnimation = _animations.isEmpty(); return { i, j };
}
}
}
return { -1, -1 };
}
int indexForAnimation = i * MatrixRowShift + j + 1; void ReplyKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
if (!active) { if (!p) return;
indexForAnimation = -indexForAnimation;
}
_animations.remove(-indexForAnimation); _savedPressed = pressed ? p : ClickHandlerPtr();
if (!_animations.contains(indexForAnimation)) { auto coords = findButtonCoordsByClickHandler(p);
_animations.insert(indexForAnimation, getms()); if (coords.i >= 0) {
} auto &button = _rows[coords.i][coords.j];
if (pressed) {
if (startAnimation && !_a_selected.animating()) { if (!button.ripple) {
_a_selected.start(); auto mask = Ui::RippleAnimation::roundRectMask(button.rect.size(), _st->buttonRadius());
} button.ripple = MakeShared<Ui::RippleAnimation>(_st->_st->ripple, std_::move(mask), [this] { _st->repaint(_item); });
return; }
button.ripple->add(_savedCoords - button.rect.topLeft());
} else {
if (button.ripple) {
button.ripple->lastStop();
}
if (_savedActive != p) {
startAnimation(coords.i, coords.j, -1);
} }
} }
} }
} }
void ReplyKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { void ReplyKeyboard::startAnimation(int i, int j, int direction) {
_st->repaint(_item); auto notStarted = _animations.isEmpty();
int indexForAnimation = (i * MatrixRowShift + j + 1) * direction;
_animations.remove(-indexForAnimation);
if (!_animations.contains(indexForAnimation)) {
_animations.insert(indexForAnimation, getms());
}
if (notStarted && !_a_selected.animating()) {
_a_selected.start();
}
} }
void ReplyKeyboard::step_selected(uint64 ms, bool timer) { void ReplyKeyboard::step_selected(uint64 ms, bool timer) {
@ -330,11 +361,15 @@ int ReplyKeyboard::Style::buttonHeight() const {
return _st->height; return _st->height;
} }
void ReplyKeyboard::Style::paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button) const { void ReplyKeyboard::Style::paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button, uint64 ms) const {
const QRect &rect = button.rect; const QRect &rect = button.rect;
bool pressed = ClickHandler::showAsPressed(button.link); paintButtonBg(p, rect, button.howMuchOver);
if (button.ripple) {
paintButtonBg(p, rect, pressed, button.howMuchOver); button.ripple->paint(p, rect.x(), rect.y(), outerWidth, ms);
if (button.ripple->empty()) {
button.ripple.reset();
}
}
paintButtonIcon(p, rect, outerWidth, button.type); paintButtonIcon(p, rect, outerWidth, button.type);
if (button.type == HistoryMessageReplyMarkup::Button::Type::Callback if (button.type == HistoryMessageReplyMarkup::Button::Type::Callback
|| button.type == HistoryMessageReplyMarkup::Button::Type::Game) { || button.type == HistoryMessageReplyMarkup::Button::Type::Game) {
@ -353,8 +388,7 @@ void ReplyKeyboard::Style::paintButton(Painter &p, int outerWidth, const ReplyKe
tx += (tw - st::botKbFont->elidew) / 2; tx += (tw - st::botKbFont->elidew) / 2;
tw = st::botKbFont->elidew; tw = st::botKbFont->elidew;
} }
int textTop = rect.y() + (pressed ? _st->downTextTop : _st->textTop); button.text.drawElided(p, tx, rect.y() + _st->textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top);
button.text.drawElided(p, tx, textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top);
} }
void HistoryMessageReplyMarkup::createFromButtonRows(const QVector<MTPKeyboardButtonRow> &v) { void HistoryMessageReplyMarkup::createFromButtonRows(const QVector<MTPKeyboardButtonRow> &v) {

View File

@ -22,8 +22,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "core/runtime_composer.h" #include "core/runtime_composer.h"
namespace Ui {
class RippleAnimation;
} // namespace Ui
namespace style { namespace style {
struct BotKeyboardButton; struct BotKeyboardButton;
struct RippleAnimation;
} // namespace style } // namespace style
class HistoryElement { class HistoryElement {
@ -294,13 +299,14 @@ public:
int buttonSkip() const; int buttonSkip() const;
int buttonPadding() const; int buttonPadding() const;
int buttonHeight() const; int buttonHeight() const;
virtual int buttonRadius() const = 0;
virtual void repaint(const HistoryItem *item) const = 0; virtual void repaint(const HistoryItem *item) const = 0;
virtual ~Style() { virtual ~Style() {
} }
protected: protected:
virtual void paintButtonBg(Painter &p, const QRect &rect, bool pressed, float64 howMuchOver) const = 0; virtual void paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const = 0;
virtual void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const = 0; virtual void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const = 0;
virtual void paintButtonLoading(Painter &p, const QRect &rect) const = 0; virtual void paintButtonLoading(Painter &p, const QRect &rect) const = 0;
virtual int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const = 0; virtual int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const = 0;
@ -308,7 +314,7 @@ public:
private: private:
const style::BotKeyboardButton *_st; const style::BotKeyboardButton *_st;
void paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button) const; void paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button, uint64 ms) const;
friend class ReplyKeyboard; friend class ReplyKeyboard;
}; };
@ -326,7 +332,7 @@ public:
int naturalWidth() const; int naturalWidth() const;
int naturalHeight() const; int naturalHeight() const;
void paint(Painter &p, int outerWidth, const QRect &clip) const; void paint(Painter &p, int outerWidth, const QRect &clip, uint64 ms) const;
ClickHandlerPtr getState(int x, int y) const; ClickHandlerPtr getState(int x, int y) const;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active); void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active);
@ -336,8 +342,7 @@ public:
void updateMessageId(); void updateMessageId();
private: private:
const HistoryItem *_item; void startAnimation(int i, int j, int direction);
int _width = 0;
friend class Style; friend class Style;
using ReplyMarkupClickHandlerPtr = QSharedPointer<ReplyMarkupClickHandler>; using ReplyMarkupClickHandlerPtr = QSharedPointer<ReplyMarkupClickHandler>;
@ -348,17 +353,33 @@ private:
float64 howMuchOver = 0.; float64 howMuchOver = 0.;
HistoryMessageReplyMarkup::Button::Type type; HistoryMessageReplyMarkup::Button::Type type;
ReplyMarkupClickHandlerPtr link; ReplyMarkupClickHandlerPtr link;
mutable QSharedPointer<Ui::RippleAnimation> ripple;
}; };
using ButtonRow = QVector<Button>; using ButtonRow = QVector<Button>;
using ButtonRows = QVector<ButtonRow>; using ButtonRows = QVector<ButtonRow>;
ButtonRows _rows;
struct ButtonCoords {
int i, j;
};
ButtonCoords findButtonCoordsByClickHandler(const ClickHandlerPtr &p);
using Animations = QMap<int, uint64>; using Animations = QMap<int, uint64>;
Animations _animations;
Animation _a_selected;
void step_selected(uint64 ms, bool timer); void step_selected(uint64 ms, bool timer);
const HistoryItem *_item;
int _width = 0;
ButtonRows _rows;
Animations _animations;
Animation _a_selected;
StylePtr _st; StylePtr _st;
ClickHandlerPtr _savedPressed;
ClickHandlerPtr _savedActive;
mutable QPoint _savedCoords;
}; };
// any HistoryItem can have this Interface for // any HistoryItem can have this Interface for

View File

@ -310,11 +310,12 @@ void HistoryMessage::KeyboardStyle::repaint(const HistoryItem *item) const {
Ui::repaintHistoryItem(item); Ui::repaintHistoryItem(item);
} }
void HistoryMessage::KeyboardStyle::paintButtonBg(Painter &p, const QRect &rect, bool down, float64 howMuchOver) const { int HistoryMessage::KeyboardStyle::buttonRadius() const {
return st::dateRadius;
}
void HistoryMessage::KeyboardStyle::paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const {
App::roundRect(p, rect, st::msgServiceBg, StickerCorners); App::roundRect(p, rect, st::msgServiceBg, StickerCorners);
if (down) {
howMuchOver = 1.;
}
if (howMuchOver > 0) { if (howMuchOver > 0) {
auto o = p.opacity(); auto o = p.opacity();
p.setOpacity(o * howMuchOver); p.setOpacity(o * howMuchOver);
@ -1278,7 +1279,7 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
height -= h; height -= h;
int top = height + st::msgBotKbButton.margin - marginBottom(); int top = height + st::msgBotKbButton.margin - marginBottom();
p.translate(left, top); p.translate(left, top);
keyboard->paint(p, width, r.translated(-left, -top)); keyboard->paint(p, width, r.translated(-left, -top), ms);
p.translate(-left, -top); p.translate(-left, -top);
} }

View File

@ -208,12 +208,14 @@ private:
public: public:
using ReplyKeyboard::Style::Style; using ReplyKeyboard::Style::Style;
int buttonRadius() const override;
void startPaint(Painter &p) const override; void startPaint(Painter &p) const override;
style::font textFont() const override; style::font textFont() const override;
void repaint(const HistoryItem *item) const override; void repaint(const HistoryItem *item) const override;
protected: protected:
void paintButtonBg(Painter &p, const QRect &rect, bool down, float64 howMuchOver) const override; void paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const override;
void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const override; void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const override;
void paintButtonLoading(Painter &p, const QRect &rect) const override; void paintButtonLoading(Painter &p, const QRect &rect) const override;
int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const override; int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const override;

View File

@ -2437,7 +2437,7 @@ void BotKeyboard::paintEvent(QPaintEvent *e) {
if (_impl) { if (_impl) {
int x = rtl() ? st::botKbScroll.width : _st->margin; int x = rtl() ? st::botKbScroll.width : _st->margin;
p.translate(x, st::botKbScroll.deltat); p.translate(x, st::botKbScroll.deltat);
_impl->paint(p, width(), clip.translated(-x, -st::botKbScroll.deltat)); _impl->paint(p, width(), clip.translated(-x, -st::botKbScroll.deltat), getms());
} }
} }
@ -2454,17 +2454,12 @@ void BotKeyboard::Style::repaint(const HistoryItem *item) const {
_parent->update(); _parent->update();
} }
void BotKeyboard::Style::paintButtonBg(Painter &p, const QRect &rect, bool down, float64 howMuchOver) const { int BotKeyboard::Style::buttonRadius() const {
if (down) { return st::buttonRadius;
App::roundRect(p, rect, st::botKbDownBg, BotKeyboardDownCorners); }
} else {
App::roundRect(p, rect, st::botKbBg, BotKeyboardCorners); void BotKeyboard::Style::paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const {
if (howMuchOver > 0) { App::roundRect(p, rect, st::botKbBg, BotKeyboardCorners);
p.setOpacity(howMuchOver);
App::roundRect(p, rect, st::botKbOverBg, BotKeyboardOverCorners);
p.setOpacity(1);
}
}
} }
void BotKeyboard::Style::paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const { void BotKeyboard::Style::paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const {
@ -3018,7 +3013,7 @@ TextWithTags::Tags textTagsFromEntities(const EntitiesInText &entities) {
HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
, _fieldBarCancel(this, st::historyReplyCancel) , _fieldBarCancel(this, st::historyReplyCancel)
, _scroll(this, st::historyScroll, false) , _scroll(this, st::historyScroll, false)
, _historyToEnd(this) , _historyToEnd(this, st::historyToDown)
, _fieldAutocomplete(this) , _fieldAutocomplete(this)
, _reportSpamPanel(this) , _reportSpamPanel(this)
, _send(this, st::historySend) , _send(this, st::historySend)

View File

@ -427,12 +427,14 @@ private:
Style(BotKeyboard *parent, const style::BotKeyboardButton &st) : ReplyKeyboard::Style(st), _parent(parent) { Style(BotKeyboard *parent, const style::BotKeyboardButton &st) : ReplyKeyboard::Style(st), _parent(parent) {
} }
int buttonRadius() const override;
void startPaint(Painter &p) const override; void startPaint(Painter &p) const override;
style::font textFont() const override; style::font textFont() const override;
void repaint(const HistoryItem *item) const override; void repaint(const HistoryItem *item) const override;
protected: protected:
void paintButtonBg(Painter &p, const QRect &rect, bool down, float64 howMuchOver) const override; void paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const override;
void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const override; void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const override;
void paintButtonLoading(Painter &p, const QRect &rect) const override; void paintButtonLoading(Painter &p, const QRect &rect) const override;
int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const override; int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const override;

View File

@ -83,7 +83,6 @@ introNextButton: RoundButton(defaultActiveButton) {
height: 56px; height: 56px;
textTop: 16px; textTop: 16px;
downTextTop: 17px;
font: font(17px); font: font(17px);
} }
@ -147,8 +146,6 @@ introBackButton: IconButton {
{ size(40px, 40px), #eeeeee }, { size(40px, 40px), #eeeeee },
{ "title_back", #969696, point(12px, 12px) }, { "title_back", #969696, point(12px, 12px) },
}; };
iconPosition: point(0px, 0px); iconPosition: point(0px, 0px);
iconPositionDown: point(0px, 0px);
} }
introBackPosition: point(32px, 32px); introBackPosition: point(32px, 32px);

View File

@ -76,7 +76,6 @@ mediaPlayerRepeatButton: IconButton {
{ "player_repeat", mediaPlayerActiveFg, point(9px, 11px) } { "player_repeat", mediaPlayerActiveFg, point(9px, 11px) }
}; };
iconPosition: point(0px, 0px); iconPosition: point(0px, 0px);
iconPositionDown: point(0px, 0px);
} }
mediaPlayerRepeatDisabledIcon: icon { mediaPlayerRepeatDisabledIcon: icon {
{ "player_repeat", #c8c8c8, point(9px, 11px)} { "player_repeat", #c8c8c8, point(9px, 11px)}
@ -103,7 +102,6 @@ mediaPlayerVolumeToggle: IconButton {
icon: mediaPlayerVolumeIcon0; icon: mediaPlayerVolumeIcon0;
iconPosition: point(8px, 11px); iconPosition: point(8px, 11px);
iconPositionDown: point(8px, 11px);
} }
mediaPlayerVolumeMargin: 10px; mediaPlayerVolumeMargin: 10px;
mediaPlayerVolumeSize: size(27px, 100px); mediaPlayerVolumeSize: size(27px, 100px);

View File

@ -51,9 +51,7 @@ mediaviewPlayButton: IconButton {
icon: icon {{ "media_play", #c7c7c7, point(3px, 0px) }}; icon: icon {{ "media_play", #c7c7c7, point(3px, 0px) }};
iconOver: icon {{ "media_play", #ffffff, point(3px, 0px) }}; iconOver: icon {{ "media_play", #ffffff, point(3px, 0px) }};
iconPosition: point(3px, 1px); iconPosition: point(3px, 1px);
iconPositionDown: point(3px, 1px);
duration: mediaviewOverDuration; duration: mediaviewOverDuration;
} }
@ -67,7 +65,6 @@ mediaviewFullScreenButton: IconButton(mediaviewPlayButton) {
icon: icon {{ "media_fullscreen_to", #c7c7c7, point(0px, 0px) }}; icon: icon {{ "media_fullscreen_to", #c7c7c7, point(0px, 0px) }};
iconOver: icon {{ "media_fullscreen_to", #ffffff, point(0px, 0px) }}; iconOver: icon {{ "media_fullscreen_to", #ffffff, point(0px, 0px) }};
iconPosition: point(0px, 1px); iconPosition: point(0px, 1px);
iconPositionDown: point(0px, 1px);
} }
mediaviewFullScreenOutIcon: icon {{ "media_fullscreen_from", #c7c7c7, point(0px, 0px) }}; mediaviewFullScreenOutIcon: icon {{ "media_fullscreen_from", #c7c7c7, point(0px, 0px) }};
mediaviewFullScreenOutIconOver: icon {{ "media_fullscreen_from", #ffffff, point(0px, 0px) }}; mediaviewFullScreenOutIconOver: icon {{ "media_fullscreen_from", #ffffff, point(0px, 0px) }};
@ -136,6 +133,10 @@ mediaviewMenu: Menu(defaultMenu) {
itemFgShortcutDisabled: mediaviewMenuFg; itemFgShortcutDisabled: mediaviewMenuFg;
separatorFg: mediaviewMenuFg; separatorFg: mediaviewMenuFg;
ripple: RippleAnimation(defaultRippleAnimation) {
color: mediaviewMenuBgRipple;
}
} }
mediaviewMenuShadow: Shadow(defaultEmptyShadow) { mediaviewMenuShadow: Shadow(defaultEmptyShadow) {
fallback: mediaviewMenuBg; fallback: mediaviewMenuBg;

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_profile.h" #include "styles/style_profile.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "ui/buttons/left_outline_button.h" #include "ui/widgets/buttons.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "boxes/report_box.h" #include "boxes/report_box.h"
#include "mainwidget.h" #include "mainwidget.h"

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_profile.h" #include "styles/style_profile.h"
#include "mtproto/file_download.h" #include "mtproto/file_download.h"
#include "ui/buttons/left_outline_button.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "boxes/contactsbox.h" #include "boxes/contactsbox.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "profile/profile_settings_widget.h" #include "profile/profile_settings_widget.h"
#include "styles/style_profile.h" #include "styles/style_profile.h"
#include "ui/buttons/left_outline_button.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "boxes/contactsbox.h" #include "boxes/contactsbox.h"

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_profile.h" #include "styles/style_profile.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "ui/buttons/left_outline_button.h" #include "ui/widgets/buttons.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "lang.h" #include "lang.h"

View File

@ -76,7 +76,6 @@ settingsEditButton: IconButton {
icon: icon {{ "settings_edit_name", #b7b7b7 }}; icon: icon {{ "settings_edit_name", #b7b7b7 }};
iconPosition: point(3px, 9px); iconPosition: point(3px, 9px);
iconPositionDown: point(3px, 10px);
} }
settingsBlocksTop: 7px; settingsBlocksTop: 7px;

View File

@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_stickers.h" #include "styles/style_stickers.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/effects/ripple_animation.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "boxes/stickersetbox.h" #include "boxes/stickersetbox.h"
#include "boxes/stickers_box.h" #include "boxes/stickers_box.h"
@ -1003,13 +1004,13 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) {
tocol = StickerPanPerRow - tocol; tocol = StickerPanPerRow - tocol;
} }
int y, tilly = 0; auto tilly = 0;
auto ms = getms();
auto &sets = shownSets(); auto &sets = shownSets();
if (_section == Section::Featured) { if (_section == Section::Featured) {
tilly += st::emojiPanHeader; tilly += st::emojiPanHeader;
for (int c = 0, l = sets.size(); c < l; ++c) { for (int c = 0, l = sets.size(); c < l; ++c) {
y = tilly; auto y = tilly;
auto &set = sets[c]; auto &set = sets[c];
tilly = y + featuredRowHeight(); tilly = y + featuredRowHeight();
if (r.top() >= tilly) continue; if (r.top() >= tilly) continue;
@ -1020,14 +1021,19 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) {
int widthForTitle = featuredContentWidth() - (st::emojiPanHeaderLeft - st::buttonRadius); int widthForTitle = featuredContentWidth() - (st::emojiPanHeaderLeft - st::buttonRadius);
if (featuredHasAddButton(c)) { if (featuredHasAddButton(c)) {
auto add = featuredAddRect(c); auto add = featuredAddRect(c);
auto selected = (_selectedFeaturedSetAdd == c); auto selected = (_selectedFeaturedSetAdd == c) || (_pressedFeaturedSetAdd == c);
auto &textBg = selected ? st::stickersTrendingAdd.textBgOver : st::stickersTrendingAdd.textBg; auto &textBg = selected ? st::stickersTrendingAdd.textBgOver : st::stickersTrendingAdd.textBg;
auto textTop = (selected && _selectedFeaturedSetAdd == _pressedFeaturedSetAdd) ? st::stickersTrendingAdd.downTextTop : st::stickersTrendingAdd.textTop;
App::roundRect(p, myrtlrect(add), textBg, ImageRoundRadius::Small); App::roundRect(p, myrtlrect(add), textBg, ImageRoundRadius::Small);
if (set.ripple) {
set.ripple->paint(p, add.x(), add.y(), width(), ms);
if (set.ripple->empty()) {
set.ripple.reset();
}
}
p.setFont(st::stickersTrendingAdd.font); p.setFont(st::stickersTrendingAdd.font);
p.setPen(selected ? st::stickersTrendingAdd.textFgOver : st::stickersTrendingAdd.textFg); p.setPen(selected ? st::stickersTrendingAdd.textFgOver : st::stickersTrendingAdd.textFg);
p.drawTextLeft(add.x() - (st::stickersTrendingAdd.width / 2), add.y() + textTop, width(), _addText, _addWidth); p.drawTextLeft(add.x() - (st::stickersTrendingAdd.width / 2), add.y() + st::stickersTrendingAdd.textTop, width(), _addText, _addWidth);
widthForTitle -= add.width() - (st::stickersTrendingAdd.width / 2); widthForTitle -= add.width() - (st::stickersTrendingAdd.width / 2);
} else { } else {
@ -1075,10 +1081,10 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) {
} }
} else { } else {
for (int c = 0, l = sets.size(); c < l; ++c) { for (int c = 0, l = sets.size(); c < l; ++c) {
y = tilly; auto y = tilly;
auto &set = sets[c]; auto &set = sets[c];
int32 size = set.pack.size(); auto size = set.pack.size();
int32 rows = (size / StickerPanPerRow) + ((size % StickerPanPerRow) ? 1 : 0); auto rows = (size / StickerPanPerRow) + ((size % StickerPanPerRow) ? 1 : 0);
tilly = y + st::emojiPanHeader + (rows * st::stickerPanSize.height()); tilly = y + st::emojiPanHeader + (rows * st::stickerPanSize.height());
if (r.y() >= tilly) continue; if (r.y() >= tilly) continue;
@ -1175,25 +1181,45 @@ void StickerPanInner::mousePressEvent(QMouseEvent *e) {
_pressed = _selected; _pressed = _selected;
_pressedFeaturedSet = _selectedFeaturedSet; _pressedFeaturedSet = _selectedFeaturedSet;
_pressedFeaturedSetAdd = _selectedFeaturedSetAdd; setPressedFeaturedSetAdd(_selectedFeaturedSetAdd);
ClickHandler::pressed(); ClickHandler::pressed();
_previewTimer.start(QApplication::startDragTime()); _previewTimer.start(QApplication::startDragTime());
} }
void StickerPanInner::setPressedFeaturedSetAdd(int newPressedFeaturedSetAdd) {
if (_pressedFeaturedSetAdd >= 0 && _pressedFeaturedSetAdd < _featuredSets.size()) {
auto &set = _featuredSets[_pressedFeaturedSetAdd];
if (set.ripple) {
set.ripple->lastStop();
}
}
_pressedFeaturedSetAdd = newPressedFeaturedSetAdd;
if (_pressedFeaturedSetAdd >= 0 && _pressedFeaturedSetAdd < _featuredSets.size()) {
auto &set = _featuredSets[_pressedFeaturedSetAdd];
if (!set.ripple) {
auto maskSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
auto mask = Ui::RippleAnimation::roundRectMask(maskSize, st::buttonRadius);
set.ripple = MakeShared<Ui::RippleAnimation>(st::stickersTrendingAdd.ripple, std_::move(mask), [this, index = _pressedFeaturedSetAdd] {
update(myrtlrect(featuredAddRect(index)));
});
}
auto rect = myrtlrect(featuredAddRect(_pressedFeaturedSetAdd));
set.ripple->add(mapFromGlobal(QCursor::pos()) - rect.topLeft());
}
}
void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
_previewTimer.stop(); _previewTimer.stop();
auto pressed = _pressed; auto pressed = base::take(_pressed, -1);
_pressed = -1; auto pressedFeaturedSet = base::take(_pressedFeaturedSet, -1);
auto pressedFeaturedSet = _pressedFeaturedSet;
_pressedFeaturedSet = -1;
auto pressedFeaturedSetAdd = _pressedFeaturedSetAdd; auto pressedFeaturedSetAdd = _pressedFeaturedSetAdd;
if (_pressedFeaturedSetAdd != _selectedFeaturedSetAdd) { setPressedFeaturedSetAdd(-1);
if (pressedFeaturedSetAdd != _selectedFeaturedSetAdd) {
update(); update();
} }
_pressedFeaturedSetAdd = -1;
ClickHandlerPtr activated = ClickHandler::unpressed(); auto activated = ClickHandler::unpressed();
if (_previewShown) { if (_previewShown) {
_previewShown = false; _previewShown = false;
@ -1360,7 +1386,8 @@ void StickerPanInner::clearSelection(bool fast) {
} }
_selected = _pressed = -1; _selected = _pressed = -1;
_selectedFeaturedSet = _pressedFeaturedSet = -1; _selectedFeaturedSet = _pressedFeaturedSet = -1;
_selectedFeaturedSetAdd = _pressedFeaturedSetAdd = -1; _selectedFeaturedSetAdd = -1;
setPressedFeaturedSetAdd(-1);
_a_selected.stop(); _a_selected.stop();
update(); update();
} else { } else {

View File

@ -37,6 +37,7 @@ class ScrollArea;
class IconButton; class IconButton;
class LinkButton; class LinkButton;
class RoundButton; class RoundButton;
class RippleAnimation;
} // namesapce Ui } // namesapce Ui
namespace internal { namespace internal {
@ -307,6 +308,8 @@ private:
static constexpr bool kRefreshIconsScrollAnimation = true; static constexpr bool kRefreshIconsScrollAnimation = true;
static constexpr bool kRefreshIconsNoAnimation = false; static constexpr bool kRefreshIconsNoAnimation = false;
void setPressedFeaturedSetAdd(int newPressedFeaturedSetAdd);
struct Set { struct Set {
Set(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), hovers(hoversSize, 0), pack(pack) { Set(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), hovers(hoversSize, 0), pack(pack) {
} }
@ -315,6 +318,7 @@ private:
QString title; QString title;
QVector<float64> hovers; QVector<float64> hovers;
StickerPack pack; StickerPack pack;
QSharedPointer<Ui::RippleAnimation> ripple;
}; };
using Sets = QList<Set>; using Sets = QList<Set>;
Sets &shownSets() { Sets &shownSets() {

View File

@ -27,7 +27,6 @@ switchPmButton: RoundButton(defaultBoxButton) {
width: 320px; width: 320px;
height: 34px; height: 34px;
textTop: 7px; textTop: 7px;
downTextTop: 8px;
} }
stickersTrendingHeader: 45px; stickersTrendingHeader: 45px;
@ -45,7 +44,6 @@ stickersTrendingAdd: RoundButton(defaultActiveButton) {
width: -17px; width: -17px;
height: 26px; height: 26px;
textTop: 4px; textTop: 4px;
downTextTop: 5px;
} }
stickerEmojiSkip: 5px; stickerEmojiSkip: 5px;
@ -113,7 +111,6 @@ emojiCategory: IconButton {
height: 46px; height: 46px;
iconPosition: point(11px, 12px); iconPosition: point(11px, 12px);
iconPositionDown: point(11px, 12px);
} }
emojiCategoryRecent: IconButton(emojiCategory) { icon: emojiRecent; } emojiCategoryRecent: IconButton(emojiCategory) { icon: emojiRecent; }
emojiCategoryPeople: IconButton(emojiCategory) { icon: emojiPeople; } emojiCategoryPeople: IconButton(emojiCategory) { icon: emojiPeople; }
@ -174,7 +171,5 @@ hashtagClose: IconButton {
icon: simpleCloseIcon; icon: simpleCloseIcon;
iconOver: simpleCloseIconOver; iconOver: simpleCloseIconOver;
iconPosition: point(10px, 10px); iconPosition: point(10px, 10px);
iconPositionDown: point(10px, 11px);
} }

View File

@ -23,46 +23,43 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_history.h" #include "styles/style_history.h"
#include "dialogs/dialogs_layout.h" #include "dialogs/dialogs_layout.h"
#include "ui/effects/ripple_animation.h"
namespace Ui { namespace Ui {
HistoryDownButton::HistoryDownButton(QWidget *parent) : AbstractButton(parent) HistoryDownButton::HistoryDownButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
, _st(st)
//, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity) //, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity)
, _a_arrowOver(animation(this, &HistoryDownButton::step_arrowOver)) { , _a_arrowOver(animation(this, &HistoryDownButton::step_arrowOver)) {
resize(_st.width, _st.height);
setCursor(style::cur_pointer); setCursor(style::cur_pointer);
int iconWidth = st::historyToDown.width();
int iconHeight = st::historyToDown.height();
int retina = cIntRetinaFactor();
resize(iconWidth, st::historyToDownPaddingTop + iconHeight);
QImage cache(iconWidth * retina, iconHeight * retina, QImage::Format_ARGB32_Premultiplied);
cache.setDevicePixelRatio(cRetinaFactor());
{
Painter p(&cache);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(0, 0, iconWidth, iconHeight, Qt::transparent);
st::historyToDown.paint(p, QPoint(0, 0), st::historyToDown.width());
}
_cache = App::pixmapFromImageInPlace(std_::move(cache));
_cache.setDevicePixelRatio(cRetinaFactor());
hide(); hide();
} }
QImage HistoryDownButton::prepareRippleMask() const {
return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
}
QPoint HistoryDownButton::prepareRippleStartPosition() const {
return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
}
void HistoryDownButton::paintEvent(QPaintEvent *e) { void HistoryDownButton::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
if (_a_show.animating(getms())) { auto ms = getms();
p.setOpacity(_a_show.current()); auto opacity = _a_show.current(ms, _shown ? 1. : 0.);
p.drawPixmap(0, st::historyToDownPaddingTop, _cache); if (opacity == 0.) {
} else if (!_shown) { if (!_shown) hide();
hide();
return; return;
} else {
st::historyToDown.paint(p, QPoint(0, st::historyToDownPaddingTop), width());
} }
st::historyToDownArrow.paint(p, QPoint(0, st::historyToDownPaddingTop), width()); p.setOpacity(opacity);
auto over = (_state & StateOver);
auto down = (_state & StateDown);
((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width());
paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms);
((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width());
if (_unreadCount > 0) { if (_unreadCount > 0) {
auto unreadString = QString::number(_unreadCount); auto unreadString = QString::number(_unreadCount);
if (unreadString.size() > 4) { if (unreadString.size() > 4) {
@ -78,18 +75,6 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) {
} }
} }
void HistoryDownButton::onStateChanged(int oldState, StateChangeSource source) {
//a_arrowOpacity.start((_state & (StateOver | StateDown)) ? st::historyAttachEmoji.overOpacity : st::historyAttachEmoji.opacity);
if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) {
_a_arrowOver.stop();
a_arrowOpacity.finish();
update();
} else {
_a_arrowOver.start();
}
}
void HistoryDownButton::setUnreadCount(int unreadCount) { void HistoryDownButton::setUnreadCount(int unreadCount) {
_unreadCount = unreadCount; _unreadCount = unreadCount;
update(); update();
@ -152,7 +137,7 @@ void EmojiButton::paintEvent(QPaintEvent *e) {
auto over = (_state & StateOver); auto over = (_state & StateOver);
auto icon = &(over ? _st.iconOver : _st.icon); auto icon = &(over ? _st.iconOver : _st.icon);
icon->paint(p, (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition, width()); icon->paint(p, _st.iconPosition, width());
p.setOpacity(1.); p.setOpacity(1.);
p.setPen(QPen(over ? st::historyEmojiCircleFgOver : st::historyEmojiCircleFg, st::historyEmojiCircleLine)); p.setPen(QPen(over ? st::historyEmojiCircleFgOver : st::historyEmojiCircleFg, st::historyEmojiCircleLine));

View File

@ -20,14 +20,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "ui/abstract_button.h" #include "ui/widgets/buttons.h"
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
namespace Ui { namespace Ui {
class HistoryDownButton : public AbstractButton { class HistoryDownButton : public RippleButton {
public: public:
HistoryDownButton(QWidget *parent); HistoryDownButton(QWidget *parent, const style::TwoIconButton &st);
void setUnreadCount(int unreadCount); void setUnreadCount(int unreadCount);
int unreadCount() const { int unreadCount() const {
@ -44,13 +44,15 @@ public:
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
void onStateChanged(int oldState, StateChangeSource source) override; QImage prepareRippleMask() const override;
QPoint prepareRippleStartPosition() const override;
private: private:
void toggleAnimated(); void toggleAnimated();
void step_arrowOver(float64 ms, bool timer); void step_arrowOver(float64 ms, bool timer);
QPixmap _cache; const style::TwoIconButton &_st;
bool _shown = false; bool _shown = false;
anim::fvalue a_arrowOpacity; anim::fvalue a_arrowOpacity;

View File

@ -1,71 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/buttons/left_outline_button.h"
namespace Ui {
LeftOutlineButton::LeftOutlineButton(QWidget *parent, const QString &text, const style::OutlineButton &st) : AbstractButton(parent)
, _text(text)
, _fullText(text)
, _textWidth(st.font->width(_text))
, _fullTextWidth(_textWidth)
, _st(st) {
resizeToWidth(_textWidth + _st.padding.left() + _st.padding.right());
setCursor(style::cur_pointer);
}
void LeftOutlineButton::setText(const QString &text) {
_text = text;
_fullText = text;
_fullTextWidth = _textWidth = _st.font->width(_text);
resizeToWidth(width());
update();
}
int LeftOutlineButton::resizeGetHeight(int newWidth) {
int availableWidth = qMax(newWidth - _st.padding.left() - _st.padding.right(), 1);
if ((availableWidth < _fullTextWidth) || (_textWidth < availableWidth)) {
_text = _st.font->elided(_fullText, availableWidth);
_textWidth = _st.font->width(_text);
}
return _st.padding.top() + _st.font->height + _st.padding.bottom();
}
void LeftOutlineButton::paintEvent(QPaintEvent *e) {
Painter p(this);
bool over = (_state & StateOver);
if (width() > _st.outlineWidth) {
p.fillRect(rtlrect(0, 0, _st.outlineWidth, height(), width()), over ? _st.outlineFgOver : _st.outlineFg);
p.fillRect(rtlrect(_st.outlineWidth, 0, width() - _st.outlineWidth, height(), width()), over ? _st.textBgOver : _st.textBg);
}
p.setFont(_st.font);
p.setPen(over ? _st.textFgOver : _st.textFg);
p.drawTextLeft(_st.padding.left(), _st.padding.top(), width(), _text, _textWidth);
}
void LeftOutlineButton::onStateChanged(int oldState, StateChangeSource source) {
update();
}
} // namespace Ui

View File

@ -1,49 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/abstract_button.h"
#include "styles/style_widgets.h"
namespace Ui {
class LeftOutlineButton : public AbstractButton {
public:
LeftOutlineButton(QWidget *parent, const QString &text, const style::OutlineButton &st = st::defaultLeftOutlineButton);
void setText(const QString &text);
protected:
void paintEvent(QPaintEvent *e) override;
void onStateChanged(int oldState, StateChangeSource source) override;
int resizeGetHeight(int newWidth) override;
private:
QString _text, _fullText;
int _textWidth, _fullTextWidth;
const style::OutlineButton &_st;
};
} // namespace Ui

View File

@ -190,6 +190,35 @@ void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, uint64 ms
clearFinished(); clearFinished();
} }
QImage RippleAnimation::maskByDrawer(QSize size, bool filled, base::lambda_unique<void(QPainter &p)> drawer) {
auto result = QImage(size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.fill(filled ? QColor(255, 255, 255) : Qt::transparent);
if (drawer) {
Painter p(&result);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setPen(Qt::NoPen);
p.setBrush(QColor(255, 255, 255));
drawer(p);
}
return std_::move(result);
}
QImage RippleAnimation::rectMask(QSize size) {
return maskByDrawer(size, true, base::lambda_unique<void(QPainter&)>());
}
QImage RippleAnimation::roundRectMask(QSize size, int radius) {
return maskByDrawer(size, false, [size, radius](QPainter &p) {
p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius);
});
}
QImage RippleAnimation::ellipseMask(QSize size) {
return maskByDrawer(size, false, [size](QPainter &p) {
p.drawEllipse(0, 0, size.width(), size.height());
});
}
void RippleAnimation::clearFinished() { void RippleAnimation::clearFinished() {
while (!_ripples.isEmpty() && _ripples.front()->finished()) { while (!_ripples.isEmpty() && _ripples.front()->finished()) {
delete base::take(_ripples.front()); delete base::take(_ripples.front());

View File

@ -45,6 +45,11 @@ public:
return _ripples.isEmpty(); return _ripples.isEmpty();
} }
static QImage maskByDrawer(QSize size, bool filled, base::lambda_unique<void(QPainter &p)> drawer);
static QImage rectMask(QSize size);
static QImage roundRectMask(QSize size, int radius);
static QImage ellipseMask(QSize size);
~RippleAnimation() { ~RippleAnimation() {
clear(); clear();
} }

View File

@ -25,7 +25,111 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui { namespace Ui {
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : AbstractButton(parent) LinkButton::LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st) : AbstractButton(parent)
, _text(text)
, _textWidth(st.font->width(_text))
, _st(st) {
resize(_textWidth, _st.font->height);
setCursor(style::cur_pointer);
}
int LinkButton::naturalWidth() const {
return _textWidth;
}
void LinkButton::paintEvent(QPaintEvent *e) {
Painter p(this);
auto &font = ((_state & StateOver) ? _st.overFont : _st.font);
auto &pen = ((_state & StateDown) ? _st.downColor : ((_state & StateOver) ? _st.overColor : _st.color));
p.setFont(font);
p.setPen(pen);
if (_textWidth > width()) {
p.drawText(0, font->ascent, font->elided(_text, width()));
} else {
p.drawText(0, font->ascent, _text);
}
}
void LinkButton::setText(const QString &text) {
_text = text;
_textWidth = _st.font->width(_text);
resize(_textWidth, _st.font->height);
update();
}
void LinkButton::onStateChanged(int oldState, StateChangeSource source) {
update();
}
RippleButton::RippleButton(QWidget *parent, const style::RippleAnimation &st) : AbstractButton(parent)
, _st(st) {
}
void RippleButton::setForceRippled(bool rippled, SetForceRippledWay way) {
if (_forceRippled != rippled) {
_forceRippled = rippled;
if (_forceRippled) {
ensureRipple();
if (_ripple->empty()) {
_ripple->addFading();
} else {
_ripple->lastUnstop();
}
} else if (_ripple) {
_ripple->lastStop();
}
}
if (way == SetForceRippledWay::SkipAnimation && _ripple) {
_ripple->lastFinish();
}
update();
}
void RippleButton::paintRipple(QPainter &p, int x, int y, uint64 ms) {
if (_ripple) {
_ripple->paint(p, x, y, width(), ms);
if (_ripple->empty()) {
_ripple.reset();
}
}
}
void RippleButton::onStateChanged(int oldState, StateChangeSource source) {
update();
auto wasDown = (oldState & StateDown);
auto down = (_state & StateDown);
if (!_st.showDuration || down == wasDown || _forceRippled) {
return;
}
if (down && (source == StateChangeSource::ByPress)) {
// Start a ripple only from mouse press.
ensureRipple();
_ripple->add(prepareRippleStartPosition());
} else if (!down && _ripple) {
// Finish ripple anyway.
_ripple->lastStop();
}
}
void RippleButton::ensureRipple() {
if (!_ripple) {
_ripple = std_::make_unique<RippleAnimation>(_st, prepareRippleMask(), [this] { update(); });
}
}
QImage RippleButton::prepareRippleMask() const {
return RippleAnimation::rectMask(size());
}
QPoint RippleButton::prepareRippleStartPosition() const {
return mapFromGlobal(QCursor::pos());
}
RippleButton::~RippleButton() = default;
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : RippleButton(parent, st.ripple)
, _text(text) , _text(text)
, _st(st) , _st(st)
, a_over(0) , a_over(0)
@ -81,6 +185,8 @@ void FlatButton::step_appearance(float64 ms, bool timer) {
} }
void FlatButton::onStateChanged(int oldState, StateChangeSource source) { void FlatButton::onStateChanged(int oldState, StateChangeSource source) {
RippleButton::onStateChanged(oldState, source);
a_over.start((_state & StateOver) ? 1. : 0.); a_over.start((_state & StateOver) ? 1. : 0.);
if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) { if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) {
_a_appearance.stop(); _a_appearance.stop();
@ -89,27 +195,6 @@ void FlatButton::onStateChanged(int oldState, StateChangeSource source) {
} else { } else {
_a_appearance.start(); _a_appearance.start();
} }
handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress));
}
void FlatButton::handleRipples(bool wasDown, bool wasPress) {
auto down = static_cast<bool>(_state & StateDown);
if (!_st.ripple.showDuration || down == wasDown) {
return;
}
if (down && wasPress) {
// Start a ripple only from mouse press.
if (!_ripple) {
_ripple = std_::make_unique<Ui::RippleAnimation>(_st.ripple, prepareRippleMask(), [this] { update(); });
}
auto clickPosition = mapFromGlobal(QCursor::pos());
_ripple->add(clickPosition);
} else if (!down && _ripple) {
// Finish ripple anyway.
_ripple->lastStop();
}
} }
void FlatButton::paintEvent(QPaintEvent *e) { void FlatButton::paintEvent(QPaintEvent *e) {
@ -121,69 +206,17 @@ void FlatButton::paintEvent(QPaintEvent *e) {
p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current())); p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current()));
auto ms = getms(); auto ms = getms();
if (_ripple) { paintRipple(p, 0, 0, ms);
_ripple->paint(p, 0, 0, width(), ms);
if (_ripple->empty()) {
_ripple.reset();
}
}
p.setFont((_state & StateOver) ? _st.overFont : _st.font); p.setFont((_state & StateOver) ? _st.overFont : _st.font);
p.setRenderHint(QPainter::TextAntialiasing); p.setRenderHint(QPainter::TextAntialiasing);
p.setPen(anim::pen(_st.color, _st.overColor, a_over.current())); p.setPen(anim::pen(_st.color, _st.overColor, a_over.current()));
auto top = (_state & StateDown) ? _st.downTextTop : ((_state & StateOver) ? _st.overTextTop : _st.textTop); r.setTop(_st.textTop);
r.setTop(top);
p.drawText(r, _text, style::al_top); p.drawText(r, _text, style::al_top);
} }
QImage FlatButton::prepareRippleMask() const { RoundButton::RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st) : RippleButton(parent, st.ripple)
auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(QColor(255, 255, 255));
return std_::move(result);
}
FlatButton::~FlatButton() = default;
LinkButton::LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st) : AbstractButton(parent)
, _text(text)
, _textWidth(st.font->width(_text))
, _st(st) {
resize(_textWidth, _st.font->height);
setCursor(style::cur_pointer);
}
int LinkButton::naturalWidth() const {
return _textWidth;
}
void LinkButton::paintEvent(QPaintEvent *e) {
Painter p(this);
auto &font = ((_state & StateOver) ? _st.overFont : _st.font);
auto &pen = ((_state & StateDown) ? _st.downColor : ((_state & StateOver) ? _st.overColor : _st.color));
p.setFont(font);
p.setPen(pen);
if (_textWidth > width()) {
p.drawText(0, font->ascent, font->elided(_text, width()));
} else {
p.drawText(0, font->ascent, _text);
}
}
void LinkButton::setText(const QString &text) {
_text = text;
_textWidth = _st.font->width(_text);
resize(_textWidth, _st.font->height);
update();
}
void LinkButton::onStateChanged(int oldState, StateChangeSource source) {
update();
}
RoundButton::RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st) : AbstractButton(parent)
, _fullText(text) , _fullText(text)
, _st(st) { , _st(st) {
setCursor(style::cur_pointer); setCursor(style::cur_pointer);
@ -264,20 +297,14 @@ void RoundButton::paintEvent(QPaintEvent *e) {
} }
auto ms = getms(); auto ms = getms();
if (_ripple) { paintRipple(p, rounded.x(), rounded.y(), ms);
_ripple->paint(p, rounded.x(), rounded.y(), width(), ms);
if (_ripple->empty()) {
_ripple.reset();
}
}
p.setFont(_st.font); p.setFont(_st.font);
int textLeft = _st.padding.left() + ((width() - innerWidth - _st.padding.left() - _st.padding.right()) / 2); int textLeft = _st.padding.left() + ((width() - innerWidth - _st.padding.left() - _st.padding.right()) / 2);
if (_fullWidthOverride < 0) { if (_fullWidthOverride < 0) {
textLeft = -_fullWidthOverride / 2; textLeft = -_fullWidthOverride / 2;
} }
int textTopDelta = (_state & StateDown) ? (_st.downTextTop - _st.textTop) : 0; int textTop = _st.padding.top() + _st.textTop;
int textTop = _st.padding.top() + _st.textTop + textTopDelta;
if (!_text.isEmpty()) { if (!_text.isEmpty()) {
p.setPen((over || down) ? _st.textFgOver : _st.textFg); p.setPen((over || down) ? _st.textFgOver : _st.textFg);
p.drawTextLeft(textLeft, textTop, width(), _text); p.drawTextLeft(textLeft, textTop, width(), _text);
@ -287,32 +314,7 @@ void RoundButton::paintEvent(QPaintEvent *e) {
p.setPen((over || down) ? _st.secondaryTextFgOver : _st.secondaryTextFg); p.setPen((over || down) ? _st.secondaryTextFgOver : _st.secondaryTextFg);
p.drawTextLeft(textLeft, textTop, width(), _secondaryText); p.drawTextLeft(textLeft, textTop, width(), _secondaryText);
} }
_st.icon.paint(p, QPoint(_st.padding.left(), _st.padding.right() + textTopDelta), width()); _st.icon.paint(p, QPoint(_st.padding.left(), _st.padding.right()), width());
}
void RoundButton::onStateChanged(int oldState, StateChangeSource source) {
update();
handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress));
}
void RoundButton::handleRipples(bool wasDown, bool wasPress) {
auto down = static_cast<bool>(_state & StateDown);
if (!_st.ripple.showDuration || down == wasDown) {
return;
}
if (down && wasPress) {
// Start a ripple only from mouse press.
if (!_ripple) {
_ripple = std_::make_unique<Ui::RippleAnimation>(_st.ripple, prepareRippleMask(), [this] { update(); });
}
auto clickPosition = mapFromGlobal(QCursor::pos()) - QPoint(_st.padding.left(), _st.padding.top());
_ripple->add(clickPosition);
} else if (!down && _ripple) {
// Finish ripple anyway.
_ripple->lastStop();
}
} }
QImage RoundButton::prepareRippleMask() const { QImage RoundButton::prepareRippleMask() const {
@ -321,22 +323,14 @@ QImage RoundButton::prepareRippleMask() const {
if (_fullWidthOverride < 0) { if (_fullWidthOverride < 0) {
rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height()); rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height());
} }
return RippleAnimation::roundRectMask(rounded.size(), st::buttonRadius);
auto result = QImage(rounded.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
{
Painter p(&result);
p.setPen(Qt::NoPen);
p.setBrush(QColor(255, 255, 255));
p.drawRoundedRect(rounded.translated(-rounded.topLeft()), st::buttonRadius, st::buttonRadius);
}
return std_::move(result);
} }
RoundButton::~RoundButton() = default; QPoint RoundButton::prepareRippleStartPosition() const {
return mapFromGlobal(QCursor::pos()) - QPoint(_st.padding.left(), _st.padding.top());
}
IconButton::IconButton(QWidget *parent, const style::IconButton &st) : AbstractButton(parent) IconButton::IconButton(QWidget *parent, const style::IconButton &st) : RippleButton(parent, st.ripple)
, _st(st) { , _st(st) {
resize(_st.width, _st.height); resize(_st.width, _st.height);
setCursor(style::cur_pointer); setCursor(style::cur_pointer);
@ -348,40 +342,15 @@ void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) {
update(); update();
} }
void IconButton::setActiveState(bool activeState, SetStateWay way) {
if (_activeState != activeState) {
_activeState = activeState;
if (_activeState) {
ensureRipple();
if (_ripple->empty()) {
_ripple->addFading();
} else {
_ripple->lastUnstop();
}
} else if (_ripple) {
_ripple->lastStop();
}
}
if (way == SetStateWay::SkipAnimation && _ripple) {
_ripple->lastFinish();
}
update();
}
void IconButton::paintEvent(QPaintEvent *e) { void IconButton::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
auto ms = getms(); auto ms = getms();
if (_ripple) { paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms);
_ripple->paint(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), width(), ms);
if (_ripple->empty()) {
_ripple.reset();
}
}
auto down = (_state & StateDown); auto down = (_state & StateDown);
auto overIconOpacity = (down || _activeState) ? 1. : _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); auto overIconOpacity = (down || forceRippled()) ? 1. : _a_over.current(getms(), (_state & StateOver) ? 1. : 0.);
auto overIcon = [this] { auto overIcon = [this] {
if (_iconOverrideOver) { if (_iconOverrideOver) {
return _iconOverrideOver; return _iconOverrideOver;
@ -399,7 +368,7 @@ void IconButton::paintEvent(QPaintEvent *e) {
return &_st.icon; return &_st.icon;
}; };
auto icon = (overIconOpacity == 1.) ? overIcon() : justIcon(); auto icon = (overIconOpacity == 1.) ? overIcon() : justIcon();
auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition; auto position = _st.iconPosition;
if (position.x() < 0) { if (position.x() < 0) {
position.setX((width() - icon->width()) / 2); position.setX((width() - icon->width()) / 2);
} }
@ -417,6 +386,8 @@ void IconButton::paintEvent(QPaintEvent *e) {
} }
void IconButton::onStateChanged(int oldState, StateChangeSource source) { void IconButton::onStateChanged(int oldState, StateChangeSource source) {
RippleButton::onStateChanged(oldState, source);
auto over = (_state & StateOver); auto over = (_state & StateOver);
if (over != (oldState & StateOver)) { if (over != (oldState & StateOver)) {
if (_st.duration) { if (_st.duration) {
@ -427,54 +398,56 @@ void IconButton::onStateChanged(int oldState, StateChangeSource source) {
update(); update();
} }
} }
handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress));
} }
void IconButton::handleRipples(bool wasDown, bool wasPress) { QPoint IconButton::prepareRippleStartPosition() const {
auto down = static_cast<bool>(_state & StateDown); return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
if (!_st.ripple.showDuration || _st.rippleAreaSize <= 0 || down == wasDown) {
return;
}
if (down && wasPress && !_activeState) {
// Start a ripple only from mouse press.
ensureRipple();
auto clickPosition = mapFromGlobal(QCursor::pos());
auto rippleCenter = QRect(_st.rippleAreaPosition, QSize(_st.rippleAreaSize, _st.rippleAreaSize)).center();
auto clickRadiusSquare = style::point::dotProduct(clickPosition - rippleCenter, clickPosition - rippleCenter);
auto startRadius = 0;
if (clickRadiusSquare * 4 > _st.rippleAreaSize * _st.rippleAreaSize) {
startRadius = sqrt(clickRadiusSquare) - (_st.rippleAreaSize / 2);
}
_ripple->add(clickPosition - _st.rippleAreaPosition, startRadius);
} else if (!down && _ripple && !_activeState) {
// Finish ripple anyway.
_ripple->lastStop();
}
}
void IconButton::ensureRipple() {
if (!_ripple) {
_ripple = std_::make_unique<Ui::RippleAnimation>(_st.ripple, prepareRippleMask(), [this] { update(); });
}
} }
QImage IconButton::prepareRippleMask() const { QImage IconButton::prepareRippleMask() const {
auto size = _st.rippleAreaSize * cIntRetinaFactor(); return RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
auto result = QImage(size, size, QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
{
Painter p(&result);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setPen(Qt::NoPen);
p.setBrush(QColor(255, 255, 255));
p.drawEllipse(0, 0, _st.rippleAreaSize, _st.rippleAreaSize);
}
return std_::move(result);
} }
IconButton::~IconButton() = default; LeftOutlineButton::LeftOutlineButton(QWidget *parent, const QString &text, const style::OutlineButton &st) : RippleButton(parent, st.ripple)
, _text(text)
, _fullText(text)
, _textWidth(st.font->width(_text))
, _fullTextWidth(_textWidth)
, _st(st) {
resizeToWidth(_textWidth + _st.padding.left() + _st.padding.right());
setCursor(style::cur_pointer);
}
void LeftOutlineButton::setText(const QString &text) {
_text = text;
_fullText = text;
_fullTextWidth = _textWidth = _st.font->width(_text);
resizeToWidth(width());
update();
}
int LeftOutlineButton::resizeGetHeight(int newWidth) {
int availableWidth = qMax(newWidth - _st.padding.left() - _st.padding.right(), 1);
if ((availableWidth < _fullTextWidth) || (_textWidth < availableWidth)) {
_text = _st.font->elided(_fullText, availableWidth);
_textWidth = _st.font->width(_text);
}
return _st.padding.top() + _st.font->height + _st.padding.bottom();
}
void LeftOutlineButton::paintEvent(QPaintEvent *e) {
Painter p(this);
bool over = (_state & StateOver), down = (_state & StateDown);
if (width() > _st.outlineWidth) {
p.fillRect(rtlrect(_st.outlineWidth, 0, width() - _st.outlineWidth, height(), width()), (over || down) ? _st.textBgOver : _st.textBg);
paintRipple(p, 0, 0, getms());
p.fillRect(rtlrect(0, 0, _st.outlineWidth, height(), width()), (over || down) ? _st.outlineFgOver : _st.outlineFg);
}
p.setFont(_st.font);
p.setPen((over || down) ? _st.textFgOver : _st.textFg);
p.drawTextLeft(_st.padding.left(), _st.padding.top(), width(), _text, _textWidth);
}
} // namespace Ui } // namespace Ui

View File

@ -27,44 +27,6 @@ namespace Ui {
class RippleAnimation; class RippleAnimation;
class FlatButton : public AbstractButton {
public:
FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st);
void step_appearance(float64 ms, bool timer);
void setOpacity(float64 o);
float64 opacity() const;
void setText(const QString &text);
void setWidth(int32 w);
int32 textWidth() const;
~FlatButton();
protected:
void paintEvent(QPaintEvent *e) override;
void onStateChanged(int oldState, StateChangeSource source) override;
private:
QImage prepareRippleMask() const;
void handleRipples(bool wasDown, bool wasPress);
QString _text, _textForAutoSize;
int _width;
const style::FlatButton &_st;
anim::fvalue a_over;
Animation _a_appearance;
float64 _opacity = 1.;
std_::unique_ptr<RippleAnimation> _ripple;
};
class LinkButton : public AbstractButton { class LinkButton : public AbstractButton {
public: public:
LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st = st::defaultLinkButton); LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st = st::defaultLinkButton);
@ -85,7 +47,73 @@ private:
}; };
class RoundButton : public AbstractButton { class RippleButton : public AbstractButton {
public:
RippleButton(QWidget *parent, const style::RippleAnimation &st);
// Displays full ripple circle constantly.
enum class SetForceRippledWay {
Default,
SkipAnimation,
};
void setForceRippled(bool rippled, SetForceRippledWay way = SetForceRippledWay::Default);
bool forceRippled() const {
return _forceRippled;
}
~RippleButton();
protected:
void paintRipple(QPainter &p, int x, int y, uint64 ms);
void onStateChanged(int oldState, StateChangeSource source) override;
virtual QImage prepareRippleMask() const;
virtual QPoint prepareRippleStartPosition() const;
private:
void ensureRipple();
void handleRipples(bool wasDown, bool wasPress);
const style::RippleAnimation &_st;
std_::unique_ptr<RippleAnimation> _ripple;
bool _forceRippled = false;
};
class FlatButton : public RippleButton {
public:
FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st);
void step_appearance(float64 ms, bool timer);
void setText(const QString &text);
void setWidth(int32 w);
int32 textWidth() const;
protected:
void paintEvent(QPaintEvent *e) override;
void onStateChanged(int oldState, StateChangeSource source) override;
private:
void setOpacity(float64 o);
float64 opacity() const;
QString _text, _textForAutoSize;
int _width;
const style::FlatButton &_st;
anim::fvalue a_over;
Animation _a_appearance;
float64 _opacity = 1.;
};
class RoundButton : public RippleButton {
public: public:
RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st); RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st);
@ -102,17 +130,13 @@ public:
}; };
void setTextTransform(TextTransform transform); void setTextTransform(TextTransform transform);
~RoundButton();
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
void onStateChanged(int oldState, StateChangeSource source) override; QImage prepareRippleMask() const override;
QPoint prepareRippleStartPosition() const override;
private: private:
QImage prepareRippleMask() const;
void handleRipples(bool wasDown, bool wasPress);
void updateText(); void updateText();
void resizeToText(); void resizeToText();
@ -128,44 +152,48 @@ private:
TextTransform _transform = TextTransform::ToUpper; TextTransform _transform = TextTransform::ToUpper;
std_::unique_ptr<RippleAnimation> _ripple;
}; };
class IconButton : public AbstractButton { class IconButton : public RippleButton {
public: public:
IconButton(QWidget *parent, const style::IconButton &st); IconButton(QWidget *parent, const style::IconButton &st);
// Pass nullptr to restore the default icon. // Pass nullptr to restore the default icon.
void setIcon(const style::icon *icon, const style::icon *iconOver = nullptr); void setIcon(const style::icon *icon, const style::icon *iconOver = nullptr);
// Displays full ripple circle constantly.
enum class SetStateWay {
Default,
SkipAnimation,
};
void setActiveState(bool activeState, SetStateWay way = SetStateWay::Default);
~IconButton();
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
void onStateChanged(int oldState, StateChangeSource source) override; void onStateChanged(int oldState, StateChangeSource source) override;
private: QImage prepareRippleMask() const override;
void ensureRipple(); QPoint prepareRippleStartPosition() const override;
QImage prepareRippleMask() const;
void handleRipples(bool wasDown, bool wasPress);
private:
const style::IconButton &_st; const style::IconButton &_st;
const style::icon *_iconOverride = nullptr; const style::icon *_iconOverride = nullptr;
const style::icon *_iconOverrideOver = nullptr; const style::icon *_iconOverrideOver = nullptr;
FloatAnimation _a_over; FloatAnimation _a_over;
std_::unique_ptr<RippleAnimation> _ripple; };
bool _activeState = false;
class LeftOutlineButton : public RippleButton {
public:
LeftOutlineButton(QWidget *parent, const QString &text, const style::OutlineButton &st = st::defaultLeftOutlineButton);
void setText(const QString &text);
protected:
void paintEvent(QPaintEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
QString _text, _fullText;
int _textWidth, _fullTextWidth;
const style::OutlineButton &_st;
}; };

View File

@ -58,6 +58,7 @@ void DropdownMenu::init() {
_menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); }); _menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); });
_menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); }); _menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); });
_menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); }); _menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); });
_menu->setMouseReleaseDelegate([this](QPoint globalPosition) { handleMouseRelease(globalPosition); });
setMouseTracking(true); setMouseTracking(true);
@ -176,6 +177,14 @@ void DropdownMenu::handleMousePress(QPoint globalPosition) {
} }
} }
void DropdownMenu::handleMouseRelease(QPoint globalPosition) {
if (_parent) {
_parent->forwardMouseRelease(globalPosition);
} else {
hideMenu();
}
}
void DropdownMenu::focusOutEvent(QFocusEvent *e) { void DropdownMenu::focusOutEvent(QFocusEvent *e) {
hideMenu(); hideMenu();
} }

View File

@ -88,6 +88,10 @@ private:
_menu->handleMousePress(globalPosition); _menu->handleMousePress(globalPosition);
} }
void handleMousePress(QPoint globalPosition); void handleMousePress(QPoint globalPosition);
void forwardMouseRelease(QPoint globalPosition) {
_menu->handleMouseRelease(globalPosition);
}
void handleMouseRelease(QPoint globalPosition);
using SubmenuPointer = QPointer<DropdownMenu>; using SubmenuPointer = QPointer<DropdownMenu>;
bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source); bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source);

View File

@ -18,6 +18,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h" #include "stdafx.h"
#include "ui/widgets/menu.h" #include "ui/widgets/menu.h"
#include "ui/effects/ripple_animation.h"
namespace Ui { namespace Ui {
Menu::Menu(QWidget *parent, const style::Menu &st) : TWidget(parent) Menu::Menu(QWidget *parent, const style::Menu &st) : TWidget(parent)
@ -150,6 +152,7 @@ void Menu::actionChanged() {
void Menu::paintEvent(QPaintEvent *e) { void Menu::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
auto ms = getms();
auto clip = e->rect(); auto clip = e->rect();
auto topskip = QRect(0, 0, width(), _st.skip); auto topskip = QRect(0, 0, width(), _st.skip);
@ -172,8 +175,15 @@ void Menu::paintEvent(QPaintEvent *e) {
p.fillRect(0, 0, width(), actionHeight, _st.itemBg); p.fillRect(0, 0, width(), actionHeight, _st.itemBg);
p.fillRect(_st.separatorPadding.left(), _st.separatorPadding.top(), width() - _st.separatorPadding.left() - _st.separatorPadding.right(), _st.separatorWidth, _st.separatorFg); p.fillRect(_st.separatorPadding.left(), _st.separatorPadding.top(), width() - _st.separatorPadding.left() - _st.separatorPadding.right(), _st.separatorWidth, _st.separatorFg);
} else { } else {
auto enabled = action->isEnabled(), selected = (i == _selected && enabled); auto enabled = action->isEnabled();
auto selected = ((i == _selected || i == _pressed) && enabled);
p.fillRect(0, 0, width(), actionHeight, selected ? _st.itemBgOver : _st.itemBg); p.fillRect(0, 0, width(), actionHeight, selected ? _st.itemBgOver : _st.itemBg);
if (data.ripple) {
data.ripple->paint(p, 0, 0, width(), ms);
if (data.ripple->empty()) {
data.ripple.reset();
}
}
if (auto icon = (selected ? data.iconOver : data.icon)) { if (auto icon = (selected ? data.iconOver : data.icon)) {
icon->paint(p, _st.itemIconPosition, width()); icon->paint(p, _st.itemIconPosition, width());
} }
@ -207,9 +217,29 @@ void Menu::itemPressed(TriggeredSource source) {
return; return;
} }
if (_selected >= 0 && _selected < _actions.size() && _actions[_selected]->isEnabled()) { if (_selected >= 0 && _selected < _actions.size() && _actions[_selected]->isEnabled()) {
if (_triggeredCallback) { _pressed = _selected;
auto actionTop = _st.skip + itemTop(_selected); if (source == TriggeredSource::Mouse) {
_triggeredCallback(_actions[_selected], actionTop, source); if (!_actionsData[_pressed].ripple) {
auto mask = RippleAnimation::rectMask(QSize(width(), _itemHeight));
_actionsData[_pressed].ripple = MakeShared<RippleAnimation>(_st.ripple, std_::move(mask), [this, selected = _pressed] {
updateItem(selected);
});
}
_actionsData[_pressed].ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(0, itemTop(_pressed)));
} else {
itemReleased(source);
}
}
}
void Menu::itemReleased(TriggeredSource source) {
auto pressed = base::take(_pressed, -1);
if (pressed >= 0 && pressed < _actions.size()) {
if (source == TriggeredSource::Mouse && _actionsData[pressed].ripple) {
_actionsData[pressed].ripple->lastStop();
}
if (pressed == _selected && _triggeredCallback) {
_triggeredCallback(_actions[_selected], itemTop(_selected), source);
} }
} }
} }
@ -290,9 +320,8 @@ void Menu::setSelected(int selected) {
_selected = selected; _selected = selected;
updateSelectedItem(); updateSelectedItem();
if (_activatedCallback) { if (_activatedCallback) {
auto actionTop = _st.skip + itemTop(_selected);
auto source = _mouseSelection ? TriggeredSource::Mouse : TriggeredSource::Keyboard; auto source = _mouseSelection ? TriggeredSource::Mouse : TriggeredSource::Keyboard;
_activatedCallback((_selected >= 0) ? _actions[_selected] : nullptr, actionTop, source); _activatedCallback((_selected >= 0) ? _actions[_selected] : nullptr, itemTop(_selected), source);
} }
} }
} }
@ -301,19 +330,23 @@ int Menu::itemTop(int index) {
if (index > _actions.size()) { if (index > _actions.size()) {
index = _actions.size(); index = _actions.size();
} }
int top = 0; int top = _st.skip;
for (int i = 0; i < index; ++i) { for (int i = 0; i < index; ++i) {
top += _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight; top += _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight;
} }
return top; return top;
} }
void Menu::updateSelectedItem() { void Menu::updateItem(int index) {
if (_selected >= 0) { if (index >= 0 && index < _actions.size()) {
update(0, _st.skip + itemTop(_selected), width(), _actions.at(_selected)->isSeparator() ? _separatorHeight : _itemHeight); update(0, itemTop(index), width(), _actions[index]->isSeparator() ? _separatorHeight : _itemHeight);
} }
} }
void Menu::updateSelectedItem() {
updateItem(_selected);
}
void Menu::mouseMoveEvent(QMouseEvent *e) { void Menu::mouseMoveEvent(QMouseEvent *e) {
handleMouseMove(e->globalPos()); handleMouseMove(e->globalPos());
} }
@ -336,6 +369,10 @@ void Menu::mousePressEvent(QMouseEvent *e) {
handleMousePress(e->globalPos()); handleMousePress(e->globalPos());
} }
void Menu::mouseReleaseEvent(QMouseEvent *e) {
handleMouseRelease(e->globalPos());
}
void Menu::handleMousePress(QPoint globalPosition) { void Menu::handleMousePress(QPoint globalPosition) {
handleMouseMove(globalPosition); handleMouseMove(globalPosition);
if (rect().contains(mapFromGlobal(globalPosition))) { if (rect().contains(mapFromGlobal(globalPosition))) {
@ -345,4 +382,12 @@ void Menu::handleMousePress(QPoint globalPosition) {
} }
} }
void Menu::handleMouseRelease(QPoint globalPosition) {
handleMouseMove(globalPosition);
itemReleased(TriggeredSource::Mouse);
if (!rect().contains(mapFromGlobal(globalPosition)) && _mouseReleaseDelegate) {
_mouseReleaseDelegate(globalPosition);
}
}
} // namespace Ui } // namespace Ui

View File

@ -24,6 +24,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui { namespace Ui {
class RippleAnimation;
class Menu : public TWidget { class Menu : public TWidget {
Q_OBJECT Q_OBJECT
@ -76,11 +78,17 @@ public:
} }
void handleMousePress(QPoint globalPosition); void handleMousePress(QPoint globalPosition);
void setMouseReleaseDelegate(base::lambda_unique<void(QPoint globalPosition)> delegate) {
_mouseReleaseDelegate = std_::move(delegate);
}
void handleMouseRelease(QPoint globalPosition);
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override; void keyPressEvent(QKeyEvent *e) override;
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 enterEvent(QEvent *e) override; void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override; void leaveEvent(QEvent *e) override;
@ -99,8 +107,10 @@ private:
void clearMouseSelection(); void clearMouseSelection();
int itemTop(int index); int itemTop(int index);
void updateItem(int index);
void updateSelectedItem(); void updateSelectedItem();
void itemPressed(TriggeredSource source); void itemPressed(TriggeredSource source);
void itemReleased(TriggeredSource source);
const style::Menu &_st; const style::Menu &_st;
@ -110,6 +120,7 @@ private:
base::lambda_unique<bool(int key)> _keyPressDelegate; base::lambda_unique<bool(int key)> _keyPressDelegate;
base::lambda_unique<void(QPoint globalPosition)> _mouseMoveDelegate; base::lambda_unique<void(QPoint globalPosition)> _mouseMoveDelegate;
base::lambda_unique<void(QPoint globalPosition)> _mousePressDelegate; base::lambda_unique<void(QPoint globalPosition)> _mousePressDelegate;
base::lambda_unique<void(QPoint globalPosition)> _mouseReleaseDelegate;
struct ActionData { struct ActionData {
bool hasSubmenu = false; bool hasSubmenu = false;
@ -117,6 +128,7 @@ private:
QString shortcut; QString shortcut;
const style::icon *icon = nullptr; const style::icon *icon = nullptr;
const style::icon *iconOver = nullptr; const style::icon *iconOver = nullptr;
QSharedPointer<RippleAnimation> ripple;
}; };
using ActionsData = QList<ActionData>; using ActionsData = QList<ActionData>;
@ -129,6 +141,7 @@ private:
bool _mouseSelection = false; bool _mouseSelection = false;
int _selected = -1; int _selected = -1;
int _pressed = -1;
bool _childShown = false; bool _childShown = false;
}; };

View File

@ -55,6 +55,7 @@ void PopupMenu::init() {
_menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); }); _menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); });
_menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); }); _menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); });
_menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); }); _menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); });
_menu->setMouseReleaseDelegate([this](QPoint globalPosition) { handleMouseRelease(globalPosition); });
handleCompositingUpdate(); handleCompositingUpdate();
@ -224,6 +225,14 @@ void PopupMenu::handleMousePress(QPoint globalPosition) {
} }
} }
void PopupMenu::handleMouseRelease(QPoint globalPosition) {
if (_parent) {
_parent->forwardMouseRelease(globalPosition);
} else {
hideMenu();
}
}
void PopupMenu::focusOutEvent(QFocusEvent *e) { void PopupMenu::focusOutEvent(QFocusEvent *e) {
hideMenu(); hideMenu();
} }

View File

@ -93,6 +93,10 @@ private:
_menu->handleMousePress(globalPosition); _menu->handleMousePress(globalPosition);
} }
void handleMousePress(QPoint globalPosition); void handleMousePress(QPoint globalPosition);
void forwardMouseRelease(QPoint globalPosition) {
_menu->handleMouseRelease(globalPosition);
}
void handleMouseRelease(QPoint globalPosition);
using SubmenuPointer = QPointer<PopupMenu>; using SubmenuPointer = QPointer<PopupMenu>;
bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source); bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source);

View File

@ -61,8 +61,6 @@ FlatButton {
height: pixels; height: pixels;
textTop: pixels; textTop: pixels;
overTextTop: pixels;
downTextTop: pixels;
font: font; font: font;
overFont: font; overFont: font;
@ -87,7 +85,6 @@ RoundButton {
padding: margins; padding: margins;
textTop: pixels; textTop: pixels;
downTextTop: pixels;
icon: icon; icon: icon;
@ -272,6 +269,8 @@ OutlineButton {
font: font; font: font;
padding: margins; padding: margins;
ripple: RippleAnimation;
} }
IconButton { IconButton {
@ -280,9 +279,7 @@ IconButton {
icon: icon; icon: icon;
iconOver: icon; iconOver: icon;
iconPosition: point; iconPosition: point;
iconPositionDown: point;
duration: int; duration: int;
@ -394,6 +391,8 @@ Menu {
widthMin: pixels; widthMin: pixels;
widthMax: pixels; widthMax: pixels;
ripple: RippleAnimation;
} }
PanelAnimation { PanelAnimation {
@ -487,7 +486,6 @@ defaultActiveButton: RoundButton {
padding: margins(0px, 0px, 0px, 0px); padding: margins(0px, 0px, 0px, 0px);
textTop: 8px; textTop: 8px;
downTextTop: 8px;
font: semiboldFont; font: semiboldFont;
@ -589,14 +587,22 @@ defaultLeftOutlineButton: OutlineButton {
font: normalFont; font: normalFont;
padding: margins(11px, 5px, 11px, 5px); padding: margins(11px, 5px, 11px, 5px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: lightButtonBgRipple;
}
} }
attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) { attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) {
outlineFgOver: attentionBoxButtonFg; outlineFgOver: attentionButtonFg;
textBgOver: attentionBoxButtonBgOver; textBgOver: attentionButtonBgOver;
textFg: attentionBoxButtonFg; textFg: attentionButtonFg;
textFgOver: attentionBoxButtonFg; textFgOver: attentionButtonFg;
ripple: RippleAnimation(defaultRippleAnimation) {
color: attentionButtonBgRipple;
}
} }
defaultInputArea: InputArea { defaultInputArea: InputArea {
@ -697,7 +703,6 @@ defaultRadiobutton: Radiobutton {
defaultIconButton: IconButton { defaultIconButton: IconButton {
iconPosition: point(-1px, -1px); iconPosition: point(-1px, -1px);
iconPositionDown: point(-1px, -1px);
} }
widgetSlideDuration: 200; widgetSlideDuration: 200;
@ -756,8 +761,8 @@ defaultMenu: Menu {
itemFg: windowFg; itemFg: windowFg;
itemFgOver: windowFgOver; itemFgOver: windowFgOver;
itemFgDisabled: #cccccc; itemFgDisabled: #cccccc;
itemFgShortcut: #999999; itemFgShortcut: windowSubTextFg;
itemFgShortcutOver: #7c99b2; itemFgShortcutOver: windowSubTextFgOver;
itemFgShortcutDisabled: #cccccc; itemFgShortcutDisabled: #cccccc;
itemIconPosition: point(0px, 0px); itemIconPosition: point(0px, 0px);
itemPadding: margins(17px, 8px, 17px, 7px); itemPadding: margins(17px, 8px, 17px, 7px);
@ -771,6 +776,8 @@ defaultMenu: Menu {
widthMin: 180px; widthMin: 180px;
widthMax: 300px; widthMax: 300px;
ripple: defaultRippleAnimation;
} }
defaultPopupMenu: PopupMenu { defaultPopupMenu: PopupMenu {
shadow: defaultRoundShadow; shadow: defaultRoundShadow;
@ -801,18 +808,42 @@ defaultDropdownMenu: DropdownMenu {
menu: defaultMenu; menu: defaultMenu;
} }
historyToDown: icon { historyToDownBelow: icon {
{ "history_down_shadow", #00000040 }, { "history_down_shadow", #00000040 },
{ "history_down_circle", #ffffff, point(4px, 4px) }, { "history_down_circle", windowBg, point(4px, 4px) },
};
historyToDownBelowOver: icon {
{ "history_down_shadow", #00000040 },
{ "history_down_circle", windowBgOver, point(4px, 4px) },
};
contactsAddIconBelow: icon {
{ "history_down_shadow", #00000040 },
{ "history_down_circle", activeButtonBg, point(4px, 4px) },
};
contactsAddIconBelowOver: icon {
{ "history_down_shadow", #00000040 },
{ "history_down_circle", activeButtonBgOver, point(4px, 4px) },
}; };
contactsAddIcon: icon { BotKeyboardButton {
{ "history_down_shadow", #00000020 }, margin: pixels;
{ "history_down_circle", activeButtonBg, point(4px, 4px) }, padding: pixels;
{ "contacts_add", activeButtonFg, point(18px, 18px) }, height: pixels;
}; textTop: pixels;
contactsAddIconOver: icon { ripple: RippleAnimation;
{ "history_down_shadow", #00000020 }, }
{ "history_down_circle", activeButtonBgOver, point(4px, 4px) },
{ "contacts_add", activeButtonFg, point(18px, 18px) }, TwoIconButton {
}; width: pixels;
height: pixels;
iconBelow: icon;
iconAbove: icon;
iconBelowOver: icon;
iconAboveOver: icon;
iconPosition: point;
rippleAreaPosition: point;
rippleAreaSize: pixels;
ripple: RippleAnimation;
}

View File

@ -56,10 +56,10 @@ TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w)
subscribe(w->searchInPeerChanged(), [this](PeerData *peer) { subscribe(w->searchInPeerChanged(), [this](PeerData *peer) {
_searchInPeer = peer; _searchInPeer = peer;
auto historyPeer = App::main() ? App::main()->historyPeer() : nullptr; auto historyPeer = App::main() ? App::main()->historyPeer() : nullptr;
_search->setActiveState(historyPeer && historyPeer == _searchInPeer); _search->setForceRippled(historyPeer && historyPeer == _searchInPeer);
}); });
subscribe(w->historyPeerChanged(), [this](PeerData *peer) { subscribe(w->historyPeerChanged(), [this](PeerData *peer) {
_search->setActiveState(peer && peer == _searchInPeer, Ui::IconButton::SetStateWay::SkipAnimation); _search->setForceRippled(peer && peer == _searchInPeer, Ui::IconButton::SetForceRippledWay::SkipAnimation);
update(); update();
}); });
@ -115,17 +115,17 @@ void TopBarWidget::showMenu() {
data->menu->deleteLater(); data->menu->deleteLater();
if (data->that && _menu == data->menu) { if (data->that && _menu == data->menu) {
_menu = nullptr; _menu = nullptr;
_menuToggle->setActiveState(false); _menuToggle->setForceRippled(false);
} }
}); });
_menu->setShowStartCallback([this, data] { _menu->setShowStartCallback([this, data] {
if (data->that && _menu == data->menu) { if (data->that && _menu == data->menu) {
_menuToggle->setActiveState(true); _menuToggle->setForceRippled(true);
} }
}); });
_menu->setHideStartCallback([this, data] { _menu->setHideStartCallback([this, data] {
if (data->that && _menu == data->menu) { if (data->that && _menu == data->menu) {
_menuToggle->setActiveState(false); _menuToggle->setForceRippled(false);
} }
}); });
_menuToggle->installEventFilter(_menu); _menuToggle->installEventFilter(_menu);

View File

@ -47,7 +47,6 @@ notifyClose: IconButton {
iconOver: simpleCloseIconOver; iconOver: simpleCloseIconOver;
iconPosition: point(10px, 10px); iconPosition: point(10px, 10px);
iconPositionDown: point(10px, 11px);
} }
notifyItemTop: 12px; notifyItemTop: 12px;
notifyTextLeft: 12px; notifyTextLeft: 12px;
@ -77,7 +76,6 @@ notifySendReply: IconButton {
icon: icon {{ "notification_send", lightButtonFg, point(3px, 9px) }}; icon: icon {{ "notification_send", lightButtonFg, point(3px, 9px) }};
iconPosition: point(0px, 0px); iconPosition: point(0px, 0px);
iconPositionDown: point(0px, 1px);
} }
titleUnreadCounterTop: 5px; titleUnreadCounterTop: 5px;
@ -96,6 +94,8 @@ mainMenuCoverNameTop: 88px;
mainMenuCoverStatusTop: 106px; mainMenuCoverStatusTop: 106px;
mainMenuSkip: 13px; mainMenuSkip: 13px;
mainMenu: Menu(defaultMenu) { mainMenu: Menu(defaultMenu) {
itemFg: windowBoldFg;
itemFgOver: windowBoldFgOver;
itemFont: semiboldFont; itemFont: semiboldFont;
itemIconPosition: point(28px, 11px); itemIconPosition: point(28px, 11px);
itemPadding: margins(76px, 14px, 28px, 14px); itemPadding: margins(76px, 14px, 28px, 14px);
@ -144,9 +144,7 @@ titleButtonMinimize: IconButton {
{ size(24px, 21px), titleButtonBgOver }, { size(24px, 21px), titleButtonBgOver },
{ "title_button_minimize", titleButtonFgOver, point(4px, 4px) }, { "title_button_minimize", titleButtonFgOver, point(4px, 4px) },
}; };
iconPosition: point(0px, 0px); iconPosition: point(0px, 0px);
iconPositionDown: point(0px, 0px);
} }
titleButtonMaximize: IconButton(titleButtonMinimize) { titleButtonMaximize: IconButton(titleButtonMinimize) {
icon: icon {{ "title_button_maximize", titleButtonFg, point(4px, 4px) }}; icon: icon {{ "title_button_maximize", titleButtonFg, point(4px, 4px) }};
@ -195,7 +193,6 @@ topBarButton: RoundButton {
padding: margins(0px, 14px, 12px, 12px); padding: margins(0px, 14px, 12px, 12px);
textTop: 6px; textTop: 6px;
downTextTop: 6px;
font: font(fsize); font: font(fsize);
@ -212,9 +209,7 @@ topBarSearch: IconButton {
icon: icon {{ "title_search", menuIconFg }}; icon: icon {{ "title_search", menuIconFg }};
iconOver: icon {{ "title_search", menuIconFgOver }}; iconOver: icon {{ "title_search", menuIconFgOver }};
iconPosition: point(15px, 18px); iconPosition: point(15px, 18px);
iconPositionDown: point(15px, 18px);
rippleAreaPosition: point(4px, 7px); rippleAreaPosition: point(4px, 7px);
rippleAreaSize: 40px; rippleAreaSize: 40px;
@ -225,9 +220,7 @@ topBarSearch: IconButton {
topBarMenuToggle: IconButton(topBarSearch) { topBarMenuToggle: IconButton(topBarSearch) {
icon: icon {{ "title_menu_dots", menuIconFg }}; icon: icon {{ "title_menu_dots", menuIconFg }};
iconOver: icon {{ "title_menu_dots", menuIconFgOver }}; iconOver: icon {{ "title_menu_dots", menuIconFgOver }};
iconPosition: point(16px, 17px); iconPosition: point(16px, 17px);
iconPositionDown: point(16px, 17px);
rippleAreaPosition: point(0px, 7px); rippleAreaPosition: point(0px, 7px);
} }

View File

@ -80,6 +80,7 @@ void MainMenu::checkSelf() {
_userpicButton->setClickedCallback([] { _userpicButton->setClickedCallback([] {
App::wnd()->showSettings(); App::wnd()->showSettings();
}); });
_userpicButton->show();
updateControlsGeometry(); updateControlsGeometry();
if (_showFinished) { if (_showFinished) {
_userpicButton->showFinished(); _userpicButton->showFinished();

View File

@ -3,4 +3,4 @@ AppVersionStrMajor 0.10
AppVersionStrSmall 0.10.20 AppVersionStrSmall 0.10.20
AppVersionStr 0.10.20 AppVersionStr 0.10.20
AlphaChannel 0 AlphaChannel 0
BetaVersion 10019007 BetaVersion 10019008

View File

@ -452,8 +452,6 @@
'<(src_loc)/stickers/stickers.h', '<(src_loc)/stickers/stickers.h',
'<(src_loc)/ui/buttons/history_down_button.cpp', '<(src_loc)/ui/buttons/history_down_button.cpp',
'<(src_loc)/ui/buttons/history_down_button.h', '<(src_loc)/ui/buttons/history_down_button.h',
'<(src_loc)/ui/buttons/left_outline_button.cpp',
'<(src_loc)/ui/buttons/left_outline_button.h',
'<(src_loc)/ui/buttons/peer_avatar_button.cpp', '<(src_loc)/ui/buttons/peer_avatar_button.cpp',
'<(src_loc)/ui/buttons/peer_avatar_button.h', '<(src_loc)/ui/buttons/peer_avatar_button.h',
'<(src_loc)/ui/effects/panel_animation.cpp', '<(src_loc)/ui/effects/panel_animation.cpp',