/* 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-2017 John Preston, https://desktop.telegram.org */ #include "info/info_wrap_widget.h" #include #include #include "info/profile/info_profile_widget.h" #include "info/media/info_media_widget.h" #include "info/info_content_widget.h" #include "info/info_memento.h" #include "info/info_top_bar.h" #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/wrap/fade_wrap.h" #include "window/window_controller.h" #include "window/window_slide_animation.h" #include "auth_session.h" #include "lang/lang_keys.h" #include "styles/style_info.h" #include "styles/style_profile.h" namespace Info { struct WrapWidget::StackItem { std::unique_ptr section; std::unique_ptr anotherTab; }; WrapWidget::WrapWidget( QWidget *parent, not_null controller, Wrap wrap, not_null memento) : SectionWidget(parent, controller) , _wrap(wrap) , _topShadow(this) { _topShadow->toggleOn(topShadowToggledValue()); showNewContent(memento->content()); } not_null WrapWidget::peer() const { return _content->peer(); } Wrap WrapWidget::wrap() const { return _wrap.current(); } void WrapWidget::setWrap(Wrap wrap) { if (_wrap.current() != wrap) { _wrap = wrap; setupTop(_content->section(), _content->peer()->id); finishShowContent(); } } void WrapWidget::createTabs() { _topTabs.create(this, st::infoTabs); auto sections = QStringList(); sections.push_back(lang(lng_profile_info_section)); sections.push_back(lang(lng_info_tab_media)); _topTabs->setSections(sections); _topTabs->sectionActivated() | rpl::map([](int index) { return static_cast(index); }) | rpl::start_with_next( [this](Tab tab) { showTab(tab); }, lifetime()); _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) { showContent(createContent(tab)); } void WrapWidget::setupTabbedTop(const Section §ion) { switch (section.type()) { case Section::Type::Profile: setupTabs(Tab::Profile); break; case Section::Type::Media: switch (section.mediaType()) { case Section::MediaType::Photo: case Section::MediaType::Video: case Section::MediaType::File: setupTabs(Tab::Media); break; default: setupTabs(Tab::None); break; } break; case Section::Type::CommonGroups: setupTabs(Tab::None); break; } } void WrapWidget::setupTop( const Section §ion, PeerId peerId) { if (wrap() == Wrap::Side && _historyStack.empty()) { setupTabbedTop(section); } else { setupTabs(Tab::None); } if (_topTabs) { _topBar.destroy(); } else { createTopBar(section, peerId); } } void WrapWidget::createTopBar( const Section §ion, PeerId peerId) { _topBar.create( this, (wrap() == Wrap::Layer) ? st::infoLayerTopBar : st::infoTopBar); _topBar->setTitle(TitleValue( section, peerId)); if (wrap() != Wrap::Layer || !_historyStack.empty()) { _topBar->enableBackButton(true); _topBar->backRequest() | rpl::start_with_next([this] { showBackFromStack(); }, _topBar->lifetime()); } if (wrap() == Wrap::Layer) { auto close = _topBar->addButton(object_ptr( _topBar, st::infoLayerTopBarClose)); close->addClickHandler([this] { controller()->hideSpecialLayer(); }); } _topBar->move(0, 0); _topBar->resizeToWidth(width()); _topBar->show(); } void WrapWidget::showBackFromStack() { auto params = Window::SectionShow( Window::SectionShow::Way::Backward); if (!_historyStack.empty()) { auto last = std::move(_historyStack.back()); _historyStack.pop_back(); _anotherTabMemento = std::move(last.anotherTab); showNewContent( last.section.get(), params); } else { controller()->showBackFromStack(params); } } not_null WrapWidget::topWidget() const { if (_topTabs) { return _topTabsBackground; } return _topBar; } void WrapWidget::showContent(object_ptr content) { _content = std::move(content); _content->show(); finishShowContent(); } void WrapWidget::finishShowContent() { updateContentGeometry(); _desiredHeights.fire(desiredHeightForContent()); _desiredShadowVisibilities.fire(_content->desiredShadowVisibility()); _topShadow->raise(); _topShadow->finishAnimating(); if (_topTabs) { _topTabs->raise(); _topTabs->finishAnimating(); } } rpl::producer WrapWidget::topShadowToggledValue() const { using namespace rpl::mappers; return rpl::combine( _wrap.value(), _desiredShadowVisibilities.events() | rpl::flatten_latest(), ($1 == Wrap::Side) || $2); } rpl::producer WrapWidget::desiredHeightForContent() const { using namespace rpl::mappers; return rpl::combine( _content->desiredHeightValue(), topWidget()->heightValue(), $1 + $2); } object_ptr WrapWidget::createContent(Tab tab) { switch (tab) { case Tab::Profile: return createProfileWidget(); case Tab::Media: return createMediaWidget(); } Unexpected("Tab value in Info::WrapWidget::createInner()"); } object_ptr WrapWidget::createProfileWidget() { auto result = object_ptr( this, _wrap.value(), controller(), _content->peer()); return result; } object_ptr WrapWidget::createMediaWidget() { auto result = object_ptr( this, _wrap.value(), controller(), _content->peer(), Media::Widget::Type::Photo); return result; } object_ptr WrapWidget::createContent( not_null memento) { return memento->createWidget( this, _wrap.value(), controller(), contentGeometry()); } bool WrapWidget::hasTopBarShadow() const { return _topShadow->toggled(); } QPixmap WrapWidget::grabForShowAnimation( const Window::SectionSlideParams ¶ms) { if (params.withTopBarShadow) { _topShadow->setVisible(false); } else { _topShadow->toggle(_topShadow->toggled(), anim::type::instant); } auto result = myGrab(this); if (params.withTopBarShadow) _topShadow->setVisible(true); return result; } void WrapWidget::doSetInnerFocus() { _content->setInnerFocus(); } void WrapWidget::showFinishedHook() { // Restore shadow visibility after showChildren() call. _topShadow->toggle(_topShadow->toggled(), anim::type::instant); } bool WrapWidget::showInternal( not_null memento, const Window::SectionShow ¶ms) { if (auto infoMemento = dynamic_cast(memento.get())) { auto content = infoMemento->content(); if (!_content->showInternal(content)) { showNewContent( content, params); } return true; } return false; } std::unique_ptr WrapWidget::createMemento() { auto result = std::make_unique(_content->peer()->id); saveState(result.get()); return std::move(result); } rpl::producer WrapWidget::desiredHeightValue() const { return rpl::single(desiredHeightForContent()) | rpl::then(_desiredHeights.events()) | rpl::flatten_latest(); } void WrapWidget::saveState(not_null memento) { memento->setInner(_content->createMemento()); } QRect WrapWidget::contentGeometry() const { return rect().marginsRemoved({ 0, topWidget()->height(), 0, 0 }); } void WrapWidget::showNewContent( not_null memento, const Window::SectionShow ¶ms) { auto saveToStack = (_content != nullptr) && (params.way == Window::SectionShow::Way::Forward); auto needAnimation = (_content != nullptr) && (params.animated != anim::type::instant); auto animationParams = SectionSlideParams(); if (needAnimation) { auto newContent = createContent(memento); animationParams.withTopBarShadow = hasTopBarShadow() && newContent->hasTopBarShadow(); animationParams.oldContentCache = grabForShowAnimation( animationParams); } if (saveToStack) { auto item = StackItem(); item.section = _content->createMemento(); if (_anotherTabMemento) { item.anotherTab = std::move(_anotherTabMemento); } _historyStack.push_back(std::move(item)); } else if (params.way == Window::SectionShow::Way::ClearStack) { _historyStack.clear(); } showNewContent(memento); if (animationParams) { showAnimated( saveToStack ? SlideDirection::FromRight : SlideDirection::FromLeft, animationParams); } } void WrapWidget::showNewContent(not_null memento) { // Validates contentGeometry(). setupTop(memento->section(), memento->peerId()); showContent(createContent(memento)); } void WrapWidget::setupTabs(Tab tab) { _tab = tab; if (_tab == Tab::None) { _topTabs.destroy(); _topTabsBackground.destroy(); } else if (!_topTabs) { createTabs(); } else { _topTabs->setActiveSection(static_cast(tab)); } } void WrapWidget::resizeEvent(QResizeEvent *e) { 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, topWidget()->height()); _content->setGeometry(contentGeometry()); } } bool WrapWidget::wheelEventFromFloatPlayer(QEvent *e) { return _content->wheelEventFromFloatPlayer(e); } QRect WrapWidget::rectForFloatPlayer() const { return _content->rectForFloatPlayer(); } WrapWidget::~WrapWidget() = default; } // namespace Info