"Fossies" - the Fresh Open Source Software Archive

Member "tdesktop-2.6.1/Telegram/SourceFiles/info/info_wrap_widget.cpp" (24 Feb 2021, 30222 Bytes) of package /linux/misc/tdesktop-2.6.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "info_wrap_widget.cpp" see the Fossies "Dox" file reference documentation.

    1 /*
    2 This file is part of Telegram Desktop,
    3 the official desktop application for the Telegram messaging service.
    4 
    5 For license and copyright information please follow this link:
    6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
    7 */
    8 #include "info/info_wrap_widget.h"
    9 
   10 #include "info/profile/info_profile_widget.h"
   11 #include "info/profile/info_profile_values.h"
   12 #include "info/media/info_media_widget.h"
   13 #include "info/info_content_widget.h"
   14 #include "info/info_controller.h"
   15 #include "info/info_memento.h"
   16 #include "info/info_top_bar.h"
   17 #include "ui/widgets/discrete_sliders.h"
   18 #include "ui/widgets/buttons.h"
   19 #include "ui/widgets/shadow.h"
   20 #include "ui/widgets/dropdown_menu.h"
   21 #include "ui/wrap/fade_wrap.h"
   22 #include "ui/search_field_controller.h"
   23 #include "core/application.h"
   24 #include "calls/calls_instance.h"
   25 #include "core/shortcuts.h"
   26 #include "window/window_session_controller.h"
   27 #include "window/window_slide_animation.h"
   28 #include "window/window_peer_menu.h"
   29 #include "boxes/peer_list_box.h"
   30 #include "boxes/confirm_box.h"
   31 #include "main/main_session.h"
   32 #include "mtproto/mtproto_config.h"
   33 #include "data/data_session.h"
   34 #include "data/data_changes.h"
   35 #include "data/data_user.h"
   36 #include "mainwidget.h"
   37 #include "lang/lang_keys.h"
   38 #include "styles/style_info.h"
   39 #include "styles/style_profile.h"
   40 
   41 namespace Info {
   42 namespace {
   43 
   44 const style::InfoTopBar &TopBarStyle(Wrap wrap) {
   45     return (wrap == Wrap::Layer)
   46         ? st::infoLayerTopBar
   47         : st::infoTopBar;
   48 }
   49 
   50 } // namespace
   51 
   52 struct WrapWidget::StackItem {
   53     std::shared_ptr<ContentMemento> section;
   54 //  std::shared_ptr<ContentMemento> anotherTab;
   55 };
   56 
   57 WrapWidget::WrapWidget(
   58     QWidget *parent,
   59     not_null<Window::SessionController*> window,
   60     Wrap wrap,
   61     not_null<Memento*> memento)
   62 : SectionWidget(parent, window)
   63 , _wrap(wrap)
   64 , _controller(createController(window, memento->content()))
   65 , _topShadow(this) {
   66     _topShadow->toggleOn(
   67         topShadowToggledValue(
   68         ) | rpl::filter([](bool shown) {
   69             return true;
   70         }));
   71     _wrap.changes(
   72     ) | rpl::start_with_next([this] {
   73         setupTop();
   74         finishShowContent();
   75     }, lifetime());
   76     selectedListValue(
   77     ) | rpl::start_with_next([this](SelectedItems &&items) {
   78         InvokeQueued(this, [this, items = std::move(items)]() mutable {
   79             if (_topBar) _topBar->setSelectedItems(std::move(items));
   80         });
   81     }, lifetime());
   82     restoreHistoryStack(memento->takeStack());
   83 }
   84 
   85 void WrapWidget::setupShortcuts() {
   86     Shortcuts::Requests(
   87     ) | rpl::filter([=] {
   88         return requireTopBarSearch();
   89     }) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
   90         using Command = Shortcuts::Command;
   91         request->check(Command::Search) && request->handle([=] {
   92             _topBar->showSearch();
   93             return true;
   94         });
   95     }, lifetime());
   96 }
   97 
   98 void WrapWidget::restoreHistoryStack(
   99         std::vector<std::shared_ptr<ContentMemento>> stack) {
  100     Expects(!stack.empty());
  101     Expects(!hasStackHistory());
  102 
  103     auto content = std::move(stack.back());
  104     stack.pop_back();
  105     if (!stack.empty()) {
  106         _historyStack.reserve(stack.size());
  107         for (auto &stackItem : stack) {
  108             auto item = StackItem();
  109             item.section = std::move(stackItem);
  110             _historyStack.push_back(std::move(item));
  111         }
  112     }
  113 
  114     startInjectingActivePeerProfiles();
  115 
  116     showNewContent(content.get());
  117 }
  118 
  119 void WrapWidget::startInjectingActivePeerProfiles() {
  120     using namespace rpl::mappers;
  121     rpl::combine(
  122         _wrap.value(),
  123         _controller->parentController()->activeChatValue()
  124     ) | rpl::filter(
  125         (_1 == Wrap::Side) && _2
  126     ) | rpl::map(
  127         _2
  128     ) | rpl::start_with_next([this](Dialogs::Key key) {
  129         injectActiveProfile(key);
  130     }, lifetime());
  131 
  132 }
  133 
  134 void WrapWidget::injectActiveProfile(Dialogs::Key key) {
  135     if (const auto peer = key.peer()) {
  136         injectActivePeerProfile(peer);
  137     //} else if (const auto feed = key.feed()) { // #feed
  138     //  injectActiveFeedProfile(feed);
  139     }
  140 }
  141 
  142 void WrapWidget::injectActivePeerProfile(not_null<PeerData*> peer) {
  143     const auto firstPeer = hasStackHistory()
  144         ? _historyStack.front().section->peer()
  145         : _controller->peer();
  146     const auto firstSectionType = hasStackHistory()
  147         ? _historyStack.front().section->section().type()
  148         : _controller->section().type();
  149     const auto firstSectionMediaType = [&] {
  150         if (firstSectionType == Section::Type::Profile) {
  151             return Section::MediaType::kCount;
  152         }
  153         return hasStackHistory()
  154             ? _historyStack.front().section->section().mediaType()
  155             : _controller->section().mediaType();
  156     }();
  157     const auto expectedType = peer->sharedMediaInfo()
  158         ? Section::Type::Media
  159         : Section::Type::Profile;
  160     const auto expectedMediaType = peer->sharedMediaInfo()
  161         ? Section::MediaType::Photo
  162         : Section::MediaType::kCount;
  163     if (firstSectionType != expectedType
  164         || firstSectionMediaType != expectedMediaType
  165         || firstPeer != peer) {
  166         auto section = peer->sharedMediaInfo()
  167             ? Section(Section::MediaType::Photo)
  168             : Section(Section::Type::Profile);
  169         injectActiveProfileMemento(std::move(
  170             Memento(peer, section).takeStack().front()));
  171     }
  172 }
  173 // // #feed
  174 //void WrapWidget::injectActiveFeedProfile(not_null<Data::Feed*> feed) {
  175 //  const auto firstFeed = hasStackHistory()
  176 //      ? _historyStack.front().section->feed()
  177 //      : _controller->feed();
  178 //  const auto firstSectionType = hasStackHistory()
  179 //      ? _historyStack.front().section->section().type()
  180 //      : _controller->section().type();
  181 //  const auto expectedType = Section::Type::Profile;
  182 //  if (firstSectionType != expectedType
  183 //      || firstFeed != feed) {
  184 //      auto section = Section(Section::Type::Profile);
  185 //      injectActiveProfileMemento(std::move(
  186 //          Memento(feed, section).takeStack().front()));
  187 //  }
  188 //}
  189 
  190 void WrapWidget::injectActiveProfileMemento(
  191         std::shared_ptr<ContentMemento> memento) {
  192     auto injected = StackItem();
  193     injected.section = std::move(memento);
  194     _historyStack.insert(
  195         _historyStack.begin(),
  196         std::move(injected));
  197     if (_content) {
  198         setupTop();
  199         finishShowContent();
  200     }
  201 }
  202 
  203 std::unique_ptr<Controller> WrapWidget::createController(
  204         not_null<Window::SessionController*> window,
  205         not_null<ContentMemento*> memento) {
  206     auto result = std::make_unique<Controller>(
  207         this,
  208         window,
  209         memento);
  210     return result;
  211 }
  212 
  213 Key WrapWidget::key() const {
  214     return _controller->key();
  215 }
  216 
  217 Dialogs::RowDescriptor WrapWidget::activeChat() const {
  218     if (const auto peer = key().peer()) {
  219         return Dialogs::RowDescriptor(peer->owner().history(peer), FullMsgId());
  220     //} else if (const auto feed = key().feed()) { // #feed
  221     //  return Dialogs::RowDescriptor(feed, FullMsgId());
  222     } else if (key().settingsSelf() || key().poll()) {
  223         return Dialogs::RowDescriptor();
  224     }
  225     Unexpected("Owner in WrapWidget::activeChat().");
  226 }
  227 
  228 // This was done for tabs support.
  229 //
  230 //void WrapWidget::createTabs() {
  231 //  _topTabs.create(this, st::infoTabs);
  232 //  auto sections = QStringList();
  233 //  sections.push_back(tr::lng_profile_info_section(tr::now).toUpper());
  234 //  sections.push_back(tr::lng_info_tab_media(tr::now).toUpper());
  235 //  _topTabs->setSections(sections);
  236 //  _topTabs->setActiveSection(static_cast<int>(_tab));
  237 //  _topTabs->finishAnimating();
  238 //
  239 //  _topTabs->sectionActivated(
  240 //  ) | rpl::map([](int index) {
  241 //      return static_cast<Tab>(index);
  242 //  }) | rpl::start_with_next(
  243 //      [this](Tab tab) { showTab(tab); },
  244 //      lifetime());
  245 //
  246 //  _topTabs->move(0, 0);
  247 //  _topTabs->resizeToWidth(width());
  248 //  _topTabs->show();
  249 //
  250 //  _topTabsBackground.create(this, st::profileBg);
  251 //  _topTabsBackground->setAttribute(Qt::WA_OpaquePaintEvent);
  252 //
  253 //  _topTabsBackground->move(0, 0);
  254 //  _topTabsBackground->resize(
  255 //      width(),
  256 //      _topTabs->height() - st::lineWidth);
  257 //  _topTabsBackground->show();
  258 //}
  259 
  260 void WrapWidget::forceContentRepaint() {
  261     // WA_OpaquePaintEvent on TopBar creates render glitches when
  262     // animating the LayerWidget's height :( Fixing by repainting.
  263 
  264     // This was done for tabs support.
  265     //
  266     //if (_topTabs) {
  267     //  _topTabsBackground->update();
  268     //}
  269 
  270     if (_topBar) {
  271         _topBar->update();
  272     }
  273     _content->update();
  274 }
  275 
  276 // This was done for tabs support.
  277 //
  278 //void WrapWidget::showTab(Tab tab) {
  279 //  if (_tab == tab) {
  280 //      return;
  281 //  }
  282 //  Expects(_content != nullptr);
  283 //  auto direction = (tab > _tab)
  284 //      ? SlideDirection::FromRight
  285 //      : SlideDirection::FromLeft;
  286 //  auto newAnotherMemento = _content->createMemento();
  287 //  if (!_anotherTabMemento) {
  288 //      _anotherTabMemento = createTabMemento(tab);
  289 //  }
  290 //  auto newController = createController(
  291 //      _controller->parentController(),
  292 //      _anotherTabMemento.get());
  293 //  auto newContent = createContent(
  294 //      _anotherTabMemento.get(),
  295 //      newController.get());
  296 //  auto animationParams = SectionSlideParams();
  297 ////    animationParams.withFade = (wrap() == Wrap::Layer);
  298 //  animationParams.withTabs = true;
  299 //  animationParams.withTopBarShadow = hasTopBarShadow()
  300 //          && newContent->hasTopBarShadow();
  301 //  animationParams.oldContentCache = grabForShowAnimation(
  302 //      animationParams);
  303 //
  304 //  _controller = std::move(newController);
  305 //  showContent(std::move(newContent));
  306 //
  307 //  showAnimated(direction, animationParams);
  308 //
  309 //  _anotherTabMemento = std::move(newAnotherMemento);
  310 //  _tab = tab;
  311 //}
  312 //
  313 //void WrapWidget::setupTabbedTop() {
  314 //  auto section = _controller->section();
  315 //  switch (section.type()) {
  316 //  case Section::Type::Profile:
  317 //      setupTabs(Tab::Profile);
  318 //      break;
  319 //  case Section::Type::Media:
  320 //      switch (section.mediaType()) {
  321 //      case Section::MediaType::Photo:
  322 //      case Section::MediaType::Video:
  323 //      case Section::MediaType::File:
  324 //          setupTabs(Tab::Media);
  325 //          break;
  326 //      default:
  327 //          setupTabs(Tab::None);
  328 //          break;
  329 //      }
  330 //      break;
  331 //  case Section::Type::CommonGroups:
  332 //  case Section::Type::Members:
  333 //      setupTabs(Tab::None);
  334 //      break;
  335 //  }
  336 //}
  337 
  338 void WrapWidget::setupTop() {
  339     // This was done for tabs support.
  340     //
  341     //if (wrap() == Wrap::Side && !hasStackHistory()) {
  342     //  setupTabbedTop();
  343     //} else {
  344     //  setupTabs(Tab::None);
  345     //}
  346     //if (_topTabs) {
  347     //  _topBar.destroy();
  348     //} else {
  349     //  createTopBar();
  350     //}
  351     createTopBar();
  352 }
  353 
  354 void WrapWidget::createTopBar() {
  355     const auto wrapValue = wrap();
  356     auto selectedItems = _topBar
  357         ? _topBar->takeSelectedItems()
  358         : SelectedItems(Section::MediaType::kCount);
  359     _topBar.create(
  360         this,
  361         _controller.get(),
  362         TopBarStyle(wrapValue),
  363         std::move(selectedItems));
  364     _topBar->cancelSelectionRequests(
  365     ) | rpl::start_with_next([this] {
  366         _content->cancelSelection();
  367     }, _topBar->lifetime());
  368 
  369     _topBar->setTitle(TitleValue(
  370         _controller->section(),
  371         _controller->key(),
  372         !hasStackHistory()));
  373     if (wrapValue == Wrap::Narrow || hasStackHistory()) {
  374         _topBar->enableBackButton();
  375         _topBar->backRequest(
  376         ) | rpl::start_with_next([=] {
  377             checkBeforeClose([=] { _controller->showBackFromStack(); });
  378         }, _topBar->lifetime());
  379     } else if (wrapValue == Wrap::Side) {
  380         auto close = _topBar->addButton(
  381             base::make_unique_q<Ui::IconButton>(
  382                 _topBar,
  383                 st::infoTopBarClose));
  384         close->addClickHandler([this] {
  385             _controller->parentController()->closeThirdSection();
  386         });
  387     }
  388     if (wrapValue == Wrap::Layer) {
  389         auto close = _topBar->addButton(
  390             base::make_unique_q<Ui::IconButton>(
  391                 _topBar,
  392                 st::infoLayerTopBarClose));
  393         close->addClickHandler([this] {
  394             checkBeforeClose([=] {
  395                 _controller->parentController()->hideSpecialLayer();
  396             });
  397         });
  398     } else if (requireTopBarSearch()) {
  399         auto search = _controller->searchFieldController();
  400         Assert(search != nullptr);
  401         setupShortcuts();
  402         _topBar->createSearchView(
  403             search,
  404             _controller->searchEnabledByContent(),
  405             _controller->takeSearchStartsFocused());
  406     }
  407     const auto section = _controller->section();
  408     if (section.type() == Section::Type::Profile
  409         && (wrapValue != Wrap::Side || hasStackHistory())) {
  410         addTopBarMenuButton();
  411         addProfileCallsButton();
  412 //      addProfileNotificationsButton();
  413     } else if (section.type() == Section::Type::Settings
  414         && (section.settingsType() == Section::SettingsType::Main
  415             || section.settingsType() == Section::SettingsType::Chat)) {
  416         addTopBarMenuButton();
  417     } else if (section.type() == Section::Type::Settings
  418         && section.settingsType() == Section::SettingsType::Information) {
  419         addContentSaveButton();
  420     }
  421 
  422     _topBar->lower();
  423     _topBar->resizeToWidth(width());
  424     _topBar->finishAnimating();
  425     _topBar->show();
  426 }
  427 
  428 void WrapWidget::checkBeforeClose(Fn<void()> close) {
  429     const auto confirmed = [=] {
  430         Ui::hideLayer();
  431         close();
  432     };
  433     if (_controller->canSaveChangesNow()) {
  434         Ui::show(Box<ConfirmBox>(
  435             tr::lng_settings_close_sure(tr::now),
  436             tr::lng_close(tr::now),
  437             confirmed));
  438     } else {
  439         confirmed();
  440     }
  441 }
  442 
  443 void WrapWidget::addTopBarMenuButton() {
  444     Expects(_topBar != nullptr);
  445 
  446     _topBarMenuToggle.reset(_topBar->addButton(
  447         base::make_unique_q<Ui::IconButton>(
  448             _topBar,
  449             (wrap() == Wrap::Layer
  450                 ? st::infoLayerTopBarMenu
  451                 : st::infoTopBarMenu))));
  452     _topBarMenuToggle->addClickHandler([this] {
  453         showTopBarMenu();
  454     });
  455 }
  456 
  457 void WrapWidget::addContentSaveButton() {
  458     Expects(_topBar != nullptr);
  459 
  460     _topBar->addButtonWithVisibility(
  461         base::make_unique_q<Ui::IconButton>(
  462             _topBar,
  463             (wrap() == Wrap::Layer
  464                 ? st::infoLayerTopBarSave
  465                 : st::infoTopBarSave)),
  466         _controller->canSaveChanges()
  467     )->addClickHandler([=] {
  468         _content->saveChanges(crl::guard(_content.data(), [=] {
  469             _controller->showBackFromStack();
  470         }));
  471     });
  472 }
  473 
  474 bool WrapWidget::closeByOutsideClick() const {
  475     return !_controller->canSaveChangesNow();
  476 }
  477 
  478 void WrapWidget::addProfileCallsButton() {
  479     Expects(_topBar != nullptr);
  480 
  481     const auto peer = key().peer();
  482     const auto user = peer ? peer->asUser() : nullptr;
  483     if (!user
  484         || user->sharedMediaInfo()
  485         || !user->session().serverConfig().phoneCallsEnabled.current()) {
  486         return;
  487     }
  488 
  489     user->session().changes().peerFlagsValue(
  490         user,
  491         Data::PeerUpdate::Flag::HasCalls
  492     ) | rpl::filter([=] {
  493         return user->hasCalls();
  494     }) | rpl::take(
  495         1
  496     ) | rpl::start_with_next([=] {
  497         _topBar->addButton(
  498             base::make_unique_q<Ui::IconButton>(
  499                 _topBar,
  500                 (wrap() == Wrap::Layer
  501                     ? st::infoLayerTopBarCall
  502                     : st::infoTopBarCall))
  503         )->addClickHandler([=] {
  504             Core::App().calls().startOutgoingCall(user, false);
  505         });
  506     }, _topBar->lifetime());
  507 
  508     if (user && user->callsStatus() == UserData::CallsStatus::Unknown) {
  509         user->updateFull();
  510     }
  511 }
  512 
  513 void WrapWidget::addProfileNotificationsButton() {
  514     Expects(_topBar != nullptr);
  515 
  516     const auto peer = key().peer();
  517     if (!peer) {
  518         return;
  519     }
  520     auto notifications = _topBar->addButton(
  521         base::make_unique_q<Ui::IconButton>(
  522             _topBar,
  523             (wrap() == Wrap::Layer
  524                 ? st::infoLayerTopBarNotifications
  525                 : st::infoTopBarNotifications)));
  526     notifications->addClickHandler([=] {
  527         const auto muteForSeconds = peer->owner().notifyIsMuted(peer)
  528             ? 0
  529             : Data::NotifySettings::kDefaultMutePeriod;
  530         peer->owner().updateNotifySettings(peer, muteForSeconds);
  531     });
  532     Profile::NotificationsEnabledValue(
  533         peer
  534     ) | rpl::start_with_next([notifications](bool enabled) {
  535         const auto iconOverride = enabled
  536             ? &st::infoNotificationsActive
  537             : nullptr;
  538         const auto rippleOverride = enabled
  539             ? &st::lightButtonBgOver
  540             : nullptr;
  541         notifications->setIconOverride(iconOverride, iconOverride);
  542         notifications->setRippleColorOverride(rippleOverride);
  543     }, notifications->lifetime());
  544 }
  545 
  546 void WrapWidget::showTopBarMenu() {
  547     if (_topBarMenu) {
  548         _topBarMenu->hideAnimated(
  549             Ui::InnerDropdown::HideOption::IgnoreShow);
  550         return;
  551     }
  552     _topBarMenu = base::make_unique_q<Ui::DropdownMenu>(this);
  553 
  554     _topBarMenu->setHiddenCallback([this] {
  555         InvokeQueued(this, [this] { _topBarMenu = nullptr; });
  556         if (auto toggle = _topBarMenuToggle.get()) {
  557             toggle->setForceRippled(false);
  558         }
  559     });
  560     _topBarMenu->setShowStartCallback([this] {
  561         if (auto toggle = _topBarMenuToggle.get()) {
  562             toggle->setForceRippled(true);
  563         }
  564     });
  565     _topBarMenu->setHideStartCallback([this] {
  566         if (auto toggle = _topBarMenuToggle.get()) {
  567             toggle->setForceRippled(false);
  568         }
  569     });
  570     _topBarMenuToggle->installEventFilter(_topBarMenu.get());
  571 
  572     const auto addAction = [=](
  573             const QString &text,
  574             Fn<void()> callback) {
  575         return _topBarMenu->addAction(text, std::move(callback));
  576     };
  577     if (const auto peer = key().peer()) {
  578         Window::FillDialogsEntryMenu(
  579             _controller->parentController(),
  580             Dialogs::EntryState{
  581                 .key = peer->owner().history(peer),
  582                 .section = Dialogs::EntryState::Section::Profile,
  583             },
  584             addAction);
  585     //} else if (const auto feed = key().feed()) { // #feed
  586     //  Window::FillFeedMenu(
  587     //      _controller->parentController(),
  588     //      feed,
  589     //      addAction,
  590     //      Window::PeerMenuSource::Profile);
  591     } else if (const auto self = key().settingsSelf()) {
  592         const auto showOther = [=](::Settings::Type type) {
  593             const auto controller = _controller.get();
  594             _topBarMenu = nullptr;
  595             controller->showSettings(type);
  596         };
  597         ::Settings::FillMenu(
  598             _controller->parentController(),
  599             _controller->section().settingsType(),
  600             showOther,
  601             addAction);
  602     } else {
  603         _topBarMenu = nullptr;
  604         return;
  605     }
  606     auto position = (wrap() == Wrap::Layer)
  607         ? st::infoLayerTopBarMenuPosition
  608         : st::infoTopBarMenuPosition;
  609     _topBarMenu->moveToRight(position.x(), position.y());
  610     _topBarMenu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
  611 }
  612 
  613 bool WrapWidget::requireTopBarSearch() const {
  614     if (!_controller->searchFieldController()) {
  615         return false;
  616     } else if (_controller->wrap() == Wrap::Layer
  617         || _controller->section().type() == Section::Type::Profile) {
  618         return false;
  619     } else if (hasStackHistory()) {
  620         return true;
  621     }
  622     // This was for top-level tabs support.
  623     //
  624     //auto section = _controller->section();
  625     //return (section.type() != Section::Type::Media)
  626     //  || !Media::TypeToTabIndex(section.mediaType()).has_value();
  627     return false;
  628 }
  629 
  630 bool WrapWidget::showBackFromStackInternal(
  631         const Window::SectionShow &params) {
  632     if (hasStackHistory()) {
  633         auto last = std::move(_historyStack.back());
  634         _historyStack.pop_back();
  635         showNewContent(
  636             last.section.get(),
  637             params.withWay(Window::SectionShow::Way::Backward));
  638         //_anotherTabMemento = std::move(last.anotherTab);
  639         return true;
  640     }
  641     return (wrap() == Wrap::Layer);
  642 }
  643 
  644 not_null<Ui::RpWidget*> WrapWidget::topWidget() const {
  645     // This was done for tabs support.
  646     //
  647     //if (_topTabs) {
  648     //  return _topTabsBackground;
  649     //}
  650     return _topBar;
  651 }
  652 
  653 void WrapWidget::showContent(object_ptr<ContentWidget> content) {
  654     if (auto old = std::exchange(_content, std::move(content))) {
  655         old->hide();
  656 
  657         // Content destructor may invoke closeBox() that will try to
  658         // start layer animation. If we won't detach old content from
  659         // its parent WrapWidget layer animation will be started with a
  660         // partially destructed grand-child widget and result in a crash.
  661         old->setParent(nullptr);
  662         old.destroy();
  663     }
  664     _content->show();
  665     _additionalScroll = 0;
  666     //_anotherTabMemento = nullptr;
  667     finishShowContent();
  668 }
  669 
  670 void WrapWidget::finishShowContent() {
  671     _content->setIsStackBottom(!hasStackHistory());
  672     updateContentGeometry();
  673     _desiredHeights.fire(desiredHeightForContent());
  674     _desiredShadowVisibilities.fire(_content->desiredShadowVisibility());
  675     _selectedLists.fire(_content->selectedListValue());
  676     _scrollTillBottomChanges.fire(_content->scrollTillBottomChanges());
  677     _topShadow->raise();
  678     _topShadow->finishAnimating();
  679     _contentChanges.fire({});
  680 
  681     // This was done for tabs support.
  682     //
  683     //if (_topTabs) {
  684     //  _topTabs->raise();
  685     //}
  686 }
  687 
  688 rpl::producer<bool> WrapWidget::topShadowToggledValue() const {
  689     // Allows always showing shadow for specific wrap value.
  690     // Was done for top level tabs support.
  691     //
  692     //using namespace rpl::mappers;
  693     //return rpl::combine(
  694     //  _controller->wrapValue(),
  695     //  _desiredShadowVisibilities.events() | rpl::flatten_latest(),
  696     //  (_1 == Wrap::Side) || _2);
  697     return _desiredShadowVisibilities.events()
  698         | rpl::flatten_latest();
  699 }
  700 
  701 rpl::producer<int> WrapWidget::desiredHeightForContent() const {
  702     using namespace rpl::mappers;
  703     return rpl::combine(
  704         _content->desiredHeightValue(),
  705         topWidget()->heightValue(),
  706         _1 + _2);
  707 }
  708 
  709 rpl::producer<SelectedItems> WrapWidget::selectedListValue() const {
  710     return _selectedLists.events() | rpl::flatten_latest();
  711 }
  712 
  713 // Was done for top level tabs support.
  714 //
  715 //std::shared_ptr<ContentMemento> WrapWidget::createTabMemento(
  716 //      Tab tab) {
  717 //  switch (tab) {
  718 //  case Tab::Profile: return std::make_shared<Profile::Memento>(
  719 //      _controller->peerId(),
  720 //      _controller->migratedPeerId());
  721 //  case Tab::Media: return std::make_shared<Media::Memento>(
  722 //      _controller->peerId(),
  723 //      _controller->migratedPeerId(),
  724 //      Media::Type::Photo);
  725 //  }
  726 //  Unexpected("Tab value in Info::WrapWidget::createInner()");
  727 //}
  728 
  729 object_ptr<ContentWidget> WrapWidget::createContent(
  730         not_null<ContentMemento*> memento,
  731         not_null<Controller*> controller) {
  732     return memento->createWidget(
  733         this,
  734         controller,
  735         contentGeometry());
  736 }
  737 
  738 // Was done for top level tabs support.
  739 //
  740 //void WrapWidget::convertProfileFromStackToTab() {
  741 //  if (!hasStackHistory()) {
  742 //      return;
  743 //  }
  744 //  auto &entry = _historyStack[0];
  745 //  if (entry.section->section().type() != Section::Type::Profile) {
  746 //      return;
  747 //  }
  748 //  auto convertInsideStack = (_historyStack.size() > 1);
  749 //  auto checkSection = convertInsideStack
  750 //      ? _historyStack[1].section->section()
  751 //      : _controller->section();
  752 //  auto &anotherMemento = convertInsideStack
  753 //      ? _historyStack[1].anotherTab
  754 //      : _anotherTabMemento;
  755 //  if (checkSection.type() != Section::Type::Media) {
  756 //      return;
  757 //  }
  758 //  if (!Info::Media::TypeToTabIndex(checkSection.mediaType())) {
  759 //      return;
  760 //  }
  761 //  anotherMemento = std::move(entry.section);
  762 //  _historyStack.erase(_historyStack.begin());
  763 //}
  764 
  765 rpl::producer<Wrap> WrapWidget::wrapValue() const {
  766     return _wrap.value();
  767 }
  768 
  769 void WrapWidget::setWrap(Wrap wrap) {
  770     // Was done for top level tabs support.
  771     //
  772     //if (_wrap.current() != Wrap::Side && wrap == Wrap::Side) {
  773     //  convertProfileFromStackToTab();
  774     //}
  775     _wrap = wrap;
  776 }
  777 
  778 rpl::producer<> WrapWidget::contentChanged() const {
  779     return _contentChanges.events();
  780 }
  781 
  782 bool WrapWidget::hasTopBarShadow() const {
  783     return _topShadow->toggled();
  784 }
  785 
  786 QPixmap WrapWidget::grabForShowAnimation(
  787         const Window::SectionSlideParams &params) {
  788     if (params.withTopBarShadow) {
  789         _topShadow->setVisible(false);
  790     } else {
  791         _topShadow->setVisible(_topShadow->toggled());
  792     }
  793     //if (params.withTabs && _topTabs) {
  794     //  _topTabs->hide();
  795     //}
  796     auto result = Ui::GrabWidget(this);
  797     if (params.withTopBarShadow) {
  798         _topShadow->setVisible(true);
  799     }
  800     //if (params.withTabs && _topTabs) {
  801     //  _topTabs->show();
  802     //}
  803     return result;
  804 }
  805 
  806 void WrapWidget::showAnimatedHook(
  807         const Window::SectionSlideParams &params) {
  808     //if (params.withTabs && _topTabs) {
  809     //  _topTabs->show();
  810     //  _topTabsBackground->show();
  811     //}
  812     if (params.withTopBarShadow) {
  813         _topShadow->setVisible(true);
  814     }
  815     _topBarSurrogate = createTopBarSurrogate(this);
  816 }
  817 
  818 void WrapWidget::doSetInnerFocus() {
  819     if (!_topBar->focusSearchField()) {
  820         _content->setInnerFocus();
  821     }
  822 }
  823 
  824 void WrapWidget::showFinishedHook() {
  825     // Restore shadow visibility after showChildren() call.
  826     _topShadow->toggle(_topShadow->toggled(), anim::type::instant);
  827     _topBarSurrogate.destroy();
  828 }
  829 
  830 bool WrapWidget::showInternal(
  831         not_null<Window::SectionMemento*> memento,
  832         const Window::SectionShow &params) {
  833     if (auto infoMemento = dynamic_cast<Memento*>(memento.get())) {
  834         if (!_controller || infoMemento->stackSize() > 1) {
  835             return false;
  836         }
  837         auto content = infoMemento->content();
  838         auto skipInternal = hasStackHistory()
  839             && (params.way == Window::SectionShow::Way::ClearStack);
  840         if (_controller->validateMementoPeer(content)) {
  841             if (!skipInternal && _content->showInternal(content)) {
  842                 highlightTopBar();
  843                 return true;
  844 
  845             // This was done for tabs support.
  846             //
  847             //} else if (_topTabs) {
  848             //  // If we open the profile being in the media tab.
  849             //  // Just switch back to the profile tab.
  850             //  auto type = content->section().type();
  851             //  if (type == Section::Type::Profile
  852             //      && _tab != Tab::Profile) {
  853             //      _anotherTabMemento = std::move(infoMemento->takeStack().back());
  854             //      _topTabs->setActiveSection(static_cast<int>(Tab::Profile));
  855             //      return true;
  856             //  } else if (type == Section::Type::Media
  857             //      && _tab != Tab::Media
  858             //      && Media::TypeToTabIndex(content->section().mediaType()).has_value()) {
  859             //      _anotherTabMemento = std::move(infoMemento->takeStack().back());
  860             //      _topTabs->setActiveSection(static_cast<int>(Tab::Media));
  861             //      return true;
  862             //  }
  863             }
  864         }
  865 
  866         // If we're in a nested section and we're asked to show
  867         // a chat profile that is at the bottom of the stack we'll
  868         // just go back in the stack all the way instead of pushing.
  869         if (returnToFirstStackFrame(content, params)) {
  870             return true;
  871         }
  872 
  873         showNewContent(
  874             content,
  875             params);
  876         return true;
  877     }
  878     return false;
  879 }
  880 
  881 void WrapWidget::highlightTopBar() {
  882     if (_topBar) {
  883         _topBar->highlight();
  884     }
  885 }
  886 
  887 std::shared_ptr<Window::SectionMemento> WrapWidget::createMemento() {
  888     auto stack = std::vector<std::shared_ptr<ContentMemento>>();
  889     stack.reserve(_historyStack.size() + 1);
  890     for (auto &stackItem : base::take(_historyStack)) {
  891         stack.push_back(std::move(stackItem.section));
  892     }
  893     stack.push_back(_content->createMemento());
  894 
  895     // We're not in valid state anymore and supposed to be destroyed.
  896     _controller = nullptr;
  897 
  898     return std::make_shared<Memento>(std::move(stack));
  899 }
  900 
  901 rpl::producer<int> WrapWidget::desiredHeightValue() const {
  902     return _desiredHeights.events_starting_with(desiredHeightForContent())
  903         | rpl::flatten_latest();
  904 }
  905 
  906 QRect WrapWidget::contentGeometry() const {
  907     return rect().marginsRemoved({ 0, topWidget()->height(), 0, 0 });
  908 }
  909 
  910 bool WrapWidget::returnToFirstStackFrame(
  911         not_null<ContentMemento*> memento,
  912         const Window::SectionShow &params) {
  913     if (!hasStackHistory()) {
  914         return false;
  915     }
  916     auto firstPeer = _historyStack.front().section->peer();
  917     auto firstSection = _historyStack.front().section->section();
  918     if (firstPeer == memento->peer()
  919         && firstSection.type() == memento->section().type()
  920         && firstSection.type() == Section::Type::Profile) {
  921         _historyStack.resize(1);
  922         _controller->showBackFromStack();
  923         return true;
  924     }
  925     return false;
  926 }
  927 
  928 void WrapWidget::showNewContent(
  929         not_null<ContentMemento*> memento,
  930         const Window::SectionShow &params) {
  931     auto saveToStack = (_content != nullptr)
  932         && (params.way == Window::SectionShow::Way::Forward);
  933     auto needAnimation = (_content != nullptr)
  934         && (params.animated != anim::type::instant);
  935     auto animationParams = SectionSlideParams();
  936     auto newController = createController(
  937         _controller->parentController(),
  938         memento);
  939     auto newContent = object_ptr<ContentWidget>(nullptr);
  940     if (needAnimation) {
  941         newContent = createContent(memento, newController.get());
  942         animationParams.withTopBarShadow = hasTopBarShadow()
  943             && newContent->hasTopBarShadow();
  944         animationParams.oldContentCache = grabForShowAnimation(
  945             animationParams);
  946         animationParams.withFade = (wrap() == Wrap::Layer);
  947     }
  948     if (saveToStack) {
  949         auto item = StackItem();
  950         item.section = _content->createMemento();
  951         //if (_anotherTabMemento) {
  952         //  item.anotherTab = std::move(_anotherTabMemento);
  953         //}
  954         _historyStack.push_back(std::move(item));
  955     } else if (params.way == Window::SectionShow::Way::ClearStack) {
  956         _historyStack.clear();
  957     }
  958 
  959     _controller = std::move(newController);
  960     if (newContent) {
  961         setupTop();
  962         showContent(std::move(newContent));
  963     } else {
  964         showNewContent(memento);
  965     }
  966     if (animationParams) {
  967         if (Ui::InFocusChain(this)) {
  968             setFocus();
  969         }
  970         showAnimated(
  971             saveToStack
  972                 ? SlideDirection::FromRight
  973                 : SlideDirection::FromLeft,
  974             animationParams);
  975     }
  976 }
  977 
  978 void WrapWidget::showNewContent(not_null<ContentMemento*> memento) {
  979     // Validates contentGeometry().
  980     setupTop();
  981     showContent(createContent(memento, _controller.get()));
  982 }
  983 
  984 // This was done for tabs support.
  985 //
  986 //void WrapWidget::setupTabs(Tab tab) {
  987 //  _tab = tab;
  988 //  if (_tab == Tab::None) {
  989 //      _topTabs.destroy();
  990 //      _topTabsBackground.destroy();
  991 //  } else if (!_topTabs) {
  992 //      createTabs();
  993 //  } else {
  994 //      _topTabs->setActiveSection(static_cast<int>(tab));
  995 //  }
  996 //}
  997 
  998 void WrapWidget::resizeEvent(QResizeEvent *e) {
  999     // This was done for tabs support.
 1000     //
 1001     //if (_topTabs) {
 1002     //  _topTabs->resizeToWidth(width());
 1003     //  _topTabsBackground->resize(
 1004     //      width(),
 1005     //      _topTabs->height() - st::lineWidth);
 1006     //}
 1007     if (_topBar) {
 1008         _topBar->resizeToWidth(width());
 1009     }
 1010     updateContentGeometry();
 1011 }
 1012 
 1013 void WrapWidget::keyPressEvent(QKeyEvent *e) {
 1014     if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
 1015         if (hasStackHistory() || wrap() != Wrap::Layer) {
 1016             checkBeforeClose([=] { _controller->showBackFromStack(); });
 1017             return;
 1018         }
 1019     }
 1020     SectionWidget::keyPressEvent(e);
 1021 }
 1022 
 1023 void WrapWidget::updateContentGeometry() {
 1024     if (_content) {
 1025         _topShadow->resizeToWidth(width());
 1026         _topShadow->moveToLeft(0, topWidget()->height());
 1027         _content->setGeometry(contentGeometry());
 1028     }
 1029 }
 1030 
 1031 bool WrapWidget::floatPlayerHandleWheelEvent(QEvent *e) {
 1032     return _content->floatPlayerHandleWheelEvent(e);
 1033 }
 1034 
 1035 QRect WrapWidget::floatPlayerAvailableRect() {
 1036     return _content->floatPlayerAvailableRect();
 1037 }
 1038 
 1039 object_ptr<Ui::RpWidget> WrapWidget::createTopBarSurrogate(
 1040         QWidget *parent) {
 1041     if (hasStackHistory() || wrap() == Wrap::Narrow) {
 1042         Assert(_topBar != nullptr);
 1043 
 1044         auto result = object_ptr<Ui::AbstractButton>(parent);
 1045         result->addClickHandler([weak = Ui::MakeWeak(this)]{
 1046             if (weak) {
 1047                 weak->_controller->showBackFromStack();
 1048             }
 1049         });
 1050         result->setGeometry(_topBar->geometry());
 1051         result->show();
 1052         return result;
 1053     }
 1054     return nullptr;
 1055 }
 1056 
 1057 void WrapWidget::updateGeometry(QRect newGeometry, int additionalScroll) {
 1058     auto scrollChanged = (_additionalScroll != additionalScroll);
 1059     auto geometryChanged = (geometry() != newGeometry);
 1060     auto shrinkingContent = (additionalScroll < _additionalScroll);
 1061     _additionalScroll = additionalScroll;
 1062 
 1063     if (geometryChanged) {
 1064         if (shrinkingContent) {
 1065             setGeometry(newGeometry);
 1066         }
 1067         if (scrollChanged) {
 1068             _content->applyAdditionalScroll(additionalScroll);
 1069         }
 1070         if (!shrinkingContent) {
 1071             setGeometry(newGeometry);
 1072         }
 1073     } else if (scrollChanged) {
 1074         _content->applyAdditionalScroll(additionalScroll);
 1075     }
 1076 }
 1077 
 1078 int WrapWidget::scrollTillBottom(int forHeight) const {
 1079     return _content->scrollTillBottom(forHeight - topWidget()->height());
 1080 }
 1081 
 1082 rpl::producer<int> WrapWidget::scrollTillBottomChanges() const {
 1083     return _scrollTillBottomChanges.events_starting_with(
 1084         _content->scrollTillBottomChanges()
 1085     ) | rpl::flatten_latest();
 1086 }
 1087 
 1088 WrapWidget::~WrapWidget() = default;
 1089 
 1090 } // namespace Info