From 530f1cbb7e6c2710b68d7bd48668661899a030de Mon Sep 17 00:00:00 2001 From: Yaraslau Tamashevich Date: Sun, 29 Sep 2024 16:51:57 +0300 Subject: [PATCH] Implement closeTab action --- src/contour/TerminalSessionManager.cpp | 98 +++++++++++++++---------- src/contour/TerminalSessionManager.h | 41 ++++++++++- src/contour/display/TerminalDisplay.cpp | 2 +- src/contour/helper.h | 2 +- src/contour/ui.template/Terminal.qml.in | 6 +- 5 files changed, 107 insertions(+), 42 deletions(-) diff --git a/src/contour/TerminalSessionManager.cpp b/src/contour/TerminalSessionManager.cpp index 1ddd2a2931..6884cef8d2 100644 --- a/src/contour/TerminalSessionManager.cpp +++ b/src/contour/TerminalSessionManager.cpp @@ -52,22 +52,16 @@ TerminalSession* TerminalSessionManager::createSession() QQmlEngine::setObjectOwnership(session, QQmlEngine::CppOwnership); _activeSession = session; + _lastTabChange = std::chrono::steady_clock::now(); return session; } void TerminalSessionManager::setSession(size_t index) { - // QML for some reason sends multiple switch requests in a row, so we need to ignore them. - auto now = std::chrono::steady_clock::now(); - if (now - _lastTabSwitch < _timeBetweenTabSwitches) - { - std::cout << "Ignoring switch request due to too frequent switch requests." << std::endl; + managerLog()(fmt::format("SET SESSION: index: {}, _sessions.size(): {}", index, _sessions.size())); + if (!isAllowedToChangeTabs()) return; - } - _lastTabSwitch = now; - - std::cout << "Switching to session " << index << std::endl; auto imageSize = display->pixelSize(); auto pageSize = display->calculatePageSize(); @@ -85,20 +79,8 @@ void TerminalSessionManager::setSession(size_t index) } display->setSession(_activeSession); display->resizeWindow(imageSize.width, imageSize.height); - - _activeSession->terminal().setGuiTabInfoForStatusLine([&]() { - std::string tabInfo; - for (size_t i = 0; i < _sessions.size(); ++i) - { - if (i == index) - tabInfo += "["; - tabInfo += std::to_string(i + 1); - if (i == index) - tabInfo += "]"; - tabInfo += " "; - } - return tabInfo; - }()); + updateStatusLine(); + _lastTabChange = std::chrono::steady_clock::now(); } void TerminalSessionManager::addSession() @@ -108,13 +90,10 @@ void TerminalSessionManager::addSession() void TerminalSessionManager::previousTab() { - auto currentSessionIndex = [](auto const& sessions, auto const& activeSession) { - auto i = std::find_if(sessions.begin(), sessions.end(), [&](auto p) { return p == activeSession; }); - return i != sessions.end() ? i - sessions.begin() : -1; - }(_sessions, _activeSession); - std::cout << "PREVIOUS: "; - std::cout << "currentSessionIndex: " << currentSessionIndex << ", _sessions.size(): " << _sessions.size() - << std::endl; + const auto currentSessionIndex = getCurrentSessionIndex(); + managerLog()(fmt::format("PREVIOUS TAB: currentSessionIndex: {}, _sessions.size(): {}", + currentSessionIndex, + _sessions.size())); if (currentSessionIndex > 0) { @@ -124,22 +103,63 @@ void TerminalSessionManager::previousTab() void TerminalSessionManager::nextTab() { - auto currentSessionIndex = [](auto const& sessions, auto const& activeSession) { - auto i = std::find_if(sessions.begin(), sessions.end(), [&](auto p) { return p == activeSession; }); - return i != sessions.end() ? i - sessions.begin() : -1; - }(_sessions, _activeSession); - - std::cout << "NEXT TAB: "; - std::cout << "currentSessionIndex: " << currentSessionIndex << ", _sessions.size(): " << _sessions.size() - << std::endl; + const auto currentSessionIndex = getCurrentSessionIndex(); + + managerLog()(fmt::format( + "NEXT TAB: currentSessionIndex: {}, _sessions.size(): {}", currentSessionIndex, _sessions.size())); if (currentSessionIndex < _sessions.size() - 1) { setSession(currentSessionIndex + 1); } } +void TerminalSessionManager::closeTab() +{ + const auto currentSessionIndex = getCurrentSessionIndex(); + managerLog()(fmt::format( + "CLOSE TAB: currentSessionIndex: {}, _sessions.size(): {}", currentSessionIndex, _sessions.size())); + + // Session was removed outside of terminal session manager, we need to switch to another tab. + if (currentSessionIndex == -1 && !_sessions.empty()) + { + // We need to switch to another tab, so we permit consequent tab changes. + // TODO: This is a bit hacky. + _lastTabChange = std::chrono::steady_clock::now() - std::chrono::seconds(1); + setSession(0); + return; + } + + if (_sessions.size() > 1) + { + + if (!isAllowedToChangeTabs()) + return; + + removeSession(*_activeSession); + + // We need to switch to another tab, so we permit consequent tab changes. + // TODO: This is a bit hacky. + _lastTabChange = std::chrono::steady_clock::now() - std::chrono::seconds(1); + + if (currentSessionIndex <= _sessions.size() - 1) + { + setSession(currentSessionIndex + 1); + } + else + { + setSession(currentSessionIndex - 1); + } + } +} + void TerminalSessionManager::removeSession(TerminalSession& thatSession) { + managerLog()(fmt::format( + "REMOVE SESSION: session: {}, _sessions.size(): {}", (void*) &thatSession, _sessions.size())); + + if (!isAllowedToChangeTabs()) + return; + _app.onExit(thatSession); // TODO: the logic behind that impl could probably be moved here. auto i = std::find_if(_sessions.begin(), _sessions.end(), [&](auto p) { return p == &thatSession; }); @@ -148,6 +168,8 @@ void TerminalSessionManager::removeSession(TerminalSession& thatSession) _sessions.erase(i); } + updateStatusLine(); + _lastTabChange = std::chrono::steady_clock::now(); // Notify app if all sessions have been killed to trigger app termination. } diff --git a/src/contour/TerminalSessionManager.h b/src/contour/TerminalSessionManager.h index b58c561aa6..2095a06345 100644 --- a/src/contour/TerminalSessionManager.h +++ b/src/contour/TerminalSessionManager.h @@ -32,6 +32,7 @@ class TerminalSessionManager: public QAbstractListModel Q_INVOKABLE void previousTab(); Q_INVOKABLE void nextTab(); + Q_INVOKABLE void closeTab(); Q_INVOKABLE void setSession(size_t index); void removeSession(TerminalSession&); @@ -48,12 +49,50 @@ class TerminalSessionManager: public QAbstractListModel private: std::unique_ptr createPty(); + [[nodiscard]] auto getCurrentSessionIndex() const + { + return [](auto const& sessions, auto const& activeSession) { + auto i = + std::find_if(sessions.begin(), sessions.end(), [&](auto p) { return p == activeSession; }); + return i != sessions.end() ? i - sessions.begin() : -1; + }(_sessions, _activeSession); + } + + void updateStatusLine() + { + const auto currentSessionIndex = getCurrentSessionIndex(); + _activeSession->terminal().setGuiTabInfoForStatusLine([&]() { + std::string tabInfo; + for (size_t i = 0; i < _sessions.size(); ++i) + { + if (i == currentSessionIndex) + tabInfo += "["; + tabInfo += std::to_string(i + 1); + if (i == currentSessionIndex) + tabInfo += "]"; + tabInfo += " "; + } + return tabInfo; + }()); + } + + bool isAllowedToChangeTabs() + { + // QML for some reason sends multiple signals requests in a row, so we need to ignore them. + auto now = std::chrono::steady_clock::now(); + if (abs(now - _lastTabChange) < _timeBetweenTabSwitches) + { + managerLog()("Ignoring change request due to too frequent change requests."); + return false; + } + return true; + } ContourGuiApp& _app; std::chrono::seconds _earlyExitThreshold; TerminalSession* _activeSession = nullptr; std::vector _sessions; - std::chrono::time_point _lastTabSwitch; + std::chrono::time_point _lastTabChange; std::chrono::milliseconds _timeBetweenTabSwitches { 300 }; }; diff --git a/src/contour/display/TerminalDisplay.cpp b/src/contour/display/TerminalDisplay.cpp index 6adde2a007..155a07b0e1 100644 --- a/src/contour/display/TerminalDisplay.cpp +++ b/src/contour/display/TerminalDisplay.cpp @@ -264,7 +264,7 @@ TerminalDisplay::~TerminalDisplay() void TerminalDisplay::setSession(TerminalSession* newSession) { - fmt::print("TerminalDisplay::setSession: {} -> {}\n", (void*) _session, (void*) newSession); + displayLog()("TerminalDisplay::setSession: {} -> {}\n", (void*) _session, (void*) newSession); if (_session == newSession) return; diff --git a/src/contour/helper.h b/src/contour/helper.h index 916fe73e60..5688f9706d 100644 --- a/src/contour/helper.h +++ b/src/contour/helper.h @@ -34,7 +34,7 @@ auto inline const displayLog = auto inline const inputLog = logstore::category("gui.input", "Logs input driver details (e.g. GUI input events)."); auto inline const sessionLog = logstore::category("gui.session", "VT terminal session logs"); - +auto inline const managerLog = logstore::category("gui.session_manager", "Sessions manager logs"); namespace detail { template diff --git a/src/contour/ui.template/Terminal.qml.in b/src/contour/ui.template/Terminal.qml.in index f77c814188..14d8206df2 100644 --- a/src/contour/ui.template/Terminal.qml.in +++ b/src/contour/ui.template/Terminal.qml.in @@ -22,6 +22,7 @@ ContourTerminal onPreviousTab: terminalSessions.previousTab() onNextTab: terminalSessions.nextTab() + onCloseTab: terminalSessions.closeTab() Rectangle { id : backgroundColor @@ -186,7 +187,10 @@ ContourTerminal onTerminated: { console.log("Client process terminated. Closing the window."); - Window.window.close(); // https://stackoverflow.com/a/53829662/386670 + if (terminalSessions.count == 0) + Window.window.close(); // https://stackoverflow.com/a/53829662/386670 + else + terminalSessions.closeTab(); } function playBell(volume) {