"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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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