Active state for IconButton added, when ripple is not hidden.

This commit is contained in:
John Preston 2016-11-16 15:06:02 +03:00
parent 7fa274a68e
commit cdef9fa14f
14 changed files with 192 additions and 45 deletions

View File

@ -1154,8 +1154,8 @@ bool DialogsInner::hasFilteredResults() const {
}
void DialogsInner::searchInPeer(PeerData *peer) {
_searchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : 0;
_searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0;
_searchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : nullptr;
_searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : nullptr;
if (_searchInPeer) {
onHashtagFilterUpdate(QStringRef());
_cancelSearchInPeer->show();
@ -2021,9 +2021,7 @@ void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) {
if ((_filter->getLastText() != query) || (inPeer && inPeer != _searchInPeer && inPeer->migrateTo() != _searchInPeer)) {
if (inPeer) {
onCancelSearch();
_searchInPeer = inPeer->migrateTo() ? inPeer->migrateTo() : inPeer;
_searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0;
_inner->searchInPeer(_searchInPeer);
setSearchInPeer(inPeer);
}
_filter->setText(query);
_filter->updatePlaceholder();
@ -2309,12 +2307,20 @@ void DialogsWidget::onFilterUpdate(bool force) {
void DialogsWidget::searchInPeer(PeerData *peer) {
onCancelSearch();
_searchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : 0;
_searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0;
_inner->searchInPeer(_searchInPeer);
setSearchInPeer(peer);
onFilterUpdate(true);
}
void DialogsWidget::setSearchInPeer(PeerData *peer) {
auto newSearchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : nullptr;
_searchInMigrated = newSearchInPeer ? newSearchInPeer->migrateFrom() : nullptr;
if (newSearchInPeer != _searchInPeer) {
_searchInPeer = newSearchInPeer;
App::main()->searchInPeerChanged().notify(_searchInPeer, true);
}
_inner->searchInPeer(_searchInPeer);
}
void DialogsWidget::onFilterCursorMoved(int from, int to) {
if (to < 0) to = _filter->cursorPosition();
QString t = _filter->getLastText();
@ -2527,8 +2533,7 @@ bool DialogsWidget::onCancelSearch() {
if (Adaptive::OneColumn()) {
Ui::showPeerHistory(_searchInPeer, ShowAtUnreadMsgId);
}
_searchInPeer = _searchInMigrated = 0;
_inner->searchInPeer(0);
setSearchInPeer(nullptr);
clearing = true;
}
_inner->clearFilter();
@ -2547,8 +2552,7 @@ void DialogsWidget::onCancelSearchInPeer() {
if (Adaptive::OneColumn() && !App::main()->selectingPeer()) {
Ui::showPeerHistory(_searchInPeer, ShowAtUnreadMsgId);
}
_searchInPeer = _searchInMigrated = 0;
_inner->searchInPeer(0);
setSearchInPeer(nullptr);
}
_inner->clearFilter();
_filter->clear();

View File

@ -308,6 +308,7 @@ private slots:
#endif // TDESKTOP_DISABLE_AUTOUPDATE
private:
void setSearchInPeer(PeerData *peer);
void showMainMenu();
void updateLockUnlockVisibility();
void updateControlsGeometry();

View File

@ -4362,7 +4362,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
App::main()->dlgUpdated(wasHistory, wasMsgId);
emit historyShown(_history, _showAtMsgId);
App::main()->topBar()->update();
App::main()->historyPeerChanged().notify(_peer, true);
update();
}

View File

@ -371,6 +371,13 @@ public:
bool contentOverlapped(const QRect &globalRect);
base::Observable<PeerData*> &searchInPeerChanged() {
return _searchInPeerChanged;
}
base::Observable<PeerData*> &historyPeerChanged() {
return _historyPeerChanged;
}
void rpcClear() override;
bool isItemVisible(HistoryItem *item);
@ -579,6 +586,9 @@ private:
void clearCachedBackground();
base::Observable<PeerData*> _searchInPeerChanged;
base::Observable<PeerData*> _historyPeerChanged;
Animation _a_show;
QPixmap _cacheUnder, _cacheOver;
anim::ivalue a_coordUnder, a_coordOver;

View File

@ -26,10 +26,13 @@ namespace Ui {
class RippleAnimation::Ripple {
public:
Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, UpdateCallback update);
Ripple(const style::RippleAnimation &st, const QPixmap &mask, UpdateCallback update);
void paint(QPainter &p, const QPixmap &mask, uint64 ms);
void stop();
void unstop();
void finish();
bool finished() const {
return _hiding && !_hide.animating();
}
@ -72,6 +75,17 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin,
_show.start(UpdateCallback(_update), 0., 1., _st.showDuration);
}
RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap &mask, UpdateCallback update)
: _st(st)
, _update(update)
, _origin(mask.width() / (2 * cIntRetinaFactor()), mask.height() / (2 * cIntRetinaFactor()))
, _radiusFrom(mask.width() + mask.height())
, _frame(mask.size(), QImage::Format_ARGB32_Premultiplied) {
_frame.setDevicePixelRatio(mask.devicePixelRatio());
_radiusTo = _radiusFrom;
_hide.start(UpdateCallback(_update), 0., 1., _st.hideDuration);
}
void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, uint64 ms) {
auto opacity = _hide.current(ms, _hiding ? 0. : 1.);
if (opacity == 0.) {
@ -110,6 +124,23 @@ void RippleAnimation::Ripple::stop() {
_hide.start(UpdateCallback(_update), 1., 0., _st.hideDuration);
}
void RippleAnimation::Ripple::unstop() {
if (_hiding) {
if (_hide.animating()) {
_hide.start(UpdateCallback(_update), 0., 1., _st.hideDuration);
}
_hiding = false;
}
}
void RippleAnimation::Ripple::finish() {
if (_update) {
_update();
}
_show.finish();
_hide.finish();
}
RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, UpdateCallback callback)
: _st(st)
, _mask(App::pixmapFromImageInPlace(std_::move(mask)))
@ -118,15 +149,33 @@ RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask,
void RippleAnimation::add(QPoint origin, int startRadius) {
lastStop();
_ripples.push_back(new Ripple(_st, origin, startRadius, _mask, _update));
}
void RippleAnimation::stopLast() {
void RippleAnimation::addFading() {
lastStop();
_ripples.push_back(new Ripple(_st, _mask, _update));
}
void RippleAnimation::lastStop() {
if (!_ripples.isEmpty()) {
_ripples.back()->stop();
}
}
void RippleAnimation::lastUnstop() {
if (!_ripples.isEmpty()) {
_ripples.back()->unstop();
}
}
void RippleAnimation::lastFinish() {
if (!_ripples.isEmpty()) {
_ripples.back()->finish();
}
}
void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, uint64 ms) {
if (_ripples.isEmpty()) {
return;

View File

@ -34,7 +34,10 @@ public:
void setMask(QImage &&mask);
void add(QPoint origin, int startRadius = 0);
void stopLast();
void addFading();
void lastStop();
void lastUnstop();
void lastFinish();
void paint(QPainter &p, int x, int y, int outerWidth, uint64 ms);

View File

@ -108,7 +108,7 @@ void FlatButton::handleRipples(bool wasDown, bool wasPress) {
_ripple->add(clickPosition);
} else if (!down && _ripple) {
// Finish ripple anyway.
_ripple->stopLast();
_ripple->lastStop();
}
}
@ -311,7 +311,7 @@ void RoundButton::handleRipples(bool wasDown, bool wasPress) {
_ripple->add(clickPosition);
} else if (!down && _ripple) {
// Finish ripple anyway.
_ripple->stopLast();
_ripple->lastStop();
}
}
@ -348,6 +348,26 @@ void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) {
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) {
Painter p(this);
@ -361,7 +381,7 @@ void IconButton::paintEvent(QPaintEvent *e) {
}
auto down = (_state & StateDown);
auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.);
auto overIconOpacity = (down || _activeState) ? 1. : _a_over.current(getms(), (_state & StateOver) ? 1. : 0.);
auto overIcon = [this] {
if (_iconOverrideOver) {
return _iconOverrideOver;
@ -378,7 +398,7 @@ void IconButton::paintEvent(QPaintEvent *e) {
}
return &_st.icon;
};
auto icon = (over == 1. || down) ? overIcon() : justIcon();
auto icon = (overIconOpacity == 1.) ? overIcon() : justIcon();
auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition;
if (position.x() < 0) {
position.setX((width() - icon->width()) / 2);
@ -387,10 +407,10 @@ void IconButton::paintEvent(QPaintEvent *e) {
position.setY((height() - icon->height()) / 2);
}
icon->paint(p, position, width());
if (over > 0. && over < 1.) {
if (overIconOpacity > 0. && overIconOpacity < 1.) {
auto iconOver = overIcon();
if (iconOver != icon) {
p.setOpacity(over);
p.setOpacity(overIconOpacity);
iconOver->paint(p, position, width());
}
}
@ -417,11 +437,9 @@ void IconButton::handleRipples(bool wasDown, bool wasPress) {
return;
}
if (down && wasPress) {
if (down && wasPress && !_activeState) {
// Start a ripple only from mouse press.
if (!_ripple) {
_ripple = std_::make_unique<Ui::RippleAnimation>(_st.ripple, prepareRippleMask(), [this] { update(); });
}
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);
@ -430,9 +448,15 @@ void IconButton::handleRipples(bool wasDown, bool wasPress) {
startRadius = sqrt(clickRadiusSquare) - (_st.rippleAreaSize / 2);
}
_ripple->add(clickPosition - _st.rippleAreaPosition, startRadius);
} else if (!down && _ripple) {
} else if (!down && _ripple && !_activeState) {
// Finish ripple anyway.
_ripple->stopLast();
_ripple->lastStop();
}
}
void IconButton::ensureRipple() {
if (!_ripple) {
_ripple = std_::make_unique<Ui::RippleAnimation>(_st.ripple, prepareRippleMask(), [this] { update(); });
}
}

View File

@ -139,6 +139,13 @@ public:
// Pass nullptr to restore the default icon.
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:
@ -147,6 +154,7 @@ protected:
void onStateChanged(int oldState, StateChangeSource source) override;
private:
void ensureRipple();
QImage prepareRippleMask() const;
void handleRipples(bool wasDown, bool wasPress);
@ -157,6 +165,7 @@ private:
FloatAnimation _a_over;
std_::unique_ptr<RippleAnimation> _ripple;
bool _activeState = false;
};

View File

@ -214,6 +214,14 @@ void InnerDropdown::prepareCache() {
}
void InnerDropdown::startOpacityAnimation(bool hiding) {
if (hiding) {
if (_hideStartCallback) {
_hideStartCallback();
}
} else if (_showStartCallback) {
_showStartCallback();
}
_hiding = false;
prepareCache();
_hiding = hiding;
@ -234,6 +242,9 @@ void InnerDropdown::showStarted() {
}
void InnerDropdown::startShowAnimation() {
if (_showStartCallback) {
_showStartCallback();
}
if (!_a_show.animating()) {
auto opacityAnimation = base::take(_a_opacity);
showChildren();
@ -289,7 +300,7 @@ bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) {
otherEnter();
} else if (e->type() == QEvent::Leave) {
otherLeave();
} else if (e->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton) {
} else if (e->type() == QEvent::MouseButtonRelease && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton) {
if (isHidden() || _hiding) {
otherEnter();
} else {

View File

@ -48,6 +48,12 @@ public:
void otherLeave();
void hideFast();
void setShowStartCallback(base::lambda_unique<void()> callback) {
_showStartCallback = std_::move(callback);
}
void setHideStartCallback(base::lambda_unique<void()> callback) {
_hideStartCallback = std_::move(callback);
}
void setHiddenCallback(base::lambda_unique<void()> callback) {
_hiddenCallback = std_::move(callback);
}
@ -110,6 +116,8 @@ private:
QTimer _hideTimer;
bool _ignoreShowEvents = false;
base::lambda_unique<void()> _showStartCallback;
base::lambda_unique<void()> _hideStartCallback;
base::lambda_unique<void()> _hiddenCallback;
ChildWidget<Ui::ScrollArea> _scroll;

View File

@ -591,12 +591,12 @@ defaultLeftOutlineButton: OutlineButton {
padding: margins(11px, 5px, 11px, 5px);
}
attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) {
outlineFgOver: #e43f3f;
outlineFgOver: attentionBoxButtonFg;
textBgOver: #faf2f2;
textBgOver: attentionBoxButtonBgOver;
textFg: #d15948;
textFgOver: #d15948;
textFg: attentionBoxButtonFg;
textFgOver: attentionBoxButtonFg;
}
defaultInputArea: InputArea {

View File

@ -53,6 +53,16 @@ TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w)
_search->setClickedCallback([this] { onSearch(); });
_menuToggle->setClickedCallback([this] { showMenu(); });
subscribe(w->searchInPeerChanged(), [this](PeerData *peer) {
_searchInPeer = peer;
auto historyPeer = App::main() ? App::main()->historyPeer() : nullptr;
_search->setActiveState(historyPeer && historyPeer == _searchInPeer);
});
subscribe(w->historyPeerChanged(), [this](PeerData *peer) {
_search->setActiveState(peer && peer == _searchInPeer, Ui::IconButton::SetStateWay::SkipAnimation);
update();
});
subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
if (Adaptive::OneColumn()) {
_unreadCounterSubscription = subscribe(Global::RefUnreadCounterUpdate(), [this] {
@ -92,20 +102,36 @@ void TopBarWidget::onSearch() {
void TopBarWidget::showMenu() {
if (auto main = App::main()) {
if (auto peer = main->peer()) {
if (auto menu = _menu.ptr()) {
_menu = nullptr;
_menuToggle->removeEventFilter(menu);
menu->setHiddenCallback([menu] { menu->deleteLater(); });
menu->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow);
} else {
if (!_menu) {
_menu.create(App::main());
struct Data {
Ui::DropdownMenu *menu = nullptr;
QPointer<TWidget> that;
};
auto data = MakeShared<Data>();
data->that = weakThis();
data->menu = _menu.ptr();
_menu->setHiddenCallback([this, data] {
data->menu->deleteLater();
if (data->that && _menu == data->menu) {
_menu = nullptr;
_menuToggle->setActiveState(false);
}
});
_menu->setShowStartCallback([this, data] {
if (data->that && _menu == data->menu) {
_menuToggle->setActiveState(true);
}
});
_menu->setHideStartCallback([this, data] {
if (data->that && _menu == data->menu) {
_menuToggle->setActiveState(false);
}
});
_menuToggle->installEventFilter(_menu);
App::main()->fillPeerMenu(peer, [this](const QString &text, base::lambda_unique<void()> callback) {
return _menu->addAction(text, std_::move(callback));
});
_menu->setHiddenCallback([this] {
_menu.destroyDelayed();
});
_menu->moveToRight(st::topBarMenuPosition.x(), st::topBarMenuPosition.y());
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
}
@ -261,7 +287,8 @@ void TopBarWidget::showAll() {
resizeEvent(nullptr);
return;
}
PeerData *h = App::main() ? App::main()->historyPeer() : 0, *o = App::main() ? App::main()->overviewPeer() : 0;
auto historyPeer = App::main() ? App::main()->historyPeer() : nullptr;
auto overviewPeer = App::main() ? App::main()->overviewPeer() : nullptr;
if (_selCount) {
_clearSelection->show();
if (_canDelete) {
@ -281,9 +308,9 @@ void TopBarWidget::showAll() {
_mediaType->hide();
}
}
if (h && !o && _clearSelection->isHidden()) {
if (historyPeer && !overviewPeer && _clearSelection->isHidden()) {
if (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) {
_info->setPeer(h);
_info->setPeer(historyPeer);
_info->show();
_menuToggle->hide();
_menu.destroy();

View File

@ -78,6 +78,7 @@ private:
anim::fvalue a_over = { 0. };
Animation _a_appearance;
PeerData *_searchInPeer = nullptr;
PeerData *_selPeer = nullptr;
int _selCount = 0;
bool _canDelete = false;

View File

@ -172,7 +172,7 @@ titleButtonClose: IconButton(titleButtonMinimize) {
// Legacy top bar.
topBarHeight: 54px;
topBarMenuPosition: point(-2px, 37px);
topBarMenuPosition: point(-2px, 35px);
topBarDuration: 200;
topBarBackward: icon {{ "title_back", #a3a3a3 }};
topBarForwardAlpha: 0.6;