aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/imports/winextras/plugins.qmltypes3
-rw-r--r--src/imports/winextras/qquickthumbnailtoolbar.cpp100
-rw-r--r--src/imports/winextras/qquickthumbnailtoolbar_p.h24
-rw-r--r--src/winextras/qwinfunctions_p.cpp8
-rw-r--r--src/winextras/qwinfunctions_p.h7
-rw-r--r--src/winextras/qwinthumbnailtoolbar.cpp258
-rw-r--r--src/winextras/qwinthumbnailtoolbar.h16
-rw-r--r--src/winextras/qwinthumbnailtoolbar_p.h33
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/manual/quickthumbnail/main.qml4
-rw-r--r--tests/manual/thumbnail/main.cpp165
-rw-r--r--tests/manual/thumbnail/thumbnail.pro4
12 files changed, 611 insertions, 14 deletions
diff --git a/src/imports/winextras/plugins.qmltypes b/src/imports/winextras/plugins.qmltypes
index f5963ce..eb1aa15 100644
--- a/src/imports/winextras/plugins.qmltypes
+++ b/src/imports/winextras/plugins.qmltypes
@@ -91,6 +91,9 @@ Module {
Property { name: "count"; type: "int"; isReadonly: true }
Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
Property { name: "buttons"; type: "QQuickThumbnailToolButton"; isList: true; isReadonly: true }
+ Property { name: "iconicThumbnailSource"; type: "QUrl" }
+ Property { name: "iconicLivePreviewSource"; type: "QUrl" }
+ Property { name: "iconicNotificationsEnabled"; type: "bool" }
Method { name: "clear" }
Method {
name: "addButton"
diff --git a/src/imports/winextras/qquickthumbnailtoolbar.cpp b/src/imports/winextras/qquickthumbnailtoolbar.cpp
index 5558cb8..6f242f9 100644
--- a/src/imports/winextras/qquickthumbnailtoolbar.cpp
+++ b/src/imports/winextras/qquickthumbnailtoolbar.cpp
@@ -42,6 +42,7 @@
#include "qquickthumbnailtoolbar_p.h"
#include "qquickthumbnailtoolbutton_p.h"
+#include "qquickiconloader_p.h"
#include <QQuickWindow>
#include <QQmlEngine>
@@ -69,6 +70,10 @@ QT_BEGIN_NAMESPACE
QQuickThumbnailToolBar::QQuickThumbnailToolBar(QQuickItem *parent) :
QQuickItem(parent)
{
+ connect(&m_toolbar, &QWinThumbnailToolBar::iconicThumbnailPixmapRequested,
+ this, &QQuickThumbnailToolBar::iconicThumbnailRequested);
+ connect(&m_toolbar, &QWinThumbnailToolBar::iconicLivePreviewPixmapRequested,
+ this, &QQuickThumbnailToolBar::iconicLivePreviewRequested);
}
QQuickThumbnailToolBar::~QQuickThumbnailToolBar()
@@ -119,6 +124,101 @@ void QQuickThumbnailToolBar::clear()
emit buttonsChanged();
}
+/*!
+ \qmlsignal ThumbnailToolBar::iconicThumbnailRequested()
+
+ This signal is emitted when the operating system requests a new iconic thumbnail pixmap,
+ typically when the thumbnail is shown.
+
+ \since 5.4
+*/
+
+/*!
+ \qmlsignal ThumbnailToolBar::iconicLivePreviewRequested()
+
+ This signal is emitted when the operating system requests a new iconic live preview pixmap,
+ typically when the user ALT-tabs to the application.
+ \since 5.4
+*/
+
+/*!
+ \qmlproperty bool ThumbnailToolBar::iconicNotificationsEnabled
+
+ This property holds whether the signals iconicThumbnailRequested()
+ or iconicLivePreviewRequested() will be emitted.
+ \since 5.4
+ */
+bool QQuickThumbnailToolBar::iconicNotificationsEnabled() const
+{
+ return m_toolbar.iconicPixmapNotificationsEnabled();
+}
+
+void QQuickThumbnailToolBar::setIconicNotificationsEnabled(bool enabled)
+{
+ if (enabled != m_toolbar.iconicPixmapNotificationsEnabled()) {
+ m_toolbar.setIconicPixmapNotificationsEnabled(enabled);
+ emit iconicNotificationsEnabledChanged();
+ }
+}
+
+void QQuickThumbnailToolBar::iconicThumbnailLoaded(const QVariant &value)
+{
+ m_toolbar.setIconicThumbnailPixmap(value.value<QPixmap>());
+}
+
+/*!
+ \qmlproperty url ThumbnailToolBar::iconicThumbnailSource
+
+ The pixmap for use as a thumbnail representation
+ \since 5.4
+ */
+void QQuickThumbnailToolBar::setIconicThumbnailSource(const QUrl &source)
+{
+ if (source == m_iconicThumbnailSource)
+ return;
+
+ if (source.isEmpty()) {
+ m_toolbar.setIconicThumbnailPixmap(QPixmap());
+ m_iconicThumbnailSource = source;
+ emit iconicThumbnailSourceChanged();
+ }
+
+ if (QQuickIconLoader::load(source, qmlEngine(this), QVariant::Pixmap, QSize(),
+ this, &QQuickThumbnailToolBar::iconicThumbnailLoaded) != QQuickIconLoader::LoadError) {
+ m_iconicThumbnailSource = source;
+ emit iconicThumbnailSourceChanged();
+ }
+}
+
+void QQuickThumbnailToolBar::iconicLivePreviewLoaded(const QVariant &value)
+{
+ m_toolbar.setIconicLivePreviewPixmap(value.value<QPixmap>());
+}
+
+/*!
+ \qmlproperty url ThumbnailToolBar::iconicLivePreviewSource
+
+ The pixmap for use as a live (peek) preview when tabbing into the application.
+ \since 5.4
+ */
+void QQuickThumbnailToolBar::setIconicLivePreviewSource(const QUrl &source)
+{
+ if (source == m_iconicLivePreviewSource)
+ return;
+
+ if (source.isEmpty()) {
+ m_toolbar.setIconicLivePreviewPixmap(QPixmap());
+ m_iconicLivePreviewSource = source;
+ emit iconicLivePreviewSourceChanged();
+ }
+
+ if (QQuickIconLoader::load(source, qmlEngine(this), QVariant::Pixmap, QSize(),
+ this, &QQuickThumbnailToolBar::iconicLivePreviewLoaded) != QQuickIconLoader::LoadError) {
+ m_iconicLivePreviewSource = source;
+ emit iconicLivePreviewSourceChanged();
+ }
+}
+
void QQuickThumbnailToolBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
{
if (change == ItemSceneChange)
diff --git a/src/imports/winextras/qquickthumbnailtoolbar_p.h b/src/imports/winextras/qquickthumbnailtoolbar_p.h
index 6a80d80..8599a63 100644
--- a/src/imports/winextras/qquickthumbnailtoolbar_p.h
+++ b/src/imports/winextras/qquickthumbnailtoolbar_p.h
@@ -45,9 +45,12 @@
#include <QQuickItem>
#include <QWinThumbnailToolBar>
+#include <QUrl>
QT_BEGIN_NAMESPACE
+class QVariant;
+
class QQuickThumbnailToolButton;
class QQuickThumbnailToolBar : public QQuickItem
@@ -56,6 +59,9 @@ class QQuickThumbnailToolBar : public QQuickItem
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QQmlListProperty<QObject> data READ data)
Q_PROPERTY(QQmlListProperty<QQuickThumbnailToolButton> buttons READ buttons NOTIFY buttonsChanged)
+ Q_PROPERTY(bool iconicNotificationsEnabled READ iconicNotificationsEnabled WRITE setIconicNotificationsEnabled NOTIFY iconicNotificationsEnabledChanged)
+ Q_PROPERTY(QUrl iconicThumbnailSource READ iconicThumbnailSource WRITE setIconicThumbnailSource NOTIFY iconicThumbnailSourceChanged)
+ Q_PROPERTY(QUrl iconicLivePreviewSource READ iconicLivePreviewSource WRITE setIconicLivePreviewSource NOTIFY iconicLivePreviewSourceChanged)
Q_CLASSINFO("DefaultProperty", "data")
public:
@@ -70,12 +76,28 @@ public:
Q_INVOKABLE void addButton(QQuickThumbnailToolButton *button);
Q_INVOKABLE void removeButton(QQuickThumbnailToolButton *button);
+ bool iconicNotificationsEnabled() const;
+ void setIconicNotificationsEnabled(bool);
+ QUrl iconicThumbnailSource() const { return m_iconicThumbnailSource; }
+ void setIconicThumbnailSource(const QUrl &);
+ QUrl iconicLivePreviewSource() const { return m_iconicLivePreviewSource; }
+ void setIconicLivePreviewSource(const QUrl &);
+
public Q_SLOTS:
void clear();
Q_SIGNALS:
void countChanged();
void buttonsChanged();
+ void iconicNotificationsEnabledChanged();
+ void iconicThumbnailSourceChanged();
+ void iconicThumbnailRequested();
+ void iconicLivePreviewSourceChanged();
+ void iconicLivePreviewRequested();
+
+private Q_SLOTS:
+ void iconicThumbnailLoaded(const QVariant &);
+ void iconicLivePreviewLoaded(const QVariant &);
protected:
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data);
@@ -87,6 +109,8 @@ private:
QWinThumbnailToolBar m_toolbar;
QList<QQuickThumbnailToolButton *> m_buttons;
+ QUrl m_iconicThumbnailSource;
+ QUrl m_iconicLivePreviewSource;
};
QT_END_NAMESPACE
diff --git a/src/winextras/qwinfunctions_p.cpp b/src/winextras/qwinfunctions_p.cpp
index 1bb0d32..452685f 100644
--- a/src/winextras/qwinfunctions_p.cpp
+++ b/src/winextras/qwinfunctions_p.cpp
@@ -66,6 +66,14 @@ void QtDwmApiDll::resolve()
(DwmIsCompositionEnabled) GetProcAddress(dwmapi, "DwmIsCompositionEnabled");
dwmEnableComposition =
(DwmEnableComposition) GetProcAddress(dwmapi, "DwmEnableComposition");
+ if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
+ dwmSetIconicThumbnail =
+ (DwmSetIconicThumbnail) GetProcAddress(dwmapi, "DwmSetIconicThumbnail");
+ dwmSetIconicLivePreviewBitmap =
+ (DwmSetIconicLivePreviewBitmap) GetProcAddress(dwmapi, "DwmSetIconicLivePreviewBitmap");
+ dwmInvalidateIconicBitmaps =
+ (DwmInvalidateIconicBitmaps) GetProcAddress(dwmapi, "DwmInvalidateIconicBitmaps");
+ }
}
}
diff --git a/src/winextras/qwinfunctions_p.h b/src/winextras/qwinfunctions_p.h
index 744eae9..0cbd0ec 100644
--- a/src/winextras/qwinfunctions_p.h
+++ b/src/winextras/qwinfunctions_p.h
@@ -99,11 +99,15 @@ struct QtDwmApiDll
typedef HRESULT (STDAPICALLTYPE *DwmEnableBlurBehindWindow)(HWND, const qt_DWM_BLURBEHIND *);
typedef HRESULT (STDAPICALLTYPE *DwmIsCompositionEnabled)(BOOL *);
typedef HRESULT (STDAPICALLTYPE *DwmEnableComposition)(UINT);
+ typedef HRESULT (STDAPICALLTYPE *DwmSetIconicThumbnail)(HWND, HBITMAP, DWORD);
+ typedef HRESULT (STDAPICALLTYPE *DwmSetIconicLivePreviewBitmap)(HWND, HBITMAP, POINT *, DWORD);
+ typedef HRESULT (STDAPICALLTYPE *DwmInvalidateIconicBitmaps)(HWND);
QtDwmApiDll()
: dwmGetColorizationColor(0), dwmSetWindowAttribute(0), dwmGetWindowAttribute(0)
, dwmExtendFrameIntoClientArea(0), dwmEnableBlurBehindWindow(0)
, dwmIsCompositionEnabled(0), dwmEnableComposition(0)
+ , dwmSetIconicThumbnail(0), dwmSetIconicLivePreviewBitmap(0), dwmInvalidateIconicBitmaps(0)
{}
void init()
@@ -130,6 +134,9 @@ struct QtDwmApiDll
DwmEnableBlurBehindWindow dwmEnableBlurBehindWindow;
DwmIsCompositionEnabled dwmIsCompositionEnabled;
DwmEnableComposition dwmEnableComposition;
+ DwmSetIconicThumbnail dwmSetIconicThumbnail;
+ DwmSetIconicLivePreviewBitmap dwmSetIconicLivePreviewBitmap;
+ DwmInvalidateIconicBitmaps dwmInvalidateIconicBitmaps;
};
struct QtShell32Dll
diff --git a/src/winextras/qwinthumbnailtoolbar.cpp b/src/winextras/qwinthumbnailtoolbar.cpp
index baa4d7c..2748b7a 100644
--- a/src/winextras/qwinthumbnailtoolbar.cpp
+++ b/src/winextras/qwinthumbnailtoolbar.cpp
@@ -45,6 +45,7 @@
#include "qwinthumbnailtoolbutton.h"
#include "qwinthumbnailtoolbutton_p.h"
#include "windowsguidsdefs_p.h"
+#include "qwinfunctions.h"
#include <QWindow>
#include <QCoreApplication>
@@ -53,14 +54,25 @@
#include "qwinevent.h"
#include "qwinfunctions.h"
+#include "qwinfunctions_p.h"
#include "qwineventfilter_p.h"
#ifndef THBN_CLICKED
# define THBN_CLICKED 0x1800
#endif
+#ifndef WM_DWMSENDICONICLIVEPREVIEWBITMAP
+# define WM_DWMSENDICONICLIVEPREVIEWBITMAP 0x0326
+#endif
+
+#ifndef WM_DWMSENDICONICTHUMBNAIL
+# define WM_DWMSENDICONICTHUMBNAIL 0x0323
+#endif
+
QT_BEGIN_NAMESPACE
+enum { dWM_SIT_DISPLAYFRAME = 1 , dWMWA_FORCE_ICONIC_REPRESENTATION = 7, dWMWA_HAS_ICONIC_BITMAP = 10 };
+
static const int windowsLimitedThumbbarSize = 7;
/*!
@@ -117,7 +129,10 @@ void QWinThumbnailToolBar::setWindow(QWindow *window)
if (d->window != window) {
if (d->window) {
d->window->removeEventFilter(d);
- d->clearToolbar();
+ if (d->window->handle()) {
+ d->clearToolbar();
+ setIconicPixmapNotificationsEnabled(false);
+ }
}
d->window = window;
if (d->window) {
@@ -209,6 +224,196 @@ int QWinThumbnailToolBar::count() const
return d->buttonList.size();
}
+void QWinThumbnailToolBarPrivate::updateIconicPixmapsEnabled(bool invalidate)
+{
+ Q_Q(QWinThumbnailToolBar);
+ qtDwmApiDll.init();
+ const HWND hwnd = handle();
+ if (!hwnd) {
+ qWarning() << Q_FUNC_INFO << "invoked with hwnd=0";
+ return;
+ }
+ if (!qtDwmApiDll.dwmInvalidateIconicBitmaps)
+ return;
+ const bool enabled = iconicThumbnail || iconicLivePreview;
+ q->setIconicPixmapNotificationsEnabled(enabled);
+ if (enabled && invalidate) {
+ const HRESULT hr = qtDwmApiDll.dwmInvalidateIconicBitmaps(hwnd);
+ if (FAILED(hr))
+ qWarning() << QWinThumbnailToolBarPrivate::msgComFailed("DwmInvalidateIconicBitmaps", hr);
+ }
+}
+
+/*
+ QWinThumbnailToolBarPrivate::IconicPixmapCache caches a HBITMAP of for one of
+ the iconic thumbnail or live preview pixmaps. When the messages
+ WM_DWMSENDICONICLIVEPREVIEWBITMAP or WM_DWMSENDICONICTHUMBNAIL are received
+ (after setting the DWM window attributes accordingly), the bitmap matching the
+ maximum size is constructed on demand.
+ */
+
+void QWinThumbnailToolBarPrivate::IconicPixmapCache::deleteBitmap()
+{
+ if (m_bitmap) {
+ DeleteObject(m_bitmap);
+ m_size = QSize();
+ m_bitmap = 0;
+ }
+}
+
+bool QWinThumbnailToolBarPrivate::IconicPixmapCache::setPixmap(const QPixmap &pixmap)
+{
+ if (pixmap.cacheKey() == m_pixmap.cacheKey())
+ return false;
+ deleteBitmap();
+ m_pixmap = pixmap;
+ return true;
+}
+
+HBITMAP QWinThumbnailToolBarPrivate::IconicPixmapCache::bitmap(const QSize &maxSize)
+{
+ if (m_pixmap.isNull())
+ return 0;
+ if (m_bitmap && m_size.width() <= maxSize.width() && m_size.height() <= maxSize.height())
+ return m_bitmap;
+ deleteBitmap();
+ QPixmap pixmap = m_pixmap;
+ if (pixmap.width() >= maxSize.width() || pixmap.height() >= maxSize.width())
+ pixmap = pixmap.scaled(maxSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ if (const HBITMAP bitmap = QtWin::toHBITMAP(pixmap, QtWin::HBitmapAlpha)) {
+ m_size = pixmap.size();
+ m_bitmap = bitmap;
+ }
+ return m_bitmap;
+}
+
+/*!
+ \fn QWinThumbnailToolBar::iconicThumbnailPixmapRequested()
+
+ This signal is emitted when the operating system requests a new iconic thumbnail pixmap,
+ typically when the thumbnail is shown.
+
+ \since 5.4
+ \sa iconicThumbnailPixmap
+*/
+
+/*!
+ \fn QWinThumbnailToolBar::iconicLivePreviewPixmapRequested()
+
+ This signal is emitted when the operating system requests a new iconic live preview pixmap,
+ typically when the user ALT-tabs to the application.
+
+ \since 5.4
+ \sa iconicLivePreviewPixmap
+*/
+
+/*!
+ \property QWinThumbnailToolBar::iconicPixmapNotificationsEnabled
+ \brief whether signals iconicThumbnailPixmapRequested() and iconicLivePreviewPixmapRequested()
+ will be emitted
+
+ \since 5.4
+ \sa QWinThumbnailToolBar::iconicThumbnailPixmap, QWinThumbnailToolBar::iconicLivePreviewPixmap
+ */
+
+bool QWinThumbnailToolBar::iconicPixmapNotificationsEnabled() const
+{
+ Q_D(const QWinThumbnailToolBar);
+ const HWND hwnd = d->handle();
+ if (!hwnd || !qtDwmApiDll.dwmGetWindowAttribute)
+ return false;
+ qtDwmApiDll.init();
+ return qtDwmApiDll.dwmGetWindowAttribute && hwnd
+ && QtDwmApiDll::booleanWindowAttribute(hwnd, dWMWA_FORCE_ICONIC_REPRESENTATION);
+}
+
+void QWinThumbnailToolBar::setIconicPixmapNotificationsEnabled(bool enabled)
+{
+ Q_D(const QWinThumbnailToolBar);
+ const HWND hwnd = d->handle();
+ if (!hwnd) {
+ qWarning() << Q_FUNC_INFO << "invoked with hwnd=0";
+ return;
+ }
+ qtDwmApiDll.init();
+ if (!qtDwmApiDll.dwmSetWindowAttribute || iconicPixmapNotificationsEnabled() == enabled)
+ return;
+ QtDwmApiDll::setBooleanWindowAttribute(hwnd, dWMWA_FORCE_ICONIC_REPRESENTATION, enabled);
+ QtDwmApiDll::setBooleanWindowAttribute(hwnd, dWMWA_HAS_ICONIC_BITMAP, enabled);
+}
+
+/*!
+ \property QWinThumbnailToolBar::iconicThumbnailPixmap
+ \brief the pixmap for use as a thumbnail representation
+
+ \since 5.4
+ \sa QWinThumbnailToolBar::iconicPixmapNotificationsEnabled
+ */
+
+void QWinThumbnailToolBar::setIconicThumbnailPixmap(const QPixmap &pixmap)
+{
+ Q_D(QWinThumbnailToolBar);
+ const bool changed = d->iconicThumbnail.setPixmap(pixmap);
+ if (d->hasHandle()) // Potentially 0 when invoked from QML loading, see _q_updateToolbar()
+ d->updateIconicPixmapsEnabled(changed && !d->withinIconicThumbnailRequest);
+}
+
+QPixmap QWinThumbnailToolBar::iconicThumbnailPixmap() const
+{
+ Q_D(const QWinThumbnailToolBar);
+ return d->iconicThumbnail.pixmap();
+}
+
+/*!
+ \property QWinThumbnailToolBar::iconicLivePreviewPixmap
+ \brief the pixmap for use as a live (peek) preview when tabbing into the application
+
+ \since 5.4
+ */
+
+void QWinThumbnailToolBar::setIconicLivePreviewPixmap(const QPixmap &pixmap)
+{
+ Q_D(QWinThumbnailToolBar);
+ const bool changed = d->iconicLivePreview.setPixmap(pixmap);
+ if (d->hasHandle()) // Potentially 0 when invoked from QML loading, see _q_updateToolbar()
+ d->updateIconicPixmapsEnabled(changed && !d->withinIconicLivePreviewRequest);
+}
+
+QPixmap QWinThumbnailToolBar::iconicLivePreviewPixmap() const
+{
+ Q_D(const QWinThumbnailToolBar);
+ return d->iconicLivePreview.pixmap();
+}
+
+inline void QWinThumbnailToolBarPrivate::updateIconicThumbnail(const MSG *message)
+{
+ qtDwmApiDll.init();
+ if (!qtDwmApiDll.dwmSetIconicThumbnail || !iconicThumbnail)
+ return;
+ const QSize maxSize(HIWORD(message->lParam), LOWORD(message->lParam));
+ if (const HBITMAP bitmap = iconicThumbnail.bitmap(maxSize)) {
+ const HRESULT hr = qtDwmApiDll.dwmSetIconicThumbnail(message->hwnd, bitmap, dWM_SIT_DISPLAYFRAME);
+ if (FAILED(hr))
+ qWarning() << QWinThumbnailToolBarPrivate::msgComFailed("DwmSetIconicThumbnail", hr);
+ }
+}
+
+inline void QWinThumbnailToolBarPrivate::updateIconicLivePreview(const MSG *message)
+{
+ qtDwmApiDll.init();
+ if (!qtDwmApiDll.dwmSetIconicLivePreviewBitmap || !iconicLivePreview)
+ return;
+ RECT rect;
+ GetClientRect(message->hwnd, &rect);
+ const QSize maxSize(rect.right, rect.bottom);
+ POINT offset = {0, 0};
+ if (const HBITMAP bitmap = iconicLivePreview.bitmap(maxSize)) {
+ const HRESULT hr = qtDwmApiDll.dwmSetIconicLivePreviewBitmap(message->hwnd, bitmap, &offset, dWM_SIT_DISPLAYFRAME);
+ if (FAILED(hr))
+ qWarning() << QWinThumbnailToolBarPrivate::msgComFailed("DwmSetIconicLivePreviewBitmap", hr);
+ }
+}
+
/*!
Removes all buttons from the thumbnail toolbar.
*/
@@ -239,7 +444,8 @@ static inline ITaskbarList4 *createTaskbarList()
}
QWinThumbnailToolBarPrivate::QWinThumbnailToolBarPrivate() :
- QObject(0), updateScheduled(false), window(0), pTbList(createTaskbarList()), q_ptr(0)
+ QObject(0), updateScheduled(false), window(0), pTbList(createTaskbarList()), q_ptr(0),
+ withinIconicThumbnailRequest(false), withinIconicLivePreviewRequest(false)
{
buttonList.reserve(windowsLimitedThumbbarSize);
QCoreApplication::instance()->installNativeEventFilter(this);
@@ -252,6 +458,16 @@ QWinThumbnailToolBarPrivate::~QWinThumbnailToolBarPrivate()
QCoreApplication::instance()->removeNativeEventFilter(this);
}
+inline bool QWinThumbnailToolBarPrivate::hasHandle() const
+{
+ return window && window->handle();
+}
+
+inline HWND QWinThumbnailToolBarPrivate::handle() const
+{
+ return hasHandle() ? reinterpret_cast<HWND>(window->winId()) : HWND(0);
+}
+
void QWinThumbnailToolBarPrivate::initToolbar()
{
#if !defined(_MSC_VER) || _MSC_VER >= 1600
@@ -259,7 +475,7 @@ void QWinThumbnailToolBarPrivate::initToolbar()
return;
THUMBBUTTON buttons[windowsLimitedThumbbarSize];
initButtons(buttons);
- HRESULT hresult = pTbList->ThumbBarAddButtons(reinterpret_cast<HWND>(window->winId()), windowsLimitedThumbbarSize, buttons);
+ HRESULT hresult = pTbList->ThumbBarAddButtons(handle(), windowsLimitedThumbbarSize, buttons);
if (FAILED(hresult))
qWarning() << msgComFailed("ThumbBarAddButtons", hresult);
#else
@@ -274,7 +490,7 @@ void QWinThumbnailToolBarPrivate::clearToolbar()
return;
THUMBBUTTON buttons[windowsLimitedThumbbarSize];
initButtons(buttons);
- HRESULT hresult = pTbList->ThumbBarUpdateButtons(reinterpret_cast<HWND>(window->winId()), windowsLimitedThumbbarSize, buttons);
+ HRESULT hresult = pTbList->ThumbBarUpdateButtons(handle(), windowsLimitedThumbbarSize, buttons);
if (FAILED(hresult))
qWarning() << msgComFailed("ThumbBarUpdateButtons", hresult);
}
@@ -301,9 +517,10 @@ void QWinThumbnailToolBarPrivate::_q_updateToolbar()
buttons[i].szTip[button->toolTip().left(sizeof(buttons[i].szTip)/sizeof(buttons[i].szTip[0]) - 1).toWCharArray(buttons[i].szTip)] = 0;
}
}
- HRESULT hresult = pTbList->ThumbBarUpdateButtons(reinterpret_cast<HWND>(window->winId()), windowsLimitedThumbbarSize, buttons);
+ HRESULT hresult = pTbList->ThumbBarUpdateButtons(handle(), windowsLimitedThumbbarSize, buttons);
if (FAILED(hresult))
qWarning() << msgComFailed("ThumbBarUpdateButtons", hresult);
+ updateIconicPixmapsEnabled(false);
freeButtonResources(buttons);
}
@@ -327,13 +544,30 @@ bool QWinThumbnailToolBarPrivate::eventFilter(QObject *object, QEvent *event)
bool QWinThumbnailToolBarPrivate::nativeEventFilter(const QByteArray &, void *message, long *result)
{
- MSG *msg = static_cast<MSG *>(message);
- if (window && msg->message == WM_COMMAND && HIWORD(msg->wParam) == THBN_CLICKED && msg->hwnd == reinterpret_cast<HWND>(window->winId())) {
- int buttonId = LOWORD(msg->wParam);
- buttonId = buttonId - (windowsLimitedThumbbarSize - qMin(windowsLimitedThumbbarSize, buttonList.size()));
- buttonList.at(buttonId)->click();
- if (result)
- *result = 0;
+ const MSG *msg = static_cast<const MSG *>(message);
+ if (handle() != msg->hwnd)
+ return false;
+ switch (msg->message) {
+ case WM_COMMAND:
+ if (HIWORD(msg->wParam) == THBN_CLICKED) {
+ const int buttonId = LOWORD(msg->wParam) - (windowsLimitedThumbbarSize - qMin(windowsLimitedThumbbarSize, buttonList.size()));
+ buttonList.at(buttonId)->click();
+ if (result)
+ *result = 0;
+ return true;
+ }
+ break;
+ case WM_DWMSENDICONICTHUMBNAIL:
+ withinIconicThumbnailRequest = true;
+ emit q_func()->iconicThumbnailPixmapRequested();
+ withinIconicThumbnailRequest = false;
+ updateIconicThumbnail(msg);
+ return true;
+ case WM_DWMSENDICONICLIVEPREVIEWBITMAP:
+ withinIconicLivePreviewRequest = true;
+ emit q_func()->iconicLivePreviewPixmapRequested();
+ withinIconicLivePreviewRequest = false;
+ updateIconicLivePreview(msg);
return true;
}
return false;
diff --git a/src/winextras/qwinthumbnailtoolbar.h b/src/winextras/qwinthumbnailtoolbar.h
index c048298..09dfc34 100644
--- a/src/winextras/qwinthumbnailtoolbar.h
+++ b/src/winextras/qwinthumbnailtoolbar.h
@@ -48,6 +48,7 @@
QT_BEGIN_NAMESPACE
+class QPixmap;
class QWindow;
class QWinThumbnailToolButton;
class QWinThumbnailToolBarPrivate;
@@ -57,6 +58,9 @@ class Q_WINEXTRAS_EXPORT QWinThumbnailToolBar : public QObject
Q_OBJECT
Q_PROPERTY(int count READ count STORED false)
Q_PROPERTY(QWindow *window READ window WRITE setWindow)
+ Q_PROPERTY(bool iconicPixmapNotificationsEnabled READ iconicPixmapNotificationsEnabled WRITE setIconicPixmapNotificationsEnabled)
+ Q_PROPERTY(QPixmap iconicThumbnailPixmap READ iconicThumbnailPixmap WRITE setIconicThumbnailPixmap)
+ Q_PROPERTY(QPixmap iconicLivePreviewPixmap READ iconicLivePreviewPixmap WRITE setIconicLivePreviewPixmap)
public:
explicit QWinThumbnailToolBar(QObject *parent = 0);
@@ -71,8 +75,20 @@ public:
QList<QWinThumbnailToolButton *> buttons() const;
int count() const;
+ bool iconicPixmapNotificationsEnabled() const;
+ void setIconicPixmapNotificationsEnabled(bool enabled);
+
+ QPixmap iconicThumbnailPixmap() const;
+ QPixmap iconicLivePreviewPixmap() const;
+
public Q_SLOTS:
void clear();
+ void setIconicThumbnailPixmap(const QPixmap &);
+ void setIconicLivePreviewPixmap(const QPixmap &);
+
+Q_SIGNALS:
+ void iconicThumbnailPixmapRequested();
+ void iconicLivePreviewPixmapRequested();
private:
Q_DISABLE_COPY(QWinThumbnailToolBar)
diff --git a/src/winextras/qwinthumbnailtoolbar_p.h b/src/winextras/qwinthumbnailtoolbar_p.h
index b799d7c..b7d8eff 100644
--- a/src/winextras/qwinthumbnailtoolbar_p.h
+++ b/src/winextras/qwinthumbnailtoolbar_p.h
@@ -47,6 +47,7 @@
#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtGui/QIcon>
+#include <QtGui/QPixmap>
#include <QtCore/QAbstractNativeEventFilter>
#include "winshobjidl_p.h"
@@ -56,6 +57,27 @@ QT_BEGIN_NAMESPACE
class QWinThumbnailToolBarPrivate : public QObject, QAbstractNativeEventFilter
{
public:
+ class IconicPixmapCache
+ {
+ public:
+ IconicPixmapCache() : m_bitmap(0) {}
+ ~IconicPixmapCache() { deleteBitmap(); }
+
+ operator bool() const { return !m_pixmap.isNull(); }
+
+ QPixmap pixmap() const { return m_pixmap; }
+ bool setPixmap(const QPixmap &p);
+
+ HBITMAP bitmap(const QSize &maxSize);
+
+ private:
+ void deleteBitmap();
+
+ QPixmap m_pixmap;
+ QSize m_size;
+ HBITMAP m_bitmap;
+ };
+
QWinThumbnailToolBarPrivate();
~QWinThumbnailToolBarPrivate();
void initToolbar();
@@ -77,9 +99,20 @@ public:
QWindow *window;
ITaskbarList4 * const pTbList;
+ IconicPixmapCache iconicThumbnail;
+ IconicPixmapCache iconicLivePreview;
+
private:
+ bool hasHandle() const;
+ HWND handle() const;
+ void updateIconicPixmapsEnabled(bool invalidate);
+ void updateIconicThumbnail(const MSG *message);
+ void updateIconicLivePreview(const MSG *message);
+
QWinThumbnailToolBar *q_ptr;
Q_DECLARE_PUBLIC(QWinThumbnailToolBar)
+ bool withinIconicThumbnailRequest;
+ bool withinIconicLivePreviewRequest;
};
QT_END_NAMESPACE
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 14a08ef..4ff3a0b 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -2,5 +2,6 @@ TEMPLATE = subdirs
qtHaveModule(widgets) {
SUBDIRS += \
dwmfeatures \
- jumplist
+ jumplist \
+ thumbnail
}
diff --git a/tests/manual/quickthumbnail/main.qml b/tests/manual/quickthumbnail/main.qml
index b422564..a28744d 100644
--- a/tests/manual/quickthumbnail/main.qml
+++ b/tests/manual/quickthumbnail/main.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
@@ -55,6 +55,8 @@ ApplicationWindow {
ThumbnailToolBar {
id: toolBar
+ iconicThumbnailSource : "qrc:/qt-project.org/qmessagebox/images/qtlogo-64.png"
+ iconicLivePreviewSource : "qrc:/qt-project.org/qmessagebox/images/qtlogo-64.png"
ThumbnailToolButton {
tooltip: "Button #1"
diff --git a/tests/manual/thumbnail/main.cpp b/tests/manual/thumbnail/main.cpp
new file mode 100644
index 0000000..bfce7bb
--- /dev/null
+++ b/tests/manual/thumbnail/main.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+ ** Contact: http://www.qt-project.org/legal
+ **
+ ** This file is part of the test suite of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and Digia. For licensing terms and
+ ** conditions see http://qt.digia.com/licensing. For further information
+ ** use the contact form at http://qt.digia.com/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 as published by the Free Software
+ ** Foundation and appearing in the file LICENSE.LGPL included in the
+ ** packaging of this file. Please review the following information to
+ ** ensure the GNU Lesser General Public License version 2.1 requirements
+ ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Digia gives you certain additional
+ ** rights. These rights are described in the Digia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU
+ ** General Public License version 3.0 as published by the Free Software
+ ** Foundation and appearing in the file LICENSE.GPL included in the
+ ** packaging of this file. Please review the following information to
+ ** ensure the GNU General Public License version 3.0 requirements will be
+ ** met: http://www.gnu.org/copyleft/gpl.html.
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include <QApplication>
+#include <QMainWindow>
+#include <QPlainTextEdit>
+#include <QMenuBar>
+#include <QMenu>
+#include <QAction>
+#include <QStyle>
+#include <QScreen>
+#include <QShortcut>
+#include <QPixmap>
+#include <QPainter>
+#include <QFontMetrics>
+#include <QtWinExtras>
+#include <QDebug>
+
+static QPixmap drawColoredPixmap(const QSize &size, const QColor &color,
+ const QString &text = QString())
+{
+ QPixmap result(size);
+ result.fill(color);
+ QPainter painter(&result);
+ painter.drawRect(QRect(QPoint(0, 0), size - QSize(1, 1)));
+ if (!text.isEmpty()) {
+ QFont font = painter.font();
+ font.setPointSize(20);
+ painter.setFont(font);
+ const QFontMetrics fontMetrics(font);
+ QRect boundingRect(fontMetrics.boundingRect(text));
+ const int x = (size.width() - boundingRect.width()) / 2;
+ const int y = size.height() - (size.height() - boundingRect.height()) / 2;
+ painter.drawText(x, y, text);
+ }
+ return result;
+}
+
+class MainWindow : public QMainWindow {
+ Q_OBJECT
+public:
+ MainWindow();
+ void initThumbnailToolBar();
+
+public slots:
+ void testButtonClicked();
+ void updateIconicThumbnailPixmap();
+ void updateIconicLivePreviewPixmap();
+ void logText(const QString &text);
+
+private:
+ QWinThumbnailToolBar *m_thumbnailToolBar;
+ QPlainTextEdit *m_logEdit;
+};
+
+MainWindow::MainWindow()
+ : m_thumbnailToolBar(new QWinThumbnailToolBar(this))
+ , m_logEdit(new QPlainTextEdit)
+{
+ setMinimumWidth(400);
+ setWindowTitle(QStringLiteral("QWinThumbnailToolBar ") + QLatin1String(QT_VERSION_STR));
+ QMenu *fileMenu = menuBar()->addMenu("&File");
+ QAction *quitAction = fileMenu->addAction("&Quit");
+ quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
+ connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
+ setCentralWidget(m_logEdit);
+}
+
+void MainWindow::initThumbnailToolBar()
+{
+ m_thumbnailToolBar->setWindow(windowHandle());
+ QWinThumbnailToolButton *testButton = new QWinThumbnailToolButton(m_thumbnailToolBar);
+ testButton->setToolTip("Test");
+ testButton->setIcon(style()->standardIcon(QStyle::SP_ComputerIcon));
+ connect(testButton, SIGNAL(clicked()), this, SLOT(testButtonClicked()));
+ m_thumbnailToolBar->addButton(testButton);
+ m_thumbnailToolBar->setIconicPixmapNotificationsEnabled(true);
+ connect(m_thumbnailToolBar, SIGNAL(iconicLivePreviewPixmapRequested()),
+ this, SLOT(updateIconicLivePreviewPixmap()));
+ connect(m_thumbnailToolBar, SIGNAL(iconicThumbnailPixmapRequested()),
+ this, SLOT(updateIconicThumbnailPixmap()));
+}
+
+void MainWindow::logText(const QString &text)
+{
+ m_logEdit->appendPlainText(text);
+ qDebug("%s", qPrintable(text));
+}
+
+void MainWindow::testButtonClicked()
+{
+ static int n = 1;
+ logText(QStringLiteral("Clicked #") + QString::number(n++));
+}
+
+void MainWindow::updateIconicThumbnailPixmap()
+{
+ static int n = 1;
+ const QString number = QString::number(n++);
+ logText(QLatin1String(__FUNCTION__) + QLatin1Char(' ') + number);
+ const QPixmap pixmap =
+ drawColoredPixmap(QSize(200, 50), Qt::yellow, QStringLiteral("ITP ") + number);
+ m_thumbnailToolBar->setIconicThumbnailPixmap(pixmap);
+}
+
+void MainWindow::updateIconicLivePreviewPixmap()
+{
+ static int n = 1;
+ const QString number = QString::number(n++);
+ logText(QLatin1String(__FUNCTION__) + QLatin1Char(' ') + number);
+ const QPixmap pixmap =
+ drawColoredPixmap(QSize(200, 50), Qt::red, QStringLiteral("ILP ") + number);
+ m_thumbnailToolBar->setIconicLivePreviewPixmap(pixmap);
+}
+
+int main(int argc, char* argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.move(QGuiApplication::primaryScreen()->availableGeometry().center()
+ - QPoint(w.width() / 2, w.height() / 2));
+ w.show();
+ w.initThumbnailToolBar();
+ return a.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/thumbnail/thumbnail.pro b/tests/manual/thumbnail/thumbnail.pro
new file mode 100644
index 0000000..2259b9b
--- /dev/null
+++ b/tests/manual/thumbnail/thumbnail.pro
@@ -0,0 +1,4 @@
+TEMPLATE = app
+QT += widgets winextras
+CONFIG -= app_bundle
+SOURCES += main.cpp