macOS: Add status bar menu with DL/UL display

Adds a macOS status bar icon with minimal menu:
- Live download/upload rates menu item
This commit is contained in:
Dru Still 2025-08-12 20:47:34 -04:00
commit 9186081a54
No known key found for this signature in database
GPG key ID: 96ED2600AEC347CC
7 changed files with 255 additions and 3 deletions

View file

@ -287,14 +287,18 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
macosdockbadge/badger.mm macosdockbadge/badger.mm
macosdockbadge/badgeview.h macosdockbadge/badgeview.h
macosdockbadge/badgeview.mm macosdockbadge/badgeview.mm
macosshiftclickhandler.h
macosshiftclickhandler.cpp macosshiftclickhandler.cpp
macosshiftclickhandler.h
macosstatusitem/itemview.h
macosstatusitem/itemview.mm
macosstatusitem/statusitem.h
macosstatusitem/statusitem.mm
macutilities.h macutilities.h
macutilities.mm macutilities.mm
powermanagement/inhibitormacos.h
powermanagement/inhibitormacos.cpp powermanagement/inhibitormacos.cpp
programupdater.h powermanagement/inhibitormacos.h
programupdater.cpp programupdater.cpp
programupdater.h
) )
target_link_libraries(qbt_gui PRIVATE target_link_libraries(qbt_gui PRIVATE
objc objc

View file

@ -0,0 +1,33 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2025 Dru Still <drustill4@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#import <AppKit/AppKit.h>
@interface ItemView: NSView
@end

View file

@ -0,0 +1,111 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2025 Dru Still <drustill4@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#import "itemview.h"
#include <QString>
#include "base/utils/misc.h"
namespace
{
static NSString * const kUploadArrow = @"\u2191";
static NSString * const kDownloadArrow = @"\u2193";
}
@interface ItemView ()
@property(nonatomic) int64_t fDownloadRate;
@property(nonatomic) int64_t fUploadRate;
@property(strong) NSStatusItem *statusItem;
@property(strong) NSMenu *statusMenu;
@property(strong) NSMenuItem *statusStatsItem;
@end
@implementation ItemView
- (instancetype)init
{
if ((self = [super init])){
_fDownloadRate = 0.0;
_fUploadRate = 0.0;
NSImage *icon = [NSImage imageNamed:@"qbittorrent_mac"];
[icon setSize:NSMakeSize(16, 16)];
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
self.statusItem.button.image = icon;
self.statusItem.button.imagePosition = NSImageOnly;
self.statusItem.button.imageScaling = NSImageScaleProportionallyDown;
self.statusItem.button.toolTip = @"qBittorrent";
self.statusItem.button.title = @"";
self.statusMenu = [[NSMenu alloc] init];
QString uploadString = Utils::Misc::friendlyUnit(self.fUploadRate, true);
QString downloadString = Utils::Misc::friendlyUnit(self.fDownloadRate, true);
NSString *uploadNSString = [NSString stringWithUTF8String:uploadString.toUtf8().constData()];
NSString *downloadNSString = [NSString stringWithUTF8String:downloadString.toUtf8().constData()];
self.statusStatsItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"%@ %@ · %@ %@",
kDownloadArrow, downloadNSString,
kUploadArrow, uploadNSString] action:nil keyEquivalent:@""];
self.statusStatsItem.action = nil;
self.statusStatsItem.target = nil;
[self.statusMenu addItem:self.statusStatsItem];
[self.statusMenu addItem:[NSMenuItem separatorItem]];
self.statusItem.menu = self.statusMenu;
}
return self;
}
- (BOOL)setRatesWithDownload:(int64_t)downloadRate upload:(int64_t)uploadRate
{
if ((downloadRate == self.fDownloadRate) && (uploadRate == self.fUploadRate))
return NO;
self.fDownloadRate = downloadRate;
self.fUploadRate = uploadRate;
QString uploadString = Utils::Misc::friendlyUnit(self.fUploadRate, true);
QString downloadString = Utils::Misc::friendlyUnit(self.fDownloadRate, true);
NSString *uploadNSString = [NSString stringWithUTF8String:uploadString.toUtf8().constData()];
NSString *downloadNSString = [NSString stringWithUTF8String:downloadString.toUtf8().constData()];
[self.statusStatsItem setTitle:[NSString stringWithFormat:@"%@ %@ · %@ %@",
kDownloadArrow, downloadNSString,
kUploadArrow, uploadNSString]];
return YES;
}
@end

View file

@ -0,0 +1,47 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2025 Dru Still <drustill4@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <memory>
namespace MacUtils
{
class StatusItem final
{
public:
StatusItem();
~StatusItem();
void updateSpeed(int64_t downloadRate, int64_t uploadRate);
private:
struct Impl;
std::unique_ptr<Impl> m_impl;
};
}

View file

@ -0,0 +1,52 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2025 Dru Still <drustill4@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "statusitem.h"
#import "itemview.h"
namespace MacUtils
{
struct StatusItem::Impl
{
ItemView *itemView = nullptr;
};
StatusItem::StatusItem()
: m_impl(std::make_unique<Impl>())
{
m_impl->itemView = [[ItemView alloc] init];
}
StatusItem::~StatusItem() = default;
void StatusItem::updateSpeed(const int64_t downloadRate, const int64_t uploadRate)
{
[m_impl->itemView setRatesWithDownload:downloadRate upload:uploadRate];
}
}

View file

@ -105,6 +105,7 @@
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
#include "macosdockbadge/badger.h" #include "macosdockbadge/badger.h"
#include "macosstatusitem/statusitem.h"
#endif #endif
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
#include "programupdater.h" #include "programupdater.h"
@ -138,6 +139,7 @@ MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, con
, m_storeExecutionLogTypes {EXECUTIONLOG_SETTINGS_KEY(u"Types"_s), Log::MsgType::ALL} , m_storeExecutionLogTypes {EXECUTIONLOG_SETTINGS_KEY(u"Types"_s), Log::MsgType::ALL}
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
, m_badger {std::make_unique<MacUtils::Badger>()} , m_badger {std::make_unique<MacUtils::Badger>()}
, m_statusItem {std::make_unique<MacUtils::StatusItem>()}
#endif // Q_OS_MACOS #endif // Q_OS_MACOS
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
@ -1465,6 +1467,7 @@ void MainWindow::loadSessionStats()
// update global information // update global information
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
m_badger->updateSpeed(status.payloadDownloadRate, status.payloadUploadRate); m_badger->updateSpeed(status.payloadDownloadRate, status.payloadUploadRate);
m_statusItem->updateSpeed(status.payloadDownloadRate, status.payloadUploadRate);
#else #else
refreshTrayIconTooltip(); refreshTrayIconTooltip();
#endif // Q_OS_MACOS #endif // Q_OS_MACOS

View file

@ -66,6 +66,7 @@ class TransferListWidget;
namespace MacUtils namespace MacUtils
{ {
class Badger; class Badger;
class StatusItem;
} }
#endif #endif
@ -261,5 +262,6 @@ private:
#endif #endif
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
std::unique_ptr<MacUtils::Badger> m_badger; std::unique_ptr<MacUtils::Badger> m_badger;
std::unique_ptr<MacUtils::StatusItem> m_statusItem;
#endif #endif
}; };