mainwidget.cpp 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "mainwidget.h"
  8. #include "api/api_updates.h"
  9. #include "api/api_views.h"
  10. #include "data/components/scheduled_messages.h"
  11. #include "data/data_document_media.h"
  12. #include "data/data_document_resolver.h"
  13. #include "data/data_forum_topic.h"
  14. #include "data/data_web_page.h"
  15. #include "data/data_game.h"
  16. #include "data/data_peer_values.h"
  17. #include "data/data_session.h"
  18. #include "data/data_changes.h"
  19. #include "data/data_folder.h"
  20. #include "data/data_channel.h"
  21. #include "data/data_chat.h"
  22. #include "data/data_user.h"
  23. #include "data/data_chat_filters.h"
  24. #include "data/data_file_origin.h"
  25. #include "data/data_histories.h"
  26. #include "data/stickers/data_stickers.h"
  27. #include "ui/chat/chat_theme.h"
  28. #include "ui/widgets/buttons.h"
  29. #include "ui/widgets/shadow.h"
  30. #include "ui/widgets/dropdown_menu.h"
  31. #include "ui/focus_persister.h"
  32. #include "ui/resize_area.h"
  33. #include "ui/text/text_utilities.h"
  34. #include "ui/toast/toast.h"
  35. #include "ui/ui_utility.h"
  36. #include "window/window_connecting_widget.h"
  37. #include "window/window_top_bar_wrap.h"
  38. #include "window/notifications_manager.h"
  39. #include "window/window_separate_id.h"
  40. #include "window/window_slide_animation.h"
  41. #include "window/window_history_hider.h"
  42. #include "window/window_controller.h"
  43. #include "window/window_peer_menu.h"
  44. #include "window/window_session_controller_link_info.h"
  45. #include "window/themes/window_theme.h"
  46. #include "chat_helpers/bot_command.h"
  47. #include "chat_helpers/tabbed_selector.h" // TabbedSelector::refreshStickers
  48. #include "chat_helpers/message_field.h"
  49. #include "info/info_memento.h"
  50. #include "apiwrap.h"
  51. #include "dialogs/dialogs_widget.h"
  52. #include "history/history_widget.h"
  53. #include "history/history_item_helpers.h" // GetErrorForSending.
  54. #include "history/view/media/history_view_media.h"
  55. #include "history/view/history_view_service_message.h"
  56. #include "history/view/history_view_sublist_section.h"
  57. #include "lang/lang_keys.h"
  58. #include "lang/lang_cloud_manager.h"
  59. #include "inline_bots/inline_bot_layout_item.h"
  60. #include "ui/boxes/confirm_box.h"
  61. #include "boxes/peer_list_controllers.h"
  62. #include "storage/storage_account.h"
  63. #include "main/main_domain.h"
  64. #include "media/audio/media_audio.h"
  65. #include "media/player/media_player_panel.h"
  66. #include "media/player/media_player_widget.h"
  67. #include "media/player/media_player_dropdown.h"
  68. #include "media/player/media_player_instance.h"
  69. #include "base/qthelp_regex.h"
  70. #include "mtproto/mtproto_dc_options.h"
  71. #include "core/update_checker.h"
  72. #include "core/shortcuts.h"
  73. #include "core/application.h"
  74. #include "core/changelogs.h"
  75. #include "core/mime_type.h"
  76. #include "calls/calls_call.h"
  77. #include "calls/calls_instance.h"
  78. #include "calls/calls_top_bar.h"
  79. #include "calls/group/calls_group_call.h"
  80. #include "export/export_settings.h"
  81. #include "export/export_manager.h"
  82. #include "export/view/export_view_top_bar.h"
  83. #include "export/view/export_view_panel_controller.h"
  84. #include "main/main_session.h"
  85. #include "main/main_session_settings.h"
  86. #include "main/main_app_config.h"
  87. #include "settings/settings_premium.h"
  88. #include "support/support_helper.h"
  89. #include "storage/storage_user_photos.h"
  90. #include "styles/style_dialogs.h"
  91. #include "styles/style_chat.h"
  92. #include "styles/style_window.h"
  93. #include <QtCore/QCoreApplication>
  94. #include <QtCore/QMimeData>
  95. namespace {
  96. void ClearBotStartToken(PeerData *peer) {
  97. if (peer && peer->isUser() && peer->asUser()->isBot()) {
  98. peer->asUser()->botInfo->startToken = QString();
  99. }
  100. }
  101. } // namespace
  102. enum StackItemType {
  103. HistoryStackItem,
  104. SectionStackItem,
  105. };
  106. class StackItem {
  107. public:
  108. explicit StackItem(PeerData *peer) : _peer(peer) {
  109. }
  110. [[nodiscard]] PeerData *peer() const {
  111. return _peer;
  112. }
  113. void setThirdSectionMemento(
  114. std::shared_ptr<Window::SectionMemento> memento);
  115. [[nodiscard]] auto takeThirdSectionMemento()
  116. -> std::shared_ptr<Window::SectionMemento> {
  117. return std::move(_thirdSectionMemento);
  118. }
  119. void setThirdSectionWeak(QPointer<Window::SectionWidget> section) {
  120. _thirdSectionWeak = section;
  121. }
  122. [[nodiscard]] QPointer<Window::SectionWidget> thirdSectionWeak() const {
  123. return _thirdSectionWeak;
  124. }
  125. [[nodiscard]] rpl::lifetime &lifetime() {
  126. return _lifetime;
  127. }
  128. [[nodiscard]] virtual StackItemType type() const = 0;
  129. [[nodiscard]] rpl::producer<> removeRequests() const {
  130. return rpl::merge(
  131. _thirdSectionRemoveRequests.events(),
  132. sectionRemoveRequests());
  133. }
  134. virtual ~StackItem() = default;
  135. private:
  136. [[nodiscard]] virtual rpl::producer<> sectionRemoveRequests() const = 0;
  137. PeerData *_peer = nullptr;
  138. QPointer<Window::SectionWidget> _thirdSectionWeak;
  139. std::shared_ptr<Window::SectionMemento> _thirdSectionMemento;
  140. rpl::event_stream<> _thirdSectionRemoveRequests;
  141. rpl::lifetime _lifetime;
  142. };
  143. class StackItemHistory final : public StackItem {
  144. public:
  145. StackItemHistory(
  146. not_null<History*> history,
  147. MsgId msgId,
  148. QVector<FullMsgId> replyReturns)
  149. : StackItem(history->peer)
  150. , history(history)
  151. , msgId(msgId)
  152. , replyReturns(replyReturns) {
  153. }
  154. StackItemType type() const override {
  155. return HistoryStackItem;
  156. }
  157. not_null<History*> history;
  158. MsgId msgId;
  159. QVector<FullMsgId> replyReturns;
  160. private:
  161. rpl::producer<> sectionRemoveRequests() const override {
  162. return rpl::never<>();
  163. }
  164. };
  165. class StackItemSection : public StackItem {
  166. public:
  167. StackItemSection(
  168. std::shared_ptr<Window::SectionMemento> memento);
  169. StackItemType type() const override {
  170. return SectionStackItem;
  171. }
  172. std::shared_ptr<Window::SectionMemento> takeMemento() {
  173. return std::move(_memento);
  174. }
  175. private:
  176. rpl::producer<> sectionRemoveRequests() const override;
  177. std::shared_ptr<Window::SectionMemento> _memento;
  178. };
  179. void StackItem::setThirdSectionMemento(
  180. std::shared_ptr<Window::SectionMemento> memento) {
  181. _thirdSectionMemento = std::move(memento);
  182. if (const auto memento = _thirdSectionMemento.get()) {
  183. memento->removeRequests(
  184. ) | rpl::start_to_stream(_thirdSectionRemoveRequests, _lifetime);
  185. }
  186. }
  187. StackItemSection::StackItemSection(
  188. std::shared_ptr<Window::SectionMemento> memento)
  189. : StackItem(nullptr)
  190. , _memento(std::move(memento)) {
  191. }
  192. rpl::producer<> StackItemSection::sectionRemoveRequests() const {
  193. if (const auto topic = _memento->topicForRemoveRequests()) {
  194. return rpl::merge(_memento->removeRequests(), topic->destroyed());
  195. }
  196. return _memento->removeRequests();
  197. }
  198. struct MainWidget::SettingBackground {
  199. explicit SettingBackground(const Data::WallPaper &data);
  200. Data::WallPaper data;
  201. std::shared_ptr<Data::DocumentMedia> dataMedia;
  202. base::binary_guard generating;
  203. };
  204. MainWidget::SettingBackground::SettingBackground(
  205. const Data::WallPaper &data)
  206. : data(data) {
  207. }
  208. MainWidget::MainWidget(
  209. QWidget *parent,
  210. not_null<Window::SessionController*> controller)
  211. : RpWidget(parent)
  212. , _controller(controller)
  213. , _dialogsWidth(st::columnMinimalWidthLeft)
  214. , _thirdColumnWidth(st::columnMinimalWidthThird)
  215. , _dialogs(windowId().hasChatsList()
  216. ? base::make_unique_q<Dialogs::Widget>(
  217. this,
  218. _controller,
  219. Dialogs::Widget::Layout::Main)
  220. : nullptr)
  221. , _history(std::in_place, this, _controller)
  222. , _sideShadow(_dialogs
  223. ? base::make_unique_q<Ui::PlainShadow>(this)
  224. : nullptr)
  225. , _playerPlaylist(this, _controller)
  226. , _changelogs(Core::Changelogs::Create(&controller->session())) {
  227. if (_dialogs) {
  228. setupConnectingWidget();
  229. }
  230. _history->cancelRequests(
  231. ) | rpl::start_with_next([=] {
  232. handleHistoryBack();
  233. }, lifetime());
  234. Core::App().calls().currentCallValue(
  235. ) | rpl::start_with_next([=](Calls::Call *call) {
  236. setCurrentCall(call);
  237. }, lifetime());
  238. Core::App().calls().currentGroupCallValue(
  239. ) | rpl::start_with_next([=](Calls::GroupCall *call) {
  240. setCurrentGroupCall(call);
  241. }, lifetime());
  242. if (_callTopBar) {
  243. _callTopBar->finishAnimating();
  244. }
  245. controller->window().setDefaultFloatPlayerDelegate(
  246. floatPlayerDelegate());
  247. Core::App().floatPlayerClosed(
  248. ) | rpl::start_with_next([=](FullMsgId itemId) {
  249. floatPlayerClosed(itemId);
  250. }, lifetime());
  251. Core::App().exportManager().currentView(
  252. ) | rpl::start_with_next([=](Export::View::PanelController *view) {
  253. setCurrentExportView(view);
  254. }, lifetime());
  255. if (_exportTopBar) {
  256. _exportTopBar->finishAnimating();
  257. }
  258. Media::Player::instance()->closePlayerRequests(
  259. ) | rpl::start_with_next([=] {
  260. closeBothPlayers();
  261. }, lifetime());
  262. Media::Player::instance()->updatedNotifier(
  263. ) | rpl::start_with_next([=](const Media::Player::TrackState &state) {
  264. handleAudioUpdate(state);
  265. }, lifetime());
  266. handleAudioUpdate(Media::Player::instance()->getState(AudioMsgId::Type::Song));
  267. handleAudioUpdate(Media::Player::instance()->getState(AudioMsgId::Type::Voice));
  268. if (_player) {
  269. _player->finishAnimating();
  270. }
  271. _controller->chatsForceDisplayWideChanges(
  272. ) | rpl::start_with_next([=] {
  273. crl::on_main(this, [=] {
  274. updateDialogsWidthAnimated();
  275. });
  276. }, lifetime());
  277. const auto filter = [=](bool mainSectionShown) {
  278. return rpl::filter([=] {
  279. return (_controller->mainSectionShown() == mainSectionShown);
  280. });
  281. };
  282. rpl::merge(
  283. Core::App().settings().dialogsWithChatWidthRatioChanges(
  284. ) | filter(true) | rpl::to_empty,
  285. Core::App().settings().dialogsNoChatWidthRatioChanges(
  286. ) | filter(false) | rpl::to_empty,
  287. Core::App().settings().thirdColumnWidthChanges() | rpl::to_empty
  288. ) | rpl::start_with_next([=] {
  289. updateControlsGeometry();
  290. }, lifetime());
  291. session().changes().historyUpdates(
  292. Data::HistoryUpdate::Flag::MessageSent
  293. ) | rpl::start_with_next([=](const Data::HistoryUpdate &update) {
  294. const auto history = update.history;
  295. history->forgetScrollState();
  296. if (const auto from = history->peer->migrateFrom()) {
  297. auto &owner = history->owner();
  298. if (const auto migrated = owner.historyLoaded(from)) {
  299. migrated->forgetScrollState();
  300. }
  301. }
  302. }, lifetime());
  303. session().changes().entryUpdates(
  304. Data::EntryUpdate::Flag::LocalDraftSet
  305. ) | rpl::start_with_next([=](const Data::EntryUpdate &update) {
  306. auto params = Window::SectionShow();
  307. params.reapplyLocalDraft = true;
  308. controller->showThread(
  309. update.entry->asThread(),
  310. ShowAtUnreadMsgId,
  311. params);
  312. controller->hideLayer();
  313. }, lifetime());
  314. // MSVC BUG + REGRESSION rpl::mappers::tuple :(
  315. using namespace rpl::mappers;
  316. _controller->activeChatValue(
  317. ) | rpl::map([](Dialogs::Key key) {
  318. const auto peer = key.peer();
  319. const auto topic = key.topic();
  320. auto canWrite = topic
  321. ? Data::CanSendAnyOfValue(
  322. topic,
  323. Data::TabbedPanelSendRestrictions())
  324. : peer
  325. ? Data::CanSendAnyOfValue(
  326. peer, Data::TabbedPanelSendRestrictions())
  327. : rpl::single(false);
  328. return std::move(
  329. canWrite
  330. ) | rpl::map([=](bool can) {
  331. return std::make_tuple(key, can);
  332. });
  333. }) | rpl::flatten_latest(
  334. ) | rpl::start_with_next([this](Dialogs::Key key, bool canWrite) {
  335. updateThirdColumnToCurrentChat(key, canWrite);
  336. }, lifetime());
  337. QCoreApplication::instance()->installEventFilter(this);
  338. Media::Player::instance()->tracksFinished(
  339. ) | rpl::start_with_next([=](AudioMsgId::Type type) {
  340. if (type == AudioMsgId::Type::Voice) {
  341. const auto songState = Media::Player::instance()->getState(
  342. AudioMsgId::Type::Song);
  343. if (!songState.id || IsStoppedOrStopping(songState.state)) {
  344. Media::Player::instance()->stopAndClose();
  345. }
  346. } else if (type == AudioMsgId::Type::Song) {
  347. const auto songState = Media::Player::instance()->getState(
  348. AudioMsgId::Type::Song);
  349. if (!songState.id) {
  350. Media::Player::instance()->stopAndClose();
  351. }
  352. }
  353. }, lifetime());
  354. _controller->adaptive().changes(
  355. ) | rpl::start_with_next([=] {
  356. handleAdaptiveLayoutUpdate();
  357. }, lifetime());
  358. if (_dialogs) {
  359. _dialogs->show();
  360. }
  361. if (_dialogs && isOneColumn()) {
  362. _history->hide();
  363. } else {
  364. _history->show();
  365. }
  366. orderWidgets();
  367. if (!Core::UpdaterDisabled()) {
  368. Core::UpdateChecker checker;
  369. checker.start();
  370. }
  371. cSetOtherOnline(0);
  372. session().data().stickers().notifySavedGifsUpdated();
  373. }
  374. MainWidget::~MainWidget() = default;
  375. Main::Session &MainWidget::session() const {
  376. return _controller->session();
  377. }
  378. not_null<Window::SessionController*> MainWidget::controller() const {
  379. return _controller;
  380. }
  381. void MainWidget::setupConnectingWidget() {
  382. using namespace rpl::mappers;
  383. _connecting = std::make_unique<Window::ConnectionState>(
  384. this,
  385. &session().account(),
  386. _controller->adaptive().oneColumnValue() | rpl::map(!_1));
  387. _controller->connectingBottomSkipValue(
  388. ) | rpl::start_with_next([=](int skip) {
  389. _connecting->setBottomSkip(skip);
  390. }, lifetime());
  391. }
  392. not_null<Media::Player::FloatDelegate*> MainWidget::floatPlayerDelegate() {
  393. return static_cast<Media::Player::FloatDelegate*>(this);
  394. }
  395. not_null<Ui::RpWidget*> MainWidget::floatPlayerWidget() {
  396. return this;
  397. }
  398. void MainWidget::floatPlayerToggleGifsPaused(bool paused) {
  399. constexpr auto kReason = Window::GifPauseReason::RoundPlaying;
  400. if (paused) {
  401. _controller->enableGifPauseReason(kReason);
  402. } else {
  403. _controller->disableGifPauseReason(kReason);
  404. }
  405. }
  406. auto MainWidget::floatPlayerGetSection(Window::Column column)
  407. -> not_null<Media::Player::FloatSectionDelegate*> {
  408. if (isThreeColumn()) {
  409. if (_dialogs && column == Window::Column::First) {
  410. return _dialogs;
  411. } else if (column == Window::Column::Second
  412. || !_dialogs
  413. || !_thirdSection) {
  414. if (_mainSection) {
  415. return _mainSection;
  416. }
  417. return _history;
  418. }
  419. return _thirdSection;
  420. } else if (isNormalColumn()) {
  421. if (_dialogs && column == Window::Column::First) {
  422. return _dialogs;
  423. } else if (_mainSection) {
  424. return _mainSection;
  425. }
  426. return _history;
  427. } else if (_mainSection) {
  428. return _mainSection;
  429. } else if (!isOneColumn() || _history->peer()) {
  430. return _history;
  431. }
  432. Assert(_dialogs != nullptr);
  433. return _dialogs;
  434. }
  435. void MainWidget::floatPlayerEnumerateSections(Fn<void(
  436. not_null<Media::Player::FloatSectionDelegate*> widget,
  437. Window::Column widgetColumn)> callback) {
  438. if (isThreeColumn()) {
  439. if (_dialogs) {
  440. callback(_dialogs, Window::Column::First);
  441. }
  442. if (_mainSection) {
  443. callback(_mainSection, Window::Column::Second);
  444. } else {
  445. callback(_history, Window::Column::Second);
  446. }
  447. if (_thirdSection) {
  448. callback(_thirdSection, Window::Column::Third);
  449. }
  450. } else if (isNormalColumn()) {
  451. if (_dialogs) {
  452. callback(_dialogs, Window::Column::First);
  453. }
  454. if (_mainSection) {
  455. callback(_mainSection, Window::Column::Second);
  456. } else {
  457. callback(_history, Window::Column::Second);
  458. }
  459. } else {
  460. if (_mainSection) {
  461. callback(_mainSection, Window::Column::Second);
  462. } else if (!isOneColumn() || _history->peer()) {
  463. callback(_history, Window::Column::Second);
  464. } else {
  465. Assert(_dialogs != nullptr);
  466. callback(_dialogs, Window::Column::First);
  467. }
  468. }
  469. }
  470. bool MainWidget::floatPlayerIsVisible(not_null<HistoryItem*> item) {
  471. return session().data().queryItemVisibility(item);
  472. }
  473. void MainWidget::floatPlayerClosed(FullMsgId itemId) {
  474. if (_player) {
  475. const auto voiceData = Media::Player::instance()->current(
  476. AudioMsgId::Type::Voice);
  477. if (voiceData.contextId() == itemId) {
  478. stopAndClosePlayer();
  479. }
  480. }
  481. }
  482. void MainWidget::floatPlayerDoubleClickEvent(
  483. not_null<const HistoryItem*> item) {
  484. _controller->showMessage(item);
  485. }
  486. bool MainWidget::setForwardDraft(
  487. not_null<Data::Thread*> thread,
  488. Data::ForwardDraft &&draft) {
  489. const auto history = thread->owningHistory();
  490. const auto items = session().data().idsToItems(draft.ids);
  491. const auto topicRootId = thread->topicRootId();
  492. const auto error = GetErrorForSending(
  493. history->peer,
  494. {
  495. .topicRootId = topicRootId,
  496. .forward = &items,
  497. .ignoreSlowmodeCountdown = true,
  498. });
  499. if (error) {
  500. Data::ShowSendErrorToast(_controller, history->peer, error);
  501. return false;
  502. }
  503. history->setForwardDraft(topicRootId, std::move(draft));
  504. _controller->showThread(
  505. thread,
  506. ShowAtUnreadMsgId,
  507. SectionShow::Way::Forward);
  508. return true;
  509. }
  510. bool MainWidget::shareUrl(
  511. not_null<Data::Thread*> thread,
  512. const QString &url,
  513. const QString &text) const {
  514. if (!Data::CanSendTexts(thread)) {
  515. _controller->show(Ui::MakeInformBox(tr::lng_share_cant()));
  516. return false;
  517. }
  518. const auto textWithTags = TextWithTags{
  519. url + '\n' + text,
  520. TextWithTags::Tags()
  521. };
  522. const auto cursor = MessageCursor{
  523. int(url.size()) + 1,
  524. int(url.size()) + 1 + int(text.size()),
  525. Ui::kQFixedMax
  526. };
  527. const auto history = thread->owningHistory();
  528. const auto topicRootId = thread->topicRootId();
  529. history->setLocalDraft(std::make_unique<Data::Draft>(
  530. textWithTags,
  531. FullReplyTo{ .topicRootId = topicRootId },
  532. cursor,
  533. Data::WebPageDraft()));
  534. history->clearLocalEditDraft(topicRootId);
  535. history->session().changes().entryUpdated(
  536. thread,
  537. Data::EntryUpdate::Flag::LocalDraftSet);
  538. return true;
  539. }
  540. bool MainWidget::sendPaths(
  541. not_null<Data::Thread*> thread,
  542. const QStringList &paths) {
  543. if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) {
  544. _controller->showToast(
  545. tr::lng_forward_send_files_cant(tr::now));
  546. return false;
  547. } else if (const auto error = Data::AnyFileRestrictionError(
  548. thread->peer())) {
  549. Data::ShowSendErrorToast(controller(), thread->peer(), error);
  550. return false;
  551. } else {
  552. _controller->showThread(
  553. thread,
  554. ShowAtTheEndMsgId,
  555. Window::SectionShow::Way::ClearStack);
  556. }
  557. return (_controller->activeChatCurrent().thread() == thread)
  558. && (_mainSection
  559. ? _mainSection->confirmSendingFiles(paths)
  560. : _history->confirmSendingFiles(paths));
  561. }
  562. bool MainWidget::filesOrForwardDrop(
  563. not_null<Data::Thread*> thread,
  564. not_null<const QMimeData*> data) {
  565. if (const auto forum = thread->asForum()) {
  566. Window::ShowDropMediaBox(
  567. _controller,
  568. Core::ShareMimeMediaData(data),
  569. forum);
  570. if (_hider) {
  571. _hider->startHide();
  572. clearHider(_hider);
  573. }
  574. return true;
  575. }
  576. if (data->hasFormat(u"application/x-td-forward"_q)) {
  577. auto draft = Data::ForwardDraft{
  578. .ids = session().data().takeMimeForwardIds(),
  579. };
  580. if (setForwardDraft(thread, std::move(draft))) {
  581. return true;
  582. }
  583. // We've already released the mouse button,
  584. // so the forwarding is cancelled.
  585. if (_hider) {
  586. _hider->startHide();
  587. clearHider(_hider);
  588. }
  589. return false;
  590. } else if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) {
  591. _controller->showToast(
  592. tr::lng_forward_send_files_cant(tr::now));
  593. return false;
  594. } else if (const auto error = Data::AnyFileRestrictionError(
  595. thread->peer())) {
  596. Data::ShowSendErrorToast(_controller, thread->peer(), error);
  597. return false;
  598. } else {
  599. _controller->showThread(
  600. thread,
  601. ShowAtTheEndMsgId,
  602. Window::SectionShow::Way::ClearStack);
  603. if (_controller->activeChatCurrent().thread() != thread) {
  604. return false;
  605. }
  606. (_mainSection
  607. ? _mainSection->confirmSendingFiles(data)
  608. : _history->confirmSendingFiles(data));
  609. return true;
  610. }
  611. }
  612. bool MainWidget::notify_switchInlineBotButtonReceived(const QString &query, UserData *samePeerBot, MsgId samePeerReplyTo) {
  613. return _history->notify_switchInlineBotButtonReceived(query, samePeerBot, samePeerReplyTo);
  614. }
  615. void MainWidget::clearHider(not_null<Window::HistoryHider*> instance) {
  616. if (_hider != instance) {
  617. return;
  618. }
  619. _hider.release();
  620. }
  621. void MainWidget::hiderLayer(base::unique_qptr<Window::HistoryHider> hider) {
  622. if (!_dialogs || _controller->window().locked()) {
  623. return;
  624. }
  625. _hider = std::move(hider);
  626. _hider->setParent(this);
  627. _hider->hidden(
  628. ) | rpl::start_with_next([=, instance = _hider.get()] {
  629. clearHider(instance);
  630. instance->hide();
  631. instance->deleteLater();
  632. }, _hider->lifetime());
  633. _hider->show();
  634. updateControlsGeometry();
  635. _dialogs->setInnerFocus();
  636. floatPlayerCheckVisibility();
  637. }
  638. void MainWidget::showDragForwardInfo() {
  639. hiderLayer(base::make_unique_q<Window::HistoryHider>(
  640. this,
  641. tr::lng_forward_choose(tr::now)));
  642. }
  643. void MainWidget::hideDragForwardInfo() {
  644. if (_hider) {
  645. _hider->startHide();
  646. _hider.release();
  647. }
  648. }
  649. void MainWidget::sendBotCommand(Bot::SendCommandRequest request) {
  650. const auto type = _mainSection
  651. ? _mainSection->sendBotCommand(request)
  652. : Window::SectionActionResult::Fallback;
  653. if (type == Window::SectionActionResult::Fallback) {
  654. _controller->showPeerHistory(
  655. request.peer,
  656. SectionShow::Way::Forward,
  657. ShowAtTheEndMsgId);
  658. _history->sendBotCommand(request);
  659. }
  660. }
  661. void MainWidget::hideSingleUseKeyboard(FullMsgId replyToId) {
  662. _history->hideSingleUseKeyboard(replyToId);
  663. }
  664. void MainWidget::searchMessages(
  665. const QString &query,
  666. Dialogs::Key inChat,
  667. PeerData *searchFrom) {
  668. const auto complex = Data::HashtagWithUsernameFromQuery(query);
  669. if (!complex.username.isEmpty()) {
  670. _controller->showPeerByLink(Window::PeerByLinkInfo{
  671. .usernameOrId = complex.username,
  672. .text = complex.hashtag,
  673. .resolveType = Window::ResolveType::HashtagSearch,
  674. });
  675. return;
  676. }
  677. auto tags = Data::SearchTagsFromQuery(query);
  678. if (_dialogs) {
  679. auto state = Dialogs::SearchState{
  680. .inChat = ((tags.empty() || inChat.sublist())
  681. ? inChat
  682. : session().data().history(session().user())),
  683. .fromPeer = inChat ? searchFrom : nullptr,
  684. .tags = tags,
  685. .query = tags.empty() ? query : QString(),
  686. };
  687. state.tab = state.defaultTabForMe();
  688. _dialogs->searchMessages(std::move(state));
  689. if (isOneColumn()) {
  690. _controller->clearSectionStack();
  691. } else {
  692. _dialogs->setInnerFocus();
  693. }
  694. } else {
  695. if (const auto sublist = inChat.sublist()) {
  696. controller()->showSection(
  697. std::make_shared<HistoryView::SublistMemento>(sublist));
  698. } else if (!tags.empty()) {
  699. inChat = controller()->session().data().history(
  700. controller()->session().user());
  701. }
  702. if ((!_mainSection
  703. || !_mainSection->searchInChatEmbedded(query, inChat, searchFrom))
  704. && !_history->searchInChatEmbedded(query, inChat, searchFrom)) {
  705. const auto account = not_null(&session().account());
  706. if (const auto window = Core::App().windowFor(account)) {
  707. if (const auto controller = window->sessionController()) {
  708. controller->content()->searchMessages(
  709. query,
  710. inChat,
  711. searchFrom);
  712. controller->widget()->activate();
  713. }
  714. }
  715. }
  716. }
  717. }
  718. void MainWidget::handleAudioUpdate(const Media::Player::TrackState &state) {
  719. using State = Media::Player::State;
  720. const auto document = state.id.audio();
  721. const auto item = session().data().message(state.id.contextId());
  722. if (!Media::Player::IsStoppedOrStopping(state.state)) {
  723. const auto ttlSeconds = item
  724. && item->media()
  725. && item->media()->ttlSeconds();
  726. if (!ttlSeconds) {
  727. createPlayer();
  728. }
  729. } else if (state.state == State::StoppedAtStart) {
  730. Media::Player::instance()->stopAndClose();
  731. }
  732. if (item) {
  733. session().data().requestItemRepaint(item);
  734. }
  735. if (document) {
  736. if (const auto items = InlineBots::Layout::documentItems()) {
  737. if (const auto i = items->find(document); i != items->end()) {
  738. for (const auto &item : i->second) {
  739. item->update();
  740. }
  741. }
  742. }
  743. }
  744. }
  745. void MainWidget::closeBothPlayers() {
  746. if (_player) {
  747. _player->hide(anim::type::normal);
  748. }
  749. _playerPlaylist->hideIgnoringEnterEvents();
  750. }
  751. void MainWidget::stopAndClosePlayer() {
  752. if (_player) {
  753. _player->entity()->stopAndClose();
  754. }
  755. }
  756. void MainWidget::createPlayer() {
  757. if (!_player) {
  758. _player.create(
  759. this,
  760. object_ptr<Media::Player::Widget>(this, this, _controller),
  761. _controller->adaptive().oneColumnValue());
  762. rpl::merge(
  763. _player->heightValue() | rpl::map_to(true),
  764. _player->shownValue()
  765. ) | rpl::start_with_next(
  766. [this] { playerHeightUpdated(); },
  767. _player->lifetime());
  768. _player->entity()->setCloseCallback([=] {
  769. Media::Player::instance()->stopAndClose();
  770. });
  771. _player->entity()->setShowItemCallback([=](
  772. not_null<const HistoryItem*> item) {
  773. const auto peer = item->history()->peer;
  774. if (const auto window = Core::App().windowFor(peer)) {
  775. if (const auto controller = window->sessionController()) {
  776. controller->showMessage(item);
  777. return;
  778. }
  779. }
  780. _controller->showMessage(item);
  781. });
  782. _player->entity()->togglePlaylistRequests(
  783. ) | rpl::start_with_next([=](bool shown) {
  784. if (!shown) {
  785. _playerPlaylist->hideFromOther();
  786. return;
  787. } else if (_playerPlaylist->isHidden()) {
  788. auto position = mapFromGlobal(QCursor::pos()).x();
  789. auto bestPosition = _playerPlaylist->bestPositionFor(position);
  790. if (rtl()) bestPosition = position + 2 * (position - bestPosition) - _playerPlaylist->width();
  791. updateMediaPlaylistPosition(bestPosition);
  792. }
  793. _playerPlaylist->showFromOther();
  794. }, _player->lifetime());
  795. orderWidgets();
  796. if (_showAnimation) {
  797. _player->show(anim::type::instant);
  798. _player->setVisible(false);
  799. Shortcuts::ToggleMediaShortcuts(true);
  800. } else {
  801. _player->hide(anim::type::instant);
  802. }
  803. }
  804. if (_player && !_player->toggled()) {
  805. if (!_showAnimation) {
  806. _player->show(anim::type::normal);
  807. _playerHeight = _contentScrollAddToY = _player->contentHeight();
  808. updateControlsGeometry();
  809. Shortcuts::ToggleMediaShortcuts(true);
  810. }
  811. }
  812. }
  813. void MainWidget::playerHeightUpdated() {
  814. if (!_player) {
  815. // Player could be already "destroyDelayed", but still handle events.
  816. return;
  817. }
  818. auto playerHeight = _player->contentHeight();
  819. if (playerHeight != _playerHeight) {
  820. _contentScrollAddToY += playerHeight - _playerHeight;
  821. _playerHeight = playerHeight;
  822. updateControlsGeometry();
  823. }
  824. if (!_playerHeight && _player->isHidden()) {
  825. const auto state = Media::Player::instance()->getState(Media::Player::instance()->getActiveType());
  826. if (!state.id || Media::Player::IsStoppedOrStopping(state.state)) {
  827. _player.destroyDelayed();
  828. }
  829. }
  830. }
  831. void MainWidget::setCurrentCall(Calls::Call *call) {
  832. if (!call && _currentGroupCall) {
  833. return;
  834. }
  835. _currentCallLifetime.destroy();
  836. _currentCall = call;
  837. if (_currentCall) {
  838. _callTopBar.destroy();
  839. _currentCall->stateValue(
  840. ) | rpl::start_with_next([=](Calls::Call::State state) {
  841. using State = Calls::Call::State;
  842. if (state != State::Established) {
  843. destroyCallTopBar();
  844. } else if (!_callTopBar) {
  845. createCallTopBar();
  846. }
  847. }, _currentCallLifetime);
  848. } else {
  849. destroyCallTopBar();
  850. }
  851. }
  852. void MainWidget::setCurrentGroupCall(Calls::GroupCall *call) {
  853. if (!call && _currentCall) {
  854. return;
  855. }
  856. _currentCallLifetime.destroy();
  857. _currentGroupCall = call;
  858. if (_currentGroupCall) {
  859. _callTopBar.destroy();
  860. _currentGroupCall->stateValue(
  861. ) | rpl::start_with_next([=](Calls::GroupCall::State state) {
  862. using State = Calls::GroupCall::State;
  863. if (state != State::Creating
  864. && state != State::Waiting
  865. && state != State::Joining
  866. && state != State::Joined
  867. && state != State::Connecting) {
  868. destroyCallTopBar();
  869. } else if (!_callTopBar) {
  870. createCallTopBar();
  871. }
  872. }, _currentCallLifetime);
  873. } else {
  874. destroyCallTopBar();
  875. }
  876. }
  877. void MainWidget::createCallTopBar() {
  878. Expects(_currentCall != nullptr || _currentGroupCall != nullptr);
  879. const auto show = controller()->uiShow();
  880. _callTopBar.create(
  881. this,
  882. (_currentCall
  883. ? object_ptr<Calls::TopBar>(this, _currentCall, show)
  884. : object_ptr<Calls::TopBar>(this, _currentGroupCall, show)));
  885. _callTopBar->entity()->initBlobsUnder(this, _callTopBar->geometryValue());
  886. _callTopBar->heightValue(
  887. ) | rpl::start_with_next([this](int value) {
  888. callTopBarHeightUpdated(value);
  889. }, lifetime());
  890. orderWidgets();
  891. if (_showAnimation) {
  892. _callTopBar->show(anim::type::instant);
  893. _callTopBar->setVisible(false);
  894. } else {
  895. _callTopBar->hide(anim::type::instant);
  896. _callTopBar->show(anim::type::normal);
  897. _callTopBarHeight = _contentScrollAddToY = _callTopBar->height();
  898. updateControlsGeometry();
  899. }
  900. }
  901. void MainWidget::destroyCallTopBar() {
  902. if (_callTopBar) {
  903. _callTopBar->hide(anim::type::normal);
  904. }
  905. }
  906. void MainWidget::callTopBarHeightUpdated(int callTopBarHeight) {
  907. if (!callTopBarHeight && !_currentCall && !_currentGroupCall) {
  908. _callTopBar.destroyDelayed();
  909. }
  910. if (callTopBarHeight != _callTopBarHeight) {
  911. _contentScrollAddToY += callTopBarHeight - _callTopBarHeight;
  912. _callTopBarHeight = callTopBarHeight;
  913. updateControlsGeometry();
  914. }
  915. }
  916. void MainWidget::setCurrentExportView(Export::View::PanelController *view) {
  917. _currentExportView = view;
  918. if (_currentExportView) {
  919. _currentExportView->progressState(
  920. ) | rpl::start_with_next([=](Export::View::Content &&data) {
  921. if (!data.rows.empty()
  922. && data.rows[0].id == Export::View::Content::kDoneId) {
  923. LOG(("Export Info: Destroy top bar by Done."));
  924. destroyExportTopBar();
  925. } else if (!_exportTopBar) {
  926. LOG(("Export Info: Create top bar by State."));
  927. createExportTopBar(std::move(data));
  928. } else {
  929. _exportTopBar->entity()->updateData(std::move(data));
  930. }
  931. }, _exportViewLifetime);
  932. } else {
  933. _exportViewLifetime.destroy();
  934. LOG(("Export Info: Destroy top bar by controller removal."));
  935. destroyExportTopBar();
  936. }
  937. }
  938. void MainWidget::createExportTopBar(Export::View::Content &&data) {
  939. _exportTopBar.create(
  940. this,
  941. object_ptr<Export::View::TopBar>(this, std::move(data)),
  942. _controller->adaptive().oneColumnValue());
  943. _exportTopBar->entity()->clicks(
  944. ) | rpl::start_with_next([=] {
  945. if (_currentExportView) {
  946. _currentExportView->activatePanel();
  947. }
  948. }, _exportTopBar->lifetime());
  949. orderWidgets();
  950. if (_showAnimation) {
  951. _exportTopBar->show(anim::type::instant);
  952. _exportTopBar->setVisible(false);
  953. } else {
  954. _exportTopBar->hide(anim::type::instant);
  955. _exportTopBar->show(anim::type::normal);
  956. _exportTopBarHeight = _contentScrollAddToY = _exportTopBar->contentHeight();
  957. updateControlsGeometry();
  958. }
  959. rpl::merge(
  960. _exportTopBar->heightValue() | rpl::map_to(true),
  961. _exportTopBar->shownValue()
  962. ) | rpl::start_with_next([=] {
  963. exportTopBarHeightUpdated();
  964. }, _exportTopBar->lifetime());
  965. }
  966. void MainWidget::destroyExportTopBar() {
  967. if (_exportTopBar) {
  968. _exportTopBar->hide(anim::type::normal);
  969. }
  970. }
  971. void MainWidget::exportTopBarHeightUpdated() {
  972. if (!_exportTopBar) {
  973. // Player could be already "destroyDelayed", but still handle events.
  974. return;
  975. }
  976. const auto exportTopBarHeight = _exportTopBar->contentHeight();
  977. if (exportTopBarHeight != _exportTopBarHeight) {
  978. _contentScrollAddToY += exportTopBarHeight - _exportTopBarHeight;
  979. _exportTopBarHeight = exportTopBarHeight;
  980. updateControlsGeometry();
  981. }
  982. if (!_exportTopBarHeight && _exportTopBar->isHidden()) {
  983. _exportTopBar.destroyDelayed();
  984. }
  985. }
  986. SendMenu::Details MainWidget::sendMenuDetails() const {
  987. return _history->sendMenuDetails();
  988. }
  989. void MainWidget::dialogsCancelled() {
  990. if (_hider) {
  991. _hider->startHide();
  992. clearHider(_hider);
  993. }
  994. _history->activate();
  995. }
  996. void MainWidget::toggleFiltersMenu(bool value) const {
  997. if (_dialogs) {
  998. _dialogs->toggleFiltersMenu(value);
  999. }
  1000. }
  1001. void MainWidget::setChatBackground(
  1002. const Data::WallPaper &background,
  1003. QImage &&image) {
  1004. using namespace Window::Theme;
  1005. if (isReadyChatBackground(background, image)) {
  1006. setReadyChatBackground(background, std::move(image));
  1007. return;
  1008. }
  1009. _background = std::make_unique<SettingBackground>(background);
  1010. if (const auto document = _background->data.document()) {
  1011. _background->dataMedia = document->createMediaView();
  1012. _background->dataMedia->thumbnailWanted(
  1013. _background->data.fileOrigin());
  1014. }
  1015. _background->data.loadDocument();
  1016. checkChatBackground();
  1017. const auto tile = Data::IsLegacy1DefaultWallPaper(background);
  1018. Window::Theme::Background()->downloadingStarted(tile);
  1019. }
  1020. bool MainWidget::isReadyChatBackground(
  1021. const Data::WallPaper &background,
  1022. const QImage &image) const {
  1023. return !image.isNull() || !background.document();
  1024. }
  1025. void MainWidget::setReadyChatBackground(
  1026. const Data::WallPaper &background,
  1027. QImage &&image) {
  1028. using namespace Window::Theme;
  1029. if (image.isNull()
  1030. && !background.document()
  1031. && background.localThumbnail()) {
  1032. image = background.localThumbnail()->original();
  1033. }
  1034. const auto resetToDefault = image.isNull()
  1035. && !background.document()
  1036. && background.backgroundColors().empty()
  1037. && !Data::IsLegacy1DefaultWallPaper(background);
  1038. const auto ready = resetToDefault
  1039. ? Data::DefaultWallPaper()
  1040. : background;
  1041. Background()->set(ready, std::move(image));
  1042. const auto tile = Data::IsLegacy1DefaultWallPaper(ready);
  1043. Background()->setTile(tile);
  1044. Ui::ForceFullRepaint(this);
  1045. }
  1046. bool MainWidget::chatBackgroundLoading() {
  1047. return (_background != nullptr);
  1048. }
  1049. float64 MainWidget::chatBackgroundProgress() const {
  1050. if (_background) {
  1051. if (_background->generating) {
  1052. return 1.;
  1053. } else if (const auto document = _background->data.document()) {
  1054. return _background->dataMedia->progress();
  1055. }
  1056. }
  1057. return 1.;
  1058. }
  1059. void MainWidget::checkChatBackground() {
  1060. if (!_background || _background->generating) {
  1061. return;
  1062. }
  1063. const auto &media = _background->dataMedia;
  1064. Assert(media != nullptr);
  1065. if (!media->loaded()) {
  1066. return;
  1067. }
  1068. const auto document = _background->data.document();
  1069. Assert(document != nullptr);
  1070. const auto generateCallback = [=](QImage &&image) {
  1071. const auto background = base::take(_background);
  1072. const auto ready = image.isNull()
  1073. ? Data::DefaultWallPaper()
  1074. : background->data;
  1075. setReadyChatBackground(ready, std::move(image));
  1076. };
  1077. _background->generating = Data::ReadBackgroundImageAsync(
  1078. media.get(),
  1079. Ui::PreprocessBackgroundImage,
  1080. generateCallback);
  1081. }
  1082. Image *MainWidget::newBackgroundThumb() {
  1083. return !_background
  1084. ? nullptr
  1085. : _background->data.localThumbnail()
  1086. ? _background->data.localThumbnail()
  1087. : _background->dataMedia
  1088. ? _background->dataMedia->thumbnail()
  1089. : nullptr;
  1090. }
  1091. void MainWidget::setInnerFocus() {
  1092. const auto setTo = [&](auto &&widget) {
  1093. if (widget->isHidden()) {
  1094. // If we try setting focus inside a hidden widget, we may
  1095. // end up focusing search field in dialogs on window activation.
  1096. setFocus();
  1097. } else {
  1098. widget->setInnerFocus();
  1099. }
  1100. };
  1101. if (_dialogs && _dialogs->searchHasFocus()) {
  1102. setTo(_dialogs);
  1103. } else if (_hider || !_history->peer()) {
  1104. if (!_hider && _mainSection) {
  1105. setTo(_mainSection);
  1106. } else if (!_hider && _thirdSection) {
  1107. setTo(_thirdSection);
  1108. } else if (_dialogs) {
  1109. setTo(_dialogs);
  1110. } else {
  1111. // Maybe we're just closing a child window, content is destroyed.
  1112. _history->setFocus();
  1113. }
  1114. } else if (_mainSection) {
  1115. setTo(_mainSection);
  1116. } else {
  1117. setTo(_history);
  1118. }
  1119. }
  1120. void MainWidget::showChooseReportMessages(
  1121. not_null<PeerData*> peer,
  1122. Data::ReportInput reportInput,
  1123. Fn<void(std::vector<MsgId>)> done) {
  1124. _history->setChooseReportMessagesDetails(reportInput, std::move(done));
  1125. _controller->showPeerHistory(
  1126. peer,
  1127. SectionShow::Way::Forward,
  1128. ShowForChooseMessagesMsgId);
  1129. controller()->showToast(tr::lng_report_please_select_messages(tr::now));
  1130. }
  1131. void MainWidget::clearChooseReportMessages() {
  1132. _history->setChooseReportMessagesDetails({}, nullptr);
  1133. }
  1134. void MainWidget::toggleChooseChatTheme(
  1135. not_null<PeerData*> peer,
  1136. std::optional<bool> show) {
  1137. _history->toggleChooseChatTheme(peer, show);
  1138. }
  1139. bool MainWidget::showHistoryInDifferentWindow(
  1140. PeerId peerId,
  1141. const SectionShow &params,
  1142. MsgId showAtMsgId) {
  1143. if (!peerId) {
  1144. // In case we don't have dialogs, we can't clear section stack.
  1145. return !_dialogs;
  1146. }
  1147. const auto peer = session().data().peer(peerId);
  1148. if (const auto separateChat = _controller->windowId().chat()) {
  1149. if (const auto history = separateChat->asHistory()) {
  1150. if (history->peer == peer) {
  1151. return false;
  1152. }
  1153. }
  1154. }
  1155. const auto window = Core::App().windowForShowingHistory(peer);
  1156. if (window == &_controller->window()) {
  1157. return false;
  1158. } else if (window) {
  1159. window->sessionController()->showPeerHistory(
  1160. peerId,
  1161. params,
  1162. showAtMsgId);
  1163. window->activate();
  1164. return true;
  1165. } else if (windowId().hasChatsList()) {
  1166. return false;
  1167. }
  1168. const auto account = not_null(&session().account());
  1169. auto primary = Core::App().separateWindowFor(account);
  1170. if (!primary) {
  1171. Core::App().domain().activate(account);
  1172. primary = Core::App().separateWindowFor(account);
  1173. }
  1174. if (primary && &primary->account() == account) {
  1175. primary->sessionController()->showPeerHistory(
  1176. peerId,
  1177. params,
  1178. showAtMsgId);
  1179. primary->activate();
  1180. }
  1181. return true;
  1182. }
  1183. void MainWidget::showHistory(
  1184. PeerId peerId,
  1185. const SectionShow &params,
  1186. MsgId showAtMsgId) {
  1187. if (peerId && _controller->window().locked()) {
  1188. if (params.activation != anim::activation::background) {
  1189. _controller->window().activate();
  1190. }
  1191. return;
  1192. } else if (auto peer = session().data().peerLoaded(peerId)) {
  1193. if (peer->migrateTo()) {
  1194. peer = peer->migrateTo();
  1195. peerId = peer->id;
  1196. if (showAtMsgId > 0) {
  1197. showAtMsgId = -showAtMsgId;
  1198. }
  1199. }
  1200. const auto unavailable = peer->computeUnavailableReason();
  1201. if (!unavailable.isEmpty()) {
  1202. if (!isPrimary()) {
  1203. _controller->window().close();
  1204. } else if (params.activation != anim::activation::background) {
  1205. _controller->show(Ui::MakeInformBox(unavailable));
  1206. _controller->window().activate();
  1207. }
  1208. return;
  1209. }
  1210. }
  1211. if ((IsServerMsgId(showAtMsgId) || Data::IsScheduledMsgId(showAtMsgId))
  1212. && _mainSection
  1213. && _mainSection->showMessage(peerId, params, showAtMsgId)) {
  1214. session().data().hideShownSpoilers();
  1215. if (params.activation != anim::activation::background) {
  1216. _controller->window().activate();
  1217. }
  1218. return;
  1219. } else if (showHistoryInDifferentWindow(peerId, params, showAtMsgId)) {
  1220. return;
  1221. }
  1222. if (peerId && params.activation != anim::activation::background) {
  1223. Core::App().hideMediaView();
  1224. _controller->window().activate();
  1225. }
  1226. const auto alreadyThatPeer = _history->peer()
  1227. && (_history->peer()->id == peerId);
  1228. if (!alreadyThatPeer
  1229. && preventsCloseSection(
  1230. [=] { showHistory(peerId, params, showAtMsgId); },
  1231. params)) {
  1232. return;
  1233. }
  1234. using OriginMessage = SectionShow::OriginMessage;
  1235. if (const auto origin = std::get_if<OriginMessage>(&params.origin)) {
  1236. if (const auto returnTo = session().data().message(origin->id)) {
  1237. if (returnTo->history()->peer->id == peerId) {
  1238. _history->pushReplyReturn(returnTo);
  1239. }
  1240. }
  1241. }
  1242. _a_dialogsWidth.stop();
  1243. using Way = SectionShow::Way;
  1244. auto way = params.way;
  1245. bool back = (way == Way::Backward || !peerId);
  1246. bool foundInStack = !peerId;
  1247. if (foundInStack || (way == Way::ClearStack)) {
  1248. for (const auto &item : _stack) {
  1249. ClearBotStartToken(item->peer());
  1250. }
  1251. _stack.clear();
  1252. } else {
  1253. for (auto i = 0, s = int(_stack.size()); i < s; ++i) {
  1254. if (_stack.at(i)->type() == HistoryStackItem && _stack.at(i)->peer()->id == peerId) {
  1255. foundInStack = true;
  1256. while (int(_stack.size()) > i + 1) {
  1257. ClearBotStartToken(_stack.back()->peer());
  1258. _stack.pop_back();
  1259. }
  1260. _stack.pop_back();
  1261. if (!back) {
  1262. back = true;
  1263. }
  1264. break;
  1265. }
  1266. }
  1267. if (const auto activeChat = _controller->activeChatCurrent()) {
  1268. if (const auto peer = activeChat.peer()) {
  1269. if (way == Way::Forward && peer->id == peerId) {
  1270. way = _mainSection ? Way::Backward : Way::ClearStack;
  1271. }
  1272. }
  1273. }
  1274. }
  1275. const auto wasActivePeer = _controller->activeChatCurrent().peer();
  1276. if (params.activation != anim::activation::background) {
  1277. _controller->window().hideSettingsAndLayer();
  1278. }
  1279. auto animatedShow = [&] {
  1280. if (_showAnimation
  1281. || Core::App().passcodeLocked()
  1282. || (params.animated == anim::type::instant)) {
  1283. return false;
  1284. }
  1285. if (!peerId) {
  1286. if (isOneColumn()) {
  1287. return _dialogs && _dialogs->isHidden();
  1288. } else {
  1289. return false;
  1290. }
  1291. }
  1292. if (_history->isHidden()) {
  1293. if (!isOneColumn() && way == Way::ClearStack) {
  1294. return false;
  1295. }
  1296. return (_mainSection != nullptr)
  1297. || (isOneColumn() && _dialogs && !_dialogs->isHidden());
  1298. }
  1299. if (back || way == Way::Forward) {
  1300. return true;
  1301. }
  1302. return false;
  1303. };
  1304. auto animationParams = animatedShow()
  1305. ? prepareHistoryAnimation(peerId)
  1306. : Window::SectionSlideParams();
  1307. if (!back && (way != Way::ClearStack)) {
  1308. // This may modify the current section, for example remove its contents.
  1309. saveSectionInStack(params);
  1310. }
  1311. if (_history->peer()
  1312. && _history->peer()->id != peerId
  1313. && way != Way::Forward) {
  1314. ClearBotStartToken(_history->peer());
  1315. }
  1316. _history->showHistory(peerId, showAtMsgId, params);
  1317. if (alreadyThatPeer && params.reapplyLocalDraft) {
  1318. _history->applyDraft(HistoryWidget::FieldHistoryAction::NewEntry);
  1319. }
  1320. auto noPeer = !_history->peer();
  1321. auto onlyDialogs = noPeer && isOneColumn();
  1322. _mainSection.destroy();
  1323. updateMainSectionShown();
  1324. updateControlsGeometry();
  1325. if (noPeer) {
  1326. _controller->setActiveChatEntry(Dialogs::Key());
  1327. _controller->setChatStyleTheme(_controller->defaultChatTheme());
  1328. }
  1329. if (onlyDialogs) {
  1330. Assert(_dialogs != nullptr);
  1331. _history->hide();
  1332. if (!_showAnimation) {
  1333. if (animationParams) {
  1334. auto direction = back ? Window::SlideDirection::FromLeft : Window::SlideDirection::FromRight;
  1335. _dialogs->showAnimated(direction, animationParams);
  1336. } else {
  1337. _dialogs->showFast();
  1338. }
  1339. }
  1340. } else {
  1341. const auto nowActivePeer = _controller->activeChatCurrent().peer();
  1342. if (nowActivePeer && nowActivePeer != wasActivePeer) {
  1343. session().api().views().removeIncremented(nowActivePeer);
  1344. }
  1345. if (isOneColumn() && _dialogs && !_dialogs->isHidden()) {
  1346. _dialogs->hide();
  1347. }
  1348. if (!_showAnimation) {
  1349. if (!animationParams.oldContentCache.isNull()) {
  1350. _history->showAnimated(
  1351. back
  1352. ? Window::SlideDirection::FromLeft
  1353. : Window::SlideDirection::FromRight,
  1354. animationParams);
  1355. } else {
  1356. _history->show();
  1357. crl::on_main(this, [=] {
  1358. _controller->widget()->setInnerFocus();
  1359. });
  1360. }
  1361. }
  1362. }
  1363. if (_dialogs && !_dialogs->isHidden()) {
  1364. if (!back) {
  1365. if (const auto history = _history->history()) {
  1366. _dialogs->scrollToEntry(Dialogs::RowDescriptor(
  1367. history,
  1368. FullMsgId(history->peer->id, showAtMsgId)));
  1369. }
  1370. }
  1371. _dialogs->update();
  1372. }
  1373. floatPlayerCheckVisibility();
  1374. }
  1375. void MainWidget::showMessage(
  1376. not_null<const HistoryItem*> item,
  1377. const SectionShow &params) {
  1378. const auto peerId = item->history()->peer->id;
  1379. const auto itemId = item->id;
  1380. if (!v::is_null(params.origin)) {
  1381. if (_mainSection) {
  1382. if (_mainSection->showMessage(peerId, params, itemId)) {
  1383. if (params.activation != anim::activation::background) {
  1384. _controller->window().activate();
  1385. _controller->window().hideSettingsAndLayer();
  1386. }
  1387. return;
  1388. }
  1389. } else if (_history->peer() == item->history()->peer) {
  1390. // showHistory may be redirected to different window,
  1391. // so we don't call activate() on current controller's window.
  1392. showHistory(peerId, params, itemId);
  1393. return;
  1394. }
  1395. }
  1396. if (const auto topic = item->topic()) {
  1397. _controller->showTopic(topic, item->id, params);
  1398. if (params.activation != anim::activation::background) {
  1399. _controller->window().activate();
  1400. }
  1401. } else {
  1402. // showPeerHistory may be redirected to different window,
  1403. // so we don't call activate() on current controller's window.
  1404. _controller->showPeerHistory(
  1405. item->history(),
  1406. params,
  1407. item->id);
  1408. }
  1409. }
  1410. void MainWidget::showForum(
  1411. not_null<Data::Forum*> forum,
  1412. const SectionShow &params) {
  1413. Expects(_dialogs != nullptr);
  1414. _dialogs->showForum(forum, params);
  1415. if (params.activation != anim::activation::background) {
  1416. _controller->window().hideSettingsAndLayer();
  1417. }
  1418. }
  1419. PeerData *MainWidget::peer() const {
  1420. return _history->peer();
  1421. }
  1422. Ui::ChatTheme *MainWidget::customChatTheme() const {
  1423. return _history->customChatTheme();
  1424. }
  1425. bool MainWidget::saveSectionInStack(
  1426. const SectionShow &params,
  1427. Window::SectionWidget *newMainSection) {
  1428. if (_mainSection) {
  1429. if (auto memento = _mainSection->createMemento()) {
  1430. if (params.dropSameFromStack
  1431. && newMainSection
  1432. && newMainSection->sameTypeAs(memento.get())) {
  1433. // When choosing saved sublist we want to save the original
  1434. // "Saved Messages" in the stack, but don't save every
  1435. // sublist in a new stack entry when clicking them through.
  1436. return false;
  1437. }
  1438. _stack.push_back(std::make_unique<StackItemSection>(
  1439. std::move(memento)));
  1440. } else {
  1441. return false;
  1442. }
  1443. } else if (const auto history = _history->history()) {
  1444. _stack.push_back(std::make_unique<StackItemHistory>(
  1445. history,
  1446. _history->msgId(),
  1447. _history->replyReturns()));
  1448. } else {
  1449. // We pretend that we "saved" the chats list state in stack,
  1450. // so that we do animate a transition from chats list to a section.
  1451. return true;
  1452. }
  1453. const auto raw = _stack.back().get();
  1454. raw->setThirdSectionWeak(_thirdSection.data());
  1455. raw->removeRequests(
  1456. ) | rpl::start_with_next([=] {
  1457. for (auto i = begin(_stack); i != end(_stack); ++i) {
  1458. if (i->get() == raw) {
  1459. _stack.erase(i);
  1460. return;
  1461. }
  1462. }
  1463. }, raw->lifetime());
  1464. return true;
  1465. }
  1466. void MainWidget::showSection(
  1467. std::shared_ptr<Window::SectionMemento> memento,
  1468. const SectionShow &params) {
  1469. if (_mainSection && _mainSection->showInternal(
  1470. memento.get(),
  1471. params)) {
  1472. if (params.activation != anim::activation::background) {
  1473. _controller->window().hideSettingsAndLayer();
  1474. }
  1475. if (const auto entry = _mainSection->activeChat(); entry.key) {
  1476. _controller->setActiveChatEntry(entry);
  1477. }
  1478. return;
  1479. //
  1480. // Now third section handles only its own showSection() requests.
  1481. // General showSection() should show layer or main_section instead.
  1482. //
  1483. //} else if (_thirdSection && _thirdSection->showInternal(
  1484. // &memento,
  1485. // params)) {
  1486. // return;
  1487. }
  1488. if (preventsCloseSection(
  1489. [=] { showSection(memento, params); },
  1490. params)) {
  1491. return;
  1492. }
  1493. // If the window was not resized, but we've enabled
  1494. // tabbedSelectorSectionEnabled or thirdSectionInfoEnabled
  1495. // we need to update adaptive layout to Adaptive::ThirdColumn().
  1496. updateColumnLayout();
  1497. showNewSection(std::move(memento), params);
  1498. }
  1499. void MainWidget::updateColumnLayout() {
  1500. updateWindowAdaptiveLayout();
  1501. }
  1502. Window::SectionSlideParams MainWidget::prepareThirdSectionAnimation(Window::SectionWidget *section) {
  1503. Expects(_thirdSection != nullptr);
  1504. Window::SectionSlideParams result;
  1505. result.withTopBarShadow = section->hasTopBarShadow();
  1506. if (!_thirdSection->hasTopBarShadow()) {
  1507. result.withTopBarShadow = false;
  1508. }
  1509. floatPlayerHideAll();
  1510. result.oldContentCache = _thirdSection->grabForShowAnimation(result);
  1511. floatPlayerShowVisible();
  1512. return result;
  1513. }
  1514. Window::SectionSlideParams MainWidget::prepareShowAnimation(
  1515. bool willHaveTopBarShadow) {
  1516. Window::SectionSlideParams result;
  1517. result.withTopBarShadow = willHaveTopBarShadow;
  1518. if (_mainSection) {
  1519. if (!_mainSection->hasTopBarShadow()) {
  1520. result.withTopBarShadow = false;
  1521. }
  1522. } else if (!_history->peer()) {
  1523. result.withTopBarShadow = false;
  1524. }
  1525. floatPlayerHideAll();
  1526. if (_player) {
  1527. _player->entity()->hideShadowAndDropdowns();
  1528. }
  1529. const auto playerPlaylistVisible = !_playerPlaylist->isHidden();
  1530. if (playerPlaylistVisible) {
  1531. _playerPlaylist->hide();
  1532. }
  1533. const auto hiderVisible = (_hider && !_hider->isHidden());
  1534. if (hiderVisible) {
  1535. _hider->hide();
  1536. }
  1537. auto sectionTop = getMainSectionTop();
  1538. if (_mainSection) {
  1539. result.oldContentCache = _mainSection->grabForShowAnimation(result);
  1540. } else if (!isOneColumn() || !_history->isHidden()) {
  1541. result.oldContentCache = _history->grabForShowAnimation(result);
  1542. } else {
  1543. result.oldContentCache = Ui::GrabWidget(this, QRect(
  1544. 0,
  1545. sectionTop,
  1546. _dialogsWidth,
  1547. height() - sectionTop));
  1548. }
  1549. if (_hider && hiderVisible) {
  1550. _hider->show();
  1551. }
  1552. if (playerPlaylistVisible) {
  1553. _playerPlaylist->show();
  1554. }
  1555. if (_player) {
  1556. _player->entity()->showShadowAndDropdowns();
  1557. }
  1558. floatPlayerShowVisible();
  1559. return result;
  1560. }
  1561. Window::SectionSlideParams MainWidget::prepareMainSectionAnimation(Window::SectionWidget *section) {
  1562. return prepareShowAnimation(section->hasTopBarShadow());
  1563. }
  1564. Window::SectionSlideParams MainWidget::prepareHistoryAnimation(PeerId historyPeerId) {
  1565. return prepareShowAnimation(historyPeerId != 0);
  1566. }
  1567. Window::SectionSlideParams MainWidget::prepareDialogsAnimation() {
  1568. return prepareShowAnimation(false);
  1569. }
  1570. void MainWidget::showNewSection(
  1571. std::shared_ptr<Window::SectionMemento> memento,
  1572. const SectionShow &params) {
  1573. using Column = Window::Column;
  1574. if (_controller->window().locked()) {
  1575. return;
  1576. }
  1577. auto saveInStack = (params.way == SectionShow::Way::Forward);
  1578. const auto thirdSectionTop = getThirdSectionTop();
  1579. const auto newThirdGeometry = QRect(
  1580. width() - st::columnMinimalWidthThird,
  1581. thirdSectionTop,
  1582. st::columnMinimalWidthThird,
  1583. height() - thirdSectionTop);
  1584. auto newThirdSection = (isThreeColumn() && params.thirdColumn)
  1585. ? memento->createWidget(
  1586. this,
  1587. _controller,
  1588. Column::Third,
  1589. newThirdGeometry)
  1590. : nullptr;
  1591. const auto layerRect = parentWidget()->rect();
  1592. if (newThirdSection) {
  1593. saveInStack = false;
  1594. } else if (auto layer = memento->createLayer(_controller, layerRect)) {
  1595. if (params.activation != anim::activation::background) {
  1596. _controller->hideLayer(anim::type::instant);
  1597. }
  1598. _controller->showSpecialLayer(std::move(layer));
  1599. return;
  1600. }
  1601. if (params.activation != anim::activation::background) {
  1602. _controller->window().hideSettingsAndLayer();
  1603. }
  1604. _a_dialogsWidth.stop();
  1605. auto mainSectionTop = getMainSectionTop();
  1606. auto newMainGeometry = QRect(
  1607. _history->x(),
  1608. mainSectionTop,
  1609. _history->width(),
  1610. height() - mainSectionTop);
  1611. auto newMainSection = newThirdSection
  1612. ? nullptr
  1613. : memento->createWidget(
  1614. this,
  1615. _controller,
  1616. isOneColumn() ? Column::First : Column::Second,
  1617. newMainGeometry);
  1618. Assert(newMainSection || newThirdSection);
  1619. auto animatedShow = [&] {
  1620. if (_showAnimation
  1621. || Core::App().passcodeLocked()
  1622. || (params.animated == anim::type::instant)
  1623. || memento->instant()) {
  1624. return false;
  1625. }
  1626. if (!isOneColumn() && params.way == SectionShow::Way::ClearStack) {
  1627. return false;
  1628. } else if (isOneColumn()
  1629. || (newThirdSection && _thirdSection)
  1630. || (newMainSection && isMainSectionShown())) {
  1631. return true;
  1632. }
  1633. return false;
  1634. }();
  1635. auto animationParams = animatedShow
  1636. ? (newThirdSection
  1637. ? prepareThirdSectionAnimation(newThirdSection)
  1638. : prepareMainSectionAnimation(newMainSection))
  1639. : Window::SectionSlideParams();
  1640. setFocus(); // otherwise dialogs widget could be focused.
  1641. if (saveInStack) {
  1642. // This may modify the current section, for example remove its contents.
  1643. if (!saveSectionInStack(params, newMainSection)) {
  1644. saveInStack = false;
  1645. animatedShow = false;
  1646. animationParams = Window::SectionSlideParams();
  1647. }
  1648. }
  1649. auto &settingSection = newThirdSection
  1650. ? _thirdSection
  1651. : _mainSection;
  1652. if (newThirdSection) {
  1653. _thirdSection = std::move(newThirdSection);
  1654. _thirdSection->removeRequests(
  1655. ) | rpl::start_with_next([=] {
  1656. destroyThirdSection();
  1657. _thirdShadow.destroy();
  1658. updateControlsGeometry();
  1659. }, _thirdSection->lifetime());
  1660. if (!_thirdShadow) {
  1661. _thirdShadow.create(this);
  1662. _thirdShadow->show();
  1663. orderWidgets();
  1664. }
  1665. updateControlsGeometry();
  1666. } else {
  1667. _mainSection = std::move(newMainSection);
  1668. _history->finishAnimating();
  1669. _history->showHistory(PeerId(), MsgId());
  1670. if (const auto entry = _mainSection->activeChat(); entry.key) {
  1671. _controller->setActiveChatEntry(entry);
  1672. }
  1673. updateMainSectionShown();
  1674. // Depends on SessionController::activeChatEntry
  1675. // for tabbed selector showing in the third column.
  1676. updateControlsGeometry();
  1677. _history->hide();
  1678. if (isOneColumn() && _dialogs) {
  1679. _dialogs->hide();
  1680. }
  1681. }
  1682. if (animationParams) {
  1683. auto back = (params.way == SectionShow::Way::Backward);
  1684. auto direction = (back || settingSection->forceAnimateBack())
  1685. ? Window::SlideDirection::FromLeft
  1686. : Window::SlideDirection::FromRight;
  1687. if (isOneColumn()) {
  1688. _controller->removeLayerBlackout();
  1689. }
  1690. settingSection->showAnimated(direction, animationParams);
  1691. } else {
  1692. settingSection->showFast();
  1693. }
  1694. floatPlayerCheckVisibility();
  1695. orderWidgets();
  1696. }
  1697. void MainWidget::checkMainSectionToLayer() {
  1698. if (!_mainSection) {
  1699. return;
  1700. }
  1701. Ui::FocusPersister persister(this);
  1702. if (auto layer = _mainSection->moveContentToLayer(rect())) {
  1703. _mainSection.destroy();
  1704. _controller->showBackFromStack(
  1705. SectionShow(
  1706. anim::type::instant,
  1707. anim::activation::background));
  1708. _controller->showSpecialLayer(
  1709. std::move(layer),
  1710. anim::type::instant);
  1711. }
  1712. updateMainSectionShown();
  1713. }
  1714. Window::SeparateId MainWidget::windowId() const {
  1715. return _controller->windowId();
  1716. }
  1717. bool MainWidget::isPrimary() const {
  1718. return _controller->isPrimary();
  1719. }
  1720. bool MainWidget::isMainSectionShown() const {
  1721. return _mainSection || _history->peer();
  1722. }
  1723. bool MainWidget::isThirdSectionShown() const {
  1724. return _thirdSection != nullptr;
  1725. }
  1726. Dialogs::RowDescriptor MainWidget::resolveChatNext(
  1727. Dialogs::RowDescriptor from) const {
  1728. return _dialogs ? _dialogs->resolveChatNext(from) : Dialogs::RowDescriptor();
  1729. }
  1730. Dialogs::RowDescriptor MainWidget::resolveChatPrevious(
  1731. Dialogs::RowDescriptor from) const {
  1732. return _dialogs ? _dialogs->resolveChatPrevious(from) : Dialogs::RowDescriptor();
  1733. }
  1734. bool MainWidget::stackIsEmpty() const {
  1735. return _stack.empty();
  1736. }
  1737. bool MainWidget::preventsCloseSection(Fn<void()> callback) const {
  1738. if (Core::App().passcodeLocked()) {
  1739. return false;
  1740. }
  1741. auto copy = callback;
  1742. return (_mainSection && _mainSection->preventsClose(std::move(copy)))
  1743. || (_history && _history->preventsClose(std::move(callback)));
  1744. }
  1745. bool MainWidget::preventsCloseSection(
  1746. Fn<void()> callback,
  1747. const SectionShow &params) const {
  1748. return !params.thirdColumn
  1749. && (params.activation != anim::activation::background)
  1750. && preventsCloseSection(std::move(callback));
  1751. }
  1752. void MainWidget::showNonPremiumLimitToast(bool download) {
  1753. const auto parent = _mainSection
  1754. ? ((QWidget*)_mainSection.data())
  1755. : (_dialogs && _history->isHidden())
  1756. ? ((QWidget*)_dialogs.get())
  1757. : ((QWidget*)_history.get());
  1758. const auto link = download
  1759. ? tr::lng_limit_download_subscribe_link(tr::now)
  1760. : tr::lng_limit_upload_subscribe_link(tr::now);
  1761. const auto better = session().appConfig().get<double>(download
  1762. ? u"upload_premium_speedup_download"_q
  1763. : u"upload_premium_speedup_upload"_q, 10.);
  1764. const auto percent = int(base::SafeRound(better * 100.));
  1765. if (percent <= 100) {
  1766. return;
  1767. }
  1768. const auto increase = ((percent % 100) || percent <= 400)
  1769. ? (download
  1770. ? tr::lng_limit_download_increase_speed
  1771. : tr::lng_limit_upload_increase_speed)(
  1772. tr::now,
  1773. lt_percent,
  1774. TextWithEntities{ QString::number(percent - 100) },
  1775. Ui::Text::RichLangValue)
  1776. : (download
  1777. ? tr::lng_limit_download_increase_times
  1778. : tr::lng_limit_upload_increase_times)(
  1779. tr::now,
  1780. lt_count,
  1781. percent / 100,
  1782. Ui::Text::RichLangValue);
  1783. auto text = (download
  1784. ? tr::lng_limit_download_subscribe
  1785. : tr::lng_limit_upload_subscribe)(
  1786. tr::now,
  1787. lt_link,
  1788. Ui::Text::Link(Ui::Text::Bold(link)),
  1789. lt_increase,
  1790. TextWithEntities{ increase },
  1791. Ui::Text::RichLangValue);
  1792. auto filter = [=](ClickHandlerPtr handler, Qt::MouseButton button) {
  1793. Settings::ShowPremium(
  1794. controller(),
  1795. download ? u"download_limit"_q : u"upload_limit"_q);
  1796. return false;
  1797. };
  1798. Ui::Toast::Show(parent, {
  1799. .title = (download
  1800. ? tr::lng_limit_download_title
  1801. : tr::lng_limit_upload_title)(tr::now),
  1802. .text = std::move(text),
  1803. .filter = std::move(filter),
  1804. .attach = RectPart::Top,
  1805. .duration = 5 * crl::time(1000),
  1806. });
  1807. }
  1808. bool MainWidget::showBackFromStack(const SectionShow &params) {
  1809. if (preventsCloseSection([=] { showBackFromStack(params); }, params)) {
  1810. return false;
  1811. }
  1812. if (_stack.empty()) {
  1813. if (_dialogs) {
  1814. _controller->clearSectionStack(params);
  1815. }
  1816. crl::on_main(this, [=] {
  1817. _controller->widget()->setInnerFocus();
  1818. });
  1819. return (_dialogs != nullptr);
  1820. }
  1821. auto item = std::move(_stack.back());
  1822. _stack.pop_back();
  1823. if (const auto currentHistoryPeer = _history->peer()) {
  1824. ClearBotStartToken(currentHistoryPeer);
  1825. }
  1826. _thirdSectionFromStack = item->takeThirdSectionMemento();
  1827. if (item->type() == HistoryStackItem) {
  1828. auto historyItem = static_cast<StackItemHistory*>(item.get());
  1829. _controller->showPeerHistory(
  1830. historyItem->peer()->id,
  1831. params.withWay(SectionShow::Way::Backward),
  1832. ShowAtUnreadMsgId);
  1833. _history->setReplyReturns(
  1834. historyItem->peer()->id,
  1835. std::move(historyItem->replyReturns));
  1836. } else if (item->type() == SectionStackItem) {
  1837. auto sectionItem = static_cast<StackItemSection*>(item.get());
  1838. showNewSection(
  1839. sectionItem->takeMemento(),
  1840. params.withWay(SectionShow::Way::Backward));
  1841. }
  1842. if (_thirdSectionFromStack && _thirdSection) {
  1843. _controller->showSection(
  1844. base::take(_thirdSectionFromStack),
  1845. SectionShow(
  1846. SectionShow::Way::ClearStack,
  1847. anim::type::instant,
  1848. anim::activation::background));
  1849. }
  1850. return true;
  1851. }
  1852. void MainWidget::orderWidgets() {
  1853. if (_dialogs) {
  1854. _dialogs->raiseWithTooltip();
  1855. }
  1856. if (_player) {
  1857. _player->raise();
  1858. }
  1859. if (_exportTopBar) {
  1860. _exportTopBar->raise();
  1861. }
  1862. if (_callTopBar) {
  1863. _callTopBar->raise();
  1864. }
  1865. if (_sideShadow) {
  1866. _sideShadow->raise();
  1867. }
  1868. if (_thirdShadow) {
  1869. _thirdShadow->raise();
  1870. }
  1871. if (_firstColumnResizeArea) {
  1872. _firstColumnResizeArea->raise();
  1873. }
  1874. if (_thirdColumnResizeArea) {
  1875. _thirdColumnResizeArea->raise();
  1876. }
  1877. if (_connecting) {
  1878. _connecting->raise();
  1879. }
  1880. floatPlayerRaiseAll();
  1881. _playerPlaylist->raise();
  1882. if (_player) {
  1883. _player->entity()->raiseDropdowns();
  1884. }
  1885. if (_hider) _hider->raise();
  1886. }
  1887. QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams &params) {
  1888. QPixmap result;
  1889. floatPlayerHideAll();
  1890. if (_player) {
  1891. _player->entity()->hideShadowAndDropdowns();
  1892. }
  1893. const auto playerPlaylistVisible = !_playerPlaylist->isHidden();
  1894. if (playerPlaylistVisible) {
  1895. _playerPlaylist->hide();
  1896. }
  1897. const auto hiderVisible = (_hider && !_hider->isHidden());
  1898. if (hiderVisible) {
  1899. _hider->hide();
  1900. }
  1901. auto sectionTop = getMainSectionTop();
  1902. if (isOneColumn()) {
  1903. result = Ui::GrabWidget(this, QRect(
  1904. 0,
  1905. sectionTop,
  1906. width(),
  1907. height() - sectionTop));
  1908. } else {
  1909. if (_sideShadow) {
  1910. _sideShadow->hide();
  1911. }
  1912. if (_thirdShadow) {
  1913. _thirdShadow->hide();
  1914. }
  1915. result = Ui::GrabWidget(this, QRect(
  1916. _dialogsWidth,
  1917. sectionTop,
  1918. width() - _dialogsWidth,
  1919. height() - sectionTop));
  1920. if (_sideShadow) {
  1921. _sideShadow->show();
  1922. }
  1923. if (_thirdShadow) {
  1924. _thirdShadow->show();
  1925. }
  1926. }
  1927. if (_hider && hiderVisible) {
  1928. _hider->show();
  1929. }
  1930. if (playerPlaylistVisible) {
  1931. _playerPlaylist->show();
  1932. }
  1933. if (_player) {
  1934. _player->entity()->showShadowAndDropdowns();
  1935. }
  1936. floatPlayerShowVisible();
  1937. return result;
  1938. }
  1939. void MainWidget::windowShown() {
  1940. _history->windowShown();
  1941. }
  1942. void MainWidget::dialogsToUp() {
  1943. if (_dialogs) {
  1944. _dialogs->jumpToTop();
  1945. }
  1946. }
  1947. void MainWidget::checkActivation() {
  1948. _history->checkActivation();
  1949. if (_mainSection) {
  1950. _mainSection->checkActivation();
  1951. }
  1952. }
  1953. void MainWidget::showAnimated(QPixmap oldContentCache, bool back) {
  1954. _showAnimation = nullptr;
  1955. showAll();
  1956. floatPlayerHideAll();
  1957. auto newContentCache = Ui::GrabWidget(this);
  1958. hideAll();
  1959. floatPlayerShowVisible();
  1960. _showAnimation = std::make_unique<Window::SlideAnimation>();
  1961. _showAnimation->setDirection(back
  1962. ? Window::SlideDirection::FromLeft
  1963. : Window::SlideDirection::FromRight);
  1964. _showAnimation->setRepaintCallback([=] { update(); });
  1965. _showAnimation->setFinishedCallback([=] { showFinished(); });
  1966. _showAnimation->setPixmaps(oldContentCache, newContentCache);
  1967. _showAnimation->start();
  1968. show();
  1969. }
  1970. void MainWidget::showFinished() {
  1971. _showAnimation = nullptr;
  1972. showAll();
  1973. activate();
  1974. }
  1975. void MainWidget::paintEvent(QPaintEvent *e) {
  1976. if (_background) {
  1977. checkChatBackground();
  1978. }
  1979. if (_showAnimation) {
  1980. auto p = QPainter(this);
  1981. _showAnimation->paintContents(p);
  1982. }
  1983. }
  1984. int MainWidget::getMainSectionTop() const {
  1985. return _callTopBarHeight + _exportTopBarHeight + _playerHeight;
  1986. }
  1987. int MainWidget::getThirdSectionTop() const {
  1988. return 0;
  1989. }
  1990. void MainWidget::hideAll() {
  1991. if (_dialogs) {
  1992. _dialogs->hide();
  1993. }
  1994. _history->hide();
  1995. if (_mainSection) {
  1996. _mainSection->hide();
  1997. }
  1998. if (_thirdSection) {
  1999. _thirdSection->hide();
  2000. }
  2001. if (_sideShadow) {
  2002. _sideShadow->hide();
  2003. }
  2004. if (_thirdShadow) {
  2005. _thirdShadow->hide();
  2006. }
  2007. if (_player) {
  2008. _player->setVisible(false);
  2009. _playerHeight = 0;
  2010. }
  2011. if (_callTopBar) {
  2012. _callTopBar->setVisible(false);
  2013. _callTopBarHeight = 0;
  2014. }
  2015. }
  2016. void MainWidget::showAll() {
  2017. if (cPasswordRecovered()) {
  2018. cSetPasswordRecovered(false);
  2019. _controller->show(Ui::MakeInformBox(
  2020. tr::lng_cloud_password_updated()));
  2021. }
  2022. if (isOneColumn()) {
  2023. if (_sideShadow) {
  2024. _sideShadow->hide();
  2025. }
  2026. if (_hider) {
  2027. _hider->hide();
  2028. }
  2029. if (_mainSection) {
  2030. _mainSection->show();
  2031. } else if (_history->peer()) {
  2032. _history->show();
  2033. _history->updateControlsGeometry();
  2034. } else {
  2035. Assert(_dialogs != nullptr);
  2036. _dialogs->showFast();
  2037. _history->hide();
  2038. }
  2039. if (_dialogs && isMainSectionShown()) {
  2040. _dialogs->hide();
  2041. }
  2042. } else {
  2043. if (_sideShadow) {
  2044. _sideShadow->show();
  2045. }
  2046. if (_hider) {
  2047. _hider->show();
  2048. }
  2049. if (_dialogs) {
  2050. _dialogs->showFast();
  2051. }
  2052. if (_mainSection) {
  2053. _mainSection->show();
  2054. } else {
  2055. _history->show();
  2056. _history->updateControlsGeometry();
  2057. }
  2058. if (_thirdSection) {
  2059. _thirdSection->show();
  2060. }
  2061. if (_thirdShadow) {
  2062. _thirdShadow->show();
  2063. }
  2064. }
  2065. if (_player) {
  2066. _player->setVisible(true);
  2067. _playerHeight = _player->contentHeight();
  2068. }
  2069. if (_callTopBar) {
  2070. _callTopBar->setVisible(true);
  2071. // show() could've send pending resize event that would update
  2072. // the height value and destroy the top bar if it was hiding.
  2073. if (_callTopBar) {
  2074. _callTopBarHeight = _callTopBar->height();
  2075. }
  2076. }
  2077. updateControlsGeometry();
  2078. floatPlayerCheckVisibility();
  2079. _controller->widget()->checkActivation();
  2080. }
  2081. void MainWidget::resizeEvent(QResizeEvent *e) {
  2082. updateControlsGeometry();
  2083. }
  2084. void MainWidget::updateMainSectionShown() {
  2085. _controller->setMainSectionShown(_mainSection || _history->peer());
  2086. }
  2087. void MainWidget::updateControlsGeometry() {
  2088. if (!width()) {
  2089. return;
  2090. }
  2091. updateWindowAdaptiveLayout();
  2092. if (_dialogs) {
  2093. const auto nochat = !_controller->mainSectionShown();
  2094. if (Core::App().settings().dialogsWidthRatio(nochat) > 0) {
  2095. _a_dialogsWidth.stop();
  2096. }
  2097. if (!_a_dialogsWidth.animating()) {
  2098. _dialogs->stopWidthAnimation();
  2099. }
  2100. }
  2101. if (isThreeColumn()) {
  2102. if (!_thirdSection
  2103. && !_controller->takeThirdSectionFromLayer()) {
  2104. auto params = Window::SectionShow(
  2105. Window::SectionShow::Way::ClearStack,
  2106. anim::type::instant,
  2107. anim::activation::background);
  2108. const auto active = _controller->activeChatCurrent();
  2109. if (const auto thread = active.thread()) {
  2110. if (Core::App().settings().tabbedSelectorSectionEnabled()) {
  2111. if (_mainSection) {
  2112. _mainSection->pushTabbedSelectorToThirdSection(
  2113. thread,
  2114. params);
  2115. } else {
  2116. _history->pushTabbedSelectorToThirdSection(
  2117. thread,
  2118. params);
  2119. }
  2120. } else if (Core::App().settings().thirdSectionInfoEnabled()) {
  2121. _controller->showSection(
  2122. (thread->asTopic()
  2123. ? std::make_shared<Info::Memento>(
  2124. thread->asTopic())
  2125. : Info::Memento::Default(
  2126. thread->asHistory()->peer)),
  2127. params.withThirdColumn());
  2128. }
  2129. }
  2130. }
  2131. } else {
  2132. destroyThirdSection();
  2133. _thirdShadow.destroy();
  2134. }
  2135. const auto mainSectionTop = getMainSectionTop();
  2136. auto dialogsWidth = _dialogs
  2137. ? qRound(_a_dialogsWidth.value(_dialogsWidth))
  2138. : isOneColumn()
  2139. ? width()
  2140. : 0;
  2141. if (isOneColumn()) {
  2142. if (_callTopBar) {
  2143. _callTopBar->resizeToWidth(dialogsWidth);
  2144. _callTopBar->moveToLeft(0, 0);
  2145. }
  2146. if (_exportTopBar) {
  2147. _exportTopBar->resizeToWidth(dialogsWidth);
  2148. _exportTopBar->moveToLeft(0, _callTopBarHeight);
  2149. }
  2150. if (_player) {
  2151. _player->resizeToWidth(dialogsWidth);
  2152. _player->moveToLeft(0, _callTopBarHeight + _exportTopBarHeight);
  2153. }
  2154. const auto mainSectionGeometry = QRect(
  2155. 0,
  2156. mainSectionTop,
  2157. dialogsWidth,
  2158. height() - mainSectionTop);
  2159. if (_dialogs) {
  2160. _dialogs->setGeometryWithTopMoved(
  2161. mainSectionGeometry,
  2162. _contentScrollAddToY);
  2163. }
  2164. _history->setGeometryWithTopMoved(
  2165. mainSectionGeometry,
  2166. _contentScrollAddToY);
  2167. if (_hider) _hider->setGeometry(0, 0, dialogsWidth, height());
  2168. } else {
  2169. auto thirdSectionWidth = _thirdSection ? _thirdColumnWidth : 0;
  2170. if (_thirdSection) {
  2171. auto thirdSectionTop = getThirdSectionTop();
  2172. _thirdSection->setGeometry(
  2173. width() - thirdSectionWidth,
  2174. thirdSectionTop,
  2175. thirdSectionWidth,
  2176. height() - thirdSectionTop);
  2177. }
  2178. const auto shadowTop = _controller->window().verticalShadowTop();
  2179. const auto shadowHeight = height() - shadowTop;
  2180. if (_dialogs) {
  2181. accumulate_min(
  2182. dialogsWidth,
  2183. width() - st::columnMinimalWidthMain);
  2184. _dialogs->setGeometryToLeft(0, 0, dialogsWidth, height());
  2185. }
  2186. if (_sideShadow) {
  2187. _sideShadow->setGeometryToLeft(
  2188. dialogsWidth,
  2189. shadowTop,
  2190. st::lineWidth,
  2191. shadowHeight);
  2192. }
  2193. if (_thirdShadow) {
  2194. _thirdShadow->setGeometryToLeft(
  2195. width() - thirdSectionWidth - st::lineWidth,
  2196. shadowTop,
  2197. st::lineWidth,
  2198. shadowHeight);
  2199. }
  2200. const auto mainSectionWidth = width()
  2201. - dialogsWidth
  2202. - thirdSectionWidth;
  2203. if (_callTopBar) {
  2204. _callTopBar->resizeToWidth(mainSectionWidth);
  2205. _callTopBar->moveToLeft(dialogsWidth, 0);
  2206. }
  2207. if (_exportTopBar) {
  2208. _exportTopBar->resizeToWidth(mainSectionWidth);
  2209. _exportTopBar->moveToLeft(dialogsWidth, _callTopBarHeight);
  2210. }
  2211. if (_player) {
  2212. _player->resizeToWidth(mainSectionWidth);
  2213. _player->moveToLeft(
  2214. dialogsWidth,
  2215. _callTopBarHeight + _exportTopBarHeight);
  2216. }
  2217. _history->setGeometryWithTopMoved(QRect(
  2218. dialogsWidth,
  2219. mainSectionTop,
  2220. mainSectionWidth,
  2221. height() - mainSectionTop
  2222. ), _contentScrollAddToY);
  2223. if (_hider) {
  2224. _hider->setGeometryToLeft(
  2225. dialogsWidth,
  2226. 0,
  2227. mainSectionWidth,
  2228. height());
  2229. }
  2230. }
  2231. if (_mainSection) {
  2232. const auto mainSectionGeometry = QRect(
  2233. _history->x(),
  2234. mainSectionTop,
  2235. _history->width(),
  2236. height() - mainSectionTop);
  2237. _mainSection->setGeometryWithTopMoved(
  2238. mainSectionGeometry,
  2239. _contentScrollAddToY);
  2240. }
  2241. refreshResizeAreas();
  2242. if (_player) {
  2243. _player->entity()->updateDropdownsGeometry();
  2244. }
  2245. updateMediaPlaylistPosition(_playerPlaylist->x());
  2246. _contentScrollAddToY = 0;
  2247. floatPlayerUpdatePositions();
  2248. }
  2249. void MainWidget::destroyThirdSection() {
  2250. if (const auto strong = _thirdSection.data()) {
  2251. if (Ui::InFocusChain(strong)) {
  2252. setFocus();
  2253. }
  2254. }
  2255. _thirdSection.destroy();
  2256. }
  2257. void MainWidget::refreshResizeAreas() {
  2258. if (!isOneColumn() && _dialogs) {
  2259. ensureFirstColumnResizeAreaCreated();
  2260. _firstColumnResizeArea->setGeometryToLeft(
  2261. _history->x(),
  2262. 0,
  2263. st::historyResizeWidth,
  2264. height());
  2265. } else if (_firstColumnResizeArea) {
  2266. _firstColumnResizeArea.destroy();
  2267. }
  2268. if (isThreeColumn() && _thirdSection) {
  2269. ensureThirdColumnResizeAreaCreated();
  2270. _thirdColumnResizeArea->setGeometryToLeft(
  2271. _thirdSection->x(),
  2272. 0,
  2273. st::historyResizeWidth,
  2274. height());
  2275. } else if (_thirdColumnResizeArea) {
  2276. _thirdColumnResizeArea.destroy();
  2277. }
  2278. }
  2279. template <typename MoveCallback, typename FinishCallback>
  2280. void MainWidget::createResizeArea(
  2281. object_ptr<Ui::ResizeArea> &area,
  2282. MoveCallback &&moveCallback,
  2283. FinishCallback &&finishCallback) {
  2284. area.create(this);
  2285. area->show();
  2286. area->addMoveLeftCallback(
  2287. std::forward<MoveCallback>(moveCallback));
  2288. area->addMoveFinishedCallback(
  2289. std::forward<FinishCallback>(finishCallback));
  2290. orderWidgets();
  2291. }
  2292. void MainWidget::ensureFirstColumnResizeAreaCreated() {
  2293. Expects(_dialogs != nullptr);
  2294. if (_firstColumnResizeArea) {
  2295. return;
  2296. }
  2297. auto moveLeftCallback = [=](int globalLeft) {
  2298. const auto newWidth = globalLeft - mapToGlobal(QPoint(0, 0)).x();
  2299. const auto newRatio = (newWidth < st::columnMinimalWidthLeft / 2)
  2300. ? 0.
  2301. : float64(newWidth) / width();
  2302. const auto nochat = !_controller->mainSectionShown();
  2303. Core::App().settings().updateDialogsWidthRatio(newRatio, nochat);
  2304. };
  2305. auto moveFinishedCallback = [=] {
  2306. if (isOneColumn()) {
  2307. return;
  2308. }
  2309. const auto nochat = !_controller->mainSectionShown();
  2310. if (Core::App().settings().dialogsWidthRatio(nochat) > 0) {
  2311. Core::App().settings().updateDialogsWidthRatio(
  2312. float64(_dialogsWidth) / width(),
  2313. nochat);
  2314. }
  2315. Core::App().saveSettingsDelayed();
  2316. };
  2317. createResizeArea(
  2318. _firstColumnResizeArea,
  2319. std::move(moveLeftCallback),
  2320. std::move(moveFinishedCallback));
  2321. }
  2322. void MainWidget::ensureThirdColumnResizeAreaCreated() {
  2323. if (_thirdColumnResizeArea) {
  2324. return;
  2325. }
  2326. auto moveLeftCallback = [=](int globalLeft) {
  2327. auto newWidth = mapToGlobal(QPoint(width(), 0)).x() - globalLeft;
  2328. Core::App().settings().setThirdColumnWidth(newWidth);
  2329. };
  2330. auto moveFinishedCallback = [=] {
  2331. if (!isThreeColumn() || !_thirdSection) {
  2332. return;
  2333. }
  2334. Core::App().settings().setThirdColumnWidth(std::clamp(
  2335. Core::App().settings().thirdColumnWidth(),
  2336. st::columnMinimalWidthThird,
  2337. st::columnMaximalWidthThird));
  2338. Core::App().saveSettingsDelayed();
  2339. };
  2340. createResizeArea(
  2341. _thirdColumnResizeArea,
  2342. std::move(moveLeftCallback),
  2343. std::move(moveFinishedCallback));
  2344. }
  2345. void MainWidget::updateDialogsWidthAnimated() {
  2346. const auto nochat = !_controller->mainSectionShown();
  2347. if (!_dialogs || Core::App().settings().dialogsWidthRatio(nochat) > 0) {
  2348. return;
  2349. }
  2350. auto dialogsWidth = _dialogsWidth;
  2351. updateWindowAdaptiveLayout();
  2352. if (Core::App().settings().dialogsWidthRatio(nochat) == 0.
  2353. && (_dialogsWidth != dialogsWidth
  2354. || _a_dialogsWidth.animating())) {
  2355. _dialogs->startWidthAnimation();
  2356. _a_dialogsWidth.start(
  2357. [this] { updateControlsGeometry(); },
  2358. dialogsWidth,
  2359. _dialogsWidth,
  2360. st::dialogsWidthDuration,
  2361. anim::easeOutCirc);
  2362. updateControlsGeometry();
  2363. }
  2364. }
  2365. bool MainWidget::saveThirdSectionToStackBack() const {
  2366. return !_stack.empty()
  2367. && _thirdSection != nullptr
  2368. && _stack.back()->thirdSectionWeak() == _thirdSection.data();
  2369. }
  2370. auto MainWidget::thirdSectionForCurrentMainSection(
  2371. Dialogs::Key key)
  2372. -> std::shared_ptr<Window::SectionMemento> {
  2373. if (_thirdSectionFromStack) {
  2374. return std::move(_thirdSectionFromStack);
  2375. } else if (const auto topic = key.topic()) {
  2376. return std::make_shared<Info::Memento>(topic);
  2377. } else if (const auto peer = key.peer()) {
  2378. return std::make_shared<Info::Memento>(
  2379. peer,
  2380. Info::Memento::DefaultSection(peer));
  2381. } else if (const auto sublist = key.sublist()) {
  2382. return std::make_shared<Info::Memento>(
  2383. session().user(),
  2384. Info::Memento::DefaultSection(session().user()));
  2385. }
  2386. Unexpected("Key in MainWidget::thirdSectionForCurrentMainSection().");
  2387. }
  2388. void MainWidget::updateThirdColumnToCurrentChat(
  2389. Dialogs::Key key,
  2390. bool canWrite) {
  2391. auto saveOldThirdSection = [&] {
  2392. if (saveThirdSectionToStackBack()) {
  2393. _stack.back()->setThirdSectionMemento(
  2394. _thirdSection->createMemento());
  2395. destroyThirdSection();
  2396. }
  2397. };
  2398. auto &settings = Core::App().settings();
  2399. auto params = Window::SectionShow(
  2400. Window::SectionShow::Way::ClearStack,
  2401. anim::type::instant,
  2402. anim::activation::background);
  2403. auto switchInfoFast = [&] {
  2404. saveOldThirdSection();
  2405. //
  2406. // Like in _controller->showPeerInfo()
  2407. //
  2408. if (isThreeColumn()
  2409. && !settings.thirdSectionInfoEnabled()) {
  2410. settings.setThirdSectionInfoEnabled(true);
  2411. Core::App().saveSettingsDelayed();
  2412. }
  2413. _controller->showSection(
  2414. thirdSectionForCurrentMainSection(key),
  2415. params.withThirdColumn());
  2416. };
  2417. auto switchTabbedFast = [&](not_null<Data::Thread*> thread) {
  2418. saveOldThirdSection();
  2419. return _mainSection
  2420. ? _mainSection->pushTabbedSelectorToThirdSection(thread, params)
  2421. : _history->pushTabbedSelectorToThirdSection(thread, params);
  2422. };
  2423. if (isThreeColumn()
  2424. && settings.tabbedSelectorSectionEnabled()
  2425. && key) {
  2426. if (!canWrite) {
  2427. switchInfoFast();
  2428. settings.setTabbedSelectorSectionEnabled(true);
  2429. settings.setTabbedReplacedWithInfo(true);
  2430. } else if (settings.tabbedReplacedWithInfo()
  2431. && key.thread()
  2432. && switchTabbedFast(key.thread())) {
  2433. settings.setTabbedReplacedWithInfo(false);
  2434. }
  2435. } else {
  2436. settings.setTabbedReplacedWithInfo(false);
  2437. if (!key) {
  2438. if (_thirdSection) {
  2439. destroyThirdSection();
  2440. _thirdShadow.destroy();
  2441. updateControlsGeometry();
  2442. }
  2443. } else if (isThreeColumn()
  2444. && settings.thirdSectionInfoEnabled()) {
  2445. switchInfoFast();
  2446. }
  2447. }
  2448. }
  2449. void MainWidget::updateMediaPlaylistPosition(int x) {
  2450. if (_player) {
  2451. auto playlistLeft = x;
  2452. auto playlistWidth = _playerPlaylist->width();
  2453. auto playlistTop = _player->y() + _player->height();
  2454. auto rightEdge = width();
  2455. if (playlistLeft + playlistWidth > rightEdge) {
  2456. playlistLeft = rightEdge - playlistWidth;
  2457. } else if (playlistLeft < 0) {
  2458. playlistLeft = 0;
  2459. }
  2460. _playerPlaylist->move(playlistLeft, playlistTop);
  2461. }
  2462. }
  2463. void MainWidget::returnTabbedSelector() {
  2464. if (!_mainSection || !_mainSection->returnTabbedSelector()) {
  2465. _history->returnTabbedSelector();
  2466. }
  2467. }
  2468. bool MainWidget::relevantForDialogsFocus(not_null<QWidget*> widget) const {
  2469. if (!_dialogs || widget->window() != window()) {
  2470. return false;
  2471. }
  2472. while (true) {
  2473. if (widget.get() == this) {
  2474. return true;
  2475. }
  2476. const auto parent = widget->parentWidget();
  2477. if (!parent) {
  2478. return false;
  2479. }
  2480. widget = parent;
  2481. }
  2482. Unexpected("Should never be here.");
  2483. }
  2484. bool MainWidget::eventFilter(QObject *o, QEvent *e) {
  2485. const auto widget = o->isWidgetType()
  2486. ? static_cast<QWidget*>(o)
  2487. : nullptr;
  2488. if (e->type() == QEvent::FocusIn) {
  2489. if (widget && relevantForDialogsFocus(widget)) {
  2490. _dialogs->updateHasFocus(widget);
  2491. } else if (widget == window()) {
  2492. crl::on_main(this, [=] {
  2493. _controller->widget()->setInnerFocus();
  2494. });
  2495. }
  2496. } else if (e->type() == QEvent::MouseButtonPress) {
  2497. if (widget && (widget->window() == window())) {
  2498. const auto event = static_cast<QMouseEvent*>(e);
  2499. if (event->button() == Qt::BackButton) {
  2500. if (!Core::App().hideMediaView()
  2501. && (!_dialogs || !_dialogs->cancelSearchByMouseBack())) {
  2502. handleHistoryBack();
  2503. }
  2504. return true;
  2505. }
  2506. }
  2507. } else if (e->type() == QEvent::Wheel) {
  2508. if (widget && (widget->window() == window())) {
  2509. if (const auto result = floatPlayerFilterWheelEvent(o, e)) {
  2510. return *result;
  2511. }
  2512. }
  2513. }
  2514. return RpWidget::eventFilter(o, e);
  2515. }
  2516. void MainWidget::handleAdaptiveLayoutUpdate() {
  2517. showAll();
  2518. if (_sideShadow) {
  2519. _sideShadow->setVisible(!isOneColumn());
  2520. }
  2521. if (_player) {
  2522. _player->updateAdaptiveLayout();
  2523. }
  2524. }
  2525. void MainWidget::handleHistoryBack() {
  2526. const auto openedFolder = _controller->openedFolder().current();
  2527. const auto openedForum = _controller->shownForum().current();
  2528. const auto rootPeer = !_stack.empty()
  2529. ? _stack.front()->peer()
  2530. : _history->peer()
  2531. ? _history->peer()
  2532. : _mainSection
  2533. ? _mainSection->activeChat().key.peer()
  2534. : nullptr;
  2535. const auto rootHistory = rootPeer
  2536. ? rootPeer->owner().historyLoaded(rootPeer)
  2537. : nullptr;
  2538. const auto rootFolder = rootHistory ? rootHistory->folder() : nullptr;
  2539. if (openedForum && (!rootPeer || rootPeer->forum() != openedForum)) {
  2540. _controller->closeForum();
  2541. } else if (!openedFolder
  2542. || (rootFolder == openedFolder)
  2543. || (!_dialogs || _dialogs->isHidden())) {
  2544. _controller->showBackFromStack();
  2545. if (_dialogs) {
  2546. _dialogs->setInnerFocus();
  2547. }
  2548. } else {
  2549. _controller->closeFolder();
  2550. }
  2551. }
  2552. void MainWidget::updateWindowAdaptiveLayout() {
  2553. const auto nochat = !_controller->mainSectionShown();
  2554. auto layout = _controller->computeColumnLayout();
  2555. auto dialogsWidthRatio = Core::App().settings().dialogsWidthRatio(nochat);
  2556. // Check if we are in a single-column layout in a wide enough window
  2557. // for the normal layout. If so, switch to the normal layout.
  2558. if (layout.windowLayout == Window::Adaptive::WindowLayout::OneColumn) {
  2559. auto chatWidth = layout.chatWidth;
  2560. //if (session().settings().tabbedSelectorSectionEnabled()
  2561. // && chatWidth >= _history->minimalWidthForTabbedSelectorSection()) {
  2562. // chatWidth -= _history->tabbedSelectorSectionWidth();
  2563. //}
  2564. auto minimalNormalWidth = st::columnMinimalWidthLeft
  2565. + st::columnMinimalWidthMain;
  2566. if (chatWidth >= minimalNormalWidth) {
  2567. // Switch layout back to normal in a wide enough window.
  2568. layout.windowLayout = Window::Adaptive::WindowLayout::Normal;
  2569. layout.dialogsWidth = st::columnMinimalWidthLeft;
  2570. layout.chatWidth = layout.bodyWidth - layout.dialogsWidth;
  2571. dialogsWidthRatio = float64(layout.dialogsWidth) / layout.bodyWidth;
  2572. }
  2573. }
  2574. // Check if we are going to create the third column and shrink the
  2575. // dialogs widget to provide a wide enough chat history column.
  2576. // Don't shrink the column on the first call, when window is inited.
  2577. if (layout.windowLayout == Window::Adaptive::WindowLayout::ThreeColumn
  2578. && _controller->widget()->positionInited()) {
  2579. //auto chatWidth = layout.chatWidth;
  2580. //if (_history->willSwitchToTabbedSelectorWithWidth(chatWidth)) {
  2581. // auto thirdColumnWidth = _history->tabbedSelectorSectionWidth();
  2582. // auto twoColumnsWidth = (layout.bodyWidth - thirdColumnWidth);
  2583. // auto sameRatioChatWidth = twoColumnsWidth - qRound(dialogsWidthRatio * twoColumnsWidth);
  2584. // auto desiredChatWidth = qMax(sameRatioChatWidth, HistoryView::WideChatWidth());
  2585. // chatWidth -= thirdColumnWidth;
  2586. // auto extendChatBy = desiredChatWidth - chatWidth;
  2587. // accumulate_min(extendChatBy, layout.dialogsWidth - st::columnMinimalWidthLeft);
  2588. // if (extendChatBy > 0) {
  2589. // layout.dialogsWidth -= extendChatBy;
  2590. // layout.chatWidth += extendChatBy;
  2591. // dialogsWidthRatio = float64(layout.dialogsWidth) / layout.bodyWidth;
  2592. // }
  2593. //}
  2594. }
  2595. Core::App().settings().updateDialogsWidthRatio(dialogsWidthRatio, nochat);
  2596. auto useSmallColumnWidth = !isOneColumn()
  2597. && !dialogsWidthRatio
  2598. && !_controller->chatsForceDisplayWide();
  2599. _dialogsWidth = !_dialogs
  2600. ? 0
  2601. : useSmallColumnWidth
  2602. ? _controller->dialogsSmallColumnWidth()
  2603. : layout.dialogsWidth;
  2604. _thirdColumnWidth = layout.thirdWidth;
  2605. _controller->adaptive().setWindowLayout(layout.windowLayout);
  2606. }
  2607. int MainWidget::backgroundFromY() const {
  2608. return -getMainSectionTop();
  2609. }
  2610. bool MainWidget::contentOverlapped(const QRect &globalRect) {
  2611. return _history->contentOverlapped(globalRect)
  2612. || _playerPlaylist->overlaps(globalRect);
  2613. }
  2614. void MainWidget::activate() {
  2615. if (_showAnimation) {
  2616. return;
  2617. } else if (const auto paths = cSendPaths(); !paths.isEmpty()) {
  2618. const auto interpret = u"interpret://"_q;
  2619. cSetSendPaths(QStringList());
  2620. if (paths[0].startsWith(interpret)) {
  2621. const auto error = Support::InterpretSendPath(
  2622. _controller,
  2623. paths[0].mid(interpret.size()));
  2624. if (!error.isEmpty()) {
  2625. _controller->show(Ui::MakeInformBox(error));
  2626. }
  2627. } else {
  2628. const auto chosen = [=](not_null<Data::Thread*> thread) {
  2629. return sendPaths(thread, paths);
  2630. };
  2631. Window::ShowChooseRecipientBox(_controller, chosen);
  2632. }
  2633. } else if (_mainSection) {
  2634. _mainSection->setInnerFocus();
  2635. } else if (_hider) {
  2636. Assert(_dialogs != nullptr);
  2637. _dialogs->setInnerFocus();
  2638. } else if (!_controller->isLayerShown()) {
  2639. if (_history->peer()) {
  2640. _history->activate();
  2641. } else {
  2642. Assert(_dialogs != nullptr);
  2643. _dialogs->setInnerFocus();
  2644. }
  2645. }
  2646. _controller->widget()->fixOrder();
  2647. }
  2648. bool MainWidget::animatingShow() const {
  2649. return _showAnimation != nullptr;
  2650. }
  2651. bool MainWidget::isOneColumn() const {
  2652. return _controller->adaptive().isOneColumn();
  2653. }
  2654. bool MainWidget::isNormalColumn() const {
  2655. return _controller->adaptive().isNormal();
  2656. }
  2657. bool MainWidget::isThreeColumn() const {
  2658. return _controller->adaptive().isThreeColumn();
  2659. }