diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index eac4e8120..7fe806827 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -91,6 +91,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/connection_box.h" #include "boxes/premium_limits_box.h" #include "ui/boxes/confirm_box.h" +#include "styles/style_window.h" #include #include @@ -185,7 +186,7 @@ Application::~Application() { } setLastActiveWindow(nullptr); - _lastActivePrimaryWindow = nullptr; + _windowInSettings = _lastActivePrimaryWindow = nullptr; _closingAsyncWindows.clear(); _secondaryWindows.clear(); _primaryWindows.clear(); @@ -289,7 +290,7 @@ void Application::run() { _primaryWindows.emplace(nullptr, std::make_unique()); setLastActiveWindow(_primaryWindows.front().second.get()); - _lastActivePrimaryWindow = _lastActiveWindow; + _windowInSettings = _lastActivePrimaryWindow = _lastActiveWindow; _domain->activeChanges( ) | rpl::start_with_next([=](not_null account) { @@ -1162,6 +1163,11 @@ void Application::localPasscodeChanged() { checkAutoLock(crl::now()); } +bool Application::savingPositionFor( + not_null window) const { + return !_windowInSettings || (_windowInSettings == window); +} + bool Application::hasActiveWindow(not_null session) const { if (Quitting() || !_lastActiveWindow) { return false; @@ -1319,6 +1325,9 @@ void Application::closeWindow(not_null window) { if (_lastActivePrimaryWindow == window) { _lastActivePrimaryWindow = next; } + if (_windowInSettings == window) { + _windowInSettings = next; + } if (_lastActiveWindow == window) { setLastActiveWindow(next); if (_lastActiveWindow) { @@ -1331,6 +1340,7 @@ void Application::closeWindow(not_null window) { if (i->second.get() == window) { Assert(_lastActiveWindow != window); Assert(_lastActivePrimaryWindow != window); + Assert(_windowInSettings != window); i = _primaryWindows.erase(i); } else { ++i; diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 79c96e8b7..4c24192ce 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -153,8 +153,8 @@ public: // Windows interface. bool hasActiveWindow(not_null session) const; - - // Don't auto-switch. + [[nodiscard]] bool savingPositionFor( + not_null window) const; [[nodiscard]] Window::Controller *activeWindow() const; [[nodiscard]] Window::Controller *activePrimaryWindow() const; [[nodiscard]] Window::Controller *separateWindowForAccount( @@ -397,6 +397,7 @@ private: std::unique_ptr> _secondaryWindows; Window::Controller *_lastActiveWindow = nullptr; Window::Controller *_lastActivePrimaryWindow = nullptr; + Window::Controller *_windowInSettings = nullptr; std::unique_ptr _mediaView; const std::unique_ptr _langpack; diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 192eac847..a38ed598d 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "tray.h" #include "styles/style_widgets.h" #include "styles/style_window.h" +#include "styles/style_dialogs.h" // ChildSkip().x() for new child windows. #include #include @@ -51,6 +52,42 @@ namespace { constexpr auto kSaveWindowPositionTimeout = crl::time(1000); +using Core::WindowPosition; + +[[nodiscard]] WindowPosition AdjustToScale(WindowPosition position) { + DEBUG_LOG(("Window Pos: Initializing first %1, %2, %3, %4 " + "(scale %5%, maximized %6)") + .arg(position.x) + .arg(position.y) + .arg(position.w) + .arg(position.h) + .arg(position.scale) + .arg(Logs::b(position.maximized))); + + if (!position.scale) { + return position; + } + const auto scaleFactor = cScale() / float64(position.scale); + if (scaleFactor != 1.) { + // Change scale while keeping the position center in place. + position.x += position.w / 2; + position.y += position.h / 2; + position.w *= scaleFactor; + position.h *= scaleFactor; + position.x -= position.w / 2; + position.y -= position.h / 2; + } + return position; +} + +[[nodiscard]] QPoint ChildSkip() { + const auto skipx = st::defaultDialogRow.padding.left() + + st::defaultDialogRow.photoSize + + st::defaultDialogRow.padding.left(); + const auto skipy = st::windowTitleHeight; + return { skipx, skipy }; +} + } // namespace const QImage &Logo() { @@ -587,34 +624,43 @@ void MainWindow::recountGeometryConstraints() { fixOrder(); } -Core::WindowPosition MainWindow::positionFromSettings() const { - auto position = Core::App().settings().windowPosition(); - DEBUG_LOG(("Window Pos: Initializing first %1, %2, %3, %4 " - "(scale %5%, maximized %6)") - .arg(position.x) - .arg(position.y) - .arg(position.w) - .arg(position.h) - .arg(position.scale) - .arg(Logs::b(position.maximized))); - - if (!position.scale) { - return position; - } - const auto scaleFactor = cScale() / float64(position.scale); - if (scaleFactor != 1.) { - // Change scale while keeping the position center in place. - position.x += position.w / 2; - position.y += position.h / 2; - position.w *= scaleFactor; - position.h *= scaleFactor; - position.x -= position.w / 2; - position.y -= position.h / 2; - } - return position; +WindowPosition MainWindow::initialPosition() const { + const auto active = Core::App().activeWindow(); + return (!active || active == &controller()) + ? AdjustToScale(Core::App().settings().windowPosition()) + : active->widget()->nextInitialChildPosition(isPrimary()); } -QRect MainWindow::countInitialGeometry(Core::WindowPosition position) { +WindowPosition MainWindow::nextInitialChildPosition(bool primary) { + const auto rect = geometry().marginsRemoved(frameMargins()); + const auto position = rect.topLeft(); + const auto adjust = [&](int value) { + return primary ? value : (value * 3 / 4); + }; + const auto width = adjust(st::windowDefaultWidth); + const auto height = adjust(st::windowDefaultHeight); + const auto skip = ChildSkip(); + const auto delta = _lastChildIndex + ? (_lastMyChildCreatePosition - position) + : skip; + if (qAbs(delta.x()) >= skip.x() || qAbs(delta.y()) >= skip.y()) { + _lastChildIndex = 1; + } else { + ++_lastChildIndex; + } + + _lastMyChildCreatePosition = position; + const auto use = position + (skip * _lastChildIndex); + return withScreenInPosition({ + .scale = cScale(), + .x = position.x(), + .y = position.y(), + .w = width, + .h = height, + }); +} + +QRect MainWindow::countInitialGeometry(WindowPosition position) { const auto primaryScreen = QGuiApplication::primaryScreen(); const auto primaryAvailable = primaryScreen ? primaryScreen->availableGeometry() @@ -735,9 +781,7 @@ void MainWindow::initGeometry() { if (initGeometryFromSystem()) { return; } - const auto geometry = countInitialGeometry(isPrimary() - ? positionFromSettings() - : SecondaryInitPosition()); + const auto geometry = countInitialGeometry(initialPosition()); DEBUG_LOG(("Window Pos: Setting first %1, %2, %3, %4" ).arg(geometry.x() ).arg(geometry.y() @@ -829,7 +873,7 @@ void MainWindow::savePosition(Qt::WindowState state) { if (state == Qt::WindowMinimized || !isVisible() - || !isPrimary() // #TODO windows + || !Core::App().savingPositionFor(&controller()) || !positionInited()) { return; } @@ -874,51 +918,38 @@ void MainWindow::savePosition(Qt::WindowState state) { } } -Core::WindowPosition MainWindow::withScreenInPosition( - Core::WindowPosition position) const { - auto centerX = position.x + position.w / 2; - auto centerY = position.y + position.h / 2; - int minDelta = 0; - QScreen *chosen = nullptr; - const auto screens = QGuiApplication::screens(); - for (auto screen : screens) { - auto delta = (screen->geometry().center() - QPoint(centerX, centerY)).manhattanLength(); - if (!chosen || delta < minDelta) { - minDelta = delta; - chosen = screen; - } - } +WindowPosition MainWindow::withScreenInPosition( + WindowPosition position) const { + const auto my = screen(); + const auto chosen = my ? my : QGuiApplication::primaryScreen(); if (!chosen) { return position; } - auto screenGeometry = chosen->geometry(); + const auto available = chosen->availableGeometry(); + if (available.width() < st::windowMinWidth + || available.height() < st::windowMinHeight) { + return position; + } + accumulate_min(position.w, available.width()); + accumulate_min(position.h, available.height()); + if (position.x + position.w > available.x() + available.width()) { + position.x = available.x() + available.width() - position.w; + } + if (position.y + position.h > available.y() + available.height()) { + position.y = available.y() + available.height() - position.h; + } + const auto geometry = chosen->geometry(); DEBUG_LOG(("Window Pos: Screen found, geometry: %1, %2, %3, %4" - ).arg(screenGeometry.x() - ).arg(screenGeometry.y() - ).arg(screenGeometry.width() - ).arg(screenGeometry.height())); - position.x -= screenGeometry.x(); - position.y -= screenGeometry.y(); + ).arg(geometry.x() + ).arg(geometry.y() + ).arg(geometry.width() + ).arg(geometry.height())); + position.x -= geometry.x(); + position.y -= geometry.y(); position.moncrc = screenNameChecksum(chosen->name()); return position; } -Core::WindowPosition MainWindow::SecondaryInitPosition() { - const auto active = Core::App().activeWindow(); - if (!active) { - return {}; - } - const auto geometry = active->widget()->geometry(); - const auto skip = st::windowMinWidth / 6; - return active->widget()->withScreenInPosition({ - .scale = cScale(), - .x = geometry.x() + skip, - .y = geometry.y() + skip, - .w = st::windowMinWidth, - .h = st::windowDefaultHeight, - }); -} - bool MainWindow::minimizeToTray() { if (Core::Quitting() || !Core::App().tray().has()) { return false; diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 882b828ab..11686b5b6 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -77,7 +77,6 @@ public: [[nodiscard]] QRect desktopRect() const; [[nodiscard]] Core::WindowPosition withScreenInPosition( Core::WindowPosition position) const; - [[nodiscard]] static Core::WindowPosition SecondaryInitPosition(); void init(); @@ -191,7 +190,9 @@ private: void updateMinimumSize(); void updatePalette(); - [[nodiscard]] Core::WindowPosition positionFromSettings() const; + [[nodiscard]] Core::WindowPosition initialPosition() const; + [[nodiscard]] Core::WindowPosition nextInitialChildPosition( + bool primary); [[nodiscard]] QRect countInitialGeometry(Core::WindowPosition position); void initGeometry(); @@ -213,6 +214,9 @@ private: bool _maximizedBeforeHide = false; + QPoint _lastMyChildCreatePosition; + int _lastChildIndex = 0; + mutable QRect _monitorRect; mutable crl::time _monitorLastGot = 0;