diff options
-rw-r--r-- | src/imports/winextras/plugins.qmltypes | 3 | ||||
-rw-r--r-- | src/imports/winextras/qquickthumbnailtoolbar.cpp | 100 | ||||
-rw-r--r-- | src/imports/winextras/qquickthumbnailtoolbar_p.h | 24 | ||||
-rw-r--r-- | src/winextras/qwinfunctions_p.cpp | 8 | ||||
-rw-r--r-- | src/winextras/qwinfunctions_p.h | 7 | ||||
-rw-r--r-- | src/winextras/qwinthumbnailtoolbar.cpp | 258 | ||||
-rw-r--r-- | src/winextras/qwinthumbnailtoolbar.h | 16 | ||||
-rw-r--r-- | src/winextras/qwinthumbnailtoolbar_p.h | 33 | ||||
-rw-r--r-- | tests/manual/manual.pro | 3 | ||||
-rw-r--r-- | tests/manual/quickthumbnail/main.qml | 4 | ||||
-rw-r--r-- | tests/manual/thumbnail/main.cpp | 165 | ||||
-rw-r--r-- | tests/manual/thumbnail/thumbnail.pro | 4 |
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 |