857 lines
24 KiB
C++
857 lines
24 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#include "window/window_main_menu.h"
|
|
|
|
#include "window/themes/window_theme.h"
|
|
#include "window/window_peer_menu.h"
|
|
#include "window/window_session_controller.h"
|
|
#include "window/window_controller.h"
|
|
#include "ui/chat/chat_theme.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "ui/widgets/labels.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/wrap/vertical_layout_reorder.h"
|
|
#include "ui/text/format_values.h" // Ui::FormatPhone
|
|
#include "ui/text/text_utilities.h"
|
|
#include "ui/text/text_options.h"
|
|
#include "ui/special_buttons.h"
|
|
#include "ui/empty_userpic.h"
|
|
#include "dialogs/ui/dialogs_layout.h"
|
|
#include "base/call_delayed.h"
|
|
#include "mainwindow.h"
|
|
#include "storage/localstorage.h"
|
|
#include "storage/storage_account.h"
|
|
#include "support/support_templates.h"
|
|
#include "settings/settings_common.h"
|
|
#include "settings/settings_calls.h"
|
|
#include "settings/settings_information.h"
|
|
#include "base/qt_signal_producer.h"
|
|
#include "boxes/about_box.h"
|
|
#include "ui/boxes/confirm_box.h"
|
|
#include "boxes/peer_list_controllers.h"
|
|
#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"
|
|
#include "data/data_user.h"
|
|
#include "data/data_changes.h"
|
|
#include "mainwidget.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"
|
|
#include "styles/style_info.h" // infoTopBarMenu
|
|
#include "styles/style_layers.h"
|
|
#include "styles/style_menu_icons.h"
|
|
|
|
#include <QtGui/QWindow>
|
|
#include <QtGui/QScreen>
|
|
|
|
#include <QtGui/QGuiApplication>
|
|
#include <QtGui/QClipboard>
|
|
|
|
namespace Window {
|
|
namespace {
|
|
|
|
void ShowCallsBox(not_null<Window::SessionController*> window) {
|
|
auto controller = std::make_unique<Calls::BoxController>(window);
|
|
const auto initBox = [
|
|
window,
|
|
controller = controller.get()
|
|
](not_null<PeerListBox*> box) {
|
|
box->addButton(tr::lng_close(), [=] {
|
|
box->closeBox();
|
|
});
|
|
using MenuPointer = base::unique_qptr<Ui::PopupMenu>;
|
|
const auto menu = std::make_shared<MenuPointer>();
|
|
const auto menuButton = box->addTopButton(st::infoTopBarMenu);
|
|
menuButton->setClickedCallback([=] {
|
|
*menu = base::make_unique_q<Ui::PopupMenu>(
|
|
menuButton,
|
|
st::popupMenuWithIcons);
|
|
const auto showSettings = [=] {
|
|
window->showSettings(
|
|
Settings::Calls::Id(),
|
|
Window::SectionShow(anim::type::instant));
|
|
};
|
|
const auto clearAll = crl::guard(box, [=] {
|
|
box->getDelegate()->show(Box(Calls::ClearCallsBox, window));
|
|
});
|
|
(*menu)->addAction(
|
|
tr::lng_settings_section_call_settings(tr::now),
|
|
showSettings,
|
|
&st::menuIconSettings);
|
|
if (controller->delegate()->peerListFullRowsCount() > 0) {
|
|
(*menu)->addAction(
|
|
tr::lng_call_box_clear_all(tr::now),
|
|
clearAll,
|
|
&st::menuIconDelete);
|
|
}
|
|
(*menu)->popup(QCursor::pos());
|
|
return true;
|
|
});
|
|
};
|
|
window->show(Box<PeerListBox>(std::move(controller), initBox));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
class MainMenu::ToggleAccountsButton final : public Ui::AbstractButton {
|
|
public:
|
|
explicit ToggleAccountsButton(QWidget *parent);
|
|
|
|
[[nodiscard]] int rightSkip() const {
|
|
return _rightSkip;
|
|
}
|
|
|
|
private:
|
|
void paintEvent(QPaintEvent *e) override;
|
|
void paintUnreadBadge(Painter &p);
|
|
|
|
void validateUnreadBadge();
|
|
[[nodiscard]] QString computeUnreadBadge() const;
|
|
|
|
int _rightSkip = 0;
|
|
Ui::Animations::Simple _toggledAnimation;
|
|
bool _toggled = false;
|
|
|
|
QString _unreadBadge;
|
|
bool _unreadBadgeStale = false;
|
|
|
|
};
|
|
|
|
class MainMenu::ResetScaleButton final : public Ui::AbstractButton {
|
|
public:
|
|
ResetScaleButton(QWidget *parent);
|
|
|
|
protected:
|
|
void paintEvent(QPaintEvent *e) override;
|
|
|
|
static constexpr auto kText = "100%";
|
|
|
|
};
|
|
|
|
MainMenu::ToggleAccountsButton::ToggleAccountsButton(QWidget *parent)
|
|
: AbstractButton(parent) {
|
|
rpl::single(rpl::empty) | rpl::then(
|
|
Core::App().unreadBadgeChanges()
|
|
) | rpl::start_with_next([=] {
|
|
_unreadBadgeStale = true;
|
|
if (!_toggled) {
|
|
validateUnreadBadge();
|
|
update();
|
|
}
|
|
}, lifetime());
|
|
|
|
auto &settings = Core::App().settings();
|
|
if (Core::App().domain().accounts().size() < 2
|
|
&& settings.mainMenuAccountsShown()) {
|
|
settings.setMainMenuAccountsShown(false);
|
|
}
|
|
settings.mainMenuAccountsShownValue(
|
|
) | rpl::filter([=](bool value) {
|
|
return (_toggled != value);
|
|
}) | rpl::start_with_next([=](bool value) {
|
|
_toggled = value;
|
|
_toggledAnimation.start(
|
|
[=] { update(); },
|
|
_toggled ? 0. : 1.,
|
|
_toggled ? 1. : 0.,
|
|
st::slideWrapDuration);
|
|
validateUnreadBadge();
|
|
}, lifetime());
|
|
_toggledAnimation.stop();
|
|
}
|
|
|
|
void MainMenu::ToggleAccountsButton::paintEvent(QPaintEvent *e) {
|
|
auto p = Painter(this);
|
|
|
|
const auto toggled = _toggledAnimation.value(_toggled ? 1. : 0.);
|
|
const auto x = 0. + width() - st::mainMenuTogglePosition.x();
|
|
const auto y = 0. + height() - st::mainMenuTogglePosition.y();
|
|
const auto size = st::mainMenuToggleSize;
|
|
const auto size2 = size / 2.;
|
|
const auto sqrt2 = sqrt(2.);
|
|
const auto stroke = (st::mainMenuToggleFourStrokes / 4.) / sqrt2;
|
|
const auto left = x - size;
|
|
const auto right = x + size;
|
|
const auto bottom = y + size2;
|
|
constexpr auto kPointCount = 6;
|
|
std::array<QPointF, kPointCount> points = { {
|
|
{ left - stroke, bottom - stroke },
|
|
{ x, bottom - stroke - size - stroke },
|
|
{ right + stroke, bottom - stroke },
|
|
{ right - stroke, bottom + stroke },
|
|
{ x, bottom + stroke - size + stroke },
|
|
{ left + stroke, bottom + stroke }
|
|
} };
|
|
const auto alpha = (toggled - 1.) * M_PI;
|
|
const auto cosalpha = cos(alpha);
|
|
const auto sinalpha = sin(alpha);
|
|
for (auto &point : points) {
|
|
auto px = point.x() - x;
|
|
auto py = point.y() - y;
|
|
point.setX(x + px * cosalpha - py * sinalpha);
|
|
point.setY(y + py * cosalpha + px * sinalpha);
|
|
}
|
|
QPainterPath path;
|
|
path.moveTo(points[0]);
|
|
for (int i = 1; i != kPointCount; ++i) {
|
|
path.lineTo(points[i]);
|
|
}
|
|
path.lineTo(points[0]);
|
|
|
|
auto hq = PainterHighQualityEnabler(p);
|
|
p.fillPath(path, st::windowSubTextFg);
|
|
|
|
paintUnreadBadge(p);
|
|
}
|
|
|
|
void MainMenu::ToggleAccountsButton::paintUnreadBadge(Painter &p) {
|
|
const auto progress = 1. - _toggledAnimation.value(_toggled ? 1. : 0.);
|
|
if (!progress) {
|
|
return;
|
|
}
|
|
validateUnreadBadge();
|
|
if (_unreadBadge.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
auto st = Settings::Badge::Style();
|
|
const auto right = width()
|
|
- st::mainMenuTogglePosition.x()
|
|
- st::mainMenuToggleSize * 3;
|
|
const auto top = height()
|
|
- st::mainMenuTogglePosition.y()
|
|
- st::mainMenuBadgeSize / 2;
|
|
p.setOpacity(progress);
|
|
Dialogs::Ui::PaintUnreadBadge(p, _unreadBadge, right, top, st);
|
|
}
|
|
|
|
void MainMenu::ToggleAccountsButton::validateUnreadBadge() {
|
|
const auto base = st::mainMenuTogglePosition.x()
|
|
+ 2 * st::mainMenuToggleSize;
|
|
if (_toggled) {
|
|
_rightSkip = base;
|
|
return;
|
|
} else if (!_unreadBadgeStale) {
|
|
return;
|
|
}
|
|
_unreadBadge = computeUnreadBadge();
|
|
|
|
_rightSkip = base;
|
|
if (!_unreadBadge.isEmpty()) {
|
|
const auto st = Settings::Badge::Style();
|
|
_rightSkip += 2 * st::mainMenuToggleSize
|
|
+ Dialogs::Ui::CountUnreadBadgeSize(_unreadBadge, st).width();
|
|
}
|
|
}
|
|
|
|
QString MainMenu::ToggleAccountsButton::computeUnreadBadge() const {
|
|
const auto state = OtherAccountsUnreadStateCurrent();
|
|
return state.allMuted
|
|
? QString()
|
|
: (state.count > 0)
|
|
? Lang::FormatCountToShort(state.count).string
|
|
: QString();
|
|
}
|
|
|
|
MainMenu::ResetScaleButton::ResetScaleButton(QWidget *parent)
|
|
: AbstractButton(parent) {
|
|
const auto margin = st::mainMenuCloudButton.height
|
|
- st::mainMenuCloudSize;
|
|
const auto textWidth = st::mainMenuResetScaleFont->width(kText);
|
|
const auto innerWidth = st::mainMenuResetScaleLeft
|
|
+ textWidth
|
|
+ st::mainMenuResetScaleRight;
|
|
const auto width = margin + innerWidth;
|
|
resize(width, st::mainMenuCloudButton.height);
|
|
}
|
|
|
|
void MainMenu::ResetScaleButton::paintEvent(QPaintEvent *e) {
|
|
Painter p(this);
|
|
|
|
const auto innerHeight = st::mainMenuCloudSize;
|
|
const auto radius = innerHeight / 2;
|
|
const auto margin = st::mainMenuCloudButton.height
|
|
- st::mainMenuCloudSize;
|
|
const auto textWidth = st::mainMenuResetScaleFont->width(kText);
|
|
const auto innerWidth = st::mainMenuResetScaleLeft
|
|
+ textWidth
|
|
+ st::mainMenuResetScaleRight;
|
|
const auto left = margin / 2;
|
|
const auto top = margin / 2;
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(st::mainMenuCloudBg);
|
|
p.drawRoundedRect(left, top, innerWidth, innerHeight, radius, radius);
|
|
|
|
st::settingsIconInterfaceScale.paint(
|
|
p,
|
|
left + st::mainMenuResetScaleIconLeft,
|
|
top + ((innerHeight - st::settingsIconInterfaceScale.height()) / 2),
|
|
width(),
|
|
st::mainMenuCloudFg->c);
|
|
|
|
p.setFont(st::mainMenuResetScaleFont);
|
|
p.setPen(st::mainMenuCloudFg);
|
|
p.drawText(
|
|
left + st::mainMenuResetScaleLeft,
|
|
top + st::mainMenuResetScaleTop + st::mainMenuResetScaleFont->ascent,
|
|
kText);
|
|
}
|
|
|
|
MainMenu::MainMenu(
|
|
QWidget *parent,
|
|
not_null<SessionController*> controller)
|
|
: LayerWidget(parent)
|
|
, _controller(controller)
|
|
, _userpicButton(
|
|
this,
|
|
_controller,
|
|
_controller->session().user(),
|
|
Ui::UserpicButton::Role::Custom,
|
|
st::mainMenuUserpic)
|
|
, _toggleAccounts(this)
|
|
, _scroll(this, st::defaultSolidScroll)
|
|
, _inner(_scroll->setOwnedWidget(
|
|
object_ptr<Ui::VerticalLayout>(_scroll.data())))
|
|
, _topShadowSkip(_inner->add(
|
|
object_ptr<Ui::FixedHeightWidget>(_inner.get(), st::lineWidth)))
|
|
, _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::VerticalLayout>(_inner.get()),
|
|
{ 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);
|
|
|
|
setupUserpicButton();
|
|
setupAccountsToggle();
|
|
setupAccounts();
|
|
setupArchive();
|
|
setupMenu();
|
|
|
|
const auto shadow = Ui::CreateChild<Ui::PlainShadow>(this);
|
|
widthValue(
|
|
) | rpl::start_with_next([=](int width) {
|
|
const auto line = st::lineWidth;
|
|
shadow->setGeometry(0, st::mainMenuCoverHeight - line, width, line);
|
|
}, shadow->lifetime());
|
|
|
|
_nightThemeSwitch.setCallback([this] {
|
|
Expects(_nightThemeToggle != nullptr);
|
|
|
|
const auto nightMode = Window::Theme::IsNightMode();
|
|
if (_nightThemeToggle->toggled() != nightMode) {
|
|
Window::Theme::ToggleNightMode();
|
|
Window::Theme::KeepApplied();
|
|
}
|
|
});
|
|
|
|
_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();
|
|
|
|
_telegram->setMarkedText(Ui::Text::Link(
|
|
qsl("Telegram Desktop"),
|
|
qsl("https://desktop.telegram.org")));
|
|
_telegram->setLinksTrusted();
|
|
_version->setMarkedText(
|
|
Ui::Text::Link(
|
|
tr::lng_settings_current_version(
|
|
tr::now,
|
|
lt_version,
|
|
currentVersionText()),
|
|
1) // Link 1.
|
|
.append(QChar(' '))
|
|
.append(QChar(8211))
|
|
.append(QChar(' '))
|
|
.append(Ui::Text::Link(tr::lng_menu_about(tr::now), 2))); // Link 2.
|
|
_version->setLink(
|
|
1,
|
|
std::make_shared<UrlClickHandler>(Core::App().changelogLink()));
|
|
_version->setLink(
|
|
2,
|
|
std::make_shared<LambdaClickHandler>([=] {
|
|
controller->show(Box<AboutBox>());
|
|
}));
|
|
|
|
_controller->session().downloaderTaskFinished(
|
|
) | rpl::start_with_next([=] {
|
|
update();
|
|
}, lifetime());
|
|
|
|
_controller->session().changes().peerUpdates(
|
|
_controller->session().user(),
|
|
Data::PeerUpdate::Flag::PhoneNumber
|
|
) | rpl::start_with_next([=] {
|
|
updatePhone();
|
|
}, lifetime());
|
|
|
|
updatePhone();
|
|
initResetScaleButton();
|
|
}
|
|
|
|
void MainMenu::setupArchive() {
|
|
using namespace Settings;
|
|
|
|
const auto controller = _controller;
|
|
const auto folder = [=] {
|
|
return controller->session().data().folderLoaded(Data::Folder::kId);
|
|
};
|
|
const auto showArchive = [=] {
|
|
if (const auto f = folder()) {
|
|
controller->openFolder(f);
|
|
controller->window().hideSettingsAndLayer();
|
|
}
|
|
};
|
|
const auto checkArchive = [=] {
|
|
const auto f = folder();
|
|
return f
|
|
&& !f->chatsList()->empty()
|
|
&& controller->session().settings().archiveInMainMenu();
|
|
};
|
|
|
|
const auto wrap = _menu->add(
|
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
|
_menu,
|
|
object_ptr<Ui::VerticalLayout>(_menu)));
|
|
const auto inner = wrap->entity();
|
|
wrap->toggle(checkArchive(), anim::type::instant);
|
|
|
|
const auto button = AddButton(
|
|
inner,
|
|
tr::lng_archived_name(),
|
|
st::mainMenuButton,
|
|
{ &st::settingsIconArchive, kIconGray });
|
|
inner->add(
|
|
object_ptr<Ui::PlainShadow>(inner),
|
|
{ 0, st::mainMenuSkip, 0, st::mainMenuSkip });
|
|
button->setAcceptBoth(true);
|
|
button->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,
|
|
st::popupMenuWithIcons);
|
|
const auto addAction = PeerMenuCallback([&](
|
|
PeerMenuCallback::Args a) {
|
|
return _contextMenu->addAction(
|
|
a.text,
|
|
std::move(a.handler),
|
|
a.icon);
|
|
});
|
|
|
|
const auto hide = [=] {
|
|
controller->session().settings().setArchiveInMainMenu(false);
|
|
controller->session().saveSettingsDelayed();
|
|
controller->window().hideSettingsAndLayer();
|
|
};
|
|
addAction(
|
|
tr::lng_context_archive_to_list(tr::now),
|
|
std::move(hide),
|
|
&st::menuIconFromMainMenu);
|
|
|
|
MenuAddMarkAsReadChatListAction(
|
|
controller,
|
|
[f = folder()] { return f->chatsList(); },
|
|
addAction);
|
|
|
|
_contextMenu->popup(QCursor::pos());
|
|
}, button->lifetime());
|
|
|
|
const auto now = folder();
|
|
auto folderValue = now
|
|
? (rpl::single(now) | rpl::type_erased())
|
|
: controller->session().data().chatsListChanges(
|
|
) | rpl::filter([](Data::Folder *folder) {
|
|
return folder && (folder->id() == Data::Folder::kId);
|
|
}) | rpl::take(1);
|
|
|
|
using namespace Settings;
|
|
Badge::AddUnread(button, rpl::single(rpl::empty) | rpl::then(std::move(
|
|
folderValue
|
|
) | rpl::map([=](not_null<Data::Folder*> folder) {
|
|
return folder->owner().chatsList(folder)->unreadStateChanges();
|
|
}) | rpl::flatten_latest() | rpl::to_empty) | rpl::map([=] {
|
|
const auto loaded = folder();
|
|
return Badge::UnreadBadge{
|
|
loaded ? loaded->chatListUnreadCount() : 0,
|
|
true,
|
|
};
|
|
}));
|
|
|
|
controller->session().data().chatsListChanges(
|
|
) | rpl::filter([](Data::Folder *folder) {
|
|
return folder && (folder->id() == Data::Folder::kId);
|
|
}) | rpl::start_with_next([=] {
|
|
const auto isArchiveVisible = checkArchive();
|
|
wrap->toggle(isArchiveVisible, anim::type::normal);
|
|
if (!isArchiveVisible) {
|
|
_contextMenu = nullptr;
|
|
}
|
|
update();
|
|
}, lifetime());
|
|
}
|
|
|
|
void MainMenu::setupUserpicButton() {
|
|
_userpicButton->setClickedCallback([=] { toggleAccounts(); });
|
|
_userpicButton->show();
|
|
}
|
|
|
|
void MainMenu::toggleAccounts() {
|
|
auto &settings = Core::App().settings();
|
|
const auto shown = !settings.mainMenuAccountsShown();
|
|
settings.setMainMenuAccountsShown(shown);
|
|
Core::App().saveSettingsDelayed();
|
|
}
|
|
|
|
void MainMenu::setupAccounts() {
|
|
const auto inner = _accounts->entity();
|
|
|
|
inner->add(object_ptr<Ui::FixedHeightWidget>(inner, st::mainMenuSkip));
|
|
auto events = Settings::SetupAccounts(inner, _controller);
|
|
inner->add(object_ptr<Ui::FixedHeightWidget>(inner, st::mainMenuSkip));
|
|
|
|
std::move(
|
|
events.currentAccountActivations
|
|
) | rpl::start_with_next([=] {
|
|
closeLayer();
|
|
}, inner->lifetime());
|
|
|
|
_accounts->toggleOn(Core::App().settings().mainMenuAccountsShownValue());
|
|
_accounts->finishAnimating();
|
|
|
|
_shadow->setDuration(0)->toggleOn(_accounts->shownValue());
|
|
}
|
|
|
|
void MainMenu::setupAccountsToggle() {
|
|
_toggleAccounts->show();
|
|
_toggleAccounts->setAcceptBoth();
|
|
_toggleAccounts->addClickHandler([=](Qt::MouseButton button) {
|
|
if (button == Qt::LeftButton) {
|
|
toggleAccounts();
|
|
} else if (button == Qt::RightButton) {
|
|
const auto menu = Ui::CreateChild<Ui::PopupMenu>(
|
|
_toggleAccounts.data());
|
|
|
|
menu->addAction(tr::lng_profile_copy_phone(tr::now), [=] {
|
|
QGuiApplication::clipboard()->setText(_phoneText);
|
|
});
|
|
menu->popup(QCursor::pos());
|
|
}
|
|
});
|
|
}
|
|
|
|
void MainMenu::parentResized() {
|
|
resize(st::mainMenuWidth, parentWidget()->height());
|
|
}
|
|
|
|
void MainMenu::setupMenu() {
|
|
using namespace Settings;
|
|
|
|
const auto controller = _controller;
|
|
const auto addAction = [&](
|
|
rpl::producer<QString> text,
|
|
IconDescriptor &&descriptor) {
|
|
return AddButton(
|
|
_menu,
|
|
std::move(text),
|
|
st::mainMenuButton,
|
|
std::move(descriptor));
|
|
};
|
|
if (!_controller->session().supportMode()) {
|
|
addAction(
|
|
tr::lng_create_group_title(),
|
|
{ &st::settingsIconGroup, kIconLightBlue }
|
|
)->setClickedCallback([=] {
|
|
controller->showNewGroup();
|
|
});
|
|
addAction(
|
|
tr::lng_create_channel_title(),
|
|
{ &st::settingsIconChannel, kIconLightOrange }
|
|
)->setClickedCallback([=] {
|
|
controller->showNewChannel();
|
|
});
|
|
addAction(
|
|
tr::lng_menu_contacts(),
|
|
{ &st::settingsIconUser, kIconRed }
|
|
)->setClickedCallback([=] {
|
|
controller->show(PrepareContactsBox(controller));
|
|
});
|
|
addAction(
|
|
tr::lng_menu_calls(),
|
|
{ &st::settingsIconCalls, kIconGreen }
|
|
)->setClickedCallback([=] {
|
|
ShowCallsBox(controller);
|
|
});
|
|
addAction(
|
|
tr::lng_saved_messages(),
|
|
{ &st::settingsIconSavedMessages, kIconLightBlue }
|
|
)->setClickedCallback([=] {
|
|
controller->content()->choosePeer(
|
|
controller->session().userPeerId(),
|
|
ShowAtUnreadMsgId);
|
|
});
|
|
} else {
|
|
addAction(
|
|
tr::lng_profile_add_contact(),
|
|
{ &st::settingsIconUser, kIconRed }
|
|
)->setClickedCallback([=] {
|
|
controller->showAddContact();
|
|
});
|
|
addAction(
|
|
rpl::single(u"Fix chats order"_q),
|
|
{ &st::settingsIconPin, kIconGreen }
|
|
)->toggleOn(rpl::single(
|
|
_controller->session().settings().supportFixChatsOrder()
|
|
))->toggledChanges(
|
|
) | rpl::start_with_next([=](bool fix) {
|
|
_controller->session().settings().setSupportFixChatsOrder(fix);
|
|
_controller->session().saveSettings();
|
|
}, _menu->lifetime());
|
|
addAction(
|
|
rpl::single(u"Reload templates"_q),
|
|
{ &st::settingsIconReload, kIconLightBlue }
|
|
)->setClickedCallback([=] {
|
|
_controller->session().supportTemplates().reload();
|
|
});
|
|
}
|
|
addAction(
|
|
tr::lng_menu_settings(),
|
|
{ &st::settingsIconSettings, kIconPurple }
|
|
)->setClickedCallback([=] {
|
|
controller->showSettings();
|
|
});
|
|
|
|
_nightThemeToggle = addAction(
|
|
tr::lng_menu_night_mode(),
|
|
{ &st::settingsIconNight, kIconDarkBlue }
|
|
)->toggleOn(_nightThemeSwitches.events_starting_with(
|
|
Window::Theme::IsNightMode()
|
|
));
|
|
_nightThemeToggle->toggledChanges(
|
|
) | rpl::filter([=](bool night) {
|
|
return (night != Window::Theme::IsNightMode());
|
|
}) | rpl::start_with_next([=](bool night) {
|
|
if (Window::Theme::Background()->editingTheme()) {
|
|
_nightThemeSwitches.fire(!night);
|
|
controller->show(Ui::MakeInformBox(
|
|
tr::lng_theme_editor_cant_change_theme()));
|
|
return;
|
|
}
|
|
const auto weak = MakeWeak(this);
|
|
const auto toggle = [=] {
|
|
if (!weak) {
|
|
Window::Theme::ToggleNightMode();
|
|
Window::Theme::KeepApplied();
|
|
} else {
|
|
_nightThemeSwitch.callOnce(st::mainMenu.itemToggle.duration);
|
|
}
|
|
};
|
|
Window::Theme::ToggleNightModeWithConfirmation(
|
|
&_controller->window(),
|
|
toggle);
|
|
}, _nightThemeToggle->lifetime());
|
|
|
|
Core::App().settings().systemDarkModeValue(
|
|
) | rpl::start_with_next([=](std::optional<bool> darkMode) {
|
|
const auto darkModeEnabled
|
|
= Core::App().settings().systemDarkModeEnabled();
|
|
if (darkModeEnabled && darkMode.has_value()) {
|
|
_nightThemeSwitches.fire_copy(*darkMode);
|
|
}
|
|
}, _nightThemeToggle->lifetime());
|
|
|
|
updatePhone();
|
|
}
|
|
|
|
void MainMenu::resizeEvent(QResizeEvent *e) {
|
|
_inner->resizeToWidth(width());
|
|
updateControlsGeometry();
|
|
}
|
|
|
|
void MainMenu::updateControlsGeometry() {
|
|
_userpicButton->moveToLeft(
|
|
st::mainMenuUserpicLeft,
|
|
st::mainMenuUserpicTop);
|
|
if (_resetScaleButton) {
|
|
_resetScaleButton->moveToRight(0, 0);
|
|
}
|
|
_toggleAccounts->setGeometry(
|
|
0,
|
|
st::mainMenuCoverNameTop,
|
|
width(),
|
|
st::mainMenuCoverHeight - st::mainMenuCoverNameTop);
|
|
// Allow cover shadow over the scrolled content.
|
|
const auto top = st::mainMenuCoverHeight - st::lineWidth;
|
|
_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::mainMenuFooterHeightMin);
|
|
if (_footer->height() != footerHeight) {
|
|
_footer->resize(_footer->width(), footerHeight);
|
|
}
|
|
}
|
|
|
|
void MainMenu::updatePhone() {
|
|
_phoneText = Ui::FormatPhone(_controller->session().user()->phone());
|
|
update();
|
|
}
|
|
|
|
void MainMenu::paintEvent(QPaintEvent *e) {
|
|
Painter p(this);
|
|
const auto clip = e->rect();
|
|
const auto cover = QRect(0, 0, width(), st::mainMenuCoverHeight);
|
|
|
|
p.fillRect(clip, st::mainMenuBg);
|
|
if (cover.intersects(clip)) {
|
|
const auto widthText = width()
|
|
- st::mainMenuCoverNameLeft
|
|
- _toggleAccounts->rightSkip();
|
|
|
|
p.setFont(st::semiboldFont);
|
|
p.setPen(st::windowBoldFg);
|
|
const auto user = _controller->session().user();
|
|
if (_nameVersion < user->nameVersion()) {
|
|
_name.setText(
|
|
st::msgNameStyle,
|
|
user->name(),
|
|
Ui::NameTextOptions());
|
|
}
|
|
_name.drawLeftElided(
|
|
p,
|
|
st::mainMenuCoverNameLeft,
|
|
st::mainMenuCoverNameTop,
|
|
widthText,
|
|
width());
|
|
p.setFont(st::mainMenuPhoneFont);
|
|
p.setPen(st::windowSubTextFg);
|
|
p.drawTextLeft(
|
|
st::mainMenuCoverStatusLeft,
|
|
st::mainMenuCoverStatusTop,
|
|
width(),
|
|
_phoneText);
|
|
}
|
|
}
|
|
|
|
void MainMenu::initResetScaleButton() {
|
|
if (!window() || !window()->windowHandle()) {
|
|
return;
|
|
}
|
|
const auto handle = window()->windowHandle();
|
|
rpl::single(
|
|
handle->screen()
|
|
) | rpl::then(
|
|
base::qt_signal_producer(handle, &QWindow::screenChanged)
|
|
) | rpl::filter([](QScreen *screen) {
|
|
return screen != nullptr;
|
|
}) | rpl::map([](QScreen * screen) {
|
|
return rpl::single(
|
|
screen->availableGeometry()
|
|
) | rpl::then(
|
|
base::qt_signal_producer(screen, &QScreen::availableGeometryChanged)
|
|
);
|
|
}) | rpl::flatten_latest(
|
|
) | rpl::map([](QRect available) {
|
|
return (available.width() >= st::windowMinWidth)
|
|
&& (available.height() >= st::windowMinHeight);
|
|
}) | rpl::distinct_until_changed(
|
|
) | rpl::start_with_next([=](bool good) {
|
|
if (good) {
|
|
_resetScaleButton.destroy();
|
|
} else {
|
|
_resetScaleButton.create(this);
|
|
_resetScaleButton->addClickHandler([] {
|
|
cSetConfigScale(style::kScaleDefault);
|
|
Local::writeSettings();
|
|
Core::Restart();
|
|
});
|
|
_resetScaleButton->show();
|
|
updateControlsGeometry();
|
|
}
|
|
}, lifetime());
|
|
}
|
|
|
|
OthersUnreadState OtherAccountsUnreadStateCurrent() {
|
|
auto &app = Core::App();
|
|
const auto active = &app.activeAccount();
|
|
auto allMuted = true;
|
|
for (const auto &[index, account] : app.domain().accounts()) {
|
|
if (account.get() == active) {
|
|
continue;
|
|
} else if (const auto session = account->maybeSession()) {
|
|
if (!session->data().unreadBadgeMuted()) {
|
|
allMuted = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
.count = (app.unreadBadge() - active->session().data().unreadBadge()),
|
|
.allMuted = allMuted,
|
|
};
|
|
}
|
|
|
|
rpl::producer<OthersUnreadState> OtherAccountsUnreadState() {
|
|
return rpl::single(rpl::empty) | rpl::then(
|
|
Core::App().unreadBadgeChanges()
|
|
) | rpl::map(OtherAccountsUnreadStateCurrent);
|
|
}
|
|
|
|
} // namespace Window
|