mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-09-01 06:55:58 +00:00
Initial support of separate windows for accounts.
This commit is contained in:
@@ -186,8 +186,9 @@ Application::~Application() {
|
||||
// Depend on primaryWindow() for now :(
|
||||
Shortcuts::Finish();
|
||||
|
||||
_closingAsyncWindows.clear();
|
||||
_secondaryWindows.clear();
|
||||
_primaryWindow = nullptr;
|
||||
_primaryWindows.clear();
|
||||
_mediaView = nullptr;
|
||||
_notifications->clearAllFast();
|
||||
|
||||
@@ -280,13 +281,15 @@ void Application::run() {
|
||||
// Create mime database, so it won't be slow later.
|
||||
QMimeDatabase().mimeTypeForName(u"text/plain"_q);
|
||||
|
||||
_primaryWindow = std::make_unique<Window::Controller>();
|
||||
_lastActiveWindow = _primaryWindow.get();
|
||||
_primaryWindows.emplace(nullptr, std::make_unique<Window::Controller>());
|
||||
_lastActiveWindow
|
||||
= _lastActivePrimaryWindow
|
||||
= _primaryWindows.front().second.get();
|
||||
|
||||
_domain->activeChanges(
|
||||
) | rpl::start_with_next([=](not_null<Main::Account*> account) {
|
||||
_primaryWindow->showAccount(account);
|
||||
}, _primaryWindow->widget()->lifetime());
|
||||
showAccount(account);
|
||||
}, _lifetime);
|
||||
|
||||
(
|
||||
_domain->activeValue(
|
||||
@@ -303,15 +306,15 @@ void Application::run() {
|
||||
) | rpl::start_with_next([=](not_null<Main::Account*> account) {
|
||||
const auto ordered = _domain->orderedAccounts();
|
||||
const auto it = ranges::find(ordered, account);
|
||||
if (it != end(ordered)) {
|
||||
if (_lastActivePrimaryWindow && it != end(ordered)) {
|
||||
const auto index = std::distance(begin(ordered), it);
|
||||
if ((index + 1) > _domain->maxAccounts()) {
|
||||
_primaryWindow->show(Box(
|
||||
_lastActivePrimaryWindow->show(Box(
|
||||
AccountsLimitBox,
|
||||
&account->session()));
|
||||
}
|
||||
}
|
||||
}, _primaryWindow->widget()->lifetime());
|
||||
}, _lifetime);
|
||||
|
||||
QCoreApplication::instance()->installEventFilter(this);
|
||||
|
||||
@@ -332,20 +335,20 @@ void Application::run() {
|
||||
|
||||
startTray();
|
||||
|
||||
_primaryWindow->widget()->show();
|
||||
_lastActivePrimaryWindow->widget()->show();
|
||||
|
||||
const auto currentGeometry = _primaryWindow->widget()->geometry();
|
||||
const auto current = _lastActivePrimaryWindow->widget()->geometry();
|
||||
_mediaView = std::make_unique<Media::View::OverlayWidget>();
|
||||
_primaryWindow->widget()->Ui::RpWidget::setGeometry(currentGeometry);
|
||||
_lastActivePrimaryWindow->widget()->Ui::RpWidget::setGeometry(current);
|
||||
|
||||
DEBUG_LOG(("Application Info: showing."));
|
||||
_primaryWindow->finishFirstShow();
|
||||
_lastActivePrimaryWindow->finishFirstShow();
|
||||
|
||||
if (!_primaryWindow->locked() && cStartToSettings()) {
|
||||
_primaryWindow->showSettings();
|
||||
if (!_lastActivePrimaryWindow->locked() && cStartToSettings()) {
|
||||
_lastActivePrimaryWindow->showSettings();
|
||||
}
|
||||
|
||||
_primaryWindow->updateIsActiveFocus();
|
||||
_lastActivePrimaryWindow->updateIsActiveFocus();
|
||||
|
||||
for (const auto &error : Shortcuts::Errors()) {
|
||||
LOG(("Shortcuts Error: %1").arg(error));
|
||||
@@ -362,11 +365,6 @@ void Application::run() {
|
||||
_mediaView->show(std::move(request));
|
||||
}
|
||||
}, _lifetime);
|
||||
_primaryWindow->openInMediaViewRequests(
|
||||
) | rpl::start_to_stream(
|
||||
_openInMediaViewRequests,
|
||||
_primaryWindow->lifetime());
|
||||
|
||||
{
|
||||
const auto countries = std::make_shared<Countries::Manager>(
|
||||
_domain.get());
|
||||
@@ -374,6 +372,25 @@ void Application::run() {
|
||||
[[maybe_unused]] const auto countriesCopy = countries;
|
||||
});
|
||||
}
|
||||
|
||||
processCreatedWindow(_lastActivePrimaryWindow);
|
||||
}
|
||||
|
||||
void Application::showAccount(not_null<Main::Account*> account) {
|
||||
if (const auto separate = separateWindowForAccount(account)) {
|
||||
_lastActivePrimaryWindow = separate;
|
||||
separate->activate();
|
||||
} else if (const auto last = activePrimaryWindow()) {
|
||||
for (auto &[key, window] : _primaryWindows) {
|
||||
if (window.get() == last && key != account.get()) {
|
||||
auto found = std::move(window);
|
||||
_primaryWindows.remove(key);
|
||||
_primaryWindows.emplace(account, std::move(found));
|
||||
break;
|
||||
}
|
||||
}
|
||||
last->showAccount(account);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::showOpenGLCrashNotification() {
|
||||
@@ -390,7 +407,7 @@ void Application::showOpenGLCrashNotification() {
|
||||
Core::App().settings().setDisableOpenGL(true);
|
||||
Local::writeSettings();
|
||||
};
|
||||
_primaryWindow->show(Ui::MakeConfirmBox({
|
||||
_lastActivePrimaryWindow->show(Ui::MakeConfirmBox({
|
||||
.text = ""
|
||||
"There may be a problem with your graphics drivers and OpenGL. "
|
||||
"Try updating your drivers.\n\n"
|
||||
@@ -447,15 +464,15 @@ void Application::startSystemDarkModeViewer() {
|
||||
|
||||
void Application::enumerateWindows(Fn<void(
|
||||
not_null<Window::Controller*>)> callback) const {
|
||||
if (_primaryWindow) {
|
||||
callback(_primaryWindow.get());
|
||||
for (const auto &window : ranges::views::values(_primaryWindows)) {
|
||||
callback(window.get());
|
||||
}
|
||||
for (const auto &window : ranges::views::values(_secondaryWindows)) {
|
||||
callback(window.get());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::processSecondaryWindow(
|
||||
void Application::processCreatedWindow(
|
||||
not_null<Window::Controller*> window) {
|
||||
window->openInMediaViewRequests(
|
||||
) | rpl::start_to_stream(_openInMediaViewRequests, window->lifetime());
|
||||
@@ -468,21 +485,29 @@ void Application::startTray() {
|
||||
) | rpl::start_with_next([=] {
|
||||
enumerateWindows([&](WindowRaw w) { w->updateIsActive(); });
|
||||
_tray->updateMenuText();
|
||||
}, _primaryWindow->widget()->lifetime());
|
||||
}, _lifetime);
|
||||
|
||||
_tray->showFromTrayRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto last = _lastActiveWindow;
|
||||
enumerateWindows([&](WindowRaw w) { w->widget()->showFromTray(); });
|
||||
if (last) {
|
||||
const auto primary = _lastActivePrimaryWindow;
|
||||
enumerateWindows([&](WindowRaw w) {
|
||||
if (w != last && w != primary) {
|
||||
w->widget()->showFromTray();
|
||||
}
|
||||
});
|
||||
if (primary) {
|
||||
primary->widget()->showFromTray();
|
||||
}
|
||||
if (last && last != primary) {
|
||||
last->widget()->showFromTray();
|
||||
}
|
||||
}, _primaryWindow->widget()->lifetime());
|
||||
}, _lifetime);
|
||||
|
||||
_tray->hideToTrayRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
enumerateWindows([&](WindowRaw w) { w->widget()->minimizeToTray(); });
|
||||
}, _primaryWindow->widget()->lifetime());
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
auto Application::prepareEmojiSourceImages()
|
||||
@@ -504,10 +529,10 @@ void Application::clearEmojiSourceImages() {
|
||||
}
|
||||
|
||||
bool Application::isActiveForTrayMenu() const {
|
||||
if (_primaryWindow && _primaryWindow->widget()->isActiveForTrayMenu()) {
|
||||
return true;
|
||||
}
|
||||
return ranges::any_of(ranges::views::values(_secondaryWindows), [=](
|
||||
return ranges::any_of(ranges::views::values(_primaryWindows), [=](
|
||||
const std::unique_ptr<Window::Controller> &controller) {
|
||||
return controller->widget()->isActiveForTrayMenu();
|
||||
}) || ranges::any_of(ranges::views::values(_secondaryWindows), [=](
|
||||
const std::unique_ptr<Window::Controller> &controller) {
|
||||
return controller->widget()->isActiveForTrayMenu();
|
||||
});
|
||||
@@ -562,8 +587,8 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
|
||||
cSetStartUrl(url.mid(0, 8192));
|
||||
checkStartUrl();
|
||||
}
|
||||
if (StartUrlRequiresActivate(url)) {
|
||||
_primaryWindow->activate();
|
||||
if (_lastActivePrimaryWindow && StartUrlRequiresActivate(url)) {
|
||||
_lastActivePrimaryWindow->activate();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
@@ -804,15 +829,15 @@ void Application::checkLocalTime() {
|
||||
|
||||
void Application::handleAppActivated() {
|
||||
checkLocalTime();
|
||||
if (_primaryWindow) {
|
||||
_primaryWindow->updateIsActiveFocus();
|
||||
if (_lastActiveWindow) {
|
||||
_lastActiveWindow->updateIsActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::handleAppDeactivated() {
|
||||
if (_primaryWindow) {
|
||||
_primaryWindow->updateIsActiveBlur();
|
||||
}
|
||||
enumerateWindows([&](not_null<Window::Controller*> w) {
|
||||
w->updateIsActiveBlur();
|
||||
});
|
||||
const auto session = _lastActiveWindow
|
||||
? _lastActiveWindow->maybeSession()
|
||||
: nullptr;
|
||||
@@ -845,8 +870,8 @@ void Application::switchDebugMode() {
|
||||
Logs::SetDebugEnabled(true);
|
||||
_launcher->writeDebugModeSetting();
|
||||
DEBUG_LOG(("Debug logs started."));
|
||||
if (_primaryWindow) {
|
||||
_primaryWindow->hideLayer();
|
||||
if (_lastActivePrimaryWindow) {
|
||||
_lastActivePrimaryWindow->hideLayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -959,13 +984,17 @@ bool Application::canApplyLangPackWithoutRestart() const {
|
||||
}
|
||||
|
||||
void Application::checkSendPaths() {
|
||||
if (!cSendPaths().isEmpty() && _primaryWindow && !_primaryWindow->locked()) {
|
||||
_primaryWindow->widget()->sendPaths();
|
||||
if (!cSendPaths().isEmpty()
|
||||
&& _lastActivePrimaryWindow
|
||||
&& !_lastActivePrimaryWindow->locked()) {
|
||||
_lastActivePrimaryWindow->widget()->sendPaths();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::checkStartUrl() {
|
||||
if (!cStartUrl().isEmpty() && _primaryWindow && !_primaryWindow->locked()) {
|
||||
if (!cStartUrl().isEmpty()
|
||||
&& _lastActivePrimaryWindow
|
||||
&& !_lastActivePrimaryWindow->locked()) {
|
||||
const auto url = cStartUrl();
|
||||
cSetStartUrl(QString());
|
||||
if (!openLocalUrl(url, {})) {
|
||||
@@ -1029,8 +1058,8 @@ bool Application::openCustomUrl(
|
||||
const auto my = context.value<ClickHandlerContext>();
|
||||
const auto controller = my.sessionWindow.get()
|
||||
? my.sessionWindow.get()
|
||||
: _primaryWindow
|
||||
? _primaryWindow->sessionController()
|
||||
: _lastActivePrimaryWindow
|
||||
? _lastActivePrimaryWindow->sessionController()
|
||||
: nullptr;
|
||||
|
||||
using namespace qthelp;
|
||||
@@ -1042,11 +1071,10 @@ bool Application::openCustomUrl(
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void Application::preventOrInvoke(Fn<void()> &&callback) {
|
||||
_primaryWindow->preventOrInvoke(std::move(callback));
|
||||
_lastActivePrimaryWindow->preventOrInvoke(std::move(callback));
|
||||
}
|
||||
|
||||
void Application::lockByPasscode() {
|
||||
@@ -1150,7 +1178,7 @@ void Application::localPasscodeChanged() {
|
||||
}
|
||||
|
||||
bool Application::hasActiveWindow(not_null<Main::Session*> session) const {
|
||||
if (Quitting() || !_primaryWindow) {
|
||||
if (Quitting() || !_lastActiveWindow) {
|
||||
return false;
|
||||
} else if (_calls->hasActivePanel(session)) {
|
||||
return true;
|
||||
@@ -1161,8 +1189,18 @@ bool Application::hasActiveWindow(not_null<Main::Session*> session) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Window::Controller *Application::primaryWindow() const {
|
||||
return _primaryWindow.get();
|
||||
Window::Controller *Application::activePrimaryWindow() const {
|
||||
return _lastActivePrimaryWindow;
|
||||
}
|
||||
|
||||
Window::Controller *Application::separateWindowForAccount(
|
||||
not_null<Main::Account*> account) const {
|
||||
for (const auto &[openedAccount, window] : _primaryWindows) {
|
||||
if (openedAccount == account.get()) {
|
||||
return window.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Window::Controller *Application::separateWindowForPeer(
|
||||
@@ -1194,22 +1232,98 @@ Window::Controller *Application::ensureSeparateWindowForPeer(
|
||||
peer->owner().history(peer),
|
||||
std::make_unique<Window::Controller>(peer, showAtMsgId)
|
||||
).first->second.get();
|
||||
processSecondaryWindow(result);
|
||||
processCreatedWindow(result);
|
||||
result->widget()->show();
|
||||
result->finishFirstShow();
|
||||
return activate(result);
|
||||
}
|
||||
|
||||
Window::Controller *Application::ensureSeparateWindowForAccount(
|
||||
not_null<Main::Account*> account) {
|
||||
const auto activate = [&](not_null<Window::Controller*> window) {
|
||||
window->activate();
|
||||
return window;
|
||||
};
|
||||
|
||||
if (const auto existing = separateWindowForAccount(account)) {
|
||||
return activate(existing);
|
||||
}
|
||||
const auto result = _primaryWindows.emplace(
|
||||
account,
|
||||
std::make_unique<Window::Controller>(account)
|
||||
).first->second.get();
|
||||
processCreatedWindow(result);
|
||||
result->widget()->show();
|
||||
result->finishFirstShow();
|
||||
return activate(result);
|
||||
}
|
||||
|
||||
Window::Controller *Application::windowFor(not_null<PeerData*> peer) const {
|
||||
if (const auto separate = separateWindowForPeer(peer)) {
|
||||
return separate;
|
||||
}
|
||||
return windowFor(&peer->account());
|
||||
}
|
||||
|
||||
Window::Controller *Application::windowFor(
|
||||
not_null<Main::Account*> account) const {
|
||||
if (const auto separate = separateWindowForAccount(account)) {
|
||||
return separate;
|
||||
}
|
||||
return activePrimaryWindow();
|
||||
}
|
||||
|
||||
Window::Controller *Application::activeWindow() const {
|
||||
return _lastActiveWindow;
|
||||
}
|
||||
|
||||
bool Application::closeNonLastAsync(not_null<Window::Controller*> window) {
|
||||
const auto hasOther = [&] {
|
||||
for (const auto &[account, primary] : _primaryWindows) {
|
||||
if (!_closingAsyncWindows.contains(primary.get())
|
||||
&& primary.get() != window
|
||||
&& primary->maybeSession()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (!hasOther) {
|
||||
return false;
|
||||
}
|
||||
_closingAsyncWindows.emplace(window);
|
||||
crl::on_main(window, [=] { closeWindow(window); });
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::closeWindow(not_null<Window::Controller*> window) {
|
||||
const auto next = (_primaryWindows.front().second.get() != window)
|
||||
? _primaryWindows.front().second.get()
|
||||
: (_primaryWindows.back().second.get() != window)
|
||||
? _primaryWindows.back().second.get()
|
||||
: nullptr;
|
||||
if (_lastActivePrimaryWindow == window) {
|
||||
_lastActivePrimaryWindow = next;
|
||||
}
|
||||
if (_lastActiveWindow == window) {
|
||||
_lastActiveWindow = next;
|
||||
if (_lastActiveWindow) {
|
||||
_lastActiveWindow->activate();
|
||||
}
|
||||
}
|
||||
_closingAsyncWindows.remove(window);
|
||||
for (auto i = begin(_primaryWindows); i != end(_primaryWindows);) {
|
||||
if (i->second.get() == window) {
|
||||
Assert(_lastActiveWindow != window);
|
||||
Assert(_lastActivePrimaryWindow != window);
|
||||
i = _primaryWindows.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
for (auto i = begin(_secondaryWindows); i != end(_secondaryWindows);) {
|
||||
if (i->second.get() == window) {
|
||||
if (_lastActiveWindow == window) {
|
||||
_lastActiveWindow = _primaryWindow.get();
|
||||
}
|
||||
Assert(_lastActiveWindow != window);
|
||||
i = _secondaryWindows.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
@@ -1218,11 +1332,12 @@ void Application::closeWindow(not_null<Window::Controller*> window) {
|
||||
}
|
||||
|
||||
void Application::closeChatFromWindows(not_null<PeerData*> peer) {
|
||||
if (const auto window = windowFor(peer)
|
||||
; window && !window->isPrimary()) {
|
||||
closeWindow(window);
|
||||
}
|
||||
for (const auto &[history, window] : _secondaryWindows) {
|
||||
if (history->peer == peer) {
|
||||
closeWindow(window.get());
|
||||
break;
|
||||
} else if (const auto session = window->sessionController()) {
|
||||
if (const auto session = window->sessionController()) {
|
||||
if (session->activeChatCurrent().peer() == peer) {
|
||||
session->showPeerHistory(
|
||||
window->singlePeer()->id,
|
||||
@@ -1230,8 +1345,8 @@ void Application::closeChatFromWindows(not_null<PeerData*> peer) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_primaryWindow && _primaryWindow->sessionController()) {
|
||||
const auto primary = _primaryWindow->sessionController();
|
||||
if (const auto window = windowFor(&peer->account())) {
|
||||
const auto primary = window->sessionController();
|
||||
if ((primary->activeChatCurrent().peer() == peer)
|
||||
&& (&primary->session() == &peer->session())) {
|
||||
primary->clearSectionStack();
|
||||
@@ -1249,6 +1364,10 @@ void Application::windowActivated(not_null<Window::Controller*> window) {
|
||||
const auto now = window;
|
||||
_lastActiveWindow = window;
|
||||
|
||||
if (window->isPrimary()) {
|
||||
_lastActivePrimaryWindow = window;
|
||||
}
|
||||
|
||||
const auto wasSession = was ? was->maybeSession() : nullptr;
|
||||
const auto nowSession = now->maybeSession();
|
||||
if (wasSession != nowSession) {
|
||||
@@ -1410,8 +1529,8 @@ void Application::quitPreventFinished() {
|
||||
}
|
||||
|
||||
void Application::quitDelayed() {
|
||||
if (_primaryWindow) {
|
||||
_primaryWindow->widget()->hide();
|
||||
for (const auto &[account, window] : _primaryWindows) {
|
||||
window->widget()->hide();
|
||||
}
|
||||
for (const auto &[history, window] : _secondaryWindows) {
|
||||
window->widget()->hide();
|
||||
|
Reference in New Issue
Block a user