Skip to content

Commit bdc4742

Browse files
committed
qt: Port Windows taskbar progress to native Windows API
1 parent eeb9cc1 commit bdc4742

File tree

5 files changed

+215
-15
lines changed

5 files changed

+215
-15
lines changed

src/qt/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ add_library(bitcoinqt STATIC EXCLUDE_FROM_ALL
257257
utilitydialog.h
258258
$<$<PLATFORM_ID:Windows>:winshutdownmonitor.cpp>
259259
$<$<PLATFORM_ID:Windows>:winshutdownmonitor.h>
260+
$<$<PLATFORM_ID:Windows>:wintaskbarprogress.cpp>
261+
$<$<PLATFORM_ID:Windows>:wintaskbarprogress.h>
260262
bitcoin.qrc
261263
${CMAKE_CURRENT_BINARY_DIR}/bitcoin_locale.qrc
262264
${CMAKE_CURRENT_BINARY_DIR}/bitcoin_rendered.qrc
@@ -366,8 +368,8 @@ endif()
366368
if(WITH_TASKBAR_PROGRESS)
367369
target_link_libraries(bitcoinqt
368370
PRIVATE
369-
"Qt${WITH_QT_VERSION}::WinExtras"
370-
$<$<PLATFORM_ID:Windows>:dwmapi>
371+
# Using native Windows API (ITaskbarList3), requires ole32 for COM
372+
$<$<PLATFORM_ID:Windows>:ole32>
371373
)
372374
endif()
373375

src/qt/bitcoingui.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
#include <qt/macdockiconhandler.h>
3636
#endif
3737
#ifdef BITCOIN_QT_WIN_TASKBAR
38-
#include <QWinTaskbarButton>
39-
#include <QWinTaskbarProgress>
38+
#include <qt/wintaskbarprogress.h>
4039
#endif
4140

4241
#include <chain.h>
@@ -232,7 +231,10 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
232231
m_app_nap_inhibitor = new CAppNapInhibitor;
233232
#endif
234233
#ifdef BITCOIN_QT_WIN_TASKBAR
235-
m_taskbar_button = new QWinTaskbarButton(this);
234+
m_taskbar_progress = new WinTaskbarProgress();
235+
if (windowHandle()) {
236+
m_taskbar_progress->setWindow(windowHandle());
237+
}
236238
#endif
237239

238240
GUIUtil::handleCloseWindowShortcut(this);
@@ -251,6 +253,7 @@ BitcoinGUI::~BitcoinGUI()
251253
delete m_app_nap_inhibitor;
252254
MacDockIconHandler::cleanup();
253255
#endif
256+
delete m_taskbar_progress;
254257

255258
delete NetWatch;
256259
delete rpcConsole;
@@ -1235,11 +1238,6 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
12351238

12361239
tooltip = tr("Processed %n block(s) of transaction history.", "", count);
12371240

1238-
#ifdef BITCOIN_QT_WIN_TASKBAR
1239-
m_taskbar_button->setWindow(windowHandle());
1240-
QWinTaskbarProgress* taskbar_progress = m_taskbar_button->progress();
1241-
#endif
1242-
12431241
// Set icon state: spinning if catching up, tick otherwise
12441242
if (secs < MAX_BLOCK_TIME_GAP) {
12451243
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
@@ -1256,7 +1254,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
12561254
progressBarLabel->setVisible(false);
12571255
progressBar->setVisible(false);
12581256
#ifdef BITCOIN_QT_WIN_TASKBAR
1259-
taskbar_progress->setVisible(false);
1257+
m_taskbar_progress->setVisible(false);
12601258
#endif
12611259
}
12621260
else
@@ -1273,8 +1271,8 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
12731271
progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5);
12741272
progressBar->setVisible(true);
12751273
#ifdef BITCOIN_QT_WIN_TASKBAR
1276-
taskbar_progress->setValue(qRound(nVerificationProgress * 100.0));
1277-
taskbar_progress->setVisible(true);
1274+
m_taskbar_progress->setValue(qRound(nVerificationProgress * 100.0));
1275+
m_taskbar_progress->setVisible(true);
12781276
#endif
12791277

12801278
tooltip = tr("Catching up…") + QString("<br>") + tooltip;
@@ -1445,6 +1443,13 @@ void BitcoinGUI::showEvent(QShowEvent *event)
14451443
showMempoolStatsAction->setEnabled(true);
14461444
aboutAction->setEnabled(true);
14471445
optionsAction->setEnabled(true);
1446+
1447+
#ifdef BITCOIN_QT_WIN_TASKBAR
1448+
// If window handle wasn't available during construction, set it now
1449+
if (m_taskbar_progress && !m_taskbar_progress->window() && windowHandle()) {
1450+
m_taskbar_progress->setWindow(windowHandle());
1451+
}
1452+
#endif
14481453
}
14491454

14501455
#ifdef ENABLE_WALLET

src/qt/bitcoingui.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ class QComboBox;
5555
class QDateTime;
5656
class QProgressBar;
5757
class QProgressDialog;
58-
class QWinTaskbarButton;
5958
QT_END_NAMESPACE
6059

60+
class WinTaskbarProgress;
61+
6162
namespace GUIUtil {
6263
class ClickableLabel;
6364
class ClickableProgressBar;
@@ -180,7 +181,7 @@ class BitcoinGUI : public QMainWindow
180181
RPCConsole* rpcConsole = nullptr;
181182
HelpMessageDialog* helpMessageDialog = nullptr;
182183
#ifdef BITCOIN_QT_WIN_TASKBAR
183-
QWinTaskbarButton* m_taskbar_button = nullptr;
184+
WinTaskbarProgress* m_taskbar_progress = nullptr;
184185
#endif
185186
ModalOverlay* modalOverlay = nullptr;
186187
MempoolStats* mempoolStats = nullptr;

src/qt/wintaskbarprogress.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright (c) 2025 The Bitcoin Knots developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <qt/wintaskbarprogress.h>
6+
7+
#include <cassert>
8+
#include <QWindow>
9+
10+
#include <windows.h>
11+
#include <shobjidl.h>
12+
13+
WinTaskbarProgress::WinTaskbarProgress()
14+
{
15+
}
16+
17+
WinTaskbarProgress::~WinTaskbarProgress()
18+
{
19+
releaseTaskbar();
20+
}
21+
22+
void WinTaskbarProgress::setWindow(QWindow* window)
23+
{
24+
assert(!m_initialized);
25+
m_window = window;
26+
}
27+
28+
void WinTaskbarProgress::initializeTaskbar()
29+
{
30+
if (m_initialized || !m_window) {
31+
return;
32+
}
33+
34+
HWND winId = reinterpret_cast<HWND>(m_window->winId());
35+
if (!winId) {
36+
return;
37+
}
38+
39+
HRESULT hr = CoCreateInstance(
40+
CLSID_TaskbarList,
41+
nullptr,
42+
CLSCTX_INPROC_SERVER,
43+
IID_ITaskbarList3,
44+
reinterpret_cast<void**>(&m_taskbarList)
45+
);
46+
47+
if (SUCCEEDED(hr) && m_taskbarList) {
48+
hr = m_taskbarList->HrInit();
49+
if (SUCCEEDED(hr)) {
50+
m_initialized = true;
51+
} else {
52+
m_taskbarList->Release();
53+
m_taskbarList = nullptr;
54+
}
55+
}
56+
}
57+
58+
void WinTaskbarProgress::releaseTaskbar()
59+
{
60+
if (m_taskbarList) {
61+
// Clear progress state before releasing
62+
if (m_window) {
63+
HWND winId = reinterpret_cast<HWND>(m_window->winId());
64+
if (winId) {
65+
m_taskbarList->SetProgressState(winId, TBPF_NOPROGRESS);
66+
}
67+
}
68+
m_taskbarList->Release();
69+
m_taskbarList = nullptr;
70+
}
71+
m_initialized = false;
72+
m_visible = false;
73+
}
74+
75+
void WinTaskbarProgress::setValue(int value)
76+
{
77+
// Lazy initialization on first use
78+
if (!m_initialized) {
79+
initializeTaskbar();
80+
}
81+
82+
if (!m_initialized || !m_taskbarList || !m_window) {
83+
return;
84+
}
85+
86+
HWND winId = reinterpret_cast<HWND>(m_window->winId());
87+
if (!winId) {
88+
return;
89+
}
90+
91+
// Clamp value to 0-100
92+
if (value < 0) value = 0;
93+
if (value > 100) value = 100;
94+
95+
// Use indeterminate mode for very low progress (< 1%) to show activity
96+
// Otherwise progress of 0 is invisible on the taskbar
97+
if (value < 1) {
98+
m_taskbarList->SetProgressState(winId, TBPF_INDETERMINATE);
99+
m_visible = true;
100+
return;
101+
}
102+
103+
// Switch to normal mode if not already visible
104+
if (!m_visible) {
105+
m_taskbarList->SetProgressState(winId, TBPF_NORMAL);
106+
m_visible = true;
107+
}
108+
109+
// Set progress value (current, maximum)
110+
m_taskbarList->SetProgressValue(winId, value, 100);
111+
}
112+
113+
void WinTaskbarProgress::setVisible(bool visible)
114+
{
115+
if (!m_initialized || !m_taskbarList || !m_window) {
116+
return;
117+
}
118+
119+
HWND winId = reinterpret_cast<HWND>(m_window->winId());
120+
if (!winId) {
121+
return;
122+
}
123+
124+
if (visible && !m_visible) {
125+
// Show normal progress
126+
m_taskbarList->SetProgressState(winId, TBPF_NORMAL);
127+
m_visible = true;
128+
} else if (!visible && m_visible) {
129+
// Hide progress
130+
m_taskbarList->SetProgressState(winId, TBPF_NOPROGRESS);
131+
m_visible = false;
132+
}
133+
}
134+
135+
void WinTaskbarProgress::reset()
136+
{
137+
setVisible(false);
138+
}

src/qt/wintaskbarprogress.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) 2025 The Bitcoin Knots developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_QT_WINTASKBARPROGRESS_H
6+
#define BITCOIN_QT_WINTASKBARPROGRESS_H
7+
8+
#ifdef BITCOIN_QT_WIN_TASKBAR
9+
#include <QWindow>
10+
11+
#include <windows.h>
12+
13+
// Forward declarations for COM interfaces
14+
struct ITaskbarList3;
15+
16+
/**
17+
* Native Windows taskbar progress indicator
18+
* Uses ITaskbarList3 COM interface for Windows 7+ compatibility
19+
* Works with both Qt5 and Qt6
20+
*/
21+
class WinTaskbarProgress
22+
{
23+
public:
24+
WinTaskbarProgress();
25+
~WinTaskbarProgress();
26+
27+
/** Set the window handle to attach progress to */
28+
void setWindow(QWindow* window);
29+
30+
/** Get the window handle */
31+
QWindow* window() const { return m_window; }
32+
33+
/** Set progress value (0-100) */
34+
void setValue(int value);
35+
36+
/** Show or hide the progress indicator */
37+
void setVisible(bool visible);
38+
39+
/** Reset progress state */
40+
void reset();
41+
42+
private:
43+
QWindow* m_window{nullptr};
44+
ITaskbarList3* m_taskbarList{nullptr};
45+
bool m_initialized{false};
46+
bool m_visible{false};
47+
48+
void initializeTaskbar();
49+
void releaseTaskbar();
50+
};
51+
52+
#endif // BITCOIN_QT_WIN_TASKBAR
53+
54+
#endif // BITCOIN_QT_WINTASKBARPROGRESS_H

0 commit comments

Comments
 (0)