Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package tail-tray for openSUSE:Factory checked in at 2026-10-05 13:04:28 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/tail-tray (Old) and /work/SRC/openSUSE:Factory/.tail-tray.new.3698736 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "tail-tray" Mon Oct 5 13:04:28 2026 rev:29 rq:1382371 version:0.2.36 Changes: -------- --- /work/SRC/openSUSE:Factory/tail-tray/tail-tray.changes 2026-09-29 17:54:04.593971967 +0200 +++ /work/SRC/openSUSE:Factory/.tail-tray.new.3698736/tail-tray.changes 2026-10-05 13:04:31.305106915 +0200 @@ -1,0 +2,19 @@ +Sun Oct 04 16:50:19 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 0.2.36: + * Bump version to 0.2.36 + * Align new show window logic when double clicking the tray icon + as well + * Cancel a login without freezing the window (#139) + * Show the main window without asking to un-maximize it (#141) + * Log out without root and move on to another account (#140) + * Stay on the current tab after a login (#142) + * Show "Connecting..." in the tray while Tailscale starts (#143) + * Replace Preferences and About in the tray with Show Window / + Hide Window (#144) + * Use the Tail Tray icon for notifications (#145) + * Show the window when tail-tray is launched again (#146) + * Refresh the Devices tab on every status update (#132) + * Stay on the current tab when the connection comes back (#131) + +------------------------------------------------------------------- Old: ---- tail-tray-0.2.35.obscpio New: ---- tail-tray-0.2.36.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ tail-tray.spec ++++++ --- /var/tmp/diff_new_pack.J7KknO/_old 2026-10-05 13:04:32.740167057 +0200 +++ /var/tmp/diff_new_pack.J7KknO/_new 2026-10-05 13:04:32.745167267 +0200 @@ -17,7 +17,7 @@ Name: tail-tray -Version: 0.2.35 +Version: 0.2.36 Release: 0 Summary: Tailscale tray menu and UI for the KDE Plasma Desktop License: GPL-3.0-only ++++++ _service ++++++ --- /var/tmp/diff_new_pack.J7KknO/_old 2026-10-05 13:04:32.785168943 +0200 +++ /var/tmp/diff_new_pack.J7KknO/_new 2026-10-05 13:04:32.791169195 +0200 @@ -2,8 +2,8 @@ <service name="obs_scm" mode="manual"> <param name="url">https://github.com/SneWs/tail-tray.git</param> <param name="scm">git</param> - <param name="revision">refs/tags/v0.2.35</param> - <param name="match-tag">v0.2.35</param> + <param name="revision">refs/tags/v0.2.36</param> + <param name="match-tag">v0.2.36</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.J7KknO/_old 2026-10-05 13:04:32.831170871 +0200 +++ /var/tmp/diff_new_pack.J7KknO/_new 2026-10-05 13:04:32.844171416 +0200 @@ -3,6 +3,6 @@ <param name="url">https://github.com/SneWs/tail-tray</param> <param name="changesrevision">5089d8a02763a01668fb90c0896fbf25b9b4d16c</param></service><service name="tar_scm"> <param name="url">https://github.com/SneWs/tail-tray.git</param> - <param name="changesrevision">577bcb0d72dd5ca8a055c05581ab78dc8c0150b4</param></service></servicedata> + <param name="changesrevision">f46cbe5929582dfdbd505d632a80635d1438e24a</param></service></servicedata> (No newline at EOF) ++++++ tail-tray-0.2.35.obscpio -> tail-tray-0.2.36.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/CMakeLists.txt new/tail-tray-0.2.36/CMakeLists.txt --- old/tail-tray-0.2.35/CMakeLists.txt 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/CMakeLists.txt 2026-10-03 12:27:17.000000000 +0200 @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.21) -project(tail-tray VERSION 0.2.35 LANGUAGES CXX) +project(tail-tray VERSION 0.2.36 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/debian/changelog new/tail-tray-0.2.36/debian/changelog --- old/tail-tray-0.2.35/debian/changelog 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/debian/changelog 2026-10-03 12:27:17.000000000 +0200 @@ -1,5 +1,5 @@ -tail-tray (0.2.35) UNRELEASED; urgency=medium +tail-tray (0.2.36) UNRELEASED; urgency=medium * New upstream release. - -- Marcus Grenängen <[email protected]> Tue, 4 May 2026 17:12:08 +0100 + -- Marcus Grenängen <[email protected]> Sat, 2 Oct 2026 12:26:28 +0100 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/AccountsTabUiManager.cpp new/tail-tray-0.2.36/src/AccountsTabUiManager.cpp --- old/tail-tray-0.2.35/src/AccountsTabUiManager.cpp 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/AccountsTabUiManager.cpp 2026-10-03 12:27:17.000000000 +0200 @@ -38,7 +38,15 @@ ); connect(ui->btnLogout, &QPushButton::clicked, this, [this]() { - pTailRunner->logout(); + // The active account is the one marked with '*'; any other is where to go next + QString nextAccountId; + for (const auto& acc : accounts) { + if (!acc.account.endsWith('*')) { + nextAccountId = acc.id; + break; + } + } + pTailRunner->logout(nextAccountId); if (ui->lstAccounts->selectedItems().count() > 0) { auto* selectedItem = ui->lstAccounts->selectedItems().first(); int row = ui->lstAccounts->row(selectedItem); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/MainWindow.cpp new/tail-tray-0.2.36/src/MainWindow.cpp --- old/tail-tray-0.2.35/src/MainWindow.cpp 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/MainWindow.cpp 2026-10-03 12:27:17.000000000 +0200 @@ -194,28 +194,26 @@ pCurrentExecution->shutdown(); } +// Not showNormal(): on a visible window it asks to un-maximize, and compositors that treat +// that request as a toggle (Hyprland) maximize the window instead. +void MainWindow::showWindow() { + setWindowState(windowState() & ~Qt::WindowMinimized); + show(); +} + void MainWindow::showSettingsTab() { ui->tabWidget->setCurrentIndex(1); - showNormal(); + showWindow(); } void MainWindow::showAccountsTab() { ui->tabWidget->setCurrentIndex(0); - showNormal(); -} - -void MainWindow::showAboutTab() { - auto tabIndex = 5; - if (settings.tailDriveEnabled()) - tabIndex = 6; - - ui->tabWidget->setCurrentIndex(tabIndex); - showNormal(); + showWindow(); } void MainWindow::showNetworkStatusTab() { ui->tabWidget->setCurrentIndex(2); - showNormal(); + showWindow(); } void MainWindow::tailscaleIsInstalled(bool installed) { @@ -436,8 +434,8 @@ pLoginInProgressDlg.reset(); if (success) { - // And bring the settings tab front and center - showSettingsTab(); + // Stay on the current tab: the login flow opened the Account tab, where the new account shows up + show(); pCurrentExecution->start(); } else { @@ -807,6 +805,14 @@ // Read settings, and it will be synced to UI once read pCurrentExecution->readSettings(); + pTrayManager->windowVisibilityChanged(isVisible()); +} + +void MainWindow::hideEvent(QHideEvent *event) { + QMainWindow::hideEvent(event); + + // A minimize also sends a hide event but leaves isVisible() true, as the tray item expects + pTrayManager->windowVisibilityChanged(isVisible()); } void MainWindow::refreshThemebasedItems() @@ -846,7 +852,6 @@ if (didChangeState) { // If we're not connected, don't allow showing/changing tabs etc if (newState == TailState::Connected) { - ui->tabWidget->setCurrentIndex(1); ui->tabNetworkStatus->setDisabled(false); ui->tabSettings->setDisabled(false); ui->tabTailDrive->setDisabled(false); @@ -923,6 +928,8 @@ } accountsTabUi->onTailStatusChanged(pTailStatus); + if (eCurrentState == TailState::Connected) + pDevicesTabManager->syncToUi(pTailStatus); #if defined(DAVFS_ENABLED) pTailDriveUiManager->stateChangedTo(eCurrentState, pTailStatus); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/MainWindow.h new/tail-tray-0.2.36/src/MainWindow.h --- old/tail-tray-0.2.35/src/MainWindow.h 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/MainWindow.h 2026-10-03 12:27:17.000000000 +0200 @@ -35,9 +35,9 @@ void shutdown() const; + void showWindow(); void showSettingsTab(); void showAccountsTab(); - void showAboutTab(); void showNetworkStatusTab(); void syncSettingsToUi() const; @@ -122,6 +122,7 @@ protected: void showEvent(QShowEvent* event) override; + void hideEvent(QHideEvent* event) override; bool event(QEvent* event) override; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/NotificationsManager.cpp new/tail-tray-0.2.36/src/NotificationsManager.cpp --- old/tail-tray-0.2.35/src/NotificationsManager.cpp 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/NotificationsManager.cpp 2026-10-03 12:27:17.000000000 +0200 @@ -2,6 +2,11 @@ #include <QDesktopServices> +#if defined(KNOTIFICATIONS_ENABLED) +// The app icon (as in tail-tray.desktop), so notifications read as Tail Tray's +static const QString appIconName = QStringLiteral("tail-tray"); +#endif + NotificationsManager::NotificationsManager(TrayMenuManager const* pTrayMgr, QObject* parent) : QObject(parent) , m_pTrayMgr(pTrayMgr) @@ -16,7 +21,7 @@ notification->setText(message); notification->setUrgency(KNotification::Urgency::LowUrgency); - notification->setIconName(iconName.isEmpty() ? QStringLiteral("notification-active") : iconName); + notification->setIconName(iconName.isEmpty() ? appIconName : iconName); notification->sendEvent(); #else @@ -38,12 +43,7 @@ notification->setUrls(QList{fileUrl}); notification->setUrgency(KNotification::Urgency::DefaultUrgency); - if (!iconName.isEmpty()) { - notification->setIconName(iconName); - } - else { - notification->setIconName("edit-image"); - } + notification->setIconName(iconName.isEmpty() ? appIconName : iconName); notification->sendEvent(); #else @@ -59,7 +59,7 @@ notification->setText(message); notification->setUrgency(KNotification::Urgency::NormalUrgency); - notification->setIconName(iconName.isEmpty() ? QStringLiteral("dialog-warning") : iconName); + notification->setIconName(iconName.isEmpty() ? appIconName : iconName); notification->sendEvent(); #else @@ -74,7 +74,7 @@ notification->setText(message); notification->setUrgency(KNotification::Urgency::HighUrgency); - notification->setIconName(iconName.isEmpty() ? QStringLiteral("dialog-error") : iconName); + notification->setIconName(iconName.isEmpty() ? appIconName : iconName); notification->sendEvent(); #else @@ -90,7 +90,7 @@ notification->setText(message); notification->setUrgency(KNotification::Urgency::CriticalUrgency); - notification->setIconName(iconName.isEmpty() ? QStringLiteral("dialog-error") : iconName); + notification->setIconName(iconName.isEmpty() ? appIconName : iconName); notification->sendEvent(); #else @@ -106,7 +106,7 @@ notification->setText(tr("A new device have been discovered on your tailnet!\n\nDevice: %1 (%2) - %3") .arg(nodeName, ipAddress, os)); - notification->setIconName("online"); + notification->setIconName(appIconName); notification->sendEvent(); #else m_pTrayMgr->trayIcon()->showMessage(tr("Tailnet Devices"), @@ -123,7 +123,7 @@ notification->setText(tr("A device have been removed from your tailnet!\n\nDevice: %1 (%2) - %3") .arg(nodeName, ipAddress, os)); - notification->setIconName("offline"); + notification->setIconName(appIconName); notification->sendEvent(); #else m_pTrayMgr->trayIcon()->showMessage(tr("Tailnet Devices"), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/NotificationsManager.h new/tail-tray-0.2.36/src/NotificationsManager.h --- old/tail-tray-0.2.35/src/NotificationsManager.h 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/NotificationsManager.h 2026-10-03 12:27:17.000000000 +0200 @@ -28,9 +28,9 @@ void showFileNotification(const QString& title, const QString& message, const QFileInfo& fileInfo, const QString& iconName = QString()); - void showWarningNotification(const QString& title, const QString& message, const QString& iconName = QString("dialog-warning")); - void showErrorNotification(const QString& title, const QString& message, const QString& iconName = QString("dialog-error")); - void showCriticalNotification(const QString& title, const QString& message, const QString& iconName = QString("dialog-error")); + void showWarningNotification(const QString& title, const QString& message, const QString& iconName = QString()); + void showErrorNotification(const QString& title, const QString& message, const QString& iconName = QString()); + void showCriticalNotification(const QString& title, const QString& message, const QString& iconName = QString()); void showNodeConnectedNotification(const QString& nodeName, const QString& ipAddress, const QString& os); void showNodeDisconnectedNotification(const QString& nodeName, const QString& ipAddress, const QString& os); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/SingleApplicationImpl.h new/tail-tray-0.2.36/src/SingleApplicationImpl.h --- old/tail-tray-0.2.35/src/SingleApplicationImpl.h 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/SingleApplicationImpl.h 2026-10-03 12:27:17.000000000 +0200 @@ -3,6 +3,8 @@ #include <QApplication> #include <QDir> +#include <QLocalServer> +#include <QLocalSocket> #include <QLockFile> #include <QStandardPaths> @@ -11,7 +13,7 @@ public: explicit SingleApplicationImpl(int &argc, char **argv) : QApplication(argc, argv) - , singleGuard(singleInstanceLockFilePath()) + , singleGuard(runtimeFilePath("tail-tray.grenangen.se.lock")) { singleGuard.setStaleLockTime(0); } @@ -26,19 +28,45 @@ } [[nodiscard]] bool claimInstance() { - return singleGuard.tryLock(0); + if (!singleGuard.tryLock(0)) + return false; + + // Later launches connect here to ask this instance to show its window. Holding the lock + // means an existing socket is a crashed instance's leftover, which would fail listen(). + QLocalServer::removeServer(activationSocketPath()); + activationServer.listen(activationSocketPath()); + connect(&activationServer, &QLocalServer::newConnection, this, [this]() { + activationServer.nextPendingConnection()->deleteLater(); + emit activationRequested(); + }); + + return true; + } + + static void activateRunningInstance() { + QLocalSocket socket; + socket.connectToServer(activationSocketPath()); + socket.waitForConnected(1000); } +signals: + void activationRequested(); + private: - static QString singleInstanceLockFilePath() { - auto lockDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); - if (lockDir.isEmpty()) - lockDir = QDir::tempPath(); + static QString runtimeFilePath(const QString& fileName) { + auto runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + if (runtimeDir.isEmpty()) + runtimeDir = QDir::tempPath(); + + return QDir(runtimeDir).absoluteFilePath(fileName); + } - return QDir(lockDir).absoluteFilePath("tail-tray.grenangen.se.lock"); + static QString activationSocketPath() { + return runtimeFilePath("tail-tray.grenangen.se.socket"); } QLockFile singleGuard; + QLocalServer activationServer; }; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/TailRunner.cpp new/tail-tray-0.2.36/src/TailRunner.cpp --- old/tail-tray-0.2.35/src/TailRunner.cpp 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/TailRunner.cpp 2026-10-03 12:27:17.000000000 +0200 @@ -40,6 +40,12 @@ } static BufferedProcessWrapper* pActiveLoginFlow = nullptr; + + // `tailscale login` first moves to a new, empty profile; this is where a cancel goes back to + static QString accountBeforeLogin; + + // Where a finished logout moves on to; empty when no other account is left + static QString accountAfterLogout; } TailRunner::TailRunner(const TailSettings& s, QObject* parent) @@ -179,11 +185,16 @@ args << "--operator" << qEnvironmentVariable("USER"); #endif + if (pActiveLoginFlow == nullptr) + accountBeforeLogin = lastKnownStatus.user.loginName; + runCommand(Command::Login, "login", args, false, true); } -void TailRunner::logout() { - runCommand(Command::Logout, "logout", QStringList(), false, true); +// Being the account's operator is enough to log out, so no pkexec here +void TailRunner::logout(const QString& nextAccountId) { + accountAfterLogout = nextAccountId; + runCommand(Command::Logout, "logout", QStringList()); } void TailRunner::cancelLoginFlow() { @@ -191,7 +202,16 @@ return; } - pActiveLoginFlow->cancel(); + // The login runs as root through pkexec, so it can't be killed from here, and deleting its + // QProcess while it runs blocks the UI for 30s. Going back to the previous profile puts + // tailscaled in Running again, which ends `tailscale login`; its output is ignored from now. + auto* flow = pActiveLoginFlow; + pActiveLoginFlow = nullptr; + disconnect(flow, nullptr, this, nullptr); + connect(flow->process(), &QProcess::finished, flow, &QObject::deleteLater); + + if (!accountBeforeLogin.isEmpty()) + switchAccount(accountBeforeLogin); } void TailRunner::start(const bool usePkExec) { @@ -503,6 +523,14 @@ else if (commandInfo == Command::SendFile) { emit fileSent(true, QString{}, process->userData()); } + else if (commandInfo == Command::Logout) { + // Tailscale is now on a blank account with no operator: only root can switch away, + // and the account list can't be read until a login sets the operator again + if (accountAfterLogout.isEmpty()) + emit accountsListed({}); + else + runCommand(Command::SwitchAccount, "switch", QStringList{accountAfterLogout}, false, true); + } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/TailRunner.h new/tail-tray-0.2.36/src/TailRunner.h --- old/tail-tray-0.2.35/src/TailRunner.h 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/TailRunner.h 2026-10-03 12:27:17.000000000 +0200 @@ -49,16 +49,6 @@ [[nodiscard]] void* userData() const { return pUserData; } [[nodiscard]] bool isRunning() const { return proc != nullptr && proc->state() == QProcess::Running; } - void cancel(const bool raiseEvents = true) { - if (proc != nullptr) { - proc->terminate(); - } - - if (raiseEvents) { - emit processFinished(this, 0, QProcess::NormalExit); - } - } - signals: void processErrorOccurred(BufferedProcessWrapper* wrapper, QProcess::ProcessError error); void processCanReadStdOut(BufferedProcessWrapper* process); @@ -102,7 +92,7 @@ void switchAccount(const QString& accountId); void login(const QString& customLoginUrl = ""); - void logout(); + void logout(const QString& nextAccountId); void cancelLoginFlow(); void start(bool usePkExec = false); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/TrayMenuManager.cpp new/tail-tray-0.2.36/src/TrayMenuManager.cpp --- old/tail-tray-0.2.35/src/TrayMenuManager.cpp 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/TrayMenuManager.cpp 2026-10-03 12:27:17.000000000 +0200 @@ -56,11 +56,12 @@ pQuitAction = std::make_unique<QAction>(tr("Quit")); pLoginAction = std::make_unique<QAction>(tr("Login")); pLogoutAction = std::make_unique<QAction>(tr("Logout")); - pPreferences = std::make_unique<QAction>(tr("Preferences")); - pAbout = std::make_unique<QAction>(tr("About...")); + pShowHideWindow = std::make_unique<QAction>(tr("Show Window")); pConnected = std::make_unique<QAction>(tr("Connected")); pConnected->setEnabled(false); pConnect = std::make_unique<QAction>(tr("Connect")); + pConnecting = std::make_unique<QAction>(tr("Connecting...")); + pConnecting->setEnabled(false); pDisconnect = std::make_unique<QAction>(tr("Disconnect")); pThisDevice = std::make_unique<QAction>(tr("This device")); pExitNodeNone = std::make_unique<QAction>(tr("None")); @@ -74,6 +75,10 @@ stateChangedTo(TailState::NotLoggedIn, TailStatus{}); } +void TrayMenuManager::windowVisibilityChanged(bool visible) { + pShowHideWindow->setText(visible ? tr("Hide Window") : tr("Show Window")); +} + void TrayMenuManager::onAccountsListed( const QList<TailAccountInfo> &foundAccounts) { accounts = foundAccounts; @@ -110,8 +115,7 @@ pTrayMenu->clear(); pTrayMenu->addAction(pLoginAction.get()); disposableConnectedMenuActions.push_back(pTrayMenu->addSeparator()); - pTrayMenu->addAction(pPreferences.get()); - pTrayMenu->addAction(pAbout.get()); + pTrayMenu->addAction(pShowHideWindow.get()); disposableConnectedMenuActions.push_back(pTrayMenu->addSeparator()); pTrayMenu->addAction(pQuitAction.get()); } @@ -120,7 +124,9 @@ const TailStatus &pTailStatus) const { pSysTray->setIcon(themeManager.getDisconnectedTrayIcon()); pTrayMenu->clear(); - pTrayMenu->addAction(pConnect.get()); + // Tailscale is already bringing the connection up, so Connect would do nothing + const bool connecting = pTailStatus.backendState == "Starting" || pTailStatus.backendState == "Running"; + pTrayMenu->addAction(connecting ? pConnecting.get() : pConnect.get()); disposableConnectedMenuActions.push_back(pTrayMenu->addSeparator()); if (pTailStatus.user.id > 0) @@ -132,8 +138,7 @@ actions->addAction(pRestartTailscale.get()); actions->addAction(pRefreshLocalDns.get()); disposableConnectedMenuActions.push_back(pTrayMenu->addSeparator()); - pTrayMenu->addAction(pPreferences.get()); - pTrayMenu->addAction(pAbout.get()); + pTrayMenu->addAction(pShowHideWindow.get()); disposableConnectedMenuActions.push_back(pTrayMenu->addSeparator()); pTrayMenu->addAction(pQuitAction.get()); @@ -489,8 +494,7 @@ }); disposableConnectedMenuActions.push_back(pTrayMenu->addSeparator()); - pTrayMenu->addAction(pPreferences.get()); - pTrayMenu->addAction(pAbout.get()); + pTrayMenu->addAction(pShowHideWindow.get()); disposableConnectedMenuActions.push_back(pTrayMenu->addSeparator()); pTrayMenu->addAction(pQuitAction.get()); @@ -535,14 +539,13 @@ connect(pDisconnect.get(), &QAction::triggered, this, [this](bool) { pTailRunner->stop(); }); - connect(pPreferences.get(), &QAction::triggered, this, [this](bool) { - auto *wnd = dynamic_cast<MainWindow *>(this->parent()); - wnd->showSettingsTab(); - }); - - connect(pAbout.get(), &QAction::triggered, this, [this](bool) { + // Opens the window on whichever tab it was left on, or hides it when it's open + connect(pShowHideWindow.get(), &QAction::triggered, this, [this](bool) { auto *wnd = dynamic_cast<MainWindow *>(this->parent()); - wnd->showAboutTab(); + if (wnd->isVisible()) + wnd->hide(); + else + wnd->showWindow(); }); connect(pThisDevice.get(), &QAction::triggered, this, [this](bool) { @@ -563,7 +566,7 @@ connect(pSysTray.get(), &QSystemTrayIcon::activated, this, [this](QSystemTrayIcon::ActivationReason reason) { - auto *wnd = dynamic_cast<MainWindow *>(this->parent()); + auto *wnd = dynamic_cast<MainWindow*>(this->parent()); if (reason == QSystemTrayIcon::ActivationReason::Trigger) { if (wnd->isVisible()) wnd->hide(); @@ -571,7 +574,7 @@ // NOTE: When settings are read, they will call settings to UI // so will be in sync on show pTailRunner->readSettings(); - wnd->showSettingsTab(); + wnd->showWindow(); } } else if (reason == QSystemTrayIcon::ActivationReason::MiddleClick) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/TrayMenuManager.h new/tail-tray-0.2.36/src/TrayMenuManager.h --- old/tail-tray-0.2.35/src/TrayMenuManager.h 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/TrayMenuManager.h 2026-10-03 12:27:17.000000000 +0200 @@ -21,6 +21,7 @@ explicit TrayMenuManager(TailSettings& s, TailRunner* runner, ThemeManager& tm, ScriptManager* scriptManager, QObject* parent = nullptr); + void windowVisibilityChanged(bool visible); void onAccountsListed(const QList<TailAccountInfo>& foundAccounts); void stateChangedTo(TailState newState, const TailStatus& pTailStatus); @@ -42,9 +43,9 @@ std::unique_ptr<QAction> pLogoutAction; std::unique_ptr<QAction> pConnected; std::unique_ptr<QAction> pConnect; + std::unique_ptr<QAction> pConnecting; std::unique_ptr<QAction> pDisconnect; - std::unique_ptr<QAction> pPreferences; - std::unique_ptr<QAction> pAbout; + std::unique_ptr<QAction> pShowHideWindow; std::unique_ptr<QAction> pThisDevice; std::unique_ptr<QAction> pExitNodeNone; std::unique_ptr<QAction> pRefreshLocalDns; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/Version.h new/tail-tray-0.2.36/src/Version.h --- old/tail-tray-0.2.35/src/Version.h 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/Version.h 2026-10-03 12:27:17.000000000 +0200 @@ -1,6 +1,6 @@ #pragma once -#define TAIL_TRAY_VERSION "0.2.35" +#define TAIL_TRAY_VERSION "0.2.36" #define TAIL_TRAY_VERSION_MAJOR 0 #define TAIL_TRAY_VERSION_MINOR 2 -#define TAIL_TRAY_VERSION_PATCH 35 +#define TAIL_TRAY_VERSION_PATCH 36 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tail-tray-0.2.35/src/main.cpp new/tail-tray-0.2.36/src/main.cpp --- old/tail-tray-0.2.35/src/main.cpp 2026-09-28 20:16:40.000000000 +0200 +++ new/tail-tray-0.2.36/src/main.cpp 2026-10-03 12:27:17.000000000 +0200 @@ -14,8 +14,12 @@ SingleApplicationImpl a(argc, argv); if (!a.claimInstance()) { - qDebug() << "Secondary instance not allowed, will quit this instance"; - return -1; + // --background (for autostart) only makes sure tail-tray runs, so the window stays put + if (!QCoreApplication::arguments().contains("--background")) { + qDebug() << "Already running, asking that instance to show its window"; + SingleApplicationImpl::activateRunningInstance(); + } + return 0; } QApplication::setQuitOnLastWindowClosed(false); @@ -69,6 +73,7 @@ } MainWindow w; + QObject::connect(&a, &SingleApplicationImpl::activationRequested, &w, &MainWindow::showWindow); // HACK: Workaround to make sure that the tray icon is updated correctly when style/theme changes { ++++++ tail-tray.obsinfo ++++++ --- /var/tmp/diff_new_pack.J7KknO/_old 2026-10-05 13:04:33.220187174 +0200 +++ /var/tmp/diff_new_pack.J7KknO/_new 2026-10-05 13:04:33.225187384 +0200 @@ -1,5 +1,5 @@ name: tail-tray -version: 0.2.35 -mtime: 1790619400 -commit: 577bcb0d72dd5ca8a055c05581ab78dc8c0150b4 +version: 0.2.36 +mtime: 1791023237 +commit: f46cbe5929582dfdbd505d632a80635d1438e24a
