diff --git a/Telegram/SourceFiles/auth_session.cpp b/Telegram/SourceFiles/auth_session.cpp index be337b3a47..f3b614b8c7 100644 --- a/Telegram/SourceFiles/auth_session.cpp +++ b/Telegram/SourceFiles/auth_session.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "apiwrap.h" #include "messenger.h" #include "storage/file_download.h" +#include "storage/localstorage.h" #include "window/notifications_manager.h" QByteArray AuthSessionData::serialize() const { @@ -40,6 +41,7 @@ QByteArray AuthSessionData::serialize() const { stream.setVersion(QDataStream::Qt_5_1); stream << static_cast(_variables.emojiPanelTab); stream << qint32(_variables.lastSeenWarningSeen ? 1 : 0); + stream << qint32(_variables.tabbedSelectorSectionEnabled ? 1 : 0); } return result; } @@ -58,8 +60,12 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) { stream.setVersion(QDataStream::Qt_5_1); qint32 emojiPanTab = static_cast(EmojiPanelTab::Emoji); qint32 lastSeenWarningSeen = 0; + qint32 tabbedSelectorSectionEnabled = 1; stream >> emojiPanTab; stream >> lastSeenWarningSeen; + if (!stream.atEnd()) { + stream >> tabbedSelectorSectionEnabled; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: Bad data for AuthSessionData::constructFromSerialized()")); return; @@ -72,6 +78,7 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) { case EmojiPanelTab::Gifs: _variables.emojiPanelTab = uncheckedTab; break; } _variables.lastSeenWarningSeen = (lastSeenWarningSeen == 1); + _variables.tabbedSelectorSectionEnabled = (tabbedSelectorSectionEnabled == 1); } AuthSession::AuthSession(UserId userId) @@ -80,6 +87,9 @@ AuthSession::AuthSession(UserId userId) , _downloader(std::make_unique()) , _notifications(std::make_unique(this)) { Expects(_userId != 0); + _saveDataTimer.setCallback([this] { + Local::writeUserSettings(); + }); } bool AuthSession::Exists() { @@ -109,4 +119,9 @@ bool AuthSession::validateSelf(const MTPUser &user) { return true; } +void AuthSession::saveDataDelayed(TimeMs delay) { + Expects(this == &AuthSession::Current()); + _saveDataTimer.callOnce(delay); +} + AuthSession::~AuthSession() = default; diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h index 232a8ebe63..abd451c752 100644 --- a/Telegram/SourceFiles/auth_session.h +++ b/Telegram/SourceFiles/auth_session.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include "base/timer.h" + namespace Storage { class Downloader; } // namespace Storage @@ -71,11 +73,18 @@ public: void setEmojiPanelTab(EmojiPanelTab tab) { _variables.emojiPanelTab = tab; } + bool tabbedSelectorSectionEnabled() const { + return _variables.tabbedSelectorSectionEnabled; + } + void setTabbedSelectorSectionEnabled(bool enabled) { + _variables.tabbedSelectorSectionEnabled = enabled; + } private: struct Variables { bool lastSeenWarningSeen = false; EmojiPanelTab emojiPanelTab = EmojiPanelTab::Emoji; + bool tabbedSelectorSectionEnabled = true; }; base::Variable _contactsLoaded = { false }; @@ -122,6 +131,7 @@ public: AuthSessionData &data() { return _data; } + void saveDataDelayed(TimeMs delay); ApiWrap &api() { return *_api; @@ -132,6 +142,7 @@ public: private: const UserId _userId = 0; AuthSessionData _data; + base::Timer _saveDataTimer; const std::unique_ptr _api; const std::unique_ptr _downloader; diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 8f545139b6..14baaa7618 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -107,7 +107,11 @@ stickersSettingsUnreadPosition: point(4px, 5px); emojiPanMargins: margins(10px, 10px, 10px, 10px); -emojiTabs: defaultTabsSlider; +emojiTabs: SettingsSlider(defaultTabsSlider) { + height: 55px; + barTop: 52px; + labelTop: 19px; +} emojiScroll: defaultSolidScroll; emojiRecent: icon {{ "emoji_recent", emojiIconFg }}; emojiRecentActive: icon {{ "emoji_recent", emojiIconFgActive }}; diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 2ab997860a..12aacca189 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -103,7 +103,10 @@ void GifsListWidget::Footer::stealFocus() { void GifsListWidget::Footer::returnFocus() { if (_focusTakenFrom) { - _focusTakenFrom->setFocus(); + if (_field->hasFocus()) { + _focusTakenFrom->setFocus(); + } + _focusTakenFrom = nullptr; } } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index b948bfbf51..aa69d2356b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -54,7 +54,7 @@ struct StickerIcon { }; -class StickersListWidget::Footer : public TabbedSelector::InnerFooter { +class StickersListWidget::Footer : public TabbedSelector::InnerFooter, private base::Subscriber { public: Footer(gsl::not_null parent); @@ -112,6 +112,10 @@ StickersListWidget::Footer::Footer(gsl::not_null parent) : setMouseTracking(true); _iconsLeft = (st::emojiPanWidth - kVisibleIconsCount * st::emojiCategory.width) / 2; + + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { + update(); + }); } template @@ -905,13 +909,13 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { emit selected(set.pack[sticker->index]); } else if (auto set = base::get_if(&pressed)) { t_assert(set->section >= 0 && set->section < sets.size()); - emit displaySet(sets[set->section].id); + displaySet(sets[set->section].id); } else if (auto button = base::get_if(&pressed)) { t_assert(button->section >= 0 && button->section < sets.size()); if (_section == Section::Featured) { - emit installSet(sets[button->section].id); + installSet(sets[button->section].id); } else { - emit removeSet(sets[button->section].id); + removeSet(sets[button->section].id); } } } @@ -1385,22 +1389,22 @@ void StickersListWidget::installSet(quint64 setId) { void StickersListWidget::removeSet(quint64 setId) { auto &sets = Global::StickerSets(); auto it = sets.constFind(setId); - if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { + if (it != sets.cend()) { _removingSetId = it->id; auto text = lng_stickers_remove_pack(lt_sticker_pack, it->title); Ui::show(Box(text, lang(lng_box_remove), base::lambda_guarded(this, [this] { Ui::hideLayer(); auto &sets = Global::RefStickerSets(); auto it = sets.find(_removingSetId); - if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { + if (it != sets.cend()) { if (it->id && it->access) { request(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)))).send(); } else if (!it->shortName.isEmpty()) { request(MTPmessages_UninstallStickerSet(MTP_inputStickerSetShortName(MTP_string(it->shortName)))).send(); } - bool writeRecent = false; - RecentStickerPack &recent(cGetRecentStickers()); - for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { + auto writeRecent = false; + auto &recent = cGetRecentStickers(); + for (auto i = recent.begin(); i != recent.cend();) { if (it->stickers.indexOf(i->first) >= 0) { i = recent.erase(i); writeRecent = true; diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp index afa597411c..89a1e614c7 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp @@ -33,8 +33,12 @@ constexpr auto kDelayedHideTimeoutMs = 3000; } // namespace -TabbedPanel::TabbedPanel(QWidget *parent, gsl::not_null controller) : TWidget(parent) -, _selector(this, controller) { +TabbedPanel::TabbedPanel(QWidget *parent, gsl::not_null controller) : TabbedPanel(parent, controller, object_ptr(nullptr, controller)) { +} + +TabbedPanel::TabbedPanel(QWidget *parent, gsl::not_null controller, object_ptr selector) : TWidget(parent) +, _selector(std::move(selector)) { + _selector->setParent(this); _selector->setRoundRadius(st::buttonRadius); resize(QRect(0, 0, st::emojiPanWidth, st::emojiPanMaxHeight).marginsAdded(innerPadding()).size()); @@ -47,11 +51,11 @@ TabbedPanel::TabbedPanel(QWidget *parent, gsl::not_null con _hideTimer.setCallback([this] { hideByTimerOrLeave(); }); - connect(_selector, SIGNAL(checkForHide()), this, SLOT(onCheckForHide())); - connect(_selector, SIGNAL(emojiSelected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr))); - connect(_selector, SIGNAL(stickerSelected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*))); - connect(_selector, SIGNAL(photoSelected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*))); - connect(_selector, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*))); + connect(_selector, &TabbedSelector::checkForHide, this, [this] { + if (!rect().contains(mapFromGlobal(QCursor::pos()))) { + _hideTimer.callOnce(kDelayedHideTimeoutMs); + } + }); connect(_selector, &TabbedSelector::cancelled, this, [this] { hideAnimated(); }); @@ -77,6 +81,10 @@ void TabbedPanel::moveBottom(int bottom) { } void TabbedPanel::updateContentHeight() { + if (isDestroying()) { + return; + } + auto addedHeight = innerPadding().top() + innerPadding().bottom(); auto wantedContentHeight = qRound(st::emojiPanHeightRatio * _bottom) - addedHeight; auto contentHeight = snap(wantedContentHeight, st::emojiPanMinHeight, st::emojiPanMaxHeight); @@ -98,8 +106,8 @@ void TabbedPanel::updateContentHeight() { } void TabbedPanel::onWndActiveChanged() { - if (!App::wnd()->windowHandle()->isActive() && !isHidden()) { - leaveEvent(0); + if (!App::wnd()->windowHandle()->isActive() && !isHidden() && !preventAutoHide()) { + hideAnimated(); } } @@ -114,7 +122,7 @@ void TabbedPanel::paintEvent(QPaintEvent *e) { auto showAnimating = _a_show.animating(ms); if (_showAnimation && !showAnimating) { _showAnimation.reset(); - if (!opacityAnimating) { + if (!opacityAnimating && !isDestroying()) { showChildren(); _selector->afterShown(); } @@ -146,6 +154,9 @@ void TabbedPanel::enterEventHook(QEvent *e) { } bool TabbedPanel::preventAutoHide() const { + if (isDestroying()) { + return false; + } return _selector->preventAutoHide(); } @@ -188,14 +199,10 @@ void TabbedPanel::hideFast() { hideFinished(); } -void TabbedPanel::refreshStickers() { - _selector->refreshStickers(); -} - void TabbedPanel::opacityAnimationCallback() { update(); if (!_a_opacity.animating()) { - if (_hiding) { + if (_hiding || isDestroying()) { _hiding = false; hideFinished(); } else if (!_a_show.animating()) { @@ -226,7 +233,7 @@ void TabbedPanel::prepareCache() { } void TabbedPanel::startOpacityAnimation(bool hiding) { - if (!_selector->isHidden()) { + if (_selector && !_selector->isHidden()) { _selector->beforeHiding(); } _hiding = false; @@ -259,12 +266,13 @@ QImage TabbedPanel::grabForAnimation() { showChildren(); myEnsureResized(this); - myEnsureResized(_selector); auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); result.setDevicePixelRatio(cRetinaFactor()); result.fill(Qt::transparent); - _selector->render(&result, _selector->geometry().topLeft()); + if (_selector) { + _selector->render(&result, _selector->geometry().topLeft()); + } _a_show = base::take(showAnimation); _showAnimation = base::take(showAnimationData); @@ -275,26 +283,50 @@ QImage TabbedPanel::grabForAnimation() { } void TabbedPanel::hideAnimated() { - if (isHidden()) return; - if (_hiding) return; + if (isHidden() || _hiding) { + return; + } _hideTimer.cancel(); - if (_selector->isSliding()) { + if (!isDestroying() && _selector->isSliding()) { _hideAfterSlide = true; } else { startOpacityAnimation(true); } } -TabbedPanel::~TabbedPanel() = default; +void TabbedPanel::toggleAnimated() { + if (isDestroying()) { + return; + } + if (isHidden() || _hiding || _hideAfterSlide) { + showAnimated(); + } else { + hideAnimated(); + } +} + +object_ptr TabbedPanel::takeSelector() { + auto result = std::move(_selector); + hideAnimated(); + return result; +} + +QPointer TabbedPanel::getSelector() const { + return _selector.data(); +} void TabbedPanel::hideFinished() { hide(); - _selector->hideFinished(); _a_show.finish(); _showAnimation.reset(); _cache = QPixmap(); _hiding = false; + if (isDestroying()) { + deleteLater(); + } else { + _selector->hideFinished(); + } } void TabbedPanel::showAnimated() { @@ -304,6 +336,9 @@ void TabbedPanel::showAnimated() { } void TabbedPanel::showStarted() { + if (isDestroying()) { + return; + } if (isHidden()) { _selector->showStarted(); moveByBottom(); @@ -315,21 +350,21 @@ void TabbedPanel::showStarted() { } bool TabbedPanel::eventFilter(QObject *obj, QEvent *e) { + if (isDestroying()) { + return false; + } if (e->type() == QEvent::Enter) { otherEnter(); } else if (e->type() == QEvent::Leave) { otherLeave(); - } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) { - if (isHidden() || _hiding || _hideAfterSlide) { - showAnimated(); - } else { - hideAnimated(); - } } return false; } void TabbedPanel::stickersInstalled(uint64 setId) { + if (isDestroying()) { + return; + } _selector->stickersInstalled(setId); if (isHidden()) { moveByBottom(); @@ -340,10 +375,6 @@ void TabbedPanel::stickersInstalled(uint64 setId) { showAnimated(); } -void TabbedPanel::setInlineQueryPeer(PeerData *peer) { - _selector->setInlineQueryPeer(peer); -} - style::margins TabbedPanel::innerPadding() const { return st::emojiPanMargins; } @@ -360,12 +391,6 @@ QRect TabbedPanel::verticalRect() const { return innerRect().marginsRemoved(style::margins(st::buttonRadius, 0, st::buttonRadius, 0)); } -void TabbedPanel::onCheckForHide() { - if (!rect().contains(mapFromGlobal(QCursor::pos()))) { - _hideTimer.callOnce(kDelayedHideTimeoutMs); - } -} - bool TabbedPanel::overlaps(const QRect &globalRect) const { if (isHidden() || !_cache.isNull()) return false; @@ -375,4 +400,6 @@ bool TabbedPanel::overlaps(const QRect &globalRect) const { || inner.marginsRemoved(QMargins(0, st::buttonRadius, 0, st::buttonRadius)).contains(testRect); } +TabbedPanel::~TabbedPanel() = default; + } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_panel.h b/Telegram/SourceFiles/chat_helpers/tabbed_panel.h index 607e8b3d9a..2319b113b0 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_panel.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_panel.h @@ -21,19 +21,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "ui/twidget.h" -#include "ui/effects/panel_animation.h" -#include "mtproto/sender.h" -#include "auth_session.h" #include "base/timer.h" -namespace InlineBots { -class Result; -} // namespace InlineBots - namespace Window { class Controller; } // namespace Window +namespace Ui { +class PanelAnimation; +} // namespace Ui + namespace ChatHelpers { class TabbedSelector; @@ -43,7 +40,10 @@ class TabbedPanel : public TWidget { public: TabbedPanel(QWidget *parent, gsl::not_null controller); + TabbedPanel(QWidget *parent, gsl::not_null controller, object_ptr selector); + object_ptr takeSelector(); + QPointer getSelector() const; void moveBottom(int bottom); void hideFast(); @@ -54,12 +54,10 @@ public: void stickersInstalled(uint64 setId); bool overlaps(const QRect &globalRect) const; - void setInlineQueryPeer(PeerData *peer); void showAnimated(); void hideAnimated(); - - void refreshStickers(); + void toggleAnimated(); ~TabbedPanel(); @@ -74,19 +72,13 @@ protected: private slots: void onWndActiveChanged(); - void onCheckForHide(); - -signals: - void emojiSelected(EmojiPtr emoji); - void stickerSelected(DocumentData *sticker); - void photoSelected(PhotoData *photo); - void inlineResultSelected(InlineBots::Result *result, UserData *bot); - - void updateStickers(); private: void hideByTimerOrLeave(); void moveByBottom(); + bool isDestroying() const { + return !_selector; + } style::margins innerPadding() const; diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_section.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_section.cpp index f0586cbbd3..1ed102d5ff 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_section.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_section.cpp @@ -20,3 +20,54 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "chat_helpers/tabbed_section.h" +#include "styles/style_chat_helpers.h" +#include "chat_helpers/tabbed_selector.h" + +namespace ChatHelpers { + +TabbedSection::TabbedSection(QWidget *parent, gsl::not_null controller) : TabbedSection(parent, controller, object_ptr(this, controller)) { +} + +TabbedSection::TabbedSection(QWidget *parent, gsl::not_null controller, object_ptr selector) : TWidget(parent) +, _selector(std::move(selector)) { + resize(st::emojiPanWidth, st::emojiPanMaxHeight); + + _selector->setParent(this); + _selector->setRoundRadius(0); + _selector->setGeometry(rect()); + _selector->showStarted(); + _selector->show(); + connect(_selector, &TabbedSelector::cancelled, this, [this] { + if (_cancelledCallback) { + _cancelledCallback(); + } + }); + + setAttribute(Qt::WA_OpaquePaintEvent, true); +} + +void TabbedSection::beforeHiding() { + _selector->beforeHiding(); +} + +void TabbedSection::afterShown() { + _selector->afterShown(); +} + +void TabbedSection::resizeEvent(QResizeEvent *e) { + _selector->setGeometry(rect()); +} + +object_ptr TabbedSection::takeSelector() { + return std::move(_selector); +} + +QPointer TabbedSection::getSelector() const { + return _selector.data(); +} + +void TabbedSection::stickersInstalled(uint64 setId) { + _selector->stickersInstalled(setId); +} + +} // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_section.h b/Telegram/SourceFiles/chat_helpers/tabbed_section.h index 85af2faec9..b3bc0b59e4 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_section.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_section.h @@ -19,3 +19,40 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once + +#include "ui/twidget.h" + +namespace Window { +class Controller; +} // namespace Window + +namespace ChatHelpers { + +class TabbedSelector; + +class TabbedSection : public TWidget { +public: + TabbedSection(QWidget *parent, gsl::not_null controller); + TabbedSection(QWidget *parent, gsl::not_null controller, object_ptr selector); + + void beforeHiding(); + void afterShown(); + void setCancelledCallback(base::lambda callback) { + _cancelledCallback = std::move(callback); + } + + object_ptr takeSelector(); + QPointer getSelector() const; + + void stickersInstalled(uint64 setId); + +protected: + void resizeEvent(QResizeEvent *e) override; + +private: + object_ptr _selector; + base::lambda _cancelledCallback; + +}; + +} // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 41c78e293a..7170784a0f 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -292,28 +292,35 @@ TabbedSelector::TabbedSelector(QWidget *parent, gsl::not_nullhide(); + tab.widget()->hide(); + } + createTabsSlider(); - _scroll->resize(st::emojiPanWidth - st::buttonRadius, height() - marginTop() - marginBottom()); - _scroll->move(st::buttonRadius, marginTop()); + _scroll->setGeometryToLeft(st::buttonRadius, marginTop(), st::emojiPanWidth - st::buttonRadius, height() - marginTop() - marginBottom()); setWidgetToScrollArea(); _bottomShadow->setGeometry(_tabsSlider->x(), _scroll->y() + _scroll->height() - st::lineWidth, _tabsSlider->width(), st::lineWidth); for (auto &tab : _tabs) { - connect(tab.widget(), &Inner::scrollToY, this, [this, tab = &tab](int y) { + auto widget = tab.widget(); + connect(widget, &Inner::scrollToY, this, [this, tab = &tab](int y) { if (tab == currentTab()) { scrollToY(y); } else { tab->saveScrollTop(y); } }); - connect(tab.widget(), &Inner::disableScroll, this, [this, tab = &tab](bool disabled) { + connect(widget, &Inner::disableScroll, this, [this, tab = &tab](bool disabled) { if (tab == currentTab()) { _scroll->disableScroll(disabled); } }); - connect(tab.widget(), SIGNAL(saveConfigDelayed(int)), this, SLOT(onSaveConfigDelayed(int))); + connect(widget, &Inner::saveConfigDelayed, this, [this](int delay) { + AuthSession::Current().saveDataDelayed(delay); + }); } connect(stickers(), SIGNAL(scrollUpdated()), this, SLOT(onScroll())); @@ -326,8 +333,6 @@ TabbedSelector::TabbedSelector(QWidget *parent, gsl::not_nullwindowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); } @@ -338,8 +343,7 @@ TabbedSelector::TabbedSelector(QWidget *parent, gsl::not_null TabbedSelector::emoji() const { diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index 1ecc528444..4ae1febb00 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -24,7 +24,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/effects/panel_animation.h" #include "mtproto/sender.h" #include "auth_session.h" -#include "base/timer.h" namespace InlineBots { class Result; @@ -79,8 +78,6 @@ protected: private slots: void onScroll(); - void onSaveConfigDelayed(int delay); - signals: void emojiSelected(EmojiPtr emoji); void stickerSelected(DocumentData *sticker); @@ -176,8 +173,6 @@ private: std::array _tabs; TabType _currentTabType = TabType::Emoji; - base::Timer _saveConfigTimer; - }; class TabbedSelector::Inner : public TWidget { diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index bc30235989..e97685f943 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -74,7 +74,8 @@ struct DialogsInner::PeerSearchResult { Dialogs::RippleRow row; }; -DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(parent) +DialogsInner::DialogsInner(QWidget *parent, gsl::not_null controller, QWidget *main) : SplittedWidget(parent) +, _controller(controller) , _dialogs(std::make_unique(Dialogs::SortMode::Date)) , _contactsNoDialogs(std::make_unique(Dialogs::SortMode::Name)) , _contacts(std::make_unique(Dialogs::SortMode::Name)) @@ -1674,7 +1675,7 @@ void DialogsInner::refresh(bool toTop) { emit mustScrollTo(0, 0); loadPeerPhotos(); } - Global::RefDialogsListDisplayForced().set(_searchInPeer || !_filter.isEmpty(), true); + _controller->dialogsListDisplayForced().set(_searchInPeer || !_filter.isEmpty(), true); update(); } @@ -1722,7 +1723,7 @@ void DialogsInner::searchInPeer(PeerData *peer) { } else { _cancelSearchInPeer->hide(); } - Global::RefDialogsListDisplayForced().set(_searchInPeer || !_filter.isEmpty(), true); + _controller->dialogsListDisplayForced().set(_searchInPeer || !_filter.isEmpty(), true); } void DialogsInner::clearFilter() { @@ -2274,7 +2275,7 @@ DialogsWidget::DialogsWidget(QWidget *parent, gsl::not_null , _cancelSearch(this, st::dialogsCancelSearch) , _lockUnlock(this, st::dialogsLock) , _scroll(this, st::dialogsScroll) { - _inner = _scroll->setOwnedWidget(object_ptr(this, parent)); + _inner = _scroll->setOwnedWidget(object_ptr(this, _controller, parent)); connect(_inner, SIGNAL(draggingScrollDelta(int)), this, SLOT(onDraggingScrollDelta(int))); connect(_inner, SIGNAL(mustScrollTo(int,int)), _scroll, SLOT(scrollToY(int,int))); connect(_inner, SIGNAL(dialogMoved(int,int)), this, SLOT(onDialogMoved(int,int))); diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 60a6d93564..ab6d7a386e 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -58,7 +58,7 @@ class DialogsInner : public Ui::SplittedWidget, public RPCSender, private base:: Q_OBJECT public: - DialogsInner(QWidget *parent, QWidget *main); + DialogsInner(QWidget *parent, gsl::not_null controller, QWidget *main); void dialogsReceived(const QVector &dialogs); void addSavedPeersAfter(const QDateTime &date); @@ -226,6 +226,8 @@ private: void savePinnedOrder(); void step_pinnedShifting(TimeMs ms, bool timer); + gsl::not_null _controller; + DialogsList _dialogs; DialogsList _dialogsImportant; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index a2fdfb84a9..7fa0839da8 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -682,10 +682,6 @@ struct Data { base::Observable UnreadCounterUpdate; base::Observable PeerChooseCancel; - float64 DialogsWidthRatio = 5. / 14; - base::Variable DialogsListFocused = { false }; - base::Variable DialogsListDisplayForced = { false }; - }; } // namespace internal @@ -805,8 +801,4 @@ DefineRefVar(Global, base::Observable, ItemRemoved); DefineRefVar(Global, base::Observable, UnreadCounterUpdate); DefineRefVar(Global, base::Observable, PeerChooseCancel); -DefineVar(Global, float64, DialogsWidthRatio); -DefineRefVar(Global, base::Variable, DialogsListFocused); -DefineRefVar(Global, base::Variable, DialogsListDisplayForced); - } // namespace Global diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 26e806a869..f1139c27c2 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -391,10 +391,6 @@ DeclareRefVar(base::Observable, ItemRemoved); DeclareRefVar(base::Observable, UnreadCounterUpdate); DeclareRefVar(base::Observable, PeerChooseCancel); -DeclareVar(float64, DialogsWidthRatio); -DeclareRefVar(base::Variable, DialogsListFocused); -DeclareRefVar(base::Variable, DialogsListDisplayForced); - } // namespace Global namespace Adaptive { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index ea1f2b2619..4ed3149a3c 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_window.h" #include "styles/style_boxes.h" #include "styles/style_profile.h" +#include "styles/style_chat_helpers.h" #include "boxes/confirm_box.h" #include "boxes/send_files_box.h" #include "boxes/share_box.h" @@ -44,6 +45,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "profile/profile_block_group_members.h" #include "core/click_handler_types.h" #include "chat_helpers/tabbed_panel.h" +#include "chat_helpers/tabbed_section.h" +#include "chat_helpers/tabbed_selector.h" #include "chat_helpers/bot_keyboard.h" #include "chat_helpers/message_field.h" #include "lang.h" @@ -71,6 +74,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace { constexpr auto kStickersUpdateTimeout = 3600000; // update not more than once in an hour +constexpr auto kSaveTabbedSelectorSectionTimeout = 1000; ApiWrap::RequestMessageDataCallback replyEditMessageDataCallback() { return [](ChannelData *channel, MsgId msgId) { @@ -468,14 +472,13 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null , _scroll(this, st::historyScroll, false) , _historyDown(_scroll, st::historyToDown) , _fieldAutocomplete(this) -, _reportSpamPanel(this) , _send(this) , _unblock(this, lang(lng_unblock_button).toUpper(), st::historyUnblock) , _botStart(this, lang(lng_bot_start).toUpper(), st::historyComposeButton) , _joinChannel(this, lang(lng_channel_join).toUpper(), st::historyComposeButton) , _muteUnmute(this, lang(lng_channel_mute).toUpper(), st::historyComposeButton) , _attachToggle(this, st::historyAttach) -, _attachEmoji(this, st::historyAttachEmoji) +, _tabbedSelectorToggle(this, st::historyAttachEmoji) , _botKeyboardShow(this, st::historyBotKeyboardShow) , _botKeyboardHide(this, st::historyBotKeyboardHide) , _botCommandStart(this, st::historyBotCommandStart) @@ -485,6 +488,7 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null , _a_recording(animation(this, &HistoryWidget::step_recording)) , _kbScroll(this, st::botKbScroll) , _tabbedPanel(this, _controller) +, _tabbedSelector(_tabbedPanel->getSelector()) , _attachDragDocument(this) , _attachDragPhoto(this) , _fileLoader(this, FileLoaderQueueStopTimeout) @@ -494,9 +498,6 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); connect(_topBar, &Window::TopBarWidget::clicked, this, [this] { topBarClick(); }); connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); - connect(_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked())); - connect(_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide())); - connect(_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); connect(_historyDown, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); connect(_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel())); _send->setClickedCallback([this] { sendButtonClicked(); }); @@ -515,11 +516,11 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null connect(_field, SIGNAL(linksChanged()), this, SLOT(onPreviewCheck())); connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onWindowVisibleChanged())); connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); - connect(_tabbedPanel, SIGNAL(emojiSelected(EmojiPtr)), _field, SLOT(onEmojiInsert(EmojiPtr))); - connect(_tabbedPanel, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*))); - connect(_tabbedPanel, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*))); - connect(_tabbedPanel, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*))); - connect(_tabbedPanel, SIGNAL(updateStickers()), this, SLOT(updateStickers())); + connect(_tabbedSelector, SIGNAL(emojiSelected(EmojiPtr)), _field, SLOT(onEmojiInsert(EmojiPtr))); + connect(_tabbedSelector, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*))); + connect(_tabbedSelector, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*))); + connect(_tabbedSelector, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*))); + connect(_tabbedSelector, SIGNAL(updateStickers()), this, SLOT(updateStickers())); connect(&_sendActionStopTimer, SIGNAL(timeout()), this, SLOT(onCancelSendAction())); connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreviewTimeout())); connect(Media::Capture::instance(), SIGNAL(error()), this, SLOT(onRecordError())); @@ -587,17 +588,15 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null _send->setRecordUpdateCallback([this](QPoint globalPos) { recordUpdateCallback(globalPos); }); _send->setRecordAnimationCallback([this] { updateField(); }); - _reportSpamPanel->move(0, 0); - _reportSpamPanel->hide(); - _attachToggle->hide(); - _attachEmoji->hide(); + _tabbedSelectorToggle->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); _silent->hide(); _botCommandStart->hide(); - _attachEmoji->installEventFilter(_tabbedPanel); + _tabbedSelectorToggle->installEventFilter(_tabbedPanel); + _tabbedSelectorToggle->setClickedCallback([this] { toggleTabbedSelectorMode(); }); connect(_botKeyboardShow, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(_botKeyboardHide, SIGNAL(clicked()), this, SLOT(onKbToggle())); @@ -638,7 +637,7 @@ void HistoryWidget::start() { } void HistoryWidget::onStickersUpdated() { - _tabbedPanel->refreshStickers(); + _tabbedSelector->refreshStickers(); updateStickersByEmoji(); } @@ -717,19 +716,45 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) { } void HistoryWidget::orderWidgets() { - _reportSpamPanel->raise(); + if (_reportSpamPanel) { + _reportSpamPanel->raise(); + } _topShadow->raise(); + if (_rightShadow) { + _rightShadow->raise(); + } if (_membersDropdown) { _membersDropdown->raise(); } if (_inlineResults) { _inlineResults->raise(); } - _tabbedPanel->raise(); + if (_tabbedPanel) { + _tabbedPanel->raise(); + } _attachDragDocument->raise(); _attachDragPhoto->raise(); } +void HistoryWidget::setReportSpamStatus(DBIPeerReportSpamStatus status) { + if (_reportSpamStatus == status) { + return; + } + _reportSpamStatus = status; + if (_reportSpamStatus == dbiprsShowButton || _reportSpamStatus == dbiprsReportSent) { + _reportSpamPanel.create(this); + connect(_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked())); + connect(_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide())); + connect(_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); + _reportSpamPanel->setReported(_reportSpamStatus == dbiprsReportSent, _peer); + _reportSpamPanel->show(); + orderWidgets(); + updateControlsGeometry(); + } else { + _reportSpamPanel.destroy(); + } +} + void HistoryWidget::updateStickersByEmoji() { int len = 0; if (!_editMsgId) { @@ -916,11 +941,15 @@ void HistoryWidget::updateSendAction(History *history, SendAction::Type type, in } void HistoryWidget::updateRecentStickers() { - _tabbedPanel->refreshStickers(); + _tabbedSelector->refreshStickers(); } void HistoryWidget::stickersInstalled(uint64 setId) { - _tabbedPanel->stickersInstalled(setId); + if (_tabbedPanel) { + _tabbedPanel->stickersInstalled(setId); + } else if (_tabbedSection) { + _tabbedSection->stickersInstalled(setId); + } } void HistoryWidget::sendActionDone(const MTPBool &result, mtpRequestId req) { @@ -1020,7 +1049,7 @@ void HistoryWidget::notify_botCommandsChanged(UserData *user) { } void HistoryWidget::notify_inlineBotRequesting(bool requesting) { - _attachEmoji->setLoading(requesting); + _tabbedSelectorToggle->setLoading(requesting); } void HistoryWidget::notify_replyMarkupUpdated(const HistoryItem *item) { @@ -1512,7 +1541,6 @@ void HistoryWidget::setReplyReturns(PeerId peer, const QList &replyReturn _replyReturn = App::histItemById(_channel, _replyReturns.back()); } } - updateControlsVisibility(); } void HistoryWidget::calcNextReplyReturn() { @@ -1678,7 +1706,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _saveEditMsgRequestId = 0; _replyEditMsg = nullptr; _editMsgId = _replyToId = 0; - _previewData = 0; + _previewData = nullptr; _previewCache.clear(); _fieldBarCancel->hide(); @@ -1695,7 +1723,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _peer = App::peer(peerId); _channel = peerToChannel(_peer->id); _canSendMessages = canSendMessages(_peer); - _tabbedPanel->setInlineQueryPeer(_peer); + _tabbedSelector->setInlineQueryPeer(_peer); } updateTopBarSelection(); @@ -1775,7 +1803,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re applyDraft(false); _send->finishAnimation(); - resizeEvent(nullptr); + updateControlsGeometry(); if (!_previewCancelled) { onPreviewParse(); } @@ -1847,68 +1875,69 @@ bool HistoryWidget::contentOverlapped(const QRect &globalRect) { return (_attachDragDocument->overlaps(globalRect) || _attachDragPhoto->overlaps(globalRect) || _fieldAutocomplete->overlaps(globalRect) - || _tabbedPanel->overlaps(globalRect) + || (_tabbedPanel && _tabbedPanel->overlaps(globalRect)) || (_inlineResults && _inlineResults->overlaps(globalRect))); } void HistoryWidget::updateReportSpamStatus() { if (!_peer || (_peer->isUser() && (_peer->id == AuthSession::CurrentUserPeerId() || isNotificationsUser(_peer->id) || isServiceUser(_peer->id) || _peer->asUser()->botInfo))) { - _reportSpamStatus = dbiprsHidden; + setReportSpamStatus(dbiprsHidden); return; } else if (!_firstLoadRequest && _history->isEmpty()) { - _reportSpamStatus = dbiprsNoButton; + setReportSpamStatus(dbiprsNoButton); if (cReportSpamStatuses().contains(_peer->id)) { cRefReportSpamStatuses().remove(_peer->id); Local::writeReportSpamStatuses(); } return; } else { - ReportSpamStatuses::const_iterator i = cReportSpamStatuses().constFind(_peer->id); + auto i = cReportSpamStatuses().constFind(_peer->id); if (i != cReportSpamStatuses().cend()) { - _reportSpamStatus = i.value(); - if (_reportSpamStatus == dbiprsNoButton) { - _reportSpamStatus = dbiprsHidden; + if (i.value() == dbiprsNoButton) { + setReportSpamStatus(dbiprsHidden); if (!_peer->isUser() || _peer->asUser()->contact < 1) { MTP::send(MTPmessages_HideReportSpam(_peer->input)); } cRefReportSpamStatuses().insert(_peer->id, _reportSpamStatus); Local::writeReportSpamStatuses(); - } else if (_reportSpamStatus == dbiprsShowButton) { - requestReportSpamSetting(); + } else { + setReportSpamStatus(i.value()); + if (_reportSpamStatus == dbiprsShowButton) { + requestReportSpamSetting(); + } } - _reportSpamPanel->setReported(_reportSpamStatus == dbiprsReportSent, _peer); return; } else if (_peer->migrateFrom()) { // migrate report status i = cReportSpamStatuses().constFind(_peer->migrateFrom()->id); if (i != cReportSpamStatuses().cend()) { - _reportSpamStatus = i.value(); - if (_reportSpamStatus == dbiprsNoButton) { - _reportSpamStatus = dbiprsHidden; + if (i.value() == dbiprsNoButton) { + setReportSpamStatus(dbiprsHidden); if (!_peer->isUser() || _peer->asUser()->contact < 1) { MTP::send(MTPmessages_HideReportSpam(_peer->input)); } - } else if (_reportSpamStatus == dbiprsShowButton) { - requestReportSpamSetting(); + } else { + setReportSpamStatus(i.value()); + if (_reportSpamStatus == dbiprsShowButton) { + requestReportSpamSetting(); + } } cRefReportSpamStatuses().insert(_peer->id, _reportSpamStatus); Local::writeReportSpamStatuses(); - - _reportSpamPanel->setReported(_reportSpamStatus == dbiprsReportSent, _peer); return; } } } + auto status = dbiprsRequesting; if (!AuthSession::Current().data().contactsLoaded().value() || _firstLoadRequest) { - _reportSpamStatus = dbiprsUnknown; + status = dbiprsUnknown; } else if (_peer->isUser() && _peer->asUser()->contact > 0) { - _reportSpamStatus = dbiprsHidden; + status = dbiprsHidden; } else { - _reportSpamStatus = dbiprsRequesting; requestReportSpamSetting(); } + setReportSpamStatus(status); if (_reportSpamStatus == dbiprsHidden) { - _reportSpamPanel->setReported(false, _peer); cRefReportSpamStatuses().insert(_peer->id, _reportSpamStatus); Local::writeReportSpamStatuses(); } @@ -1925,11 +1954,13 @@ void HistoryWidget::reportSpamSettingDone(const MTPPeerSettings &result, mtpRequ _reportSpamSettingRequestId = 0; if (result.type() == mtpc_peerSettings) { - const auto &d(result.c_peerSettings()); - DBIPeerReportSpamStatus status = d.is_report_spam() ? dbiprsShowButton : dbiprsHidden; + auto &d = result.c_peerSettings(); + auto status = d.is_report_spam() ? dbiprsShowButton : dbiprsHidden; if (status != _reportSpamStatus) { - _reportSpamStatus = status; - _reportSpamPanel->setReported(false, _peer); + setReportSpamStatus(status); + if (_reportSpamPanel) { + _reportSpamPanel->setReported(false, _peer); + } cRefReportSpamStatuses().insert(_peer->id, _reportSpamStatus); Local::writeReportSpamStatuses(); @@ -1961,35 +1992,20 @@ void HistoryWidget::updateControlsVisibility() { } updateHistoryDownVisibility(); if (!_history || _a_show.animating()) { - _reportSpamPanel->hide(); - _scroll->hide(); - _kbScroll->hide(); - _send->hide(); - _unblock->hide(); - _botStart->hide(); - _joinChannel->hide(); - _muteUnmute->hide(); - _fieldAutocomplete->hide(); - _field->hide(); - _fieldBarCancel->hide(); - _attachToggle->hide(); - _attachEmoji->hide(); - _silent->hide(); - _historyDown->hide(); - _botKeyboardShow->hide(); - _botKeyboardHide->hide(); - _botCommandStart->hide(); - _tabbedPanel->hide(); - if (_inlineResults) { - _inlineResults->hide(); - } - if (_pinnedBar) { - _pinnedBar->cancel->hide(); - _pinnedBar->shadow->hide(); + if (_tabbedSection && !_tabbedSection->isHidden()) { + _tabbedSection->beforeHiding(); } + hideChildren(); return; } + if (_tabbedSection) { + if (_tabbedSection->isHidden()) { + _tabbedSection->show(); + _tabbedSection->afterShown(); + } + _rightShadow->show(); + } if (_pinnedBar) { _pinnedBar->cancel->show(); _pinnedBar->shadow->show(); @@ -1999,10 +2015,8 @@ void HistoryWidget::updateControlsVisibility() { } else if (!_firstLoadRequest && _scroll->isHidden()) { _scroll->show(); } - if (_reportSpamStatus == dbiprsShowButton || _reportSpamStatus == dbiprsReportSent) { + if (_reportSpamPanel) { _reportSpamPanel->show(); - } else { - _reportSpamPanel->hide(); } if (isBlocked() || isJoinChannel() || isMuteUnmute()) { if (isBlocked()) { @@ -2036,17 +2050,19 @@ void HistoryWidget::updateControlsVisibility() { _kbScroll->hide(); _fieldBarCancel->hide(); _attachToggle->hide(); - _attachEmoji->hide(); + _tabbedSelectorToggle->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); - _tabbedPanel->hide(); + if (_tabbedPanel) { + _tabbedPanel->hide(); + } if (_inlineResults) { _inlineResults->hide(); } if (!_field->isHidden()) { _field->hide(); - resizeEvent(0); + updateControlsGeometry(); update(); } } else if (_canSendMessages) { @@ -2062,7 +2078,7 @@ void HistoryWidget::updateControlsVisibility() { _kbShown = false; _send->hide(); _field->hide(); - _attachEmoji->hide(); + _tabbedSelectorToggle->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); @@ -2079,7 +2095,7 @@ void HistoryWidget::updateControlsVisibility() { updateSendButtonType(); if (_recording) { _field->hide(); - _attachEmoji->hide(); + _tabbedSelectorToggle->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); @@ -2094,19 +2110,19 @@ void HistoryWidget::updateControlsVisibility() { _field->show(); if (_kbShown) { _kbScroll->show(); - _attachEmoji->hide(); + _tabbedSelectorToggle->hide(); _botKeyboardHide->show(); _botKeyboardShow->hide(); _botCommandStart->hide(); } else if (_kbReplyTo) { _kbScroll->hide(); - _attachEmoji->show(); + _tabbedSelectorToggle->show(); _botKeyboardHide->hide(); _botKeyboardShow->hide(); _botCommandStart->hide(); } else { _kbScroll->hide(); - _attachEmoji->show(); + _tabbedSelectorToggle->show(); _botKeyboardHide->hide(); if (_keyboard->hasMarkup()) { _botKeyboardShow->show(); @@ -2131,7 +2147,7 @@ void HistoryWidget::updateControlsVisibility() { if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { if (_fieldBarCancel->isHidden()) { _fieldBarCancel->show(); - resizeEvent(0); + updateControlsGeometry(); update(); } } else { @@ -2150,18 +2166,20 @@ void HistoryWidget::updateControlsVisibility() { _kbScroll->hide(); _fieldBarCancel->hide(); _attachToggle->hide(); - _attachEmoji->hide(); + _tabbedSelectorToggle->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); - _tabbedPanel->hide(); + if (_tabbedPanel) { + _tabbedPanel->hide(); + } if (_inlineResults) { _inlineResults->hide(); } _kbScroll->hide(); if (!_field->isHidden()) { _field->hide(); - resizeEvent(0); + updateControlsGeometry(); update(); } } @@ -2373,7 +2391,7 @@ void HistoryWidget::historyLoaded() { } void HistoryWidget::windowShown() { - resizeEvent(0); + updateControlsGeometry(); } bool HistoryWidget::doWeReadServerHistory() const { @@ -2675,7 +2693,9 @@ bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtp void HistoryWidget::hideSelectorControlsAnimated() { _fieldAutocomplete->hideAnimated(); - _tabbedPanel->hideAnimated(); + if (_tabbedPanel) { + _tabbedPanel->hideAnimated(); + } if (_inlineResults) { _inlineResults->hideAnimated(); } @@ -2894,30 +2914,15 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: historyDownAnimationFinish(); _topShadow->setVisible(params.withTopBarShadow ? false : true); _cacheOver = App::main()->grabForShowAnimation(params); - _topShadow->setVisible(params.withTopBarShadow ? true : false); - _topBar->hide(); - _scroll->hide(); - _kbScroll->hide(); - _reportSpamPanel->hide(); - _historyDown->hide(); - _attachToggle->hide(); - _attachEmoji->hide(); - _fieldAutocomplete->hide(); - _silent->hide(); - _botKeyboardShow->hide(); - _botKeyboardHide->hide(); - _botCommandStart->hide(); - _field->hide(); - _fieldBarCancel->hide(); - _send->hide(); - _unblock->hide(); - _botStart->hide(); - _joinChannel->hide(); - _muteUnmute->hide(); - if (_pinnedBar) { - _pinnedBar->shadow->hide(); - _pinnedBar->cancel->hide(); + if (_tabbedSection && !_tabbedSection->isHidden()) { + _tabbedSection->beforeHiding(); + } + hideChildren(); + if (params.withTopBarShadow) _topShadow->show(); + if (params.withTabbedSection && _tabbedSection) { + _tabbedSection->show(); + _tabbedSection->afterShown(); } if (_showDirection == Window::SlideDirection::FromLeft) { @@ -2937,8 +2942,6 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: void HistoryWidget::animationCallback() { update(); if (!_a_show.animating()) { - _topShadow->setVisible(_peer != nullptr); - _topBar->setVisible(_peer != nullptr); historyDownAnimationFinish(); _cacheUnder = _cacheOver = QPixmap(); doneShow(); @@ -3588,11 +3591,11 @@ void HistoryWidget::onKbToggle(bool manual) { _history->lastKeyboardHiddenId = 0; } } - resizeEvent(0); + updateControlsGeometry(); if (_botKeyboardHide->isHidden() && canWriteMessage() && !_a_show.animating()) { - _attachEmoji->show(); + _tabbedSelectorToggle->show(); } else { - _attachEmoji->hide(); + _tabbedSelectorToggle->hide(); } updateField(); } @@ -3694,6 +3697,67 @@ void HistoryWidget::topBarClick() { } } +void HistoryWidget::updateTabbedSelectorSectionShown() { + auto tabbedSelectorSectionEnabled = AuthSession::Current().data().tabbedSelectorSectionEnabled(); + auto shown = tabbedSelectorSectionEnabled && (width() >= minimalWidthForTabbedSelectorSection()); + auto shownNow = (_tabbedSection != nullptr); + if (shown == shownNow) { + return; + } + + if (shown) { + _tabbedSection.create(this, _controller, _tabbedPanel->takeSelector()); + _tabbedSection->setCancelledCallback([this] { setInnerFocus(); }); + _rightShadow.create(this, st::shadowFg); + auto destroyingPanel = std::move(_tabbedPanel); + updateControlsVisibility(); + } else { + _tabbedPanel.create(this, _controller, _tabbedSection->takeSelector()); + _tabbedSelectorToggle->installEventFilter(_tabbedPanel); + _tabbedSection.destroy(); + _rightShadow.destroy(); + } + orderWidgets(); +} + +int HistoryWidget::minimalWidthForTabbedSelectorSection() const { + return st::windowMinWidth + st::emojiPanWidth; +} + +void HistoryWidget::toggleTabbedSelectorMode() { + auto sectionEnabled = AuthSession::Current().data().tabbedSelectorSectionEnabled(); + if (_tabbedSection) { + AuthSession::Current().data().setTabbedSelectorSectionEnabled(false); + AuthSession::Current().saveDataDelayed(kSaveTabbedSelectorSectionTimeout); + updateTabbedSelectorSectionShown(); + recountChatWidth(); + updateControlsGeometry(); + } else if (_controller->provideChatWidth(minimalWidthForTabbedSelectorSection())) { + if (!AuthSession::Current().data().tabbedSelectorSectionEnabled()) { + AuthSession::Current().data().setTabbedSelectorSectionEnabled(true); + AuthSession::Current().saveDataDelayed(kSaveTabbedSelectorSectionTimeout); + } + updateTabbedSelectorSectionShown(); + recountChatWidth(); + updateControlsGeometry(); + } else { + t_assert(_tabbedPanel != nullptr); + _tabbedPanel->toggleAnimated(); + } +} + +void HistoryWidget::recountChatWidth() { + _chatWidth = width(); + if (_tabbedSection) { + _chatWidth -= _tabbedSection->width(); + } + auto layout = (_chatWidth < st::adaptiveChatWideWidth) ? Adaptive::ChatLayout::Normal : Adaptive::ChatLayout::Wide; + if (layout != Global::AdaptiveChatLayout()) { + Global::SetAdaptiveChatLayout(layout); + Adaptive::Changed().notify(true); + } +} + void HistoryWidget::updateOnlineDisplay() { if (!_history) return; @@ -3783,36 +3847,38 @@ void HistoryWidget::moveFieldControls() { auto keyboardHeight = 0; auto bottom = height(); auto maxKeyboardHeight = st::historyComposeFieldMaxHeight - _field->height(); - _keyboard->resizeToWidth(width(), maxKeyboardHeight); + _keyboard->resizeToWidth(_chatWidth, maxKeyboardHeight); if (_kbShown) { keyboardHeight = qMin(_keyboard->height(), maxKeyboardHeight); bottom -= keyboardHeight; - _kbScroll->setGeometry(0, bottom, width(), keyboardHeight); + _kbScroll->setGeometryToLeft(0, bottom, _chatWidth, keyboardHeight); } -// _attachToggle ------- _inlineResults --------------------------------- _tabbedPanel ------- _fieldBarCancel -// (_attachDocument|_attachPhoto) _field (_silent|_cmdStart|_kbShow) (_kbHide|_attachEmoji) [_broadcast] _send +// _attachToggle --------- _inlineResults -------------------------------------- _tabbedPanel --------- _fieldBarCancel +// (_attachDocument|_attachPhoto) _field (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) [_broadcast] _send // (_botStart|_unblock|_joinChannel|_muteUnmute) auto buttonsBottom = bottom - _attachToggle->height(); auto left = 0; _attachToggle->moveToLeft(left, buttonsBottom); left += _attachToggle->width(); _field->moveToLeft(left, bottom - _field->height() - st::historySendPadding); - auto right = st::historySendRight; + auto right = (width() - _chatWidth) + st::historySendRight; _send->moveToRight(right, buttonsBottom); right += _send->width(); - _attachEmoji->moveToRight(right, buttonsBottom); + _tabbedSelectorToggle->moveToRight(right, buttonsBottom); _botKeyboardHide->moveToRight(right, buttonsBottom); right += _botKeyboardHide->width(); _botKeyboardShow->moveToRight(right, buttonsBottom); _botCommandStart->moveToRight(right, buttonsBottom); _silent->moveToRight(right, buttonsBottom); - _fieldBarCancel->moveToRight(0, _field->y() - st::historySendPadding - _fieldBarCancel->height()); + _fieldBarCancel->moveToRight(width() - _chatWidth, _field->y() - st::historySendPadding - _fieldBarCancel->height()); if (_inlineResults) { _inlineResults->moveBottom(_field->y() - st::historySendPadding); } - _tabbedPanel->moveBottom(buttonsBottom); + if (_tabbedPanel) { + _tabbedPanel->moveBottom(buttonsBottom); + } - auto fullWidthButtonRect = QRect(0, bottom - _botStart->height(), width(), _botStart->height()); + auto fullWidthButtonRect = myrtlrect(0, bottom - _botStart->height(), _chatWidth, _botStart->height()); _botStart->setGeometry(fullWidthButtonRect); _unblock->setGeometry(fullWidthButtonRect); _joinChannel->setGeometry(fullWidthButtonRect); @@ -3821,9 +3887,9 @@ void HistoryWidget::moveFieldControls() { void HistoryWidget::updateFieldSize() { auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup(); - auto fieldWidth = width() - _attachToggle->width() - st::historySendRight; + auto fieldWidth = _chatWidth - _attachToggle->width() - st::historySendRight; fieldWidth -= _send->width(); - fieldWidth -= _attachEmoji->width(); + fieldWidth -= _tabbedSelectorToggle->width(); if (kbShowShown) fieldWidth -= _botKeyboardShow->width(); if (_cmdStartShown) fieldWidth -= _botCommandStart->width(); if (hasSilentToggle()) fieldWidth -= _silent->width(); @@ -4297,8 +4363,10 @@ void HistoryWidget::reportSpamDone(PeerData *peer, const MTPBool &result, mtpReq cRefReportSpamStatuses().insert(peer->id, dbiprsReportSent); Local::writeReportSpamStatuses(); } - _reportSpamStatus = dbiprsReportSent; - _reportSpamPanel->setReported(_reportSpamStatus == dbiprsReportSent, peer); + setReportSpamStatus(dbiprsReportSent); + if (_reportSpamPanel) { + _reportSpamPanel->setReported(_reportSpamStatus == dbiprsReportSent, peer); + } } bool HistoryWidget::reportSpamFail(const RPCError &error, mtpRequestId req) { @@ -4317,7 +4385,7 @@ void HistoryWidget::onReportSpamHide() { MTP::send(MTPmessages_HideReportSpam(_peer->input)); } - _reportSpamStatus = dbiprsHidden; + setReportSpamStatus(dbiprsHidden); updateControlsVisibility(); } @@ -4366,7 +4434,7 @@ void HistoryWidget::grapWithoutTopBarShadow() { void HistoryWidget::grabFinish() { _inGrab = false; - resizeEvent(0); + updateControlsGeometry(); _topShadow->show(); } @@ -4421,31 +4489,32 @@ void HistoryWidget::notify_handlePendingHistoryUpdate() { } void HistoryWidget::resizeEvent(QResizeEvent *e) { - auto layout = (width() < st::adaptiveChatWideWidth) ? Adaptive::ChatLayout::Normal : Adaptive::ChatLayout::Wide; - if (layout != Global::AdaptiveChatLayout()) { - Global::SetAdaptiveChatLayout(layout); - Adaptive::Changed().notify(true); - } + updateTabbedSelectorSectionShown(); + recountChatWidth(); updateControlsGeometry(); } void HistoryWidget::updateControlsGeometry() { - _topBar->setGeometryToLeft(0, 0, width(), st::topBarHeight); - _reportSpamPanel->resize(width(), _reportSpamPanel->height()); + if (_tabbedSection) { + _tabbedSection->setGeometryToRight(0, 0, st::emojiPanWidth, height()); + } + _topBar->setGeometryToLeft(0, 0, _chatWidth, st::topBarHeight); moveFieldControls(); auto scrollAreaTop = _topBar->bottomNoMargins(); if (_pinnedBar) { - _pinnedBar->cancel->move(width() - _pinnedBar->cancel->width(), scrollAreaTop); + _pinnedBar->cancel->moveToLeft(_chatWidth - _pinnedBar->cancel->width(), scrollAreaTop); scrollAreaTop += st::historyReplyHeight; - _pinnedBar->shadow->setGeometry(0, scrollAreaTop, width(), st::lineWidth); + _pinnedBar->shadow->setGeometryToLeft(0, scrollAreaTop, _chatWidth, st::lineWidth); } if (_scroll->y() != scrollAreaTop) { - _scroll->move(0, scrollAreaTop); - _reportSpamPanel->move(0, scrollAreaTop); + _scroll->moveToLeft(0, scrollAreaTop); _fieldAutocomplete->setBoundings(_scroll->geometry()); } + if (_reportSpamPanel) { + _reportSpamPanel->setGeometryToLeft(0, _scroll->y(), _chatWidth, _reportSpamPanel->height()); + } updateListSize(false, false, { ScrollChangeAdd, App::main() ? App::main()->contentScrollAddToY() : 0 }); @@ -4474,8 +4543,12 @@ void HistoryWidget::updateControlsGeometry() { break; } - _topShadow->resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth); - _topShadow->moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, _topBar->bottomNoMargins()); + if (_rightShadow) { + _rightShadow->setGeometryToLeft(_chatWidth - st::lineWidth, 0, st::lineWidth, height()); + } + auto topShadowLeft = (Adaptive::OneColumn() || _inGrab) ? 0 : st::lineWidth; + auto topShadowRight = _rightShadow ? st::lineWidth : 0; + _topShadow->setGeometryToLeft(topShadowLeft, _topBar->bottomNoMargins(), _chatWidth - topShadowLeft - topShadowRight, st::lineWidth); } void HistoryWidget::itemRemoved(HistoryItem *item) { @@ -4540,9 +4613,9 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh } int wasScrollTop = _scroll->scrollTop(); bool wasAtBottom = wasScrollTop + 1 > _scroll->scrollTopMax(); - bool needResize = (_scroll->width() != width()) || (_scroll->height() != newScrollHeight); + bool needResize = (_scroll->width() != _chatWidth) || (_scroll->height() != newScrollHeight); if (needResize) { - _scroll->resize(width(), newScrollHeight); + _scroll->resize(_chatWidth, newScrollHeight); // on initial updateListSize we didn't put the _scroll->scrollTop correctly yet // so visibleAreaUpdated() call will erase it with the new (undefined) value if (!initial) { @@ -4737,11 +4810,11 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { if (!_a_show.animating()) { if (hasMarkup) { _kbScroll->show(); - _attachEmoji->hide(); + _tabbedSelectorToggle->hide(); _botKeyboardHide->show(); } else { _kbScroll->hide(); - _attachEmoji->show(); + _tabbedSelectorToggle->show(); _botKeyboardHide->hide(); } _botKeyboardShow->hide(); @@ -4760,7 +4833,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { } else { if (!_a_show.animating()) { _kbScroll->hide(); - _attachEmoji->show(); + _tabbedSelectorToggle->show(); _botKeyboardHide->hide(); _botKeyboardShow->show(); _botCommandStart->hide(); @@ -4776,7 +4849,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { } else { if (!_scroll->isHidden()) { _kbScroll->hide(); - _attachEmoji->show(); + _tabbedSelectorToggle->show(); _botKeyboardHide->hide(); _botKeyboardShow->hide(); _botCommandStart->show(); @@ -4789,7 +4862,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { updateMouseTracking(); } } - resizeEvent(0); + updateControlsGeometry(); update(); } @@ -5000,7 +5073,7 @@ void HistoryWidget::updatePinnedBar(bool force) { _peer->asChannel()->mgInfo->pinnedMsgId = 0; } destroyPinnedBar(); - resizeEvent(0); + updateControlsGeometry(); } } @@ -5052,7 +5125,7 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { if (_scroll->scrollTop() != unreadBarTop()) { _scroll->scrollToY(_scroll->scrollTop() - st::historyReplyHeight); } - resizeEvent(0); + updateControlsGeometry(); } return result; } @@ -5227,7 +5300,7 @@ void HistoryWidget::onReplyToMessage() { if (!_field->isHidden()) _fieldBarCancel->show(); updateMouseTracking(); updateReplyToName(); - resizeEvent(0); + updateControlsGeometry(); updateField(); } @@ -5282,7 +5355,7 @@ void HistoryWidget::onEditMessage() { updateFieldPlaceholder(); updateMouseTracking(); updateReplyToName(); - resizeEvent(nullptr); + updateControlsGeometry(); updateField(); _saveDraftText = true; @@ -5308,7 +5381,7 @@ void HistoryWidget::onUnpinMessage() { _peer->asChannel()->mgInfo->pinnedMsgId = 0; if (pinnedMsgVisibilityUpdated()) { - resizeEvent(0); + updateControlsGeometry(); update(); } @@ -5327,7 +5400,7 @@ void HistoryWidget::onPinnedHide() { if (!_peer || !_peer->isMegagroup()) return; if (!_peer->asChannel()->mgInfo->pinnedMsgId) { if (pinnedMsgVisibilityUpdated()) { - resizeEvent(0); + updateControlsGeometry(); update(); } return; @@ -5339,7 +5412,7 @@ void HistoryWidget::onPinnedHide() { Global::RefHiddenPinnedMessages().insert(_peer->id, _peer->asChannel()->mgInfo->pinnedMsgId); Local::writeUserSettings(); if (pinnedMsgVisibilityUpdated()) { - resizeEvent(0); + updateControlsGeometry(); update(); } } @@ -5372,7 +5445,7 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) { updateBotKeyboard(); - resizeEvent(0); + updateControlsGeometry(); update(); } else if (auto localDraft = (_history ? _history->localDraft() : nullptr)) { if (localDraft->msgId) { @@ -5404,7 +5477,7 @@ void HistoryWidget::cancelReplyAfterMediaSend(bool lastKeyboardUsed) { int HistoryWidget::countMembersDropdownHeightMax() const { int result = height() - st::membersInnerDropdown.padding.top() - st::membersInnerDropdown.padding.bottom(); - result -= _attachEmoji->height(); + result -= _tabbedSelectorToggle->height(); accumulate_min(result, st::membersInnerHeightMax); return result; } @@ -5440,7 +5513,7 @@ void HistoryWidget::cancelEdit() { updateBotKeyboard(); updateFieldPlaceholder(); - resizeEvent(nullptr); + updateControlsGeometry(); update(); } @@ -5604,7 +5677,7 @@ void HistoryWidget::updatePreview() { _fieldBarCancel->hide(); updateMouseTracking(); } - resizeEvent(0); + updateControlsGeometry(); update(); } @@ -5911,8 +5984,8 @@ void HistoryWidget::updateReplyToName() { } void HistoryWidget::updateField() { - int32 fy = _scroll->y() + _scroll->height(); - update(0, fy, width(), height() - fy); + auto fieldAreaTop = _scroll->y() + _scroll->height(); + rtlupdate(0, fieldAreaTop, _chatWidth, height() - fieldAreaTop); } void HistoryWidget::drawField(Painter &p, const QRect &rect) { @@ -5936,7 +6009,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { backh += st::historyReplyHeight; } bool drawPreview = (_previewData && _previewData->pendingTill >= 0) && !_replyForwardPressed; - p.fillRect(0, backy, width(), backh, st::historyReplyBg); + p.fillRect(myrtlrect(0, backy, _chatWidth, backh), st::historyReplyBg); if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { int32 replyLeft = st::historyReplySkip; (_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); @@ -5954,14 +6027,14 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { if (_editMsgId) { paintEditHeader(p, rect, replyLeft, backy); } else { - _replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); + _replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), _chatWidth - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); } p.setPen(((drawMsgText->toHistoryMessage() && drawMsgText->toHistoryMessage()->emptyText()) || drawMsgText->serviceMsg()) ? st::historyComposeAreaFgService : st::historyComposeAreaFg); - _replyEditMsgText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); + _replyEditMsgText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, _chatWidth - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); } else { p.setFont(st::msgDateFont); p.setPen(st::historyComposeAreaFgService); - p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right())); + p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), _chatWidth - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right())); } } } else if (from && text) { @@ -5981,7 +6054,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { p.setPen(st::historyReplyNameFg); from->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top(), width() - forwardLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); p.setPen(serviceColor ? st::historyComposeAreaFgService : st::historyComposeAreaFg); - text->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - forwardLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); + text->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, _chatWidth - forwardLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); } } if (drawPreview) { @@ -6001,9 +6074,9 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { previewLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } p.setPen(st::historyReplyNameFg); - _previewTitle.drawElided(p, previewLeft, backy + st::msgReplyPadding.top(), width() - previewLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); + _previewTitle.drawElided(p, previewLeft, backy + st::msgReplyPadding.top(), _chatWidth - previewLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); p.setPen(st::historyComposeAreaFg); - _previewDescription.drawElided(p, previewLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - previewLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); + _previewDescription.drawElided(p, previewLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, _chatWidth - previewLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); } } @@ -6015,12 +6088,12 @@ constexpr int FullDayInMs = 86400 * 1000; } // namespace void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int top) const { - if (!rect.intersects(QRect(left, top, width() - left, st::normalFont->height))) { + if (!rect.intersects(myrtlrect(left, top, _chatWidth - left, st::normalFont->height))) { return; } p.setFont(st::msgServiceNameFont); - p.drawText(left, top + st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, lang(lng_edit_message)); + p.drawTextLeft(left, top + st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, width(), lang(lng_edit_message)); if (!_replyEditMsg) return; @@ -6045,7 +6118,7 @@ void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int } // Restart timer only if we are sure that we've painted the whole timer. - if (rect.contains(QRect(left, top, width() - left, st::normalFont->height)) && updateIn > 0) { + if (rect.contains(myrtlrect(left, top, _chatWidth - left, st::normalFont->height)) && updateIn > 0) { _updateEditTimeLeftDisplay.start(updateIn); } @@ -6064,17 +6137,17 @@ void HistoryWidget::drawRecording(Painter &p, float64 recordActive) { auto d = 2 * qRound(st::historyRecordSignalMin + (delta * (st::historyRecordSignalMax - st::historyRecordSignalMin))); { PainterHighQualityEnabler hq(p); - p.drawEllipse(_attachToggle->x() + (_attachEmoji->width() - d) / 2, _attachToggle->y() + (_attachToggle->height() - d) / 2, d, d); + p.drawEllipse(_attachToggle->x() + (_tabbedSelectorToggle->width() - d) / 2, _attachToggle->y() + (_attachToggle->height() - d) / 2, d, d); } auto duration = formatDurationText(_recordingSamples / Media::Player::kDefaultFrequency); p.setFont(st::historyRecordFont); p.setPen(st::historyRecordDurationFg); - p.drawText(_attachToggle->x() + _attachEmoji->width(), _attachToggle->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration); + p.drawText(_attachToggle->x() + _tabbedSelectorToggle->width(), _attachToggle->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration); - int32 left = _attachToggle->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send->width() - st::historyRecordVoice.width()) / 2); - int32 right = width() - _send->width(); + int32 left = _attachToggle->x() + _tabbedSelectorToggle->width() + st::historyRecordFont->width(duration) + ((_send->width() - st::historyRecordVoice.width()) / 2); + int32 right = _chatWidth - _send->width(); p.setPen(anim::pen(st::historyRecordCancel, st::historyRecordCancelActive, 1. - recordActive)); p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachToggle->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, lang(lng_record_cancel)); @@ -6087,10 +6160,10 @@ void HistoryWidget::drawPinnedBar(Painter &p) { Text *from = 0, *text = 0; bool serviceColor = false, hasForward = readyToForward(); ImagePtr preview; - p.fillRect(0, top, width(), st::historyReplyHeight, st::historyPinnedBg); + p.fillRect(myrtlrect(0, top, _chatWidth, st::historyReplyHeight), st::historyPinnedBg); top += st::msgReplyPadding.top(); - QRect rbar(rtlrect(st::msgReplyBarSkip + st::msgReplyBarPos.x(), top + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), width())); + QRect rbar(rtlrect(st::msgReplyBarSkip + st::msgReplyBarPos.x(), top + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), _chatWidth)); p.fillRect(rbar, st::msgInReplyBarColor); int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip; @@ -6108,11 +6181,11 @@ void HistoryWidget::drawPinnedBar(Painter &p) { p.drawText(left, top + st::msgServiceNameFont->ascent, lang(lng_pinned_message)); p.setPen(((_pinnedBar->msg->toHistoryMessage() && _pinnedBar->msg->toHistoryMessage()->emptyText()) || _pinnedBar->msg->serviceMsg()) ? st::historyComposeAreaFgService : st::historyComposeAreaFg); - _pinnedBar->text.drawElided(p, left, top + st::msgServiceNameFont->height, width() - left - _pinnedBar->cancel->width() - st::msgReplyPadding.right()); + _pinnedBar->text.drawElided(p, left, top + st::msgServiceNameFont->height, _chatWidth - left - _pinnedBar->cancel->width() - st::msgReplyPadding.right()); } else { p.setFont(st::msgDateFont); p.setPen(st::historyComposeAreaFgService); - p.drawText(left, top + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - left - _pinnedBar->cancel->width() - st::msgReplyPadding.right())); + p.drawText(left, top + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), _chatWidth - left - _pinnedBar->cancel->width() - st::msgReplyPadding.right())); } } @@ -6134,10 +6207,11 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { _historyDownShown.step(ms); auto progress = _a_show.current(ms, 1.); if (_a_show.animating()) { + auto animationWidth = (!_tabbedSection || _tabbedSection->isHidden()) ? width() : _chatWidth; auto retina = cIntRetinaFactor(); auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft); auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); - auto coordOver = fromLeft ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto coordOver = fromLeft ? anim::interpolate(0, animationWidth, progress) : anim::interpolate(animationWidth, 0, progress); auto shadow = fromLeft ? (1. - progress) : progress; if (coordOver > 0) { p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, height() * retina)); @@ -6151,7 +6225,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { return; } - QRect fill(0, 0, width(), App::main()->height()); + QRect fill(0, 0, _history ? _chatWidth : width(), App::main()->height()); auto fromy = App::main()->backgroundFromY(); auto x = 0, y = 0; QPixmap cached = App::main()->cachedBackground(fill, x, y); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index adb72fd5df..7189e92b9a 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -58,6 +58,8 @@ class TopBarWidget; namespace ChatHelpers { class TabbedPanel; +class TabbedSection; +class TabbedSelector; } // namespace ChatHelpers class DragArea; @@ -476,6 +478,11 @@ private: void fullPeerUpdated(PeerData *peer); void topBarClick(); + void toggleTabbedSelectorMode(); + void updateTabbedSelectorSectionShown(); + void recountChatWidth(); + int minimalWidthForTabbedSelectorSection() const; + void setReportSpamStatus(DBIPeerReportSpamStatus status); void animationCallback(); void updateOverStates(QPoint pos); @@ -529,6 +536,7 @@ private: int _replyToNameVersion = 0; void updateReplyToName(); + int _chatWidth = 0; MsgId _editMsgId = 0; HistoryItem *_replyEditMsg = nullptr; @@ -739,7 +747,7 @@ private: bool showRecordButton() const; bool showInlineBotCancel() const; - object_ptr _reportSpamPanel; + object_ptr _reportSpamPanel = { nullptr }; object_ptr _send; object_ptr _unblock; @@ -749,7 +757,7 @@ private: mtpRequestId _unblockRequest = 0; mtpRequestId _reportSpamRequest = 0; object_ptr _attachToggle; - object_ptr _attachEmoji; + object_ptr _tabbedSelectorToggle; object_ptr _botKeyboardShow; object_ptr _botKeyboardHide; object_ptr _botCommandStart; @@ -781,6 +789,8 @@ private: object_ptr _inlineResults = { nullptr }; object_ptr _tabbedPanel; + object_ptr _tabbedSection = { nullptr }; + QPointer _tabbedSelector; DragState _attachDrag = DragStateNone; object_ptr _attachDragDocument, _attachDragPhoto; @@ -814,6 +824,7 @@ private: QTimer _saveDraftTimer, _saveCloudDraftTimer; object_ptr _topShadow; + object_ptr _rightShadow = { nullptr }; bool _inGrab = false; }; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 6b79a14839..a094a3ee97 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -121,7 +121,6 @@ void Widget::changeLanguage(int32 languageId) { } void Widget::setInnerFocus() { - Global::RefDialogsListFocused().set(false, true); if (getStep()->animating()) { setFocus(); } else { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 113379a24d..46aabc572c 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -113,12 +113,15 @@ MainWidget::MainWidget(QWidget *parent, gsl::not_null contr subscribe(AuthSession::Current().api().fullPeerUpdated(), [this](PeerData *peer) { emit peerUpdated(peer); }); - subscribe(Global::RefDialogsListFocused(), [this](bool) { + subscribe(_controller->dialogsListFocused(), [this](bool) { updateDialogsWidthAnimated(); }); - subscribe(Global::RefDialogsListDisplayForced(), [this](bool) { + subscribe(_controller->dialogsListDisplayForced(), [this](bool) { updateDialogsWidthAnimated(); }); + subscribe(_controller->dialogsWidthRatio(), [this](float64) { + updateControlsGeometry(); + }); QCoreApplication::instance()->installEventFilter(this); @@ -2141,7 +2144,7 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show } } - Global::RefDialogsListFocused().set(false, true); + _controller->dialogsListFocused().set(false, true); _a_dialogsWidth.finish(); bool back = (way == Ui::ShowWay::Backward || !peerId); @@ -2348,7 +2351,7 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool return; } - Global::RefDialogsListFocused().set(false, true); + _controller->dialogsListFocused().set(false, true); _a_dialogsWidth.finish(); auto animatedShow = [this] { @@ -2411,9 +2414,10 @@ void MainWidget::showWideSection(const Window::SectionMemento &memento) { showNewWideSection(&memento, false, true); } -Window::SectionSlideParams MainWidget::prepareShowAnimation(bool willHaveTopBarShadow) { +Window::SectionSlideParams MainWidget::prepareShowAnimation(bool willHaveTopBarShadow, bool willHaveTabbedSection) { Window::SectionSlideParams result; result.withTopBarShadow = willHaveTopBarShadow; + result.withTabbedSection = willHaveTabbedSection; if (selectingPeer() && Adaptive::OneColumn()) { result.withTopBarShadow = false; } else if (_wideSection) { @@ -2423,6 +2427,9 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation(bool willHaveTopBarS } else if (!_overview && !_history->peer()) { result.withTopBarShadow = false; } + if ((selectingPeer() && Adaptive::OneColumn()) || !_history->peer()) { + result.withTabbedSection = false; + } if (_player) { _player->hideShadow(); @@ -2480,25 +2487,25 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation(bool willHaveTopBarS } Window::SectionSlideParams MainWidget::prepareWideSectionAnimation(Window::SectionWidget *section) { - return prepareShowAnimation(section->hasTopBarShadow()); + return prepareShowAnimation(section->hasTopBarShadow(), false); } Window::SectionSlideParams MainWidget::prepareHistoryAnimation(PeerId historyPeerId) { - return prepareShowAnimation(historyPeerId != 0); + return prepareShowAnimation(historyPeerId != 0, historyPeerId != 0); } Window::SectionSlideParams MainWidget::prepareOverviewAnimation() { - return prepareShowAnimation(true); + return prepareShowAnimation(true, false); } Window::SectionSlideParams MainWidget::prepareDialogsAnimation() { - return prepareShowAnimation(false); + return prepareShowAnimation(false, false); } void MainWidget::showNewWideSection(const Window::SectionMemento *memento, bool back, bool saveInStack) { QPixmap animCache; - Global::RefDialogsListFocused().set(false, true); + _controller->dialogsListFocused().set(false, true); _a_dialogsWidth.finish(); auto newWideGeometry = QRect(_history->x(), _playerHeight, _history->width(), height() - _playerHeight); @@ -3058,22 +3065,21 @@ bool MainWidget::eventFilter(QObject *o, QEvent *e) { } else if (e->type() == QEvent::MouseButtonRelease) { _resizingSide = false; if (!Adaptive::OneColumn()) { - Global::SetDialogsWidthRatio(float64(_dialogsWidth) / width()); + _controller->dialogsWidthRatio().set(float64(_dialogsWidth) / width(), true); } Local::writeUserSettings(); } else if (e->type() == QEvent::MouseMove && _resizingSide) { auto newWidth = mouseLeft() - _resizingSideShift; - Global::SetDialogsWidthRatio(float64(newWidth) / width()); - updateControlsGeometry(); + _controller->dialogsWidthRatio().set(float64(newWidth) / width(), true); } } else if (e->type() == QEvent::FocusIn) { if (auto widget = qobject_cast(o)) { if (_history == widget || _history->isAncestorOf(widget) || (_overview && (_overview == widget || _overview->isAncestorOf(widget))) || (_wideSection && (_wideSection == widget || _wideSection->isAncestorOf(widget)))) { - Global::RefDialogsListFocused().set(false, false); + _controller->dialogsListFocused().set(false); } else if (_dialogs == widget || _dialogs->isAncestorOf(widget)) { - Global::RefDialogsListFocused().set(true, false); + _controller->dialogsListFocused().set(true); } } } else if (e->type() == QEvent::MouseButtonPress) { @@ -3094,62 +3100,14 @@ void MainWidget::handleAdaptiveLayoutUpdate() { } void MainWidget::updateWindowAdaptiveLayout() { - auto layout = Adaptive::WindowLayout::OneColumn; - - auto dialogsWidth = qRound(width() * Global::DialogsWidthRatio()); - auto historyWidth = width() - dialogsWidth; - accumulate_max(historyWidth, st::windowMinWidth); - dialogsWidth = width() - historyWidth; - - auto useOneColumnLayout = [this, dialogsWidth] { - auto someSectionShown = !selectingPeer() && isSectionShown(); - if (dialogsWidth < st::dialogsPadding.x() && (Adaptive::OneColumn() || someSectionShown)) { - return true; - } - if (width() < st::windowMinWidth + st::dialogsWidthMin) { - return true; - } - return false; - }; - auto useSmallColumnLayout = [this, dialogsWidth] { - // used if useOneColumnLayout() == false. - if (dialogsWidth < st::dialogsWidthMin / 2) { - return true; - } - return false; - }; - if (useOneColumnLayout()) { - dialogsWidth = width(); - } else if (useSmallColumnLayout()) { - layout = Adaptive::WindowLayout::SmallColumn; - auto forceWideDialogs = [this] { - if (Global::DialogsListDisplayForced().value()) { - return true; - } else if (Global::DialogsListFocused().value()) { - return true; - } - return !isSectionShown(); - }; - if (forceWideDialogs()) { - dialogsWidth = st::dialogsWidthMin; - } else { - dialogsWidth = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPadding.x(); - } - } else { - layout = Adaptive::WindowLayout::Normal; - accumulate_max(dialogsWidth, st::dialogsWidthMin); - } - _dialogsWidth = dialogsWidth; - if (layout != Global::AdaptiveWindowLayout()) { - Global::SetAdaptiveWindowLayout(layout); + auto layout = _controller->computeColumnLayout(); + _dialogsWidth = layout.dialogsWidth; + if (layout.windowLayout != Global::AdaptiveWindowLayout()) { + Global::SetAdaptiveWindowLayout(layout.windowLayout); Adaptive::Changed().notify(true); } } -bool MainWidget::needBackButton() { - return isSectionShown(); -} - bool MainWidget::paintTopBar(Painter &p, int decreaseWidth, TimeMs ms) { if (_overview) { return _overview->paintTopBar(p, decreaseWidth); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 4e4c2a32e7..579d022ff0 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -144,7 +144,7 @@ class MainWidget : public TWidget, public RPCSender, private base::Subscriber { public: MainWidget(QWidget *parent, gsl::not_null controller); - bool needBackButton(); + bool isSectionShown() const; // Temporary methods, while top bar was not done inside HistoryWidget / OverviewWidget. bool paintTopBar(Painter &, int decreaseWidth, TimeMs ms); @@ -481,9 +481,8 @@ private: void overviewLoaded(History *history, const MTPmessages_Messages &result, mtpRequestId req); void mediaOverviewUpdated(const Notify::PeerUpdate &update); - Window::SectionSlideParams prepareShowAnimation(bool willHaveTopBarShadow); + Window::SectionSlideParams prepareShowAnimation(bool willHaveTopBarShadow, bool willHaveTabbedSection); void showNewWideSection(const Window::SectionMemento *memento, bool back, bool saveInStack); - bool isSectionShown() const; // All this methods use the prepareShowAnimation(). Window::SectionSlideParams prepareWideSectionAnimation(Window::SectionWidget *section); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index a236784d25..5195ddaa5c 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -110,15 +110,25 @@ MainWindow::MainWindow() { connect(&_autoLockTimer, SIGNAL(timeout()), this, SLOT(checkAutoLock())); - subscribe(Global::RefSelfChanged(), [this]() { updateGlobalMenu(); }); + subscribe(Global::RefSelfChanged(), [this] { updateGlobalMenu(); }); subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &data) { themeUpdated(data); }); + subscribe(Messenger::Instance().authSessionChanged(), [this] { checkAuthSession(); }); + checkAuthSession(); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_OpaquePaintEvent); } +void MainWindow::checkAuthSession() { + if (AuthSession::Exists()) { + _controller = std::make_unique(this); + } else { + _controller = nullptr; + } +} + void MainWindow::inactivePress(bool inactive) { _inactivePress = inactive; if (_inactivePress) { @@ -200,7 +210,6 @@ void MainWindow::clearWidgetsHook() { auto wasMain = (_main != nullptr); _passcode.destroyDelayed(); _main.destroy(); - _controller.reset(); _intro.destroy(); if (wasMain) { App::clearHistories(); @@ -352,7 +361,7 @@ void MainWindow::setupMain(const MTPUser *self) { t_assert(AuthSession::Exists()); - _controller = std::make_unique(this); + _main.create(bodyWidget(), controller()); _main->show(); updateControlsGeometry(); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index a9f5b19f19..948001f35d 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -199,6 +199,7 @@ private slots: void onWindowActiveChanged(); private: + void checkAuthSession(); void showConnecting(const QString &text, const QString &reconnect = QString()); void hideConnecting(); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 2886b8d989..47eae21c3c 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -99,7 +99,7 @@ MediaView::MediaView(QWidget*) : TWidget(nullptr) }); } }; - subscribe(Messenger::Instance().authSessionChanged(), [this, subscribeToDownloadFinished] { + subscribe(Messenger::Instance().authSessionChanged(), [subscribeToDownloadFinished] { subscribeToDownloadFinished(); }); subscribeToDownloadFinished(); diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index 59262ef7c8..5a9cb9f9cc 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -38,6 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "history/history_location_manager.h" #include "ui/widgets/tooltip.h" #include "storage/serialize_common.h" +#include "window/window_controller.h" namespace { @@ -51,7 +52,7 @@ Messenger *Messenger::InstancePointer() { struct Messenger::Private { UserId authSessionUserId = 0; - std::unique_ptr authSessionData; + std::unique_ptr storedAuthSession; MTP::Instance::Config mtpConfig; MTP::AuthKeysList mtpKeysToDestroy; }; @@ -220,14 +221,14 @@ void Messenger::setAuthSessionUserId(UserId userId) { _private->authSessionUserId = userId; } -void Messenger::setAuthSessionData(std::unique_ptr data) { +void Messenger::setAuthSessionFromStorage(std::unique_ptr data) { Expects(!authSession()); - _private->authSessionData = std::move(data); + _private->storedAuthSession = std::move(data); } AuthSessionData *Messenger::getAuthSessionData() { if (_private->authSessionUserId) { - return _private->authSessionData.get(); + return _private->storedAuthSession ? &_private->storedAuthSession->data : nullptr; } else if (AuthSession::Exists()) { return &AuthSession::Current().data(); } @@ -301,11 +302,15 @@ void Messenger::startMtp() { if (_private->authSessionUserId) { authSessionCreate(base::take(_private->authSessionUserId)); } - if (_private->authSessionData) { + if (_private->storedAuthSession) { if (_authSession) { - _authSession->data().copyFrom(*_private->authSessionData); + _authSession->data().copyFrom(_private->storedAuthSession->data); + if (auto window = App::wnd()) { + t_assert(window->controller() != nullptr); + window->controller()->dialogsWidthRatio().set(_private->storedAuthSession->dialogsWidthRatio); + } } - _private->authSessionData.reset(); + _private->storedAuthSession.reset(); } } @@ -613,14 +618,14 @@ void Messenger::onSwitchTestMode() { void Messenger::authSessionCreate(UserId userId) { Expects(_mtproto != nullptr); _authSession = std::make_unique(userId); - authSessionChanged().notify(); + authSessionChanged().notify(true); } void Messenger::authSessionDestroy() { _authSession.reset(); - _private->authSessionData.reset(); + _private->storedAuthSession.reset(); _private->authSessionUserId = 0; - authSessionChanged().notify(); + authSessionChanged().notify(true); } void Messenger::setInternalLinkDomain(const QString &domain) const { diff --git a/Telegram/SourceFiles/messenger.h b/Telegram/SourceFiles/messenger.h index 43d10169a8..73428f100d 100644 --- a/Telegram/SourceFiles/messenger.h +++ b/Telegram/SourceFiles/messenger.h @@ -33,6 +33,10 @@ class MainWidget; class FileUploader; class Translator; +namespace Local { +struct StoredAuthSession; +} // namespace Local + class Messenger final : public QObject, public RPCSender, private base::Subscriber { Q_OBJECT @@ -62,7 +66,7 @@ public: void setMtpMainDcId(MTP::DcId mainDcId); void setMtpKey(MTP::DcId dcId, const MTP::AuthKey::Data &keyData); void setAuthSessionUserId(UserId userId); - void setAuthSessionData(std::unique_ptr data); + void setAuthSessionFromStorage(std::unique_ptr data); AuthSessionData *getAuthSessionData(); // Serialization. diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 2a5dff84b5..c173bf83a6 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -29,6 +29,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/input_fields.h" #include "styles/style_boxes.h" #include "window/window_slide_animation.h" +#include "window/window_controller.h" #include "auth_session.h" PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) @@ -187,6 +188,8 @@ void PasscodeWidget::resizeEvent(QResizeEvent *e) { } void PasscodeWidget::setInnerFocus() { - Global::RefDialogsListFocused().set(false, true); + if (auto controller = App::wnd()->controller()) { + controller->dialogsListFocused().set(false, true); + } _passcode->setFocusFast(); } diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index f4862c524d..65806fa704 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -35,6 +35,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "application.h" #include "apiwrap.h" #include "auth_session.h" +#include "window/window_controller.h" #include @@ -638,12 +639,12 @@ enum class WriteMapWhen { Soon, }; -std::unique_ptr AuthSessionDataCache; -AuthSessionData &GetAuthSessionDataCache() { - if (!AuthSessionDataCache) { - AuthSessionDataCache = std::make_unique(); +std::unique_ptr StoredAuthSessionCache; +StoredAuthSession &GetStoredAuthSessionCache() { + if (!StoredAuthSessionCache) { + StoredAuthSessionCache = std::make_unique(); } - return *AuthSessionDataCache; + return *StoredAuthSessionCache; } void _writeMap(WriteMapWhen when = WriteMapWhen::Soon); @@ -1010,7 +1011,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting if (!_checkStreamStatus(stream)) return false; Global::SetDialogsModeEnabled(enabled == 1); - Dialogs::Mode mode = Dialogs::Mode::All; + auto mode = Dialogs::Mode::All; if (enabled) { mode = static_cast(modeInt); if (mode != Dialogs::Mode::All && mode != Dialogs::Mode::Important) { @@ -1086,7 +1087,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting stream >> v; if (!_checkStreamStatus(stream)) return false; - Global::SetDialogsWidthRatio(v / 1000000.); + GetStoredAuthSessionCache().dialogsWidthRatio = v / 1000000.; } break; case dbiLastSeenWarningSeenOld: { @@ -1094,7 +1095,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting stream >> v; if (!_checkStreamStatus(stream)) return false; - GetAuthSessionDataCache().setLastSeenWarningSeen(v == 1); + GetStoredAuthSessionCache().data.setLastSeenWarningSeen(v == 1); } break; case dbiAuthSessionData: { @@ -1102,7 +1103,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting stream >> v; if (!_checkStreamStatus(stream)) return false; - GetAuthSessionDataCache().constructFromSerialized(v); + GetStoredAuthSessionCache().data.constructFromSerialized(v); } break; case dbiWorkMode: { @@ -1707,8 +1708,9 @@ void _writeUserSettings() { recentEmojiPreloadData.push_back(qMakePair(item.first->id(), item.second)); } } - auto userDataInstance = AuthSessionDataCache ? AuthSessionDataCache.get() : Messenger::Instance().getAuthSessionData(); + auto userDataInstance = StoredAuthSessionCache ? &StoredAuthSessionCache->data : Messenger::Instance().getAuthSessionData(); auto userData = userDataInstance ? userDataInstance->serialize() : QByteArray(); + auto dialogsWidthRatio = StoredAuthSessionCache ? StoredAuthSessionCache->dialogsWidthRatio : (App::wnd() ? App::wnd()->controller()->dialogsWidthRatio().value() : Window::Controller::kDefaultDialogsWidthRatio); uint32 size = 21 * (sizeof(quint32) + sizeof(qint32)); size += sizeof(quint32) + Serialize::stringSize(Global::AskDownloadPath() ? QString() : Global::DownloadPath()) + Serialize::bytearraySize(Global::AskDownloadPath() ? QByteArray() : Global::DownloadPathBookmark()); @@ -1753,7 +1755,7 @@ void _writeUserSettings() { data.stream << quint32(dbiDialogsMode) << qint32(Global::DialogsModeEnabled() ? 1 : 0) << static_cast(Global::DialogsMode()); data.stream << quint32(dbiModerateMode) << qint32(Global::ModerateModeEnabled() ? 1 : 0); data.stream << quint32(dbiAutoPlay) << qint32(cAutoPlayGif() ? 1 : 0); - data.stream << quint32(dbiDialogsWidthRatio) << qint32(snap(qRound(Global::DialogsWidthRatio() * 1000000), 0, 1000000)); + data.stream << quint32(dbiDialogsWidthRatio) << qint32(snap(qRound(dialogsWidthRatio * 1000000), 0, 1000000)); data.stream << quint32(dbiUseExternalVideoPlayer) << qint32(cUseExternalVideoPlayer()); if (!userData.isEmpty()) { data.stream << quint32(dbiAuthSessionData) << userData; @@ -2063,7 +2065,7 @@ ReadMapState _readMap(const QByteArray &pass) { _readUserSettings(); _readMtpData(); - Messenger::Instance().setAuthSessionData(std::move(AuthSessionDataCache)); + Messenger::Instance().setAuthSessionFromStorage(std::move(StoredAuthSessionCache)); LOG(("Map read time: %1").arg(getms() - ms)); if (_oldSettingsVersion < AppVersion) { @@ -2360,7 +2362,7 @@ void reset() { _savedGifsKey = 0; _backgroundKey = _userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = 0; _oldMapVersion = _oldSettingsVersion = 0; - AuthSessionDataCache.reset(); + StoredAuthSessionCache.reset(); _mapChanged = true; _writeMap(WriteMapWhen::Now); diff --git a/Telegram/SourceFiles/storage/localstorage.h b/Telegram/SourceFiles/storage/localstorage.h index 0b3b3c3994..9039e08a93 100644 --- a/Telegram/SourceFiles/storage/localstorage.h +++ b/Telegram/SourceFiles/storage/localstorage.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "core/basic_types.h" #include "storage/file_download.h" +#include "auth_session.h" namespace Window { namespace Theme { @@ -31,6 +32,11 @@ struct Cached; namespace Local { +struct StoredAuthSession { + AuthSessionData data; + float64 dialogsWidthRatio; +}; + void start(); void finish(); diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 2f03e1c6b9..c75ed90035 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -339,17 +339,24 @@ void MainWindow::showRightColumn(object_ptr widget) { } auto nowRightWidth = _rightColumn ? _rightColumn->width() : 0; setMinimumWidth(st::windowMinWidth + nowRightWidth); - auto nowWidth = width(); - if (!isMaximized()) { - auto desktop = QDesktopWidget().availableGeometry(this); - auto newWidth = qMin(wasWidth + nowRightWidth - wasRightWidth, desktop.width()); - auto newLeft = qMin(x(), desktop.x() + desktop.width() - newWidth); - if (x() != newLeft || width() != newWidth) { - setGeometry(newLeft, y(), newWidth, height()); - } else { - updateControlsGeometry(); - } + tryToExtendWidthBy(wasWidth + nowRightWidth - wasRightWidth - width()); + } else { + updateControlsGeometry(); + } +} + +bool MainWindow::canExtendWidthBy(int addToWidth) { + auto desktop = QDesktopWidget().availableGeometry(this); + return (width() + addToWidth) <= desktop.width(); +} + +void MainWindow::tryToExtendWidthBy(int addToWidth) { + auto desktop = QDesktopWidget().availableGeometry(this); + auto newWidth = qMin(width() + addToWidth, desktop.width()); + auto newLeft = qMin(x(), desktop.x() + desktop.width() - newWidth); + if (x() != newLeft || width() != newWidth) { + setGeometry(newLeft, y(), newWidth, height()); } else { updateControlsGeometry(); } diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index e2e77560dc..b7f30e7988 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -69,6 +69,8 @@ public: QWidget *filedialogParent(); void showRightColumn(object_ptr widget); + bool canExtendWidthBy(int addToWidth); + void tryToExtendWidthBy(int addToWidth); virtual void updateTrayMenu(bool force = false) { } diff --git a/Telegram/SourceFiles/window/section_widget.h b/Telegram/SourceFiles/window/section_widget.h index 316617ba70..e3f1ba5d2c 100644 --- a/Telegram/SourceFiles/window/section_widget.h +++ b/Telegram/SourceFiles/window/section_widget.h @@ -30,6 +30,7 @@ class SectionMemento; struct SectionSlideParams { QPixmap oldContentCache; bool withTopBarShadow = false; + bool withTabbedSection = false; explicit operator bool() const { return !oldContentCache.isNull(); diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index a241efbd1e..e5cf852122 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -21,6 +21,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/window_controller.h" #include "window/main_window.h" +#include "mainwidget.h" +#include "styles/style_window.h" +#include "styles/style_dialogs.h" namespace Window { @@ -50,4 +53,74 @@ bool Controller::isGifPausedAtLeastFor(GifPauseReason reason) const { return (static_cast(_gifPauseReasons) >= 2 * static_cast(reason)) || !window()->isActive(); } +Controller::ColumnLayout Controller::computeColumnLayout() { + auto layout = Adaptive::WindowLayout::OneColumn; + + auto bodyWidth = window()->bodyWidget()->width(); + auto dialogsWidth = qRound(bodyWidth * dialogsWidthRatio().value()); + auto historyWidth = bodyWidth - dialogsWidth; + accumulate_max(historyWidth, st::windowMinWidth); + dialogsWidth = bodyWidth - historyWidth; + + auto useOneColumnLayout = [this, bodyWidth, dialogsWidth] { + auto someSectionShown = !App::main()->selectingPeer() && App::main()->isSectionShown(); + if (dialogsWidth < st::dialogsPadding.x() && (Adaptive::OneColumn() || someSectionShown)) { + return true; + } + if (bodyWidth < st::windowMinWidth + st::dialogsWidthMin) { + return true; + } + return false; + }; + auto useSmallColumnLayout = [this, dialogsWidth] { + // used if useOneColumnLayout() == false. + if (dialogsWidth < st::dialogsWidthMin / 2) { + return true; + } + return false; + }; + if (useOneColumnLayout()) { + dialogsWidth = bodyWidth; + } else if (useSmallColumnLayout()) { + layout = Adaptive::WindowLayout::SmallColumn; + auto forceWideDialogs = [this] { + if (dialogsListDisplayForced().value()) { + return true; + } else if (dialogsListFocused().value()) { + return true; + } + return !App::main()->isSectionShown(); + }; + if (forceWideDialogs()) { + dialogsWidth = st::dialogsWidthMin; + } else { + dialogsWidth = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPadding.x(); + } + } else { + layout = Adaptive::WindowLayout::Normal; + accumulate_max(dialogsWidth, st::dialogsWidthMin); + } + return { bodyWidth, dialogsWidth, layout }; +} + +bool Controller::provideChatWidth(int requestedWidth) { + auto currentLayout = computeColumnLayout(); + auto chatWidth = currentLayout.bodyWidth - currentLayout.dialogsWidth; + if (currentLayout.windowLayout == Adaptive::WindowLayout::OneColumn) { + chatWidth = currentLayout.bodyWidth; + } + if (chatWidth >= requestedWidth) { + return true; + } + if (!window()->canExtendWidthBy(requestedWidth - chatWidth)) { + return false; + } + window()->tryToExtendWidthBy(requestedWidth - chatWidth); + auto newLayout = computeColumnLayout(); + if (newLayout.windowLayout != Adaptive::WindowLayout::OneColumn) { + dialogsWidthRatio().set(float64(newLayout.bodyWidth - requestedWidth) / newLayout.bodyWidth, true); + } + return true; +} + } // namespace Window diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index a2a66d3bf7..bcdc05812a 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -36,6 +36,8 @@ class MainWindow; class Controller { public: + static constexpr auto kDefaultDialogsWidthRatio = 5. / 14; + Controller(gsl::not_null window) : _window(window) { } @@ -63,6 +65,24 @@ public: } bool isGifPausedAtLeastFor(GifPauseReason reason) const; + struct ColumnLayout { + int bodyWidth; + int dialogsWidth; + Adaptive::WindowLayout windowLayout; + }; + ColumnLayout computeColumnLayout(); + bool provideChatWidth(int requestedWidth); + + base::Variable &dialogsWidthRatio() { + return _dialogsWidthRatio; + } + base::Variable &dialogsListFocused() { + return _dialogsListFocused; + } + base::Variable &dialogsListDisplayForced() { + return _dialogsListDisplayForced; + } + private: gsl::not_null _window; @@ -72,6 +92,10 @@ private: GifPauseReasons _gifPauseReasons = { 0 }; base::Observable _gifPauseLevelChanged; + base::Variable _dialogsWidthRatio = { kDefaultDialogsWidthRatio }; + base::Variable _dialogsListFocused = { false }; + base::Variable _dialogsListDisplayForced = { false }; + }; } // namespace Window