| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "mainwidget.h"
- #include "api/api_updates.h"
- #include "api/api_views.h"
- #include "data/components/scheduled_messages.h"
- #include "data/data_document_media.h"
- #include "data/data_document_resolver.h"
- #include "data/data_forum_topic.h"
- #include "data/data_web_page.h"
- #include "data/data_game.h"
- #include "data/data_peer_values.h"
- #include "data/data_session.h"
- #include "data/data_changes.h"
- #include "data/data_folder.h"
- #include "data/data_channel.h"
- #include "data/data_chat.h"
- #include "data/data_user.h"
- #include "data/data_chat_filters.h"
- #include "data/data_file_origin.h"
- #include "data/data_histories.h"
- #include "data/stickers/data_stickers.h"
- #include "ui/chat/chat_theme.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/shadow.h"
- #include "ui/widgets/dropdown_menu.h"
- #include "ui/focus_persister.h"
- #include "ui/resize_area.h"
- #include "ui/text/text_utilities.h"
- #include "ui/toast/toast.h"
- #include "ui/ui_utility.h"
- #include "window/window_connecting_widget.h"
- #include "window/window_top_bar_wrap.h"
- #include "window/notifications_manager.h"
- #include "window/window_separate_id.h"
- #include "window/window_slide_animation.h"
- #include "window/window_history_hider.h"
- #include "window/window_controller.h"
- #include "window/window_peer_menu.h"
- #include "window/window_session_controller_link_info.h"
- #include "window/themes/window_theme.h"
- #include "chat_helpers/bot_command.h"
- #include "chat_helpers/tabbed_selector.h" // TabbedSelector::refreshStickers
- #include "chat_helpers/message_field.h"
- #include "info/info_memento.h"
- #include "apiwrap.h"
- #include "dialogs/dialogs_widget.h"
- #include "history/history_widget.h"
- #include "history/history_item_helpers.h" // GetErrorForSending.
- #include "history/view/media/history_view_media.h"
- #include "history/view/history_view_service_message.h"
- #include "history/view/history_view_sublist_section.h"
- #include "lang/lang_keys.h"
- #include "lang/lang_cloud_manager.h"
- #include "inline_bots/inline_bot_layout_item.h"
- #include "ui/boxes/confirm_box.h"
- #include "boxes/peer_list_controllers.h"
- #include "storage/storage_account.h"
- #include "main/main_domain.h"
- #include "media/audio/media_audio.h"
- #include "media/player/media_player_panel.h"
- #include "media/player/media_player_widget.h"
- #include "media/player/media_player_dropdown.h"
- #include "media/player/media_player_instance.h"
- #include "base/qthelp_regex.h"
- #include "mtproto/mtproto_dc_options.h"
- #include "core/update_checker.h"
- #include "core/shortcuts.h"
- #include "core/application.h"
- #include "core/changelogs.h"
- #include "core/mime_type.h"
- #include "calls/calls_call.h"
- #include "calls/calls_instance.h"
- #include "calls/calls_top_bar.h"
- #include "calls/group/calls_group_call.h"
- #include "export/export_settings.h"
- #include "export/export_manager.h"
- #include "export/view/export_view_top_bar.h"
- #include "export/view/export_view_panel_controller.h"
- #include "main/main_session.h"
- #include "main/main_session_settings.h"
- #include "main/main_app_config.h"
- #include "settings/settings_premium.h"
- #include "support/support_helper.h"
- #include "storage/storage_user_photos.h"
- #include "styles/style_dialogs.h"
- #include "styles/style_chat.h"
- #include "styles/style_window.h"
- #include <QtCore/QCoreApplication>
- #include <QtCore/QMimeData>
- namespace {
- void ClearBotStartToken(PeerData *peer) {
- if (peer && peer->isUser() && peer->asUser()->isBot()) {
- peer->asUser()->botInfo->startToken = QString();
- }
- }
- } // namespace
- enum StackItemType {
- HistoryStackItem,
- SectionStackItem,
- };
- class StackItem {
- public:
- explicit StackItem(PeerData *peer) : _peer(peer) {
- }
- [[nodiscard]] PeerData *peer() const {
- return _peer;
- }
- void setThirdSectionMemento(
- std::shared_ptr<Window::SectionMemento> memento);
- [[nodiscard]] auto takeThirdSectionMemento()
- -> std::shared_ptr<Window::SectionMemento> {
- return std::move(_thirdSectionMemento);
- }
- void setThirdSectionWeak(QPointer<Window::SectionWidget> section) {
- _thirdSectionWeak = section;
- }
- [[nodiscard]] QPointer<Window::SectionWidget> thirdSectionWeak() const {
- return _thirdSectionWeak;
- }
- [[nodiscard]] rpl::lifetime &lifetime() {
- return _lifetime;
- }
- [[nodiscard]] virtual StackItemType type() const = 0;
- [[nodiscard]] rpl::producer<> removeRequests() const {
- return rpl::merge(
- _thirdSectionRemoveRequests.events(),
- sectionRemoveRequests());
- }
- virtual ~StackItem() = default;
- private:
- [[nodiscard]] virtual rpl::producer<> sectionRemoveRequests() const = 0;
- PeerData *_peer = nullptr;
- QPointer<Window::SectionWidget> _thirdSectionWeak;
- std::shared_ptr<Window::SectionMemento> _thirdSectionMemento;
- rpl::event_stream<> _thirdSectionRemoveRequests;
- rpl::lifetime _lifetime;
- };
- class StackItemHistory final : public StackItem {
- public:
- StackItemHistory(
- not_null<History*> history,
- MsgId msgId,
- QVector<FullMsgId> replyReturns)
- : StackItem(history->peer)
- , history(history)
- , msgId(msgId)
- , replyReturns(replyReturns) {
- }
- StackItemType type() const override {
- return HistoryStackItem;
- }
- not_null<History*> history;
- MsgId msgId;
- QVector<FullMsgId> replyReturns;
- private:
- rpl::producer<> sectionRemoveRequests() const override {
- return rpl::never<>();
- }
- };
- class StackItemSection : public StackItem {
- public:
- StackItemSection(
- std::shared_ptr<Window::SectionMemento> memento);
- StackItemType type() const override {
- return SectionStackItem;
- }
- std::shared_ptr<Window::SectionMemento> takeMemento() {
- return std::move(_memento);
- }
- private:
- rpl::producer<> sectionRemoveRequests() const override;
- std::shared_ptr<Window::SectionMemento> _memento;
- };
- void StackItem::setThirdSectionMemento(
- std::shared_ptr<Window::SectionMemento> memento) {
- _thirdSectionMemento = std::move(memento);
- if (const auto memento = _thirdSectionMemento.get()) {
- memento->removeRequests(
- ) | rpl::start_to_stream(_thirdSectionRemoveRequests, _lifetime);
- }
- }
- StackItemSection::StackItemSection(
- std::shared_ptr<Window::SectionMemento> memento)
- : StackItem(nullptr)
- , _memento(std::move(memento)) {
- }
- rpl::producer<> StackItemSection::sectionRemoveRequests() const {
- if (const auto topic = _memento->topicForRemoveRequests()) {
- return rpl::merge(_memento->removeRequests(), topic->destroyed());
- }
- return _memento->removeRequests();
- }
- struct MainWidget::SettingBackground {
- explicit SettingBackground(const Data::WallPaper &data);
- Data::WallPaper data;
- std::shared_ptr<Data::DocumentMedia> dataMedia;
- base::binary_guard generating;
- };
- MainWidget::SettingBackground::SettingBackground(
- const Data::WallPaper &data)
- : data(data) {
- }
- MainWidget::MainWidget(
- QWidget *parent,
- not_null<Window::SessionController*> controller)
- : RpWidget(parent)
- , _controller(controller)
- , _dialogsWidth(st::columnMinimalWidthLeft)
- , _thirdColumnWidth(st::columnMinimalWidthThird)
- , _dialogs(windowId().hasChatsList()
- ? base::make_unique_q<Dialogs::Widget>(
- this,
- _controller,
- Dialogs::Widget::Layout::Main)
- : nullptr)
- , _history(std::in_place, this, _controller)
- , _sideShadow(_dialogs
- ? base::make_unique_q<Ui::PlainShadow>(this)
- : nullptr)
- , _playerPlaylist(this, _controller)
- , _changelogs(Core::Changelogs::Create(&controller->session())) {
- if (_dialogs) {
- setupConnectingWidget();
- }
- _history->cancelRequests(
- ) | rpl::start_with_next([=] {
- handleHistoryBack();
- }, lifetime());
- Core::App().calls().currentCallValue(
- ) | rpl::start_with_next([=](Calls::Call *call) {
- setCurrentCall(call);
- }, lifetime());
- Core::App().calls().currentGroupCallValue(
- ) | rpl::start_with_next([=](Calls::GroupCall *call) {
- setCurrentGroupCall(call);
- }, lifetime());
- if (_callTopBar) {
- _callTopBar->finishAnimating();
- }
- controller->window().setDefaultFloatPlayerDelegate(
- floatPlayerDelegate());
- Core::App().floatPlayerClosed(
- ) | rpl::start_with_next([=](FullMsgId itemId) {
- floatPlayerClosed(itemId);
- }, lifetime());
- Core::App().exportManager().currentView(
- ) | rpl::start_with_next([=](Export::View::PanelController *view) {
- setCurrentExportView(view);
- }, lifetime());
- if (_exportTopBar) {
- _exportTopBar->finishAnimating();
- }
- Media::Player::instance()->closePlayerRequests(
- ) | rpl::start_with_next([=] {
- closeBothPlayers();
- }, lifetime());
- Media::Player::instance()->updatedNotifier(
- ) | rpl::start_with_next([=](const Media::Player::TrackState &state) {
- handleAudioUpdate(state);
- }, lifetime());
- handleAudioUpdate(Media::Player::instance()->getState(AudioMsgId::Type::Song));
- handleAudioUpdate(Media::Player::instance()->getState(AudioMsgId::Type::Voice));
- if (_player) {
- _player->finishAnimating();
- }
- _controller->chatsForceDisplayWideChanges(
- ) | rpl::start_with_next([=] {
- crl::on_main(this, [=] {
- updateDialogsWidthAnimated();
- });
- }, lifetime());
- const auto filter = [=](bool mainSectionShown) {
- return rpl::filter([=] {
- return (_controller->mainSectionShown() == mainSectionShown);
- });
- };
- rpl::merge(
- Core::App().settings().dialogsWithChatWidthRatioChanges(
- ) | filter(true) | rpl::to_empty,
- Core::App().settings().dialogsNoChatWidthRatioChanges(
- ) | filter(false) | rpl::to_empty,
- Core::App().settings().thirdColumnWidthChanges() | rpl::to_empty
- ) | rpl::start_with_next([=] {
- updateControlsGeometry();
- }, lifetime());
- session().changes().historyUpdates(
- Data::HistoryUpdate::Flag::MessageSent
- ) | rpl::start_with_next([=](const Data::HistoryUpdate &update) {
- const auto history = update.history;
- history->forgetScrollState();
- if (const auto from = history->peer->migrateFrom()) {
- auto &owner = history->owner();
- if (const auto migrated = owner.historyLoaded(from)) {
- migrated->forgetScrollState();
- }
- }
- }, lifetime());
- session().changes().entryUpdates(
- Data::EntryUpdate::Flag::LocalDraftSet
- ) | rpl::start_with_next([=](const Data::EntryUpdate &update) {
- auto params = Window::SectionShow();
- params.reapplyLocalDraft = true;
- controller->showThread(
- update.entry->asThread(),
- ShowAtUnreadMsgId,
- params);
- controller->hideLayer();
- }, lifetime());
- // MSVC BUG + REGRESSION rpl::mappers::tuple :(
- using namespace rpl::mappers;
- _controller->activeChatValue(
- ) | rpl::map([](Dialogs::Key key) {
- const auto peer = key.peer();
- const auto topic = key.topic();
- auto canWrite = topic
- ? Data::CanSendAnyOfValue(
- topic,
- Data::TabbedPanelSendRestrictions())
- : peer
- ? Data::CanSendAnyOfValue(
- peer, Data::TabbedPanelSendRestrictions())
- : rpl::single(false);
- return std::move(
- canWrite
- ) | rpl::map([=](bool can) {
- return std::make_tuple(key, can);
- });
- }) | rpl::flatten_latest(
- ) | rpl::start_with_next([this](Dialogs::Key key, bool canWrite) {
- updateThirdColumnToCurrentChat(key, canWrite);
- }, lifetime());
- QCoreApplication::instance()->installEventFilter(this);
- Media::Player::instance()->tracksFinished(
- ) | rpl::start_with_next([=](AudioMsgId::Type type) {
- if (type == AudioMsgId::Type::Voice) {
- const auto songState = Media::Player::instance()->getState(
- AudioMsgId::Type::Song);
- if (!songState.id || IsStoppedOrStopping(songState.state)) {
- Media::Player::instance()->stopAndClose();
- }
- } else if (type == AudioMsgId::Type::Song) {
- const auto songState = Media::Player::instance()->getState(
- AudioMsgId::Type::Song);
- if (!songState.id) {
- Media::Player::instance()->stopAndClose();
- }
- }
- }, lifetime());
- _controller->adaptive().changes(
- ) | rpl::start_with_next([=] {
- handleAdaptiveLayoutUpdate();
- }, lifetime());
- if (_dialogs) {
- _dialogs->show();
- }
- if (_dialogs && isOneColumn()) {
- _history->hide();
- } else {
- _history->show();
- }
- orderWidgets();
- if (!Core::UpdaterDisabled()) {
- Core::UpdateChecker checker;
- checker.start();
- }
- cSetOtherOnline(0);
- session().data().stickers().notifySavedGifsUpdated();
- }
- MainWidget::~MainWidget() = default;
- Main::Session &MainWidget::session() const {
- return _controller->session();
- }
- not_null<Window::SessionController*> MainWidget::controller() const {
- return _controller;
- }
- void MainWidget::setupConnectingWidget() {
- using namespace rpl::mappers;
- _connecting = std::make_unique<Window::ConnectionState>(
- this,
- &session().account(),
- _controller->adaptive().oneColumnValue() | rpl::map(!_1));
- _controller->connectingBottomSkipValue(
- ) | rpl::start_with_next([=](int skip) {
- _connecting->setBottomSkip(skip);
- }, lifetime());
- }
- not_null<Media::Player::FloatDelegate*> MainWidget::floatPlayerDelegate() {
- return static_cast<Media::Player::FloatDelegate*>(this);
- }
- not_null<Ui::RpWidget*> MainWidget::floatPlayerWidget() {
- return this;
- }
- void MainWidget::floatPlayerToggleGifsPaused(bool paused) {
- constexpr auto kReason = Window::GifPauseReason::RoundPlaying;
- if (paused) {
- _controller->enableGifPauseReason(kReason);
- } else {
- _controller->disableGifPauseReason(kReason);
- }
- }
- auto MainWidget::floatPlayerGetSection(Window::Column column)
- -> not_null<Media::Player::FloatSectionDelegate*> {
- if (isThreeColumn()) {
- if (_dialogs && column == Window::Column::First) {
- return _dialogs;
- } else if (column == Window::Column::Second
- || !_dialogs
- || !_thirdSection) {
- if (_mainSection) {
- return _mainSection;
- }
- return _history;
- }
- return _thirdSection;
- } else if (isNormalColumn()) {
- if (_dialogs && column == Window::Column::First) {
- return _dialogs;
- } else if (_mainSection) {
- return _mainSection;
- }
- return _history;
- } else if (_mainSection) {
- return _mainSection;
- } else if (!isOneColumn() || _history->peer()) {
- return _history;
- }
- Assert(_dialogs != nullptr);
- return _dialogs;
- }
- void MainWidget::floatPlayerEnumerateSections(Fn<void(
- not_null<Media::Player::FloatSectionDelegate*> widget,
- Window::Column widgetColumn)> callback) {
- if (isThreeColumn()) {
- if (_dialogs) {
- callback(_dialogs, Window::Column::First);
- }
- if (_mainSection) {
- callback(_mainSection, Window::Column::Second);
- } else {
- callback(_history, Window::Column::Second);
- }
- if (_thirdSection) {
- callback(_thirdSection, Window::Column::Third);
- }
- } else if (isNormalColumn()) {
- if (_dialogs) {
- callback(_dialogs, Window::Column::First);
- }
- if (_mainSection) {
- callback(_mainSection, Window::Column::Second);
- } else {
- callback(_history, Window::Column::Second);
- }
- } else {
- if (_mainSection) {
- callback(_mainSection, Window::Column::Second);
- } else if (!isOneColumn() || _history->peer()) {
- callback(_history, Window::Column::Second);
- } else {
- Assert(_dialogs != nullptr);
- callback(_dialogs, Window::Column::First);
- }
- }
- }
- bool MainWidget::floatPlayerIsVisible(not_null<HistoryItem*> item) {
- return session().data().queryItemVisibility(item);
- }
- void MainWidget::floatPlayerClosed(FullMsgId itemId) {
- if (_player) {
- const auto voiceData = Media::Player::instance()->current(
- AudioMsgId::Type::Voice);
- if (voiceData.contextId() == itemId) {
- stopAndClosePlayer();
- }
- }
- }
- void MainWidget::floatPlayerDoubleClickEvent(
- not_null<const HistoryItem*> item) {
- _controller->showMessage(item);
- }
- bool MainWidget::setForwardDraft(
- not_null<Data::Thread*> thread,
- Data::ForwardDraft &&draft) {
- const auto history = thread->owningHistory();
- const auto items = session().data().idsToItems(draft.ids);
- const auto topicRootId = thread->topicRootId();
- const auto error = GetErrorForSending(
- history->peer,
- {
- .topicRootId = topicRootId,
- .forward = &items,
- .ignoreSlowmodeCountdown = true,
- });
- if (error) {
- Data::ShowSendErrorToast(_controller, history->peer, error);
- return false;
- }
- history->setForwardDraft(topicRootId, std::move(draft));
- _controller->showThread(
- thread,
- ShowAtUnreadMsgId,
- SectionShow::Way::Forward);
- return true;
- }
- bool MainWidget::shareUrl(
- not_null<Data::Thread*> thread,
- const QString &url,
- const QString &text) const {
- if (!Data::CanSendTexts(thread)) {
- _controller->show(Ui::MakeInformBox(tr::lng_share_cant()));
- return false;
- }
- const auto textWithTags = TextWithTags{
- url + '\n' + text,
- TextWithTags::Tags()
- };
- const auto cursor = MessageCursor{
- int(url.size()) + 1,
- int(url.size()) + 1 + int(text.size()),
- Ui::kQFixedMax
- };
- const auto history = thread->owningHistory();
- const auto topicRootId = thread->topicRootId();
- history->setLocalDraft(std::make_unique<Data::Draft>(
- textWithTags,
- FullReplyTo{ .topicRootId = topicRootId },
- cursor,
- Data::WebPageDraft()));
- history->clearLocalEditDraft(topicRootId);
- history->session().changes().entryUpdated(
- thread,
- Data::EntryUpdate::Flag::LocalDraftSet);
- return true;
- }
- bool MainWidget::sendPaths(
- not_null<Data::Thread*> thread,
- const QStringList &paths) {
- if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) {
- _controller->showToast(
- tr::lng_forward_send_files_cant(tr::now));
- return false;
- } else if (const auto error = Data::AnyFileRestrictionError(
- thread->peer())) {
- Data::ShowSendErrorToast(controller(), thread->peer(), error);
- return false;
- } else {
- _controller->showThread(
- thread,
- ShowAtTheEndMsgId,
- Window::SectionShow::Way::ClearStack);
- }
- return (_controller->activeChatCurrent().thread() == thread)
- && (_mainSection
- ? _mainSection->confirmSendingFiles(paths)
- : _history->confirmSendingFiles(paths));
- }
- bool MainWidget::filesOrForwardDrop(
- not_null<Data::Thread*> thread,
- not_null<const QMimeData*> data) {
- if (const auto forum = thread->asForum()) {
- Window::ShowDropMediaBox(
- _controller,
- Core::ShareMimeMediaData(data),
- forum);
- if (_hider) {
- _hider->startHide();
- clearHider(_hider);
- }
- return true;
- }
- if (data->hasFormat(u"application/x-td-forward"_q)) {
- auto draft = Data::ForwardDraft{
- .ids = session().data().takeMimeForwardIds(),
- };
- if (setForwardDraft(thread, std::move(draft))) {
- return true;
- }
- // We've already released the mouse button,
- // so the forwarding is cancelled.
- if (_hider) {
- _hider->startHide();
- clearHider(_hider);
- }
- return false;
- } else if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) {
- _controller->showToast(
- tr::lng_forward_send_files_cant(tr::now));
- return false;
- } else if (const auto error = Data::AnyFileRestrictionError(
- thread->peer())) {
- Data::ShowSendErrorToast(_controller, thread->peer(), error);
- return false;
- } else {
- _controller->showThread(
- thread,
- ShowAtTheEndMsgId,
- Window::SectionShow::Way::ClearStack);
- if (_controller->activeChatCurrent().thread() != thread) {
- return false;
- }
- (_mainSection
- ? _mainSection->confirmSendingFiles(data)
- : _history->confirmSendingFiles(data));
- return true;
- }
- }
- bool MainWidget::notify_switchInlineBotButtonReceived(const QString &query, UserData *samePeerBot, MsgId samePeerReplyTo) {
- return _history->notify_switchInlineBotButtonReceived(query, samePeerBot, samePeerReplyTo);
- }
- void MainWidget::clearHider(not_null<Window::HistoryHider*> instance) {
- if (_hider != instance) {
- return;
- }
- _hider.release();
- }
- void MainWidget::hiderLayer(base::unique_qptr<Window::HistoryHider> hider) {
- if (!_dialogs || _controller->window().locked()) {
- return;
- }
- _hider = std::move(hider);
- _hider->setParent(this);
- _hider->hidden(
- ) | rpl::start_with_next([=, instance = _hider.get()] {
- clearHider(instance);
- instance->hide();
- instance->deleteLater();
- }, _hider->lifetime());
- _hider->show();
- updateControlsGeometry();
- _dialogs->setInnerFocus();
- floatPlayerCheckVisibility();
- }
- void MainWidget::showDragForwardInfo() {
- hiderLayer(base::make_unique_q<Window::HistoryHider>(
- this,
- tr::lng_forward_choose(tr::now)));
- }
- void MainWidget::hideDragForwardInfo() {
- if (_hider) {
- _hider->startHide();
- _hider.release();
- }
- }
- void MainWidget::sendBotCommand(Bot::SendCommandRequest request) {
- const auto type = _mainSection
- ? _mainSection->sendBotCommand(request)
- : Window::SectionActionResult::Fallback;
- if (type == Window::SectionActionResult::Fallback) {
- _controller->showPeerHistory(
- request.peer,
- SectionShow::Way::Forward,
- ShowAtTheEndMsgId);
- _history->sendBotCommand(request);
- }
- }
- void MainWidget::hideSingleUseKeyboard(FullMsgId replyToId) {
- _history->hideSingleUseKeyboard(replyToId);
- }
- void MainWidget::searchMessages(
- const QString &query,
- Dialogs::Key inChat,
- PeerData *searchFrom) {
- const auto complex = Data::HashtagWithUsernameFromQuery(query);
- if (!complex.username.isEmpty()) {
- _controller->showPeerByLink(Window::PeerByLinkInfo{
- .usernameOrId = complex.username,
- .text = complex.hashtag,
- .resolveType = Window::ResolveType::HashtagSearch,
- });
- return;
- }
- auto tags = Data::SearchTagsFromQuery(query);
- if (_dialogs) {
- auto state = Dialogs::SearchState{
- .inChat = ((tags.empty() || inChat.sublist())
- ? inChat
- : session().data().history(session().user())),
- .fromPeer = inChat ? searchFrom : nullptr,
- .tags = tags,
- .query = tags.empty() ? query : QString(),
- };
- state.tab = state.defaultTabForMe();
- _dialogs->searchMessages(std::move(state));
- if (isOneColumn()) {
- _controller->clearSectionStack();
- } else {
- _dialogs->setInnerFocus();
- }
- } else {
- if (const auto sublist = inChat.sublist()) {
- controller()->showSection(
- std::make_shared<HistoryView::SublistMemento>(sublist));
- } else if (!tags.empty()) {
- inChat = controller()->session().data().history(
- controller()->session().user());
- }
- if ((!_mainSection
- || !_mainSection->searchInChatEmbedded(query, inChat, searchFrom))
- && !_history->searchInChatEmbedded(query, inChat, searchFrom)) {
- const auto account = not_null(&session().account());
- if (const auto window = Core::App().windowFor(account)) {
- if (const auto controller = window->sessionController()) {
- controller->content()->searchMessages(
- query,
- inChat,
- searchFrom);
- controller->widget()->activate();
- }
- }
- }
- }
- }
- void MainWidget::handleAudioUpdate(const Media::Player::TrackState &state) {
- using State = Media::Player::State;
- const auto document = state.id.audio();
- const auto item = session().data().message(state.id.contextId());
- if (!Media::Player::IsStoppedOrStopping(state.state)) {
- const auto ttlSeconds = item
- && item->media()
- && item->media()->ttlSeconds();
- if (!ttlSeconds) {
- createPlayer();
- }
- } else if (state.state == State::StoppedAtStart) {
- Media::Player::instance()->stopAndClose();
- }
- if (item) {
- session().data().requestItemRepaint(item);
- }
- if (document) {
- if (const auto items = InlineBots::Layout::documentItems()) {
- if (const auto i = items->find(document); i != items->end()) {
- for (const auto &item : i->second) {
- item->update();
- }
- }
- }
- }
- }
- void MainWidget::closeBothPlayers() {
- if (_player) {
- _player->hide(anim::type::normal);
- }
- _playerPlaylist->hideIgnoringEnterEvents();
- }
- void MainWidget::stopAndClosePlayer() {
- if (_player) {
- _player->entity()->stopAndClose();
- }
- }
- void MainWidget::createPlayer() {
- if (!_player) {
- _player.create(
- this,
- object_ptr<Media::Player::Widget>(this, this, _controller),
- _controller->adaptive().oneColumnValue());
- rpl::merge(
- _player->heightValue() | rpl::map_to(true),
- _player->shownValue()
- ) | rpl::start_with_next(
- [this] { playerHeightUpdated(); },
- _player->lifetime());
- _player->entity()->setCloseCallback([=] {
- Media::Player::instance()->stopAndClose();
- });
- _player->entity()->setShowItemCallback([=](
- not_null<const HistoryItem*> item) {
- const auto peer = item->history()->peer;
- if (const auto window = Core::App().windowFor(peer)) {
- if (const auto controller = window->sessionController()) {
- controller->showMessage(item);
- return;
- }
- }
- _controller->showMessage(item);
- });
- _player->entity()->togglePlaylistRequests(
- ) | rpl::start_with_next([=](bool shown) {
- if (!shown) {
- _playerPlaylist->hideFromOther();
- return;
- } else if (_playerPlaylist->isHidden()) {
- auto position = mapFromGlobal(QCursor::pos()).x();
- auto bestPosition = _playerPlaylist->bestPositionFor(position);
- if (rtl()) bestPosition = position + 2 * (position - bestPosition) - _playerPlaylist->width();
- updateMediaPlaylistPosition(bestPosition);
- }
- _playerPlaylist->showFromOther();
- }, _player->lifetime());
- orderWidgets();
- if (_showAnimation) {
- _player->show(anim::type::instant);
- _player->setVisible(false);
- Shortcuts::ToggleMediaShortcuts(true);
- } else {
- _player->hide(anim::type::instant);
- }
- }
- if (_player && !_player->toggled()) {
- if (!_showAnimation) {
- _player->show(anim::type::normal);
- _playerHeight = _contentScrollAddToY = _player->contentHeight();
- updateControlsGeometry();
- Shortcuts::ToggleMediaShortcuts(true);
- }
- }
- }
- void MainWidget::playerHeightUpdated() {
- if (!_player) {
- // Player could be already "destroyDelayed", but still handle events.
- return;
- }
- auto playerHeight = _player->contentHeight();
- if (playerHeight != _playerHeight) {
- _contentScrollAddToY += playerHeight - _playerHeight;
- _playerHeight = playerHeight;
- updateControlsGeometry();
- }
- if (!_playerHeight && _player->isHidden()) {
- const auto state = Media::Player::instance()->getState(Media::Player::instance()->getActiveType());
- if (!state.id || Media::Player::IsStoppedOrStopping(state.state)) {
- _player.destroyDelayed();
- }
- }
- }
- void MainWidget::setCurrentCall(Calls::Call *call) {
- if (!call && _currentGroupCall) {
- return;
- }
- _currentCallLifetime.destroy();
- _currentCall = call;
- if (_currentCall) {
- _callTopBar.destroy();
- _currentCall->stateValue(
- ) | rpl::start_with_next([=](Calls::Call::State state) {
- using State = Calls::Call::State;
- if (state != State::Established) {
- destroyCallTopBar();
- } else if (!_callTopBar) {
- createCallTopBar();
- }
- }, _currentCallLifetime);
- } else {
- destroyCallTopBar();
- }
- }
- void MainWidget::setCurrentGroupCall(Calls::GroupCall *call) {
- if (!call && _currentCall) {
- return;
- }
- _currentCallLifetime.destroy();
- _currentGroupCall = call;
- if (_currentGroupCall) {
- _callTopBar.destroy();
- _currentGroupCall->stateValue(
- ) | rpl::start_with_next([=](Calls::GroupCall::State state) {
- using State = Calls::GroupCall::State;
- if (state != State::Creating
- && state != State::Waiting
- && state != State::Joining
- && state != State::Joined
- && state != State::Connecting) {
- destroyCallTopBar();
- } else if (!_callTopBar) {
- createCallTopBar();
- }
- }, _currentCallLifetime);
- } else {
- destroyCallTopBar();
- }
- }
- void MainWidget::createCallTopBar() {
- Expects(_currentCall != nullptr || _currentGroupCall != nullptr);
- const auto show = controller()->uiShow();
- _callTopBar.create(
- this,
- (_currentCall
- ? object_ptr<Calls::TopBar>(this, _currentCall, show)
- : object_ptr<Calls::TopBar>(this, _currentGroupCall, show)));
- _callTopBar->entity()->initBlobsUnder(this, _callTopBar->geometryValue());
- _callTopBar->heightValue(
- ) | rpl::start_with_next([this](int value) {
- callTopBarHeightUpdated(value);
- }, lifetime());
- orderWidgets();
- if (_showAnimation) {
- _callTopBar->show(anim::type::instant);
- _callTopBar->setVisible(false);
- } else {
- _callTopBar->hide(anim::type::instant);
- _callTopBar->show(anim::type::normal);
- _callTopBarHeight = _contentScrollAddToY = _callTopBar->height();
- updateControlsGeometry();
- }
- }
- void MainWidget::destroyCallTopBar() {
- if (_callTopBar) {
- _callTopBar->hide(anim::type::normal);
- }
- }
- void MainWidget::callTopBarHeightUpdated(int callTopBarHeight) {
- if (!callTopBarHeight && !_currentCall && !_currentGroupCall) {
- _callTopBar.destroyDelayed();
- }
- if (callTopBarHeight != _callTopBarHeight) {
- _contentScrollAddToY += callTopBarHeight - _callTopBarHeight;
- _callTopBarHeight = callTopBarHeight;
- updateControlsGeometry();
- }
- }
- void MainWidget::setCurrentExportView(Export::View::PanelController *view) {
- _currentExportView = view;
- if (_currentExportView) {
- _currentExportView->progressState(
- ) | rpl::start_with_next([=](Export::View::Content &&data) {
- if (!data.rows.empty()
- && data.rows[0].id == Export::View::Content::kDoneId) {
- LOG(("Export Info: Destroy top bar by Done."));
- destroyExportTopBar();
- } else if (!_exportTopBar) {
- LOG(("Export Info: Create top bar by State."));
- createExportTopBar(std::move(data));
- } else {
- _exportTopBar->entity()->updateData(std::move(data));
- }
- }, _exportViewLifetime);
- } else {
- _exportViewLifetime.destroy();
- LOG(("Export Info: Destroy top bar by controller removal."));
- destroyExportTopBar();
- }
- }
- void MainWidget::createExportTopBar(Export::View::Content &&data) {
- _exportTopBar.create(
- this,
- object_ptr<Export::View::TopBar>(this, std::move(data)),
- _controller->adaptive().oneColumnValue());
- _exportTopBar->entity()->clicks(
- ) | rpl::start_with_next([=] {
- if (_currentExportView) {
- _currentExportView->activatePanel();
- }
- }, _exportTopBar->lifetime());
- orderWidgets();
- if (_showAnimation) {
- _exportTopBar->show(anim::type::instant);
- _exportTopBar->setVisible(false);
- } else {
- _exportTopBar->hide(anim::type::instant);
- _exportTopBar->show(anim::type::normal);
- _exportTopBarHeight = _contentScrollAddToY = _exportTopBar->contentHeight();
- updateControlsGeometry();
- }
- rpl::merge(
- _exportTopBar->heightValue() | rpl::map_to(true),
- _exportTopBar->shownValue()
- ) | rpl::start_with_next([=] {
- exportTopBarHeightUpdated();
- }, _exportTopBar->lifetime());
- }
- void MainWidget::destroyExportTopBar() {
- if (_exportTopBar) {
- _exportTopBar->hide(anim::type::normal);
- }
- }
- void MainWidget::exportTopBarHeightUpdated() {
- if (!_exportTopBar) {
- // Player could be already "destroyDelayed", but still handle events.
- return;
- }
- const auto exportTopBarHeight = _exportTopBar->contentHeight();
- if (exportTopBarHeight != _exportTopBarHeight) {
- _contentScrollAddToY += exportTopBarHeight - _exportTopBarHeight;
- _exportTopBarHeight = exportTopBarHeight;
- updateControlsGeometry();
- }
- if (!_exportTopBarHeight && _exportTopBar->isHidden()) {
- _exportTopBar.destroyDelayed();
- }
- }
- SendMenu::Details MainWidget::sendMenuDetails() const {
- return _history->sendMenuDetails();
- }
- void MainWidget::dialogsCancelled() {
- if (_hider) {
- _hider->startHide();
- clearHider(_hider);
- }
- _history->activate();
- }
- void MainWidget::toggleFiltersMenu(bool value) const {
- if (_dialogs) {
- _dialogs->toggleFiltersMenu(value);
- }
- }
- void MainWidget::setChatBackground(
- const Data::WallPaper &background,
- QImage &&image) {
- using namespace Window::Theme;
- if (isReadyChatBackground(background, image)) {
- setReadyChatBackground(background, std::move(image));
- return;
- }
- _background = std::make_unique<SettingBackground>(background);
- if (const auto document = _background->data.document()) {
- _background->dataMedia = document->createMediaView();
- _background->dataMedia->thumbnailWanted(
- _background->data.fileOrigin());
- }
- _background->data.loadDocument();
- checkChatBackground();
- const auto tile = Data::IsLegacy1DefaultWallPaper(background);
- Window::Theme::Background()->downloadingStarted(tile);
- }
- bool MainWidget::isReadyChatBackground(
- const Data::WallPaper &background,
- const QImage &image) const {
- return !image.isNull() || !background.document();
- }
- void MainWidget::setReadyChatBackground(
- const Data::WallPaper &background,
- QImage &&image) {
- using namespace Window::Theme;
- if (image.isNull()
- && !background.document()
- && background.localThumbnail()) {
- image = background.localThumbnail()->original();
- }
- const auto resetToDefault = image.isNull()
- && !background.document()
- && background.backgroundColors().empty()
- && !Data::IsLegacy1DefaultWallPaper(background);
- const auto ready = resetToDefault
- ? Data::DefaultWallPaper()
- : background;
- Background()->set(ready, std::move(image));
- const auto tile = Data::IsLegacy1DefaultWallPaper(ready);
- Background()->setTile(tile);
- Ui::ForceFullRepaint(this);
- }
- bool MainWidget::chatBackgroundLoading() {
- return (_background != nullptr);
- }
- float64 MainWidget::chatBackgroundProgress() const {
- if (_background) {
- if (_background->generating) {
- return 1.;
- } else if (const auto document = _background->data.document()) {
- return _background->dataMedia->progress();
- }
- }
- return 1.;
- }
- void MainWidget::checkChatBackground() {
- if (!_background || _background->generating) {
- return;
- }
- const auto &media = _background->dataMedia;
- Assert(media != nullptr);
- if (!media->loaded()) {
- return;
- }
- const auto document = _background->data.document();
- Assert(document != nullptr);
- const auto generateCallback = [=](QImage &&image) {
- const auto background = base::take(_background);
- const auto ready = image.isNull()
- ? Data::DefaultWallPaper()
- : background->data;
- setReadyChatBackground(ready, std::move(image));
- };
- _background->generating = Data::ReadBackgroundImageAsync(
- media.get(),
- Ui::PreprocessBackgroundImage,
- generateCallback);
- }
- Image *MainWidget::newBackgroundThumb() {
- return !_background
- ? nullptr
- : _background->data.localThumbnail()
- ? _background->data.localThumbnail()
- : _background->dataMedia
- ? _background->dataMedia->thumbnail()
- : nullptr;
- }
- void MainWidget::setInnerFocus() {
- const auto setTo = [&](auto &&widget) {
- if (widget->isHidden()) {
- // If we try setting focus inside a hidden widget, we may
- // end up focusing search field in dialogs on window activation.
- setFocus();
- } else {
- widget->setInnerFocus();
- }
- };
- if (_dialogs && _dialogs->searchHasFocus()) {
- setTo(_dialogs);
- } else if (_hider || !_history->peer()) {
- if (!_hider && _mainSection) {
- setTo(_mainSection);
- } else if (!_hider && _thirdSection) {
- setTo(_thirdSection);
- } else if (_dialogs) {
- setTo(_dialogs);
- } else {
- // Maybe we're just closing a child window, content is destroyed.
- _history->setFocus();
- }
- } else if (_mainSection) {
- setTo(_mainSection);
- } else {
- setTo(_history);
- }
- }
- void MainWidget::showChooseReportMessages(
- not_null<PeerData*> peer,
- Data::ReportInput reportInput,
- Fn<void(std::vector<MsgId>)> done) {
- _history->setChooseReportMessagesDetails(reportInput, std::move(done));
- _controller->showPeerHistory(
- peer,
- SectionShow::Way::Forward,
- ShowForChooseMessagesMsgId);
- controller()->showToast(tr::lng_report_please_select_messages(tr::now));
- }
- void MainWidget::clearChooseReportMessages() {
- _history->setChooseReportMessagesDetails({}, nullptr);
- }
- void MainWidget::toggleChooseChatTheme(
- not_null<PeerData*> peer,
- std::optional<bool> show) {
- _history->toggleChooseChatTheme(peer, show);
- }
- bool MainWidget::showHistoryInDifferentWindow(
- PeerId peerId,
- const SectionShow ¶ms,
- MsgId showAtMsgId) {
- if (!peerId) {
- // In case we don't have dialogs, we can't clear section stack.
- return !_dialogs;
- }
- const auto peer = session().data().peer(peerId);
- if (const auto separateChat = _controller->windowId().chat()) {
- if (const auto history = separateChat->asHistory()) {
- if (history->peer == peer) {
- return false;
- }
- }
- }
- const auto window = Core::App().windowForShowingHistory(peer);
- if (window == &_controller->window()) {
- return false;
- } else if (window) {
- window->sessionController()->showPeerHistory(
- peerId,
- params,
- showAtMsgId);
- window->activate();
- return true;
- } else if (windowId().hasChatsList()) {
- return false;
- }
- const auto account = not_null(&session().account());
- auto primary = Core::App().separateWindowFor(account);
- if (!primary) {
- Core::App().domain().activate(account);
- primary = Core::App().separateWindowFor(account);
- }
- if (primary && &primary->account() == account) {
- primary->sessionController()->showPeerHistory(
- peerId,
- params,
- showAtMsgId);
- primary->activate();
- }
- return true;
- }
- void MainWidget::showHistory(
- PeerId peerId,
- const SectionShow ¶ms,
- MsgId showAtMsgId) {
- if (peerId && _controller->window().locked()) {
- if (params.activation != anim::activation::background) {
- _controller->window().activate();
- }
- return;
- } else if (auto peer = session().data().peerLoaded(peerId)) {
- if (peer->migrateTo()) {
- peer = peer->migrateTo();
- peerId = peer->id;
- if (showAtMsgId > 0) {
- showAtMsgId = -showAtMsgId;
- }
- }
- const auto unavailable = peer->computeUnavailableReason();
- if (!unavailable.isEmpty()) {
- if (!isPrimary()) {
- _controller->window().close();
- } else if (params.activation != anim::activation::background) {
- _controller->show(Ui::MakeInformBox(unavailable));
- _controller->window().activate();
- }
- return;
- }
- }
- if ((IsServerMsgId(showAtMsgId) || Data::IsScheduledMsgId(showAtMsgId))
- && _mainSection
- && _mainSection->showMessage(peerId, params, showAtMsgId)) {
- session().data().hideShownSpoilers();
- if (params.activation != anim::activation::background) {
- _controller->window().activate();
- }
- return;
- } else if (showHistoryInDifferentWindow(peerId, params, showAtMsgId)) {
- return;
- }
- if (peerId && params.activation != anim::activation::background) {
- Core::App().hideMediaView();
- _controller->window().activate();
- }
- const auto alreadyThatPeer = _history->peer()
- && (_history->peer()->id == peerId);
- if (!alreadyThatPeer
- && preventsCloseSection(
- [=] { showHistory(peerId, params, showAtMsgId); },
- params)) {
- return;
- }
- using OriginMessage = SectionShow::OriginMessage;
- if (const auto origin = std::get_if<OriginMessage>(¶ms.origin)) {
- if (const auto returnTo = session().data().message(origin->id)) {
- if (returnTo->history()->peer->id == peerId) {
- _history->pushReplyReturn(returnTo);
- }
- }
- }
- _a_dialogsWidth.stop();
- using Way = SectionShow::Way;
- auto way = params.way;
- bool back = (way == Way::Backward || !peerId);
- bool foundInStack = !peerId;
- if (foundInStack || (way == Way::ClearStack)) {
- for (const auto &item : _stack) {
- ClearBotStartToken(item->peer());
- }
- _stack.clear();
- } else {
- for (auto i = 0, s = int(_stack.size()); i < s; ++i) {
- if (_stack.at(i)->type() == HistoryStackItem && _stack.at(i)->peer()->id == peerId) {
- foundInStack = true;
- while (int(_stack.size()) > i + 1) {
- ClearBotStartToken(_stack.back()->peer());
- _stack.pop_back();
- }
- _stack.pop_back();
- if (!back) {
- back = true;
- }
- break;
- }
- }
- if (const auto activeChat = _controller->activeChatCurrent()) {
- if (const auto peer = activeChat.peer()) {
- if (way == Way::Forward && peer->id == peerId) {
- way = _mainSection ? Way::Backward : Way::ClearStack;
- }
- }
- }
- }
- const auto wasActivePeer = _controller->activeChatCurrent().peer();
- if (params.activation != anim::activation::background) {
- _controller->window().hideSettingsAndLayer();
- }
- auto animatedShow = [&] {
- if (_showAnimation
- || Core::App().passcodeLocked()
- || (params.animated == anim::type::instant)) {
- return false;
- }
- if (!peerId) {
- if (isOneColumn()) {
- return _dialogs && _dialogs->isHidden();
- } else {
- return false;
- }
- }
- if (_history->isHidden()) {
- if (!isOneColumn() && way == Way::ClearStack) {
- return false;
- }
- return (_mainSection != nullptr)
- || (isOneColumn() && _dialogs && !_dialogs->isHidden());
- }
- if (back || way == Way::Forward) {
- return true;
- }
- return false;
- };
- auto animationParams = animatedShow()
- ? prepareHistoryAnimation(peerId)
- : Window::SectionSlideParams();
- if (!back && (way != Way::ClearStack)) {
- // This may modify the current section, for example remove its contents.
- saveSectionInStack(params);
- }
- if (_history->peer()
- && _history->peer()->id != peerId
- && way != Way::Forward) {
- ClearBotStartToken(_history->peer());
- }
- _history->showHistory(peerId, showAtMsgId, params);
- if (alreadyThatPeer && params.reapplyLocalDraft) {
- _history->applyDraft(HistoryWidget::FieldHistoryAction::NewEntry);
- }
- auto noPeer = !_history->peer();
- auto onlyDialogs = noPeer && isOneColumn();
- _mainSection.destroy();
- updateMainSectionShown();
- updateControlsGeometry();
- if (noPeer) {
- _controller->setActiveChatEntry(Dialogs::Key());
- _controller->setChatStyleTheme(_controller->defaultChatTheme());
- }
- if (onlyDialogs) {
- Assert(_dialogs != nullptr);
- _history->hide();
- if (!_showAnimation) {
- if (animationParams) {
- auto direction = back ? Window::SlideDirection::FromLeft : Window::SlideDirection::FromRight;
- _dialogs->showAnimated(direction, animationParams);
- } else {
- _dialogs->showFast();
- }
- }
- } else {
- const auto nowActivePeer = _controller->activeChatCurrent().peer();
- if (nowActivePeer && nowActivePeer != wasActivePeer) {
- session().api().views().removeIncremented(nowActivePeer);
- }
- if (isOneColumn() && _dialogs && !_dialogs->isHidden()) {
- _dialogs->hide();
- }
- if (!_showAnimation) {
- if (!animationParams.oldContentCache.isNull()) {
- _history->showAnimated(
- back
- ? Window::SlideDirection::FromLeft
- : Window::SlideDirection::FromRight,
- animationParams);
- } else {
- _history->show();
- crl::on_main(this, [=] {
- _controller->widget()->setInnerFocus();
- });
- }
- }
- }
- if (_dialogs && !_dialogs->isHidden()) {
- if (!back) {
- if (const auto history = _history->history()) {
- _dialogs->scrollToEntry(Dialogs::RowDescriptor(
- history,
- FullMsgId(history->peer->id, showAtMsgId)));
- }
- }
- _dialogs->update();
- }
- floatPlayerCheckVisibility();
- }
- void MainWidget::showMessage(
- not_null<const HistoryItem*> item,
- const SectionShow ¶ms) {
- const auto peerId = item->history()->peer->id;
- const auto itemId = item->id;
- if (!v::is_null(params.origin)) {
- if (_mainSection) {
- if (_mainSection->showMessage(peerId, params, itemId)) {
- if (params.activation != anim::activation::background) {
- _controller->window().activate();
- _controller->window().hideSettingsAndLayer();
- }
- return;
- }
- } else if (_history->peer() == item->history()->peer) {
- // showHistory may be redirected to different window,
- // so we don't call activate() on current controller's window.
- showHistory(peerId, params, itemId);
- return;
- }
- }
- if (const auto topic = item->topic()) {
- _controller->showTopic(topic, item->id, params);
- if (params.activation != anim::activation::background) {
- _controller->window().activate();
- }
- } else {
- // showPeerHistory may be redirected to different window,
- // so we don't call activate() on current controller's window.
- _controller->showPeerHistory(
- item->history(),
- params,
- item->id);
- }
- }
- void MainWidget::showForum(
- not_null<Data::Forum*> forum,
- const SectionShow ¶ms) {
- Expects(_dialogs != nullptr);
- _dialogs->showForum(forum, params);
- if (params.activation != anim::activation::background) {
- _controller->window().hideSettingsAndLayer();
- }
- }
- PeerData *MainWidget::peer() const {
- return _history->peer();
- }
- Ui::ChatTheme *MainWidget::customChatTheme() const {
- return _history->customChatTheme();
- }
- bool MainWidget::saveSectionInStack(
- const SectionShow ¶ms,
- Window::SectionWidget *newMainSection) {
- if (_mainSection) {
- if (auto memento = _mainSection->createMemento()) {
- if (params.dropSameFromStack
- && newMainSection
- && newMainSection->sameTypeAs(memento.get())) {
- // When choosing saved sublist we want to save the original
- // "Saved Messages" in the stack, but don't save every
- // sublist in a new stack entry when clicking them through.
- return false;
- }
- _stack.push_back(std::make_unique<StackItemSection>(
- std::move(memento)));
- } else {
- return false;
- }
- } else if (const auto history = _history->history()) {
- _stack.push_back(std::make_unique<StackItemHistory>(
- history,
- _history->msgId(),
- _history->replyReturns()));
- } else {
- // We pretend that we "saved" the chats list state in stack,
- // so that we do animate a transition from chats list to a section.
- return true;
- }
- const auto raw = _stack.back().get();
- raw->setThirdSectionWeak(_thirdSection.data());
- raw->removeRequests(
- ) | rpl::start_with_next([=] {
- for (auto i = begin(_stack); i != end(_stack); ++i) {
- if (i->get() == raw) {
- _stack.erase(i);
- return;
- }
- }
- }, raw->lifetime());
- return true;
- }
- void MainWidget::showSection(
- std::shared_ptr<Window::SectionMemento> memento,
- const SectionShow ¶ms) {
- if (_mainSection && _mainSection->showInternal(
- memento.get(),
- params)) {
- if (params.activation != anim::activation::background) {
- _controller->window().hideSettingsAndLayer();
- }
- if (const auto entry = _mainSection->activeChat(); entry.key) {
- _controller->setActiveChatEntry(entry);
- }
- return;
- //
- // Now third section handles only its own showSection() requests.
- // General showSection() should show layer or main_section instead.
- //
- //} else if (_thirdSection && _thirdSection->showInternal(
- // &memento,
- // params)) {
- // return;
- }
- if (preventsCloseSection(
- [=] { showSection(memento, params); },
- params)) {
- return;
- }
- // If the window was not resized, but we've enabled
- // tabbedSelectorSectionEnabled or thirdSectionInfoEnabled
- // we need to update adaptive layout to Adaptive::ThirdColumn().
- updateColumnLayout();
- showNewSection(std::move(memento), params);
- }
- void MainWidget::updateColumnLayout() {
- updateWindowAdaptiveLayout();
- }
- Window::SectionSlideParams MainWidget::prepareThirdSectionAnimation(Window::SectionWidget *section) {
- Expects(_thirdSection != nullptr);
- Window::SectionSlideParams result;
- result.withTopBarShadow = section->hasTopBarShadow();
- if (!_thirdSection->hasTopBarShadow()) {
- result.withTopBarShadow = false;
- }
- floatPlayerHideAll();
- result.oldContentCache = _thirdSection->grabForShowAnimation(result);
- floatPlayerShowVisible();
- return result;
- }
- Window::SectionSlideParams MainWidget::prepareShowAnimation(
- bool willHaveTopBarShadow) {
- Window::SectionSlideParams result;
- result.withTopBarShadow = willHaveTopBarShadow;
- if (_mainSection) {
- if (!_mainSection->hasTopBarShadow()) {
- result.withTopBarShadow = false;
- }
- } else if (!_history->peer()) {
- result.withTopBarShadow = false;
- }
- floatPlayerHideAll();
- if (_player) {
- _player->entity()->hideShadowAndDropdowns();
- }
- const auto playerPlaylistVisible = !_playerPlaylist->isHidden();
- if (playerPlaylistVisible) {
- _playerPlaylist->hide();
- }
- const auto hiderVisible = (_hider && !_hider->isHidden());
- if (hiderVisible) {
- _hider->hide();
- }
- auto sectionTop = getMainSectionTop();
- if (_mainSection) {
- result.oldContentCache = _mainSection->grabForShowAnimation(result);
- } else if (!isOneColumn() || !_history->isHidden()) {
- result.oldContentCache = _history->grabForShowAnimation(result);
- } else {
- result.oldContentCache = Ui::GrabWidget(this, QRect(
- 0,
- sectionTop,
- _dialogsWidth,
- height() - sectionTop));
- }
- if (_hider && hiderVisible) {
- _hider->show();
- }
- if (playerPlaylistVisible) {
- _playerPlaylist->show();
- }
- if (_player) {
- _player->entity()->showShadowAndDropdowns();
- }
- floatPlayerShowVisible();
- return result;
- }
- Window::SectionSlideParams MainWidget::prepareMainSectionAnimation(Window::SectionWidget *section) {
- return prepareShowAnimation(section->hasTopBarShadow());
- }
- Window::SectionSlideParams MainWidget::prepareHistoryAnimation(PeerId historyPeerId) {
- return prepareShowAnimation(historyPeerId != 0);
- }
- Window::SectionSlideParams MainWidget::prepareDialogsAnimation() {
- return prepareShowAnimation(false);
- }
- void MainWidget::showNewSection(
- std::shared_ptr<Window::SectionMemento> memento,
- const SectionShow ¶ms) {
- using Column = Window::Column;
- if (_controller->window().locked()) {
- return;
- }
- auto saveInStack = (params.way == SectionShow::Way::Forward);
- const auto thirdSectionTop = getThirdSectionTop();
- const auto newThirdGeometry = QRect(
- width() - st::columnMinimalWidthThird,
- thirdSectionTop,
- st::columnMinimalWidthThird,
- height() - thirdSectionTop);
- auto newThirdSection = (isThreeColumn() && params.thirdColumn)
- ? memento->createWidget(
- this,
- _controller,
- Column::Third,
- newThirdGeometry)
- : nullptr;
- const auto layerRect = parentWidget()->rect();
- if (newThirdSection) {
- saveInStack = false;
- } else if (auto layer = memento->createLayer(_controller, layerRect)) {
- if (params.activation != anim::activation::background) {
- _controller->hideLayer(anim::type::instant);
- }
- _controller->showSpecialLayer(std::move(layer));
- return;
- }
- if (params.activation != anim::activation::background) {
- _controller->window().hideSettingsAndLayer();
- }
- _a_dialogsWidth.stop();
- auto mainSectionTop = getMainSectionTop();
- auto newMainGeometry = QRect(
- _history->x(),
- mainSectionTop,
- _history->width(),
- height() - mainSectionTop);
- auto newMainSection = newThirdSection
- ? nullptr
- : memento->createWidget(
- this,
- _controller,
- isOneColumn() ? Column::First : Column::Second,
- newMainGeometry);
- Assert(newMainSection || newThirdSection);
- auto animatedShow = [&] {
- if (_showAnimation
- || Core::App().passcodeLocked()
- || (params.animated == anim::type::instant)
- || memento->instant()) {
- return false;
- }
- if (!isOneColumn() && params.way == SectionShow::Way::ClearStack) {
- return false;
- } else if (isOneColumn()
- || (newThirdSection && _thirdSection)
- || (newMainSection && isMainSectionShown())) {
- return true;
- }
- return false;
- }();
- auto animationParams = animatedShow
- ? (newThirdSection
- ? prepareThirdSectionAnimation(newThirdSection)
- : prepareMainSectionAnimation(newMainSection))
- : Window::SectionSlideParams();
- setFocus(); // otherwise dialogs widget could be focused.
- if (saveInStack) {
- // This may modify the current section, for example remove its contents.
- if (!saveSectionInStack(params, newMainSection)) {
- saveInStack = false;
- animatedShow = false;
- animationParams = Window::SectionSlideParams();
- }
- }
- auto &settingSection = newThirdSection
- ? _thirdSection
- : _mainSection;
- if (newThirdSection) {
- _thirdSection = std::move(newThirdSection);
- _thirdSection->removeRequests(
- ) | rpl::start_with_next([=] {
- destroyThirdSection();
- _thirdShadow.destroy();
- updateControlsGeometry();
- }, _thirdSection->lifetime());
- if (!_thirdShadow) {
- _thirdShadow.create(this);
- _thirdShadow->show();
- orderWidgets();
- }
- updateControlsGeometry();
- } else {
- _mainSection = std::move(newMainSection);
- _history->finishAnimating();
- _history->showHistory(PeerId(), MsgId());
- if (const auto entry = _mainSection->activeChat(); entry.key) {
- _controller->setActiveChatEntry(entry);
- }
- updateMainSectionShown();
- // Depends on SessionController::activeChatEntry
- // for tabbed selector showing in the third column.
- updateControlsGeometry();
- _history->hide();
- if (isOneColumn() && _dialogs) {
- _dialogs->hide();
- }
- }
- if (animationParams) {
- auto back = (params.way == SectionShow::Way::Backward);
- auto direction = (back || settingSection->forceAnimateBack())
- ? Window::SlideDirection::FromLeft
- : Window::SlideDirection::FromRight;
- if (isOneColumn()) {
- _controller->removeLayerBlackout();
- }
- settingSection->showAnimated(direction, animationParams);
- } else {
- settingSection->showFast();
- }
- floatPlayerCheckVisibility();
- orderWidgets();
- }
- void MainWidget::checkMainSectionToLayer() {
- if (!_mainSection) {
- return;
- }
- Ui::FocusPersister persister(this);
- if (auto layer = _mainSection->moveContentToLayer(rect())) {
- _mainSection.destroy();
- _controller->showBackFromStack(
- SectionShow(
- anim::type::instant,
- anim::activation::background));
- _controller->showSpecialLayer(
- std::move(layer),
- anim::type::instant);
- }
- updateMainSectionShown();
- }
- Window::SeparateId MainWidget::windowId() const {
- return _controller->windowId();
- }
- bool MainWidget::isPrimary() const {
- return _controller->isPrimary();
- }
- bool MainWidget::isMainSectionShown() const {
- return _mainSection || _history->peer();
- }
- bool MainWidget::isThirdSectionShown() const {
- return _thirdSection != nullptr;
- }
- Dialogs::RowDescriptor MainWidget::resolveChatNext(
- Dialogs::RowDescriptor from) const {
- return _dialogs ? _dialogs->resolveChatNext(from) : Dialogs::RowDescriptor();
- }
- Dialogs::RowDescriptor MainWidget::resolveChatPrevious(
- Dialogs::RowDescriptor from) const {
- return _dialogs ? _dialogs->resolveChatPrevious(from) : Dialogs::RowDescriptor();
- }
- bool MainWidget::stackIsEmpty() const {
- return _stack.empty();
- }
- bool MainWidget::preventsCloseSection(Fn<void()> callback) const {
- if (Core::App().passcodeLocked()) {
- return false;
- }
- auto copy = callback;
- return (_mainSection && _mainSection->preventsClose(std::move(copy)))
- || (_history && _history->preventsClose(std::move(callback)));
- }
- bool MainWidget::preventsCloseSection(
- Fn<void()> callback,
- const SectionShow ¶ms) const {
- return !params.thirdColumn
- && (params.activation != anim::activation::background)
- && preventsCloseSection(std::move(callback));
- }
- void MainWidget::showNonPremiumLimitToast(bool download) {
- const auto parent = _mainSection
- ? ((QWidget*)_mainSection.data())
- : (_dialogs && _history->isHidden())
- ? ((QWidget*)_dialogs.get())
- : ((QWidget*)_history.get());
- const auto link = download
- ? tr::lng_limit_download_subscribe_link(tr::now)
- : tr::lng_limit_upload_subscribe_link(tr::now);
- const auto better = session().appConfig().get<double>(download
- ? u"upload_premium_speedup_download"_q
- : u"upload_premium_speedup_upload"_q, 10.);
- const auto percent = int(base::SafeRound(better * 100.));
- if (percent <= 100) {
- return;
- }
- const auto increase = ((percent % 100) || percent <= 400)
- ? (download
- ? tr::lng_limit_download_increase_speed
- : tr::lng_limit_upload_increase_speed)(
- tr::now,
- lt_percent,
- TextWithEntities{ QString::number(percent - 100) },
- Ui::Text::RichLangValue)
- : (download
- ? tr::lng_limit_download_increase_times
- : tr::lng_limit_upload_increase_times)(
- tr::now,
- lt_count,
- percent / 100,
- Ui::Text::RichLangValue);
- auto text = (download
- ? tr::lng_limit_download_subscribe
- : tr::lng_limit_upload_subscribe)(
- tr::now,
- lt_link,
- Ui::Text::Link(Ui::Text::Bold(link)),
- lt_increase,
- TextWithEntities{ increase },
- Ui::Text::RichLangValue);
- auto filter = [=](ClickHandlerPtr handler, Qt::MouseButton button) {
- Settings::ShowPremium(
- controller(),
- download ? u"download_limit"_q : u"upload_limit"_q);
- return false;
- };
- Ui::Toast::Show(parent, {
- .title = (download
- ? tr::lng_limit_download_title
- : tr::lng_limit_upload_title)(tr::now),
- .text = std::move(text),
- .filter = std::move(filter),
- .attach = RectPart::Top,
- .duration = 5 * crl::time(1000),
- });
- }
- bool MainWidget::showBackFromStack(const SectionShow ¶ms) {
- if (preventsCloseSection([=] { showBackFromStack(params); }, params)) {
- return false;
- }
- if (_stack.empty()) {
- if (_dialogs) {
- _controller->clearSectionStack(params);
- }
- crl::on_main(this, [=] {
- _controller->widget()->setInnerFocus();
- });
- return (_dialogs != nullptr);
- }
- auto item = std::move(_stack.back());
- _stack.pop_back();
- if (const auto currentHistoryPeer = _history->peer()) {
- ClearBotStartToken(currentHistoryPeer);
- }
- _thirdSectionFromStack = item->takeThirdSectionMemento();
- if (item->type() == HistoryStackItem) {
- auto historyItem = static_cast<StackItemHistory*>(item.get());
- _controller->showPeerHistory(
- historyItem->peer()->id,
- params.withWay(SectionShow::Way::Backward),
- ShowAtUnreadMsgId);
- _history->setReplyReturns(
- historyItem->peer()->id,
- std::move(historyItem->replyReturns));
- } else if (item->type() == SectionStackItem) {
- auto sectionItem = static_cast<StackItemSection*>(item.get());
- showNewSection(
- sectionItem->takeMemento(),
- params.withWay(SectionShow::Way::Backward));
- }
- if (_thirdSectionFromStack && _thirdSection) {
- _controller->showSection(
- base::take(_thirdSectionFromStack),
- SectionShow(
- SectionShow::Way::ClearStack,
- anim::type::instant,
- anim::activation::background));
- }
- return true;
- }
- void MainWidget::orderWidgets() {
- if (_dialogs) {
- _dialogs->raiseWithTooltip();
- }
- if (_player) {
- _player->raise();
- }
- if (_exportTopBar) {
- _exportTopBar->raise();
- }
- if (_callTopBar) {
- _callTopBar->raise();
- }
- if (_sideShadow) {
- _sideShadow->raise();
- }
- if (_thirdShadow) {
- _thirdShadow->raise();
- }
- if (_firstColumnResizeArea) {
- _firstColumnResizeArea->raise();
- }
- if (_thirdColumnResizeArea) {
- _thirdColumnResizeArea->raise();
- }
- if (_connecting) {
- _connecting->raise();
- }
- floatPlayerRaiseAll();
- _playerPlaylist->raise();
- if (_player) {
- _player->entity()->raiseDropdowns();
- }
- if (_hider) _hider->raise();
- }
- QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
- QPixmap result;
- floatPlayerHideAll();
- if (_player) {
- _player->entity()->hideShadowAndDropdowns();
- }
- const auto playerPlaylistVisible = !_playerPlaylist->isHidden();
- if (playerPlaylistVisible) {
- _playerPlaylist->hide();
- }
- const auto hiderVisible = (_hider && !_hider->isHidden());
- if (hiderVisible) {
- _hider->hide();
- }
- auto sectionTop = getMainSectionTop();
- if (isOneColumn()) {
- result = Ui::GrabWidget(this, QRect(
- 0,
- sectionTop,
- width(),
- height() - sectionTop));
- } else {
- if (_sideShadow) {
- _sideShadow->hide();
- }
- if (_thirdShadow) {
- _thirdShadow->hide();
- }
- result = Ui::GrabWidget(this, QRect(
- _dialogsWidth,
- sectionTop,
- width() - _dialogsWidth,
- height() - sectionTop));
- if (_sideShadow) {
- _sideShadow->show();
- }
- if (_thirdShadow) {
- _thirdShadow->show();
- }
- }
- if (_hider && hiderVisible) {
- _hider->show();
- }
- if (playerPlaylistVisible) {
- _playerPlaylist->show();
- }
- if (_player) {
- _player->entity()->showShadowAndDropdowns();
- }
- floatPlayerShowVisible();
- return result;
- }
- void MainWidget::windowShown() {
- _history->windowShown();
- }
- void MainWidget::dialogsToUp() {
- if (_dialogs) {
- _dialogs->jumpToTop();
- }
- }
- void MainWidget::checkActivation() {
- _history->checkActivation();
- if (_mainSection) {
- _mainSection->checkActivation();
- }
- }
- void MainWidget::showAnimated(QPixmap oldContentCache, bool back) {
- _showAnimation = nullptr;
- showAll();
- floatPlayerHideAll();
- auto newContentCache = Ui::GrabWidget(this);
- hideAll();
- floatPlayerShowVisible();
- _showAnimation = std::make_unique<Window::SlideAnimation>();
- _showAnimation->setDirection(back
- ? Window::SlideDirection::FromLeft
- : Window::SlideDirection::FromRight);
- _showAnimation->setRepaintCallback([=] { update(); });
- _showAnimation->setFinishedCallback([=] { showFinished(); });
- _showAnimation->setPixmaps(oldContentCache, newContentCache);
- _showAnimation->start();
- show();
- }
- void MainWidget::showFinished() {
- _showAnimation = nullptr;
- showAll();
- activate();
- }
- void MainWidget::paintEvent(QPaintEvent *e) {
- if (_background) {
- checkChatBackground();
- }
- if (_showAnimation) {
- auto p = QPainter(this);
- _showAnimation->paintContents(p);
- }
- }
- int MainWidget::getMainSectionTop() const {
- return _callTopBarHeight + _exportTopBarHeight + _playerHeight;
- }
- int MainWidget::getThirdSectionTop() const {
- return 0;
- }
- void MainWidget::hideAll() {
- if (_dialogs) {
- _dialogs->hide();
- }
- _history->hide();
- if (_mainSection) {
- _mainSection->hide();
- }
- if (_thirdSection) {
- _thirdSection->hide();
- }
- if (_sideShadow) {
- _sideShadow->hide();
- }
- if (_thirdShadow) {
- _thirdShadow->hide();
- }
- if (_player) {
- _player->setVisible(false);
- _playerHeight = 0;
- }
- if (_callTopBar) {
- _callTopBar->setVisible(false);
- _callTopBarHeight = 0;
- }
- }
- void MainWidget::showAll() {
- if (cPasswordRecovered()) {
- cSetPasswordRecovered(false);
- _controller->show(Ui::MakeInformBox(
- tr::lng_cloud_password_updated()));
- }
- if (isOneColumn()) {
- if (_sideShadow) {
- _sideShadow->hide();
- }
- if (_hider) {
- _hider->hide();
- }
- if (_mainSection) {
- _mainSection->show();
- } else if (_history->peer()) {
- _history->show();
- _history->updateControlsGeometry();
- } else {
- Assert(_dialogs != nullptr);
- _dialogs->showFast();
- _history->hide();
- }
- if (_dialogs && isMainSectionShown()) {
- _dialogs->hide();
- }
- } else {
- if (_sideShadow) {
- _sideShadow->show();
- }
- if (_hider) {
- _hider->show();
- }
- if (_dialogs) {
- _dialogs->showFast();
- }
- if (_mainSection) {
- _mainSection->show();
- } else {
- _history->show();
- _history->updateControlsGeometry();
- }
- if (_thirdSection) {
- _thirdSection->show();
- }
- if (_thirdShadow) {
- _thirdShadow->show();
- }
- }
- if (_player) {
- _player->setVisible(true);
- _playerHeight = _player->contentHeight();
- }
- if (_callTopBar) {
- _callTopBar->setVisible(true);
- // show() could've send pending resize event that would update
- // the height value and destroy the top bar if it was hiding.
- if (_callTopBar) {
- _callTopBarHeight = _callTopBar->height();
- }
- }
- updateControlsGeometry();
- floatPlayerCheckVisibility();
- _controller->widget()->checkActivation();
- }
- void MainWidget::resizeEvent(QResizeEvent *e) {
- updateControlsGeometry();
- }
- void MainWidget::updateMainSectionShown() {
- _controller->setMainSectionShown(_mainSection || _history->peer());
- }
- void MainWidget::updateControlsGeometry() {
- if (!width()) {
- return;
- }
- updateWindowAdaptiveLayout();
- if (_dialogs) {
- const auto nochat = !_controller->mainSectionShown();
- if (Core::App().settings().dialogsWidthRatio(nochat) > 0) {
- _a_dialogsWidth.stop();
- }
- if (!_a_dialogsWidth.animating()) {
- _dialogs->stopWidthAnimation();
- }
- }
- if (isThreeColumn()) {
- if (!_thirdSection
- && !_controller->takeThirdSectionFromLayer()) {
- auto params = Window::SectionShow(
- Window::SectionShow::Way::ClearStack,
- anim::type::instant,
- anim::activation::background);
- const auto active = _controller->activeChatCurrent();
- if (const auto thread = active.thread()) {
- if (Core::App().settings().tabbedSelectorSectionEnabled()) {
- if (_mainSection) {
- _mainSection->pushTabbedSelectorToThirdSection(
- thread,
- params);
- } else {
- _history->pushTabbedSelectorToThirdSection(
- thread,
- params);
- }
- } else if (Core::App().settings().thirdSectionInfoEnabled()) {
- _controller->showSection(
- (thread->asTopic()
- ? std::make_shared<Info::Memento>(
- thread->asTopic())
- : Info::Memento::Default(
- thread->asHistory()->peer)),
- params.withThirdColumn());
- }
- }
- }
- } else {
- destroyThirdSection();
- _thirdShadow.destroy();
- }
- const auto mainSectionTop = getMainSectionTop();
- auto dialogsWidth = _dialogs
- ? qRound(_a_dialogsWidth.value(_dialogsWidth))
- : isOneColumn()
- ? width()
- : 0;
- if (isOneColumn()) {
- if (_callTopBar) {
- _callTopBar->resizeToWidth(dialogsWidth);
- _callTopBar->moveToLeft(0, 0);
- }
- if (_exportTopBar) {
- _exportTopBar->resizeToWidth(dialogsWidth);
- _exportTopBar->moveToLeft(0, _callTopBarHeight);
- }
- if (_player) {
- _player->resizeToWidth(dialogsWidth);
- _player->moveToLeft(0, _callTopBarHeight + _exportTopBarHeight);
- }
- const auto mainSectionGeometry = QRect(
- 0,
- mainSectionTop,
- dialogsWidth,
- height() - mainSectionTop);
- if (_dialogs) {
- _dialogs->setGeometryWithTopMoved(
- mainSectionGeometry,
- _contentScrollAddToY);
- }
- _history->setGeometryWithTopMoved(
- mainSectionGeometry,
- _contentScrollAddToY);
- if (_hider) _hider->setGeometry(0, 0, dialogsWidth, height());
- } else {
- auto thirdSectionWidth = _thirdSection ? _thirdColumnWidth : 0;
- if (_thirdSection) {
- auto thirdSectionTop = getThirdSectionTop();
- _thirdSection->setGeometry(
- width() - thirdSectionWidth,
- thirdSectionTop,
- thirdSectionWidth,
- height() - thirdSectionTop);
- }
- const auto shadowTop = _controller->window().verticalShadowTop();
- const auto shadowHeight = height() - shadowTop;
- if (_dialogs) {
- accumulate_min(
- dialogsWidth,
- width() - st::columnMinimalWidthMain);
- _dialogs->setGeometryToLeft(0, 0, dialogsWidth, height());
- }
- if (_sideShadow) {
- _sideShadow->setGeometryToLeft(
- dialogsWidth,
- shadowTop,
- st::lineWidth,
- shadowHeight);
- }
- if (_thirdShadow) {
- _thirdShadow->setGeometryToLeft(
- width() - thirdSectionWidth - st::lineWidth,
- shadowTop,
- st::lineWidth,
- shadowHeight);
- }
- const auto mainSectionWidth = width()
- - dialogsWidth
- - thirdSectionWidth;
- if (_callTopBar) {
- _callTopBar->resizeToWidth(mainSectionWidth);
- _callTopBar->moveToLeft(dialogsWidth, 0);
- }
- if (_exportTopBar) {
- _exportTopBar->resizeToWidth(mainSectionWidth);
- _exportTopBar->moveToLeft(dialogsWidth, _callTopBarHeight);
- }
- if (_player) {
- _player->resizeToWidth(mainSectionWidth);
- _player->moveToLeft(
- dialogsWidth,
- _callTopBarHeight + _exportTopBarHeight);
- }
- _history->setGeometryWithTopMoved(QRect(
- dialogsWidth,
- mainSectionTop,
- mainSectionWidth,
- height() - mainSectionTop
- ), _contentScrollAddToY);
- if (_hider) {
- _hider->setGeometryToLeft(
- dialogsWidth,
- 0,
- mainSectionWidth,
- height());
- }
- }
- if (_mainSection) {
- const auto mainSectionGeometry = QRect(
- _history->x(),
- mainSectionTop,
- _history->width(),
- height() - mainSectionTop);
- _mainSection->setGeometryWithTopMoved(
- mainSectionGeometry,
- _contentScrollAddToY);
- }
- refreshResizeAreas();
- if (_player) {
- _player->entity()->updateDropdownsGeometry();
- }
- updateMediaPlaylistPosition(_playerPlaylist->x());
- _contentScrollAddToY = 0;
- floatPlayerUpdatePositions();
- }
- void MainWidget::destroyThirdSection() {
- if (const auto strong = _thirdSection.data()) {
- if (Ui::InFocusChain(strong)) {
- setFocus();
- }
- }
- _thirdSection.destroy();
- }
- void MainWidget::refreshResizeAreas() {
- if (!isOneColumn() && _dialogs) {
- ensureFirstColumnResizeAreaCreated();
- _firstColumnResizeArea->setGeometryToLeft(
- _history->x(),
- 0,
- st::historyResizeWidth,
- height());
- } else if (_firstColumnResizeArea) {
- _firstColumnResizeArea.destroy();
- }
- if (isThreeColumn() && _thirdSection) {
- ensureThirdColumnResizeAreaCreated();
- _thirdColumnResizeArea->setGeometryToLeft(
- _thirdSection->x(),
- 0,
- st::historyResizeWidth,
- height());
- } else if (_thirdColumnResizeArea) {
- _thirdColumnResizeArea.destroy();
- }
- }
- template <typename MoveCallback, typename FinishCallback>
- void MainWidget::createResizeArea(
- object_ptr<Ui::ResizeArea> &area,
- MoveCallback &&moveCallback,
- FinishCallback &&finishCallback) {
- area.create(this);
- area->show();
- area->addMoveLeftCallback(
- std::forward<MoveCallback>(moveCallback));
- area->addMoveFinishedCallback(
- std::forward<FinishCallback>(finishCallback));
- orderWidgets();
- }
- void MainWidget::ensureFirstColumnResizeAreaCreated() {
- Expects(_dialogs != nullptr);
- if (_firstColumnResizeArea) {
- return;
- }
- auto moveLeftCallback = [=](int globalLeft) {
- const auto newWidth = globalLeft - mapToGlobal(QPoint(0, 0)).x();
- const auto newRatio = (newWidth < st::columnMinimalWidthLeft / 2)
- ? 0.
- : float64(newWidth) / width();
- const auto nochat = !_controller->mainSectionShown();
- Core::App().settings().updateDialogsWidthRatio(newRatio, nochat);
- };
- auto moveFinishedCallback = [=] {
- if (isOneColumn()) {
- return;
- }
- const auto nochat = !_controller->mainSectionShown();
- if (Core::App().settings().dialogsWidthRatio(nochat) > 0) {
- Core::App().settings().updateDialogsWidthRatio(
- float64(_dialogsWidth) / width(),
- nochat);
- }
- Core::App().saveSettingsDelayed();
- };
- createResizeArea(
- _firstColumnResizeArea,
- std::move(moveLeftCallback),
- std::move(moveFinishedCallback));
- }
- void MainWidget::ensureThirdColumnResizeAreaCreated() {
- if (_thirdColumnResizeArea) {
- return;
- }
- auto moveLeftCallback = [=](int globalLeft) {
- auto newWidth = mapToGlobal(QPoint(width(), 0)).x() - globalLeft;
- Core::App().settings().setThirdColumnWidth(newWidth);
- };
- auto moveFinishedCallback = [=] {
- if (!isThreeColumn() || !_thirdSection) {
- return;
- }
- Core::App().settings().setThirdColumnWidth(std::clamp(
- Core::App().settings().thirdColumnWidth(),
- st::columnMinimalWidthThird,
- st::columnMaximalWidthThird));
- Core::App().saveSettingsDelayed();
- };
- createResizeArea(
- _thirdColumnResizeArea,
- std::move(moveLeftCallback),
- std::move(moveFinishedCallback));
- }
- void MainWidget::updateDialogsWidthAnimated() {
- const auto nochat = !_controller->mainSectionShown();
- if (!_dialogs || Core::App().settings().dialogsWidthRatio(nochat) > 0) {
- return;
- }
- auto dialogsWidth = _dialogsWidth;
- updateWindowAdaptiveLayout();
- if (Core::App().settings().dialogsWidthRatio(nochat) == 0.
- && (_dialogsWidth != dialogsWidth
- || _a_dialogsWidth.animating())) {
- _dialogs->startWidthAnimation();
- _a_dialogsWidth.start(
- [this] { updateControlsGeometry(); },
- dialogsWidth,
- _dialogsWidth,
- st::dialogsWidthDuration,
- anim::easeOutCirc);
- updateControlsGeometry();
- }
- }
- bool MainWidget::saveThirdSectionToStackBack() const {
- return !_stack.empty()
- && _thirdSection != nullptr
- && _stack.back()->thirdSectionWeak() == _thirdSection.data();
- }
- auto MainWidget::thirdSectionForCurrentMainSection(
- Dialogs::Key key)
- -> std::shared_ptr<Window::SectionMemento> {
- if (_thirdSectionFromStack) {
- return std::move(_thirdSectionFromStack);
- } else if (const auto topic = key.topic()) {
- return std::make_shared<Info::Memento>(topic);
- } else if (const auto peer = key.peer()) {
- return std::make_shared<Info::Memento>(
- peer,
- Info::Memento::DefaultSection(peer));
- } else if (const auto sublist = key.sublist()) {
- return std::make_shared<Info::Memento>(
- session().user(),
- Info::Memento::DefaultSection(session().user()));
- }
- Unexpected("Key in MainWidget::thirdSectionForCurrentMainSection().");
- }
- void MainWidget::updateThirdColumnToCurrentChat(
- Dialogs::Key key,
- bool canWrite) {
- auto saveOldThirdSection = [&] {
- if (saveThirdSectionToStackBack()) {
- _stack.back()->setThirdSectionMemento(
- _thirdSection->createMemento());
- destroyThirdSection();
- }
- };
- auto &settings = Core::App().settings();
- auto params = Window::SectionShow(
- Window::SectionShow::Way::ClearStack,
- anim::type::instant,
- anim::activation::background);
- auto switchInfoFast = [&] {
- saveOldThirdSection();
- //
- // Like in _controller->showPeerInfo()
- //
- if (isThreeColumn()
- && !settings.thirdSectionInfoEnabled()) {
- settings.setThirdSectionInfoEnabled(true);
- Core::App().saveSettingsDelayed();
- }
- _controller->showSection(
- thirdSectionForCurrentMainSection(key),
- params.withThirdColumn());
- };
- auto switchTabbedFast = [&](not_null<Data::Thread*> thread) {
- saveOldThirdSection();
- return _mainSection
- ? _mainSection->pushTabbedSelectorToThirdSection(thread, params)
- : _history->pushTabbedSelectorToThirdSection(thread, params);
- };
- if (isThreeColumn()
- && settings.tabbedSelectorSectionEnabled()
- && key) {
- if (!canWrite) {
- switchInfoFast();
- settings.setTabbedSelectorSectionEnabled(true);
- settings.setTabbedReplacedWithInfo(true);
- } else if (settings.tabbedReplacedWithInfo()
- && key.thread()
- && switchTabbedFast(key.thread())) {
- settings.setTabbedReplacedWithInfo(false);
- }
- } else {
- settings.setTabbedReplacedWithInfo(false);
- if (!key) {
- if (_thirdSection) {
- destroyThirdSection();
- _thirdShadow.destroy();
- updateControlsGeometry();
- }
- } else if (isThreeColumn()
- && settings.thirdSectionInfoEnabled()) {
- switchInfoFast();
- }
- }
- }
- void MainWidget::updateMediaPlaylistPosition(int x) {
- if (_player) {
- auto playlistLeft = x;
- auto playlistWidth = _playerPlaylist->width();
- auto playlistTop = _player->y() + _player->height();
- auto rightEdge = width();
- if (playlistLeft + playlistWidth > rightEdge) {
- playlistLeft = rightEdge - playlistWidth;
- } else if (playlistLeft < 0) {
- playlistLeft = 0;
- }
- _playerPlaylist->move(playlistLeft, playlistTop);
- }
- }
- void MainWidget::returnTabbedSelector() {
- if (!_mainSection || !_mainSection->returnTabbedSelector()) {
- _history->returnTabbedSelector();
- }
- }
- bool MainWidget::relevantForDialogsFocus(not_null<QWidget*> widget) const {
- if (!_dialogs || widget->window() != window()) {
- return false;
- }
- while (true) {
- if (widget.get() == this) {
- return true;
- }
- const auto parent = widget->parentWidget();
- if (!parent) {
- return false;
- }
- widget = parent;
- }
- Unexpected("Should never be here.");
- }
- bool MainWidget::eventFilter(QObject *o, QEvent *e) {
- const auto widget = o->isWidgetType()
- ? static_cast<QWidget*>(o)
- : nullptr;
- if (e->type() == QEvent::FocusIn) {
- if (widget && relevantForDialogsFocus(widget)) {
- _dialogs->updateHasFocus(widget);
- } else if (widget == window()) {
- crl::on_main(this, [=] {
- _controller->widget()->setInnerFocus();
- });
- }
- } else if (e->type() == QEvent::MouseButtonPress) {
- if (widget && (widget->window() == window())) {
- const auto event = static_cast<QMouseEvent*>(e);
- if (event->button() == Qt::BackButton) {
- if (!Core::App().hideMediaView()
- && (!_dialogs || !_dialogs->cancelSearchByMouseBack())) {
- handleHistoryBack();
- }
- return true;
- }
- }
- } else if (e->type() == QEvent::Wheel) {
- if (widget && (widget->window() == window())) {
- if (const auto result = floatPlayerFilterWheelEvent(o, e)) {
- return *result;
- }
- }
- }
- return RpWidget::eventFilter(o, e);
- }
- void MainWidget::handleAdaptiveLayoutUpdate() {
- showAll();
- if (_sideShadow) {
- _sideShadow->setVisible(!isOneColumn());
- }
- if (_player) {
- _player->updateAdaptiveLayout();
- }
- }
- void MainWidget::handleHistoryBack() {
- const auto openedFolder = _controller->openedFolder().current();
- const auto openedForum = _controller->shownForum().current();
- const auto rootPeer = !_stack.empty()
- ? _stack.front()->peer()
- : _history->peer()
- ? _history->peer()
- : _mainSection
- ? _mainSection->activeChat().key.peer()
- : nullptr;
- const auto rootHistory = rootPeer
- ? rootPeer->owner().historyLoaded(rootPeer)
- : nullptr;
- const auto rootFolder = rootHistory ? rootHistory->folder() : nullptr;
- if (openedForum && (!rootPeer || rootPeer->forum() != openedForum)) {
- _controller->closeForum();
- } else if (!openedFolder
- || (rootFolder == openedFolder)
- || (!_dialogs || _dialogs->isHidden())) {
- _controller->showBackFromStack();
- if (_dialogs) {
- _dialogs->setInnerFocus();
- }
- } else {
- _controller->closeFolder();
- }
- }
- void MainWidget::updateWindowAdaptiveLayout() {
- const auto nochat = !_controller->mainSectionShown();
- auto layout = _controller->computeColumnLayout();
- auto dialogsWidthRatio = Core::App().settings().dialogsWidthRatio(nochat);
- // Check if we are in a single-column layout in a wide enough window
- // for the normal layout. If so, switch to the normal layout.
- if (layout.windowLayout == Window::Adaptive::WindowLayout::OneColumn) {
- auto chatWidth = layout.chatWidth;
- //if (session().settings().tabbedSelectorSectionEnabled()
- // && chatWidth >= _history->minimalWidthForTabbedSelectorSection()) {
- // chatWidth -= _history->tabbedSelectorSectionWidth();
- //}
- auto minimalNormalWidth = st::columnMinimalWidthLeft
- + st::columnMinimalWidthMain;
- if (chatWidth >= minimalNormalWidth) {
- // Switch layout back to normal in a wide enough window.
- layout.windowLayout = Window::Adaptive::WindowLayout::Normal;
- layout.dialogsWidth = st::columnMinimalWidthLeft;
- layout.chatWidth = layout.bodyWidth - layout.dialogsWidth;
- dialogsWidthRatio = float64(layout.dialogsWidth) / layout.bodyWidth;
- }
- }
- // Check if we are going to create the third column and shrink the
- // dialogs widget to provide a wide enough chat history column.
- // Don't shrink the column on the first call, when window is inited.
- if (layout.windowLayout == Window::Adaptive::WindowLayout::ThreeColumn
- && _controller->widget()->positionInited()) {
- //auto chatWidth = layout.chatWidth;
- //if (_history->willSwitchToTabbedSelectorWithWidth(chatWidth)) {
- // auto thirdColumnWidth = _history->tabbedSelectorSectionWidth();
- // auto twoColumnsWidth = (layout.bodyWidth - thirdColumnWidth);
- // auto sameRatioChatWidth = twoColumnsWidth - qRound(dialogsWidthRatio * twoColumnsWidth);
- // auto desiredChatWidth = qMax(sameRatioChatWidth, HistoryView::WideChatWidth());
- // chatWidth -= thirdColumnWidth;
- // auto extendChatBy = desiredChatWidth - chatWidth;
- // accumulate_min(extendChatBy, layout.dialogsWidth - st::columnMinimalWidthLeft);
- // if (extendChatBy > 0) {
- // layout.dialogsWidth -= extendChatBy;
- // layout.chatWidth += extendChatBy;
- // dialogsWidthRatio = float64(layout.dialogsWidth) / layout.bodyWidth;
- // }
- //}
- }
- Core::App().settings().updateDialogsWidthRatio(dialogsWidthRatio, nochat);
- auto useSmallColumnWidth = !isOneColumn()
- && !dialogsWidthRatio
- && !_controller->chatsForceDisplayWide();
- _dialogsWidth = !_dialogs
- ? 0
- : useSmallColumnWidth
- ? _controller->dialogsSmallColumnWidth()
- : layout.dialogsWidth;
- _thirdColumnWidth = layout.thirdWidth;
- _controller->adaptive().setWindowLayout(layout.windowLayout);
- }
- int MainWidget::backgroundFromY() const {
- return -getMainSectionTop();
- }
- bool MainWidget::contentOverlapped(const QRect &globalRect) {
- return _history->contentOverlapped(globalRect)
- || _playerPlaylist->overlaps(globalRect);
- }
- void MainWidget::activate() {
- if (_showAnimation) {
- return;
- } else if (const auto paths = cSendPaths(); !paths.isEmpty()) {
- const auto interpret = u"interpret://"_q;
- cSetSendPaths(QStringList());
- if (paths[0].startsWith(interpret)) {
- const auto error = Support::InterpretSendPath(
- _controller,
- paths[0].mid(interpret.size()));
- if (!error.isEmpty()) {
- _controller->show(Ui::MakeInformBox(error));
- }
- } else {
- const auto chosen = [=](not_null<Data::Thread*> thread) {
- return sendPaths(thread, paths);
- };
- Window::ShowChooseRecipientBox(_controller, chosen);
- }
- } else if (_mainSection) {
- _mainSection->setInnerFocus();
- } else if (_hider) {
- Assert(_dialogs != nullptr);
- _dialogs->setInnerFocus();
- } else if (!_controller->isLayerShown()) {
- if (_history->peer()) {
- _history->activate();
- } else {
- Assert(_dialogs != nullptr);
- _dialogs->setInnerFocus();
- }
- }
- _controller->widget()->fixOrder();
- }
- bool MainWidget::animatingShow() const {
- return _showAnimation != nullptr;
- }
- bool MainWidget::isOneColumn() const {
- return _controller->adaptive().isOneColumn();
- }
- bool MainWidget::isNormalColumn() const {
- return _controller->adaptive().isNormal();
- }
- bool MainWidget::isThreeColumn() const {
- return _controller->adaptive().isThreeColumn();
- }
|