From c6c75a1980ce1c04e8f2dca20b91f344c1f1c2bb Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 3 Oct 2017 15:57:11 +0100 Subject: [PATCH] Animate Info-to-Info transitions. --- .../SourceFiles/info/info_content_widget.cpp | 5 + .../SourceFiles/info/info_content_widget.h | 1 + .../SourceFiles/info/info_layer_widget.cpp | 2 +- .../SourceFiles/info/info_section_widget.cpp | 4 +- .../SourceFiles/info/info_section_widget.h | 1 - Telegram/SourceFiles/info/info_top_bar.cpp | 2 +- .../SourceFiles/info/info_wrap_widget.cpp | 100 ++++++++++-------- Telegram/SourceFiles/info/info_wrap_widget.h | 33 +++--- .../info/profile/info_profile_button.cpp | 7 +- .../profile/info_profile_inner_widget.cpp | 66 +++++------- .../info/profile/info_profile_members.cpp | 21 ++-- .../profile/profile_common_groups_section.cpp | 4 +- .../SourceFiles/ui/effects/fade_animation.cpp | 3 - .../SourceFiles/window/section_widget.cpp | 2 - .../SourceFiles/window/top_bar_widget.cpp | 1 + .../window/window_slide_animation.cpp | 7 +- 16 files changed, 128 insertions(+), 131 deletions(-) diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index 3da954a01f..99be3b683a 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -125,12 +125,17 @@ rpl::producer ContentWidget::desiredHeightValue() const { return value + _scrollTopSkip; }); } + rpl::producer ContentWidget::desiredShadowVisibility() const { using namespace rpl::mappers; return _scroll->scrollTopValue() | rpl::map($1 > 0); } +bool ContentWidget::hasTopBarShadow() const { + return (_scroll->scrollTop() > 0); +} + int ContentWidget::scrollTopSave() const { return _scroll->scrollTop(); } diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index 55cdfa84c4..ebd4995307 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -62,6 +62,7 @@ public: rpl::producer desiredHeightValue() const override; rpl::producer desiredShadowVisibility() const; + bool hasTopBarShadow() const; virtual void setInnerFocus() { _inner->setFocus(); diff --git a/Telegram/SourceFiles/info/info_layer_widget.cpp b/Telegram/SourceFiles/info/info_layer_widget.cpp index 0a5ba82ded..87650d4df0 100644 --- a/Telegram/SourceFiles/info/info_layer_widget.cpp +++ b/Telegram/SourceFiles/info/info_layer_widget.cpp @@ -57,7 +57,7 @@ LayerWidget::LayerWidget( void LayerWidget::setupHeightConsumers() { _content->desiredHeightValue() | rpl::start_with_next([this](int height) { - _desiredHeight = height; + accumulate_max(_desiredHeight, height); resizeToWidth(width()); }, lifetime()); } diff --git a/Telegram/SourceFiles/info/info_section_widget.cpp b/Telegram/SourceFiles/info/info_section_widget.cpp index b9f2fda294..c1b24eca59 100644 --- a/Telegram/SourceFiles/info/info_section_widget.cpp +++ b/Telegram/SourceFiles/info/info_section_widget.cpp @@ -56,7 +56,7 @@ void SectionWidget::init() { } PeerData *SectionWidget::peerForDialogs() const { - return _content->peer(); + return _content->peerForDialogs(); } bool SectionWidget::hasTopBarShadow() const { @@ -73,7 +73,7 @@ void SectionWidget::doSetInnerFocus() { } void SectionWidget::showFinishedHook() { - _content->showFinished(); + _content->showFast(); } bool SectionWidget::showInternal( diff --git a/Telegram/SourceFiles/info/info_section_widget.h b/Telegram/SourceFiles/info/info_section_widget.h index 64db718955..b1a440d8b7 100644 --- a/Telegram/SourceFiles/info/info_section_widget.h +++ b/Telegram/SourceFiles/info/info_section_widget.h @@ -47,7 +47,6 @@ public: Wrap wrap, not_null memento); - not_null peer() const; PeerData *peerForDialogs() const override; bool hasTopBarShadow() const override; diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index 4e68d64f98..8ae70aa70b 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -49,7 +49,7 @@ void TopBar::enableBackButton(bool enable) { if (enable) { _back.create(this, _st.back); _back->clicks() - | rpl::start_to_stream(_backClicks, lifetime()); + | rpl::start_to_stream(_backClicks, _back->lifetime()); } else { _back.destroy(); } diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index b75ca8dc01..acaf3bab3f 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -50,9 +50,8 @@ WrapWidget::WrapWidget( not_null controller, Wrap wrap, not_null memento) -: RpWidget(parent) +: SectionWidget(parent, controller) , _wrap(wrap) -, _controller(controller) , _topShadow(this) { _topShadow->toggleOn(topShadowToggledValue()); showNewContent(memento->content()); @@ -89,6 +88,14 @@ void WrapWidget::createTabs() { _topTabs->move(0, 0); _topTabs->resizeToWidth(width()); _topTabs->show(); + + _topTabsBackground.create(this, st::profileBg); + + _topTabsBackground->move(0, 0); + _topTabsBackground->resize( + width(), + _topTabs->height() - st::lineWidth); + _topTabsBackground->show(); } void WrapWidget::showTab(Tab tab) { @@ -156,10 +163,9 @@ void WrapWidget::createTopBar( auto close = _topBar->addButton(object_ptr( _topBar, st::infoLayerTopBarClose)); - close->clicks() - | rpl::start_with_next([this] { - _controller->hideSpecialLayer(); - }, close->lifetime()); + close->addClickHandler([this] { + controller()->hideSpecialLayer(); + }); } _topBar->move(0, 0); @@ -178,39 +184,20 @@ void WrapWidget::showBackFromStack() { last.section.get(), params); } else { - _controller->showBackFromStack(params); + controller()->showBackFromStack(params); } } not_null WrapWidget::topWidget() const { if (_topTabs) { - return _topTabs; + return _topTabsBackground; } return _topBar; } -int WrapWidget::topHeightAddition() const { - return _topTabs ? -st::lineWidth : 0; -} - -int WrapWidget::topHeight() const { - return topWidget()->height() + topHeightAddition(); -} - -rpl::producer WrapWidget::topHeightValue() const { - using namespace rpl::mappers; - return topWidget()->heightValue() - | rpl::map($1 + topHeightAddition()); -} - void WrapWidget::showContent(object_ptr content) { _content = std::move(content); _content->show(); - _topShadow->raise(); - if (_topTabs) { - _topTabs->raise(); - } - finishShowContent(); } @@ -218,10 +205,12 @@ void WrapWidget::finishShowContent() { updateContentGeometry(); _desiredHeights.fire(desiredHeightForContent()); _desiredShadowVisibilities.fire(_content->desiredShadowVisibility()); + _topShadow->raise(); + _topShadow->finishAnimating(); if (_topTabs) { + _topTabs->raise(); _topTabs->finishAnimating(); } - _topShadow->finishAnimating(); } rpl::producer WrapWidget::topShadowToggledValue() const { @@ -236,7 +225,7 @@ rpl::producer WrapWidget::desiredHeightForContent() const { using namespace rpl::mappers; return rpl::combine( _content->desiredHeightValue(), - topHeightValue(), + topWidget()->heightValue(), $1 + $2); } @@ -282,17 +271,23 @@ bool WrapWidget::hasTopBarShadow() const { QPixmap WrapWidget::grabForShowAnimation( const Window::SectionSlideParams ¶ms) { - if (params.withTopBarShadow) _topShadow->hide(anim::type::instant); + if (params.withTopBarShadow) { + _topShadow->setVisible(false); + } else { + _topShadow->toggle(_topShadow->toggled(), anim::type::instant); + } auto result = myGrab(this); - if (params.withTopBarShadow) _topShadow->show(anim::type::instant); + if (params.withTopBarShadow) _topShadow->setVisible(true); return result; } -void WrapWidget::setInnerFocus() { +void WrapWidget::doSetInnerFocus() { _content->setInnerFocus(); } -void WrapWidget::showFinished() { +void WrapWidget::showFinishedHook() { + // Restore shadow visibility after showChildren() call. + _topShadow->toggle(_topShadow->toggled(), anim::type::instant); } bool WrapWidget::showInternal( @@ -328,7 +323,7 @@ void WrapWidget::saveState(not_null memento) { } QRect WrapWidget::contentGeometry() const { - return rect().marginsRemoved({ 0, topHeight(), 0, 0 }); + return rect().marginsRemoved({ 0, topWidget()->height(), 0, 0 }); } void WrapWidget::showNewContent( @@ -336,14 +331,15 @@ void WrapWidget::showNewContent( const Window::SectionShow ¶ms) { auto saveToStack = (_content != nullptr) && (params.way == Window::SectionShow::Way::Forward); - auto showAnimated = (_content != nullptr) + auto needAnimation = (_content != nullptr) && (params.animated != anim::type::instant); - if (showAnimated) { + auto animationParams = SectionSlideParams(); + if (needAnimation) { auto newContent = createContent(memento); - auto params = SectionSlideParams(); -// params.withTopBarShadow = newContent; - } else { - + animationParams.withTopBarShadow = hasTopBarShadow() + && newContent->hasTopBarShadow(); + animationParams.oldContentCache = grabForShowAnimation( + animationParams); } if (saveToStack) { auto item = StackItem(); @@ -356,6 +352,13 @@ void WrapWidget::showNewContent( _historyStack.clear(); } showNewContent(memento); + if (animationParams) { + showAnimated( + saveToStack + ? SlideDirection::FromRight + : SlideDirection::FromLeft, + animationParams); + } } void WrapWidget::showNewContent(not_null memento) { @@ -368,6 +371,7 @@ void WrapWidget::setupTabs(Tab tab) { _tab = tab; if (_tab == Tab::None) { _topTabs.destroy(); + _topTabsBackground.destroy(); } else if (!_topTabs) { createTabs(); } else { @@ -376,23 +380,25 @@ void WrapWidget::setupTabs(Tab tab) { } void WrapWidget::resizeEvent(QResizeEvent *e) { - topWidget()->resizeToWidth(width()); + if (_topTabs) { + _topTabs->resizeToWidth(width()); + _topTabsBackground->resize( + width(), + _topTabs->height() - st::lineWidth); + } else if (_topBar) { + _topBar->resizeToWidth(width()); + } updateContentGeometry(); } void WrapWidget::updateContentGeometry() { if (_content) { _topShadow->resizeToWidth(width()); - _topShadow->moveToLeft(0, topHeight()); + _topShadow->moveToLeft(0, topWidget()->height()); _content->setGeometry(contentGeometry()); } } -void WrapWidget::paintEvent(QPaintEvent *e) { - Painter p(this); - p.fillRect(e->rect(), st::profileBg); -} - bool WrapWidget::wheelEventFromFloatPlayer(QEvent *e) { return _content->wheelEventFromFloatPlayer(e); } diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h index b730ef7615..9b9ab97e1b 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.h +++ b/Telegram/SourceFiles/info/info_wrap_widget.h @@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Ui { class SettingsSlider; class FadeShadow; +class PlainShadow; } // namespace Ui namespace Window { @@ -86,7 +87,7 @@ private: }; -class WrapWidget final : public Ui::RpWidget { +class WrapWidget final : public Window::SectionWidget { public: WrapWidget( QWidget *parent, @@ -95,39 +96,38 @@ public: not_null memento); not_null peer() const; + PeerData *peerForDialogs() const override { + return peer(); + } + Wrap wrap() const; void setWrap(Wrap wrap); - bool hasTopBarShadow() const; + bool hasTopBarShadow() const override; QPixmap grabForShowAnimation( - const Window::SectionSlideParams ¶ms); + const Window::SectionSlideParams ¶ms) override; bool showInternal( not_null memento, - const Window::SectionShow ¶ms); - std::unique_ptr createMemento(); + const Window::SectionShow ¶ms) override; + std::unique_ptr createMemento() override; rpl::producer desiredHeightValue() const; - void setInnerFocus(); - void showFinished(); - void updateInternalState(not_null memento); void saveState(not_null memento); // Float player interface. - bool wheelEventFromFloatPlayer(QEvent *e); - QRect rectForFloatPlayer() const; + bool wheelEventFromFloatPlayer(QEvent *e) override; + QRect rectForFloatPlayer() const override; ~WrapWidget(); protected: void resizeEvent(QResizeEvent *e); - void paintEvent(QPaintEvent *e); - not_null controller() const { - return _controller; - } + void doSetInnerFocus() override; + void showFinishedHook() override; private: using SlideDirection = Window::SlideDirection; @@ -153,9 +153,6 @@ private: PeerId peerId); not_null topWidget() const; - int topHeightAddition() const; - int topHeight() const; - rpl::producer topHeightValue() const; QRect contentGeometry() const; rpl::producer desiredHeightForContent() const; @@ -172,8 +169,8 @@ private: not_null memento); rpl::variable _wrap; - not_null _controller; object_ptr _content = { nullptr }; + object_ptr _topTabsBackground = { nullptr }; object_ptr _topTabs = { nullptr }; object_ptr _topBar = { nullptr }; object_ptr _topShadow; diff --git a/Telegram/SourceFiles/info/profile/info_profile_button.cpp b/Telegram/SourceFiles/info/profile/info_profile_button.cpp index 038ef8385d..dd0da70c9e 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_button.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_button.cpp @@ -51,10 +51,9 @@ Button *Button::toggleOn(rpl::producer &&toggled) { isOver() ? _st.toggleOver : _st.toggle, false, [this] { rtlupdate(toggleRect()); }); - clicks() - | rpl::start_with_next([this] { - _toggle->setCheckedAnimated(!_toggle->checked()); - }, lifetime()); + addClickHandler([this] { + _toggle->setCheckedAnimated(!_toggle->checked()); + }); std::move(toggled) | rpl::start_with_next([this](bool toggled) { _toggle->setCheckedAnimated(toggled); diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index e36ec2cda0..c8c3c62fbc 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -204,14 +204,13 @@ object_ptr InnerWidget::setupMuteToggle( st::infoNotificationsButton); result->toggleOn( NotificationsEnabledValue(_peer) - )->clicks() - | rpl::start_with_next([this] { - App::main()->updateNotifySetting( - _peer, - _peer->isMuted() - ? NotifySettingSetNotify - : NotifySettingSetMuted); - }, result->lifetime()); + )->addClickHandler([this] { + App::main()->updateNotifySetting( + _peer, + _peer->isMuted() + ? NotifySettingSetNotify + : NotifySettingSetMuted); + }); object_ptr( result, st::infoIconNotifications, @@ -240,26 +239,24 @@ void InnerWidget::setupUserButtons( )->toggleOn( _controller->historyPeer.value() | rpl::map($1 != user) - )->entity()->clicks() - | rpl::start_with_next([this, user] { - _controller->showPeerHistory( - user, - Window::SectionShow::Way::Forward); - }, wrap->lifetime()); + )->entity()->addClickHandler([this, user] { + _controller->showPeerHistory( + user, + Window::SectionShow::Way::Forward); + }); addButton( Lang::Viewer(lng_info_add_as_contact) | ToUpperValue() )->toggleOn( CanAddContactValue(user) - )->entity()->clicks() - | rpl::start_with_next([user] { - auto firstName = user->firstName; - auto lastName = user->lastName; - auto phone = user->phone().isEmpty() - ? App::phoneFromSharedContact(user->bareId()) - : user->phone(); - Ui::show(Box(firstName, lastName, phone)); - }, wrap->lifetime()); + )->entity()->addClickHandler([user] { + auto firstName = user->firstName; + auto lastName = user->lastName; + auto phone = user->phone().isEmpty() + ? App::phoneFromSharedContact(user->bareId()) + : user->phone(); + Ui::show(Box(firstName, lastName, phone)); + }); topSkip->toggleOn(std::move(tracker).atLeastOneShownValue()); } @@ -311,10 +308,9 @@ object_ptr InnerWidget::setupSharedMedia( [phrase = mediaText(type)](int count) { return phrase(lt_count, count); } - )->entity()->clicks() - | rpl::start_with_next([peer = _peer, type] { - SharedMediaShowOverview(type, App::history(peer)); - }, content->lifetime()); + )->entity()->addClickHandler([peer = _peer, type] { + SharedMediaShowOverview(type, App::history(peer)); + }); }; auto addCommonGroupsButton = [&](not_null user) { return addButton( @@ -322,12 +318,11 @@ object_ptr InnerWidget::setupSharedMedia( [](int count) { return lng_profile_common_groups(lt_count, count); } - )->entity()->clicks() - | rpl::start_with_next([this, peer = _peer] { - _controller->showSection( - ::Profile::CommonGroups::SectionMemento( - peer->asUser())); - }, content->lifetime()); + )->entity()->addClickHandler([this, peer = _peer] { + _controller->showSection( + ::Profile::CommonGroups::SectionMemento( + peer->asUser())); + }); }; addMediaButton(MediaType::Photo); addMediaButton(MediaType::Video); @@ -388,10 +383,7 @@ object_ptr InnerWidget::setupUserActions( st)) )->toggleOn( std::move(toggleOn) - )->entity()->clicks() - | rpl::start_with_next([callback = std::move(callback)] { - callback(); - }, result->lifetime()); + )->entity()->addClickHandler(std::move(callback)); }; addButton( diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.cpp b/Telegram/SourceFiles/info/profile/info_profile_members.cpp index 102042407c..d4d739cb0f 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_members.cpp @@ -117,24 +117,21 @@ void Members::setupButtons() { newWidth); }, _addMember->lifetime()); _addMember->showOn(rpl::duplicate(addMemberShown)); - _addMember->clicks() // TODO throttle(ripple duration) - | rpl::start_with_next([this] { - this->addMember(); - }, _addMember->lifetime()); + _addMember->addClickHandler([this] { // TODO throttle(ripple duration) + this->addMember(); + }); auto searchShown = MembersCountValue(_peer) | rpl::map($1 >= kEnableSearchMembersAfterCount) | rpl::distinct_until_changed() | rpl::start_spawning(lifetime()); _search->showOn(rpl::duplicate(searchShown)); - _search->clicks() - | rpl::start_with_next([this] { - this->showSearch(); - }, _search->lifetime()); - _cancelSearch->clicks() - | rpl::start_with_next([this] { - this->cancelSearch(); - }, _cancelSearch->lifetime()); + _search->addClickHandler([this] { + this->showSearch(); + }); + _cancelSearch->addClickHandler([this] { + this->cancelSearch(); + }); rpl::combine( std::move(addMemberShown), diff --git a/Telegram/SourceFiles/profile/profile_common_groups_section.cpp b/Telegram/SourceFiles/profile/profile_common_groups_section.cpp index 22e8776567..7059597ecd 100644 --- a/Telegram/SourceFiles/profile/profile_common_groups_section.cpp +++ b/Telegram/SourceFiles/profile/profile_common_groups_section.cpp @@ -310,6 +310,8 @@ void InnerWidget::mouseMoveEvent(QMouseEvent *e) { } void InnerWidget::mouseReleaseEvent(QMouseEvent *e) { + setCursor(_selected ? style::cur_pointer : style::cur_default); + updateRow(_selected); updateRow(_pressed); auto pressed = std::exchange(_pressed, -1); if (pressed >= 0 && pressed < _items.size()) { @@ -322,8 +324,6 @@ void InnerWidget::mouseReleaseEvent(QMouseEvent *e) { Window::SectionShow::Way::Forward); } } - setCursor(_selected ? style::cur_pointer : style::cur_default); - updateRow(_selected); } InnerWidget::Item *InnerWidget::computeItem(PeerData *group) { diff --git a/Telegram/SourceFiles/ui/effects/fade_animation.cpp b/Telegram/SourceFiles/ui/effects/fade_animation.cpp index 01757753bd..8a308d95b7 100644 --- a/Telegram/SourceFiles/ui/effects/fade_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/fade_animation.cpp @@ -118,9 +118,6 @@ void FadeAnimation::stopAnimation() { } if (_visible == _widget->isHidden()) { _widget->setVisible(_visible); - if (_visible) { - _widget->showChildren(); - } } } diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index b19386c1cb..e7f05ce0af 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -84,8 +84,6 @@ void SectionWidget::showFast() { } void SectionWidget::paintEvent(QPaintEvent *e) { - if (Ui::skipPaintEvent(this, e)) return; - if (_showAnimation) { Painter p(this); _showAnimation->paintContents(p, e->rect()); diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 404e5f7699..68c4c806d1 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -396,6 +396,7 @@ void TopBarWidget::updateControlsVisibility() { _call->hide(); _info->hide(); _menuToggle->hide(); + _infoToggle->hide(); _menu.destroy(); } if (_membersShowArea) { diff --git a/Telegram/SourceFiles/window/window_slide_animation.cpp b/Telegram/SourceFiles/window/window_slide_animation.cpp index 1bde0556fa..a2b4a26868 100644 --- a/Telegram/SourceFiles/window/window_slide_animation.cpp +++ b/Telegram/SourceFiles/window/window_slide_animation.cpp @@ -72,7 +72,12 @@ void SlideAnimation::start() { auto delta = st::slideShift; auto fromLeft = (_direction == SlideDirection::FromLeft); if (fromLeft) std::swap(_cacheUnder, _cacheOver); - _animation.start([this] { animationCallback(); }, fromLeft ? 1. : 0., fromLeft ? 0. : 1., st::slideDuration, transition()); + _animation.start( + [this] { animationCallback(); }, + fromLeft ? 1. : 0., + fromLeft ? 0. : 1., + st::slideDuration, + transition()); _repaintCallback(); }