2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-31 06:26:18 +00:00

Allow switching accounts from the main menu.

This commit is contained in:
John Preston
2020-06-22 17:01:41 +04:00
parent f129b6b90d
commit 5f238a71f9
9 changed files with 430 additions and 102 deletions

View File

@@ -13,9 +13,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/widgets/menu.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/text/text_utilities.h"
#include "ui/special_buttons.h"
#include "ui/empty_userpic.h"
#include "base/call_delayed.h"
#include "mainwindow.h"
#include "storage/localstorage.h"
#include "storage/storage_account.h"
@@ -27,8 +32,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_box_controller.h"
#include "lang/lang_keys.h"
#include "core/click_handler_types.h"
#include "core/core_settings.h"
#include "core/application.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "main/main_account.h"
#include "main/main_domain.h"
#include "mtproto/mtp_instance.h"
#include "mtproto/mtproto_config.h"
#include "data/data_folder.h"
#include "data/data_session.h"
@@ -37,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "app.h"
#include "styles/style_window.h"
#include "styles/style_widgets.h"
#include "styles/style_dialogs.h"
#include "styles/style_settings.h"
#include "styles/style_boxes.h"
@@ -70,7 +81,22 @@ bool IsShadowShown(const QImage &img, const QRect r, float64 intensityText) {
namespace Window {
class MainMenu::ResetScaleButton : public Ui::AbstractButton {
class MainMenu::AccountButton final : public Ui::RippleButton {
public:
AccountButton(QWidget *parent, not_null<Main::Account*> account);
private:
void paintEvent(QPaintEvent *e) override;
const not_null<Main::Account*> _account;
const style::Menu &_st;
std::shared_ptr<Data::CloudImageView> _userpicView;
int _unreadBadge = 0;
bool _unreadBadgeMuted = true;
};
class MainMenu::ResetScaleButton final : public Ui::AbstractButton {
public:
ResetScaleButton(QWidget *parent);
@@ -81,6 +107,61 @@ protected:
};
MainMenu::AccountButton::AccountButton(
QWidget *parent,
not_null<Main::Account*> account)
: RippleButton(parent, st::defaultRippleAnimation)
, _account(account)
, _st(st::mainMenu){
const auto height = _st.itemPadding.top()
+ _st.itemStyle.font->height
+ _st.itemPadding.bottom();
resize(width(), height);
_account->sessionValue(
) | rpl::filter([=](Main::Session *session) {
return (session != nullptr);
}) | rpl::start_with_next([=](not_null<Main::Session*> session) {
rpl::single(
rpl::empty_value()
) | rpl::then(
session->data().unreadBadgeChanges()
) | rpl::start_with_next([=] {
_unreadBadge = session->data().unreadBadge();
_unreadBadgeMuted = session->data().unreadBadgeMuted();
update();
}, lifetime());
}, lifetime());
}
void MainMenu::AccountButton::paintEvent(QPaintEvent *e) {
Expects(_account->sessionExists());
const auto &session = _account->session();
auto p = Painter(this);
const auto over = isOver();
p.fillRect(rect(), over ? _st.itemBgOver : _st.itemBg);
paintRipple(p, 0, 0);
const auto available = width() - 2 * _st.itemPadding.left();
session.user()->paintUserpicLeft(
p,
_userpicView,
_st.itemIconPosition.x(),
_st.itemIconPosition.y(),
width(),
height() - 2 * _st.itemIconPosition.y());
p.setPen(over ? _st.itemFgOver : _st.itemFg);
session.user()->nameText().drawElided(
p,
_st.itemPadding.left(),
_st.itemPadding.top(),
available);
}
MainMenu::ResetScaleButton::ResetScaleButton(QWidget *parent)
: AbstractButton(parent) {
const auto margin = st::mainMenuCloudButton.height
@@ -130,63 +211,38 @@ MainMenu::MainMenu(
not_null<SessionController*> controller)
: LayerWidget(parent)
, _controller(controller)
, _menu(this, st::mainMenu)
, _telegram(this, st::mainMenuTelegramLabel)
, _version(this, st::mainMenuVersionLabel) {
, _userpicButton(
this,
_controller,
_controller->session().user(),
Ui::UserpicButton::Role::Custom,
st::mainMenuUserpic)
, _toggleAccounts(this)
, _archiveButton(this, st::mainMenuCloudButton)
, _scroll(this, st::defaultSolidScroll)
, _inner(_scroll->setOwnedWidget(
object_ptr<Ui::VerticalLayout>(_scroll.data())))
, _accounts(_inner->add(object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_inner.get(),
object_ptr<Ui::VerticalLayout>(_inner.get()))))
, _shadow(_inner->add(object_ptr<Ui::SlideWrap<Ui::PlainShadow>>(
_inner.get(),
object_ptr<Ui::PlainShadow>(_inner.get()))))
, _menu(_inner->add(
object_ptr<Ui::Menu>(_inner.get(), st::mainMenu),
{ 0, st::mainMenuSkip, 0, 0 }))
, _footer(_inner->add(object_ptr<Ui::RpWidget>(_inner.get())))
, _telegram(
Ui::CreateChild<Ui::FlatLabel>(_footer.get(), st::mainMenuTelegramLabel))
, _version(
Ui::CreateChild<Ui::FlatLabel>(
_footer.get(),
st::mainMenuVersionLabel)) {
setAttribute(Qt::WA_OpaquePaintEvent);
const auto showSelfChat = [=] {
controller->content()->choosePeer(
_controller->session().userPeerId(),
ShowAtUnreadMsgId);
};
const auto showArchive = [=] {
const auto folder = _controller->session().data().folderLoaded(
Data::Folder::kId);
if (folder) {
controller->openFolder(folder);
Ui::hideSettingsAndLayer();
}
};
const auto checkArchive = [=] {
const auto folder = _controller->session().data().folderLoaded(
Data::Folder::kId);
return folder
&& !folder->chatsList()->empty()
&& _controller->session().settings().archiveInMainMenu();
};
_userpicButton.create(
this,
_controller,
_controller->session().user(),
Ui::UserpicButton::Role::Custom,
st::mainMenuUserpic);
_userpicButton->setClickedCallback(showSelfChat);
_userpicButton->show();
_cloudButton.create(this, st::mainMenuCloudButton);
_cloudButton->setClickedCallback(showSelfChat);
_cloudButton->show();
_archiveButton.create(this, st::mainMenuCloudButton);
_archiveButton->setHidden(!checkArchive());
_archiveButton->setAcceptBoth(true);
_archiveButton->clicks(
) | rpl::start_with_next([=](Qt::MouseButton which) {
if (which == Qt::LeftButton) {
showArchive();
return;
} else if (which != Qt::RightButton) {
return;
}
_contextMenu = base::make_unique_q<Ui::PopupMenu>(this);
_contextMenu->addAction(
tr::lng_context_archive_to_list(tr::now), [=] {
_controller->session().settings().setArchiveInMainMenu(false);
_controller->session().saveSettingsDelayed();
Ui::hideSettingsAndLayer();
});
_contextMenu->popup(QCursor::pos());
}, _archiveButton->lifetime());
setupArchiveButton();
setupUserpicButton();
setupAccounts();
_nightThemeSwitch.setCallback([this] {
if (const auto action = *_nightThemeAction) {
@@ -198,6 +254,19 @@ MainMenu::MainMenu(
}
});
_footer->heightValue(
) | rpl::start_with_next([=] {
_telegram->moveToLeft(st::mainMenuFooterLeft, _footer->height() - st::mainMenuTelegramBottom - _telegram->height());
_version->moveToLeft(st::mainMenuFooterLeft, _footer->height() - st::mainMenuVersionBottom - _version->height());
}, _footer->lifetime());
rpl::combine(
heightValue(),
_inner->heightValue()
) | rpl::start_with_next([=] {
updateInnerControlsGeometry();
}, _inner->lifetime());
parentResized();
_menu->setTriggeredCallback([](QAction *action, int actionTop, Ui::Menu::TriggeredSource source) {
emit action->triggered();
@@ -235,15 +304,219 @@ MainMenu::MainMenu(
refreshBackground();
}
});
updatePhone();
initResetScaleButton();
}
void MainMenu::setupArchiveButton() {
const auto showArchive = [=] {
const auto folder = _controller->session().data().folderLoaded(
Data::Folder::kId);
if (folder) {
_controller->openFolder(folder);
Ui::hideSettingsAndLayer();
}
};
const auto checkArchive = [=] {
const auto folder = _controller->session().data().folderLoaded(
Data::Folder::kId);
return folder
&& !folder->chatsList()->empty()
&& _controller->session().settings().archiveInMainMenu();
};
_archiveButton->setVisible(checkArchive());
_archiveButton->setAcceptBoth(true);
_archiveButton->clicks(
) | rpl::start_with_next([=](Qt::MouseButton which) {
if (which == Qt::LeftButton) {
showArchive();
return;
} else if (which != Qt::RightButton) {
return;
}
_contextMenu = base::make_unique_q<Ui::PopupMenu>(this);
_contextMenu->addAction(
tr::lng_context_archive_to_list(tr::now), [=] {
_controller->session().settings().setArchiveInMainMenu(false);
_controller->session().saveSettingsDelayed();
Ui::hideSettingsAndLayer();
});
_contextMenu->popup(QCursor::pos());
}, _archiveButton->lifetime());
_controller->session().data().chatsListChanges(
) | rpl::filter([](Data::Folder *folder) {
return folder && (folder->id() == Data::Folder::kId);
}) | rpl::start_with_next([=](Data::Folder *folder) {
_archiveButton->setHidden(!checkArchive());
_archiveButton->setVisible(checkArchive());
update();
}, lifetime());
updatePhone();
initResetScaleButton();
}
void MainMenu::setupUserpicButton() {
_userpicButton->setClickedCallback([=] {
_controller->content()->choosePeer(
_controller->session().userPeerId(),
ShowAtUnreadMsgId);
});
_userpicButton->show();
}
void MainMenu::setupAccounts() {
const auto inner = _accounts->entity();
inner->add(object_ptr<Ui::FixedHeightWidget>(inner, st::mainMenuSkip));
_addAccount = setupAddAccount(inner);
inner->add(object_ptr<Ui::FixedHeightWidget>(inner, st::mainMenuSkip));
rpl::single(
rpl::empty_value()
) | rpl::then(Core::App().domain().accountsChanges(
)) | rpl::start_with_next([=] {
const auto &list = Core::App().domain().accounts();
const auto exists = [&](not_null<Main::Account*> account) {
for (const auto &[index, existing] : list) {
if (account == existing.get()) {
return true;
}
}
return false;
};
for (auto i = _watched.begin(); i != _watched.end();) {
if (!exists(i->first)) {
i = _watched.erase(i);
} else {
++i;
}
}
for (const auto &[index, account] : list) {
if (_watched.emplace(account.get()).second) {
account->sessionChanges(
) | rpl::start_with_next([=](Main::Session *session) {
rebuildAccounts();
}, lifetime());
}
}
rebuildAccounts();
}, lifetime());
_accounts->toggleOn(Core::App().settings().mainMenuAccountsShownValue());
_accounts->finishAnimating();
_shadow->setDuration(0)->toggleOn(_accounts->shownValue());
_toggleAccounts->show();
_toggleAccounts->setClickedCallback([=] {
auto &settings = Core::App().settings();
const auto shown = !settings.mainMenuAccountsShown();
settings.setMainMenuAccountsShown(shown);
});
}
void MainMenu::rebuildAccounts() {
const auto inner = _accounts->entity();
auto count = 0;
for (auto &[account, button] : _watched) {
if (!account->sessionExists()) {
button = nullptr;
} else if (!button) {
button.reset(inner->insert(
++count,
object_ptr<AccountButton>(inner, account)));
button->setClickedCallback([=] {
auto activate = [=, guard = _accountSwitchGuard.make_guard()]{
if (guard) {
Core::App().domain().activate(account);
}
};
base::call_delayed(
st::defaultRippleAnimation.hideDuration,
account,
std::move(activate));
});
} else {
++count;
}
}
inner->resizeToWidth(_accounts->width());
_addAccount->toggle(
(count < Main::Domain::kMaxAccounts),
anim::type::instant);
}
not_null<Ui::SlideWrap<Ui::RippleButton>*> MainMenu::setupAddAccount(
not_null<Ui::VerticalLayout*> container) {
const auto result = container->add(
object_ptr<Ui::SlideWrap<Ui::RippleButton>>(
container.get(),
object_ptr<Ui::RippleButton>(
container.get(),
st::defaultRippleAnimation)))->setDuration(0);
const auto st = &st::mainMenu;
const auto height = st->itemPadding.top()
+ st->itemStyle.font->height
+ st->itemPadding.bottom();
const auto button = result->entity();
button->resize(button->width(), height);
button->paintRequest(
) | rpl::start_with_next([=] {
auto p = Painter(button);
const auto over = button->isOver();
p.fillRect(button->rect(), over ? st->itemBgOver : st->itemBg);
button->paintRipple(p, 0, 0);
const auto &icon = over
? st::mainMenuAddAccountOver
: st::mainMenuAddAccount;
icon.paint(p, st->itemIconPosition, width());
p.setPen(over ? st->itemFgOver : st->itemFg);
p.setFont(st->itemStyle.font);
p.drawTextLeft(
st->itemPadding.left(),
st->itemPadding.top(),
width(),
tr::lng_menu_add_account(tr::now));
}, button->lifetime());
const auto add = [=](MTP::Environment environment) {
auto &domain = Core::App().domain();
if (domain.accounts().size() < Main::Domain::kMaxAccounts) {
domain.activate(domain.add(environment));
} else {
for (auto &[index, account] : domain.accounts()) {
if (!account->sessionExists()
&& account->mtp().environment() == environment) {
domain.activate(account.get());
break;
}
}
}
};
button->setAcceptBoth(true);
button->clicks(
) | rpl::start_with_next([=](Qt::MouseButton which) {
if (which == Qt::LeftButton) {
add(MTP::Environment::Production);
return;
} else if (which != Qt::RightButton
|| !(button->clickModifiers() & Qt::ShiftModifier)
|| !(button->clickModifiers() & Qt::AltModifier)) {
return;
}
_contextMenu = base::make_unique_q<Ui::PopupMenu>(this);
_contextMenu->addAction("Production Server", [=] {
add(MTP::Environment::Production);
});
_contextMenu->addAction("Test Server", [=] {
add(MTP::Environment::Test);
});
_contextMenu->popup(QCursor::pos());
}, _archiveButton->lifetime());
return result;
}
void MainMenu::parentResized() {
@@ -374,31 +647,41 @@ void MainMenu::refreshBackground() {
void MainMenu::resizeEvent(QResizeEvent *e) {
_menu->setForceWidth(width());
_inner->resizeToWidth(width());
updateControlsGeometry();
}
void MainMenu::updateControlsGeometry() {
if (_userpicButton) {
_userpicButton->moveToLeft(st::mainMenuUserpicLeft, st::mainMenuUserpicTop);
}
if (_cloudButton) {
const auto offset = st::mainMenuCloudSize / 4;
const auto y = st::mainMenuCoverHeight
- _cloudButton->height()
- offset;
_cloudButton->moveToRight(offset, y);
if (_archiveButton) {
_archiveButton->moveToRight(
offset,
y - _cloudButton->height());
}
}
_userpicButton->moveToLeft(st::mainMenuUserpicLeft, st::mainMenuUserpicTop);
if (_resetScaleButton) {
_resetScaleButton->moveToRight(0, 0);
_archiveButton->moveToRight(_resetScaleButton->width(), 0);
} else {
const auto offset = st::mainMenuCloudSize / 4;
_archiveButton->moveToRight(offset, offset);
}
_toggleAccounts->setGeometry(
0,
st::mainMenuCoverNameTop,
width(),
st::mainMenuCoverHeight - st::mainMenuCoverNameTop);
const auto top = st::mainMenuCoverHeight;
_scroll->setGeometry(0, top, width(), height() - top);
updateInnerControlsGeometry();
}
void MainMenu::updateInnerControlsGeometry() {
const auto contentHeight = _accounts->height()
+ _shadow->height()
+ st::mainMenuSkip
+ _menu->height();
const auto available = height() - st::mainMenuCoverHeight - contentHeight;
const auto footerHeight = std::max(
available,
st::mainMenuTelegramBottom + _telegram->height() + st::mainMenuSkip);
if (_footer->height() != footerHeight) {
_footer->resize(_footer->width(), footerHeight);
}
_menu->moveToLeft(0, st::mainMenuCoverHeight + st::mainMenuSkip);
_telegram->moveToLeft(st::mainMenuFooterLeft, height() - st::mainMenuTelegramBottom - _telegram->height());
_version->moveToLeft(st::mainMenuFooterLeft, height() - st::mainMenuVersionBottom - _version->height());
}
void MainMenu::updatePhone() {
@@ -425,9 +708,7 @@ void MainMenu::paintEvent(QPaintEvent *e) {
}
if (!cover.isEmpty()) {
const auto widthText = _cloudButton
? _cloudButton->x() - st::mainMenuCloudSize
: width() - 2 * st::mainMenuCoverTextLeft;
const auto widthText = width() - 2 * st::mainMenuCoverTextLeft;
if (isFill) {
p.fillRect(cover, st::mainMenuCoverBg);
@@ -442,16 +723,6 @@ void MainMenu::paintEvent(QPaintEvent *e) {
width());
p.setFont(st::normalFont);
p.drawTextLeft(st::mainMenuCoverTextLeft, st::mainMenuCoverStatusTop, width(), _phoneText);
if (_cloudButton) {
Ui::EmptyUserpic::PaintSavedMessages(
p,
_cloudButton->x() + (_cloudButton->width() - st::mainMenuCloudSize) / 2,
_cloudButton->y() + (_cloudButton->height() - st::mainMenuCloudSize) / 2,
width(),
st::mainMenuCloudSize,
isFill ? st::mainMenuCloudBg : st::msgServiceBg,
isFill ? st::mainMenuCloudFg : st::msgServiceFg);
}
// Draw Archive button.
if (!_archiveButton->isHidden()) {