| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWidgets module 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 The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| #include "qwindowsxpstyle_p.h" |
| #include "qwindowsxpstyle_p_p.h" |
| |
| #include <private/qobject_p.h> |
| #include <private/qpaintengine_raster_p.h> |
| #include <private/qapplication_p.h> |
| #include <qpa/qplatformnativeinterface.h> |
| #include <private/qstylehelper_p.h> |
| #include <private/qwidget_p.h> |
| #include <qpainter.h> |
| #include <qpaintengine.h> |
| #include <qwidget.h> |
| #include <qbackingstore.h> |
| #include <qapplication.h> |
| #include <qpixmapcache.h> |
| #include <private/qapplication_p.h> |
| #include <qpa/qplatformnativeinterface.h> |
| |
| #if QT_CONFIG(toolbutton) |
| #include <qtoolbutton.h> |
| #endif |
| #if QT_CONFIG(tabbar) |
| #include <qtabbar.h> |
| #endif |
| #if QT_CONFIG(combobox) |
| #include <qcombobox.h> |
| #endif |
| #if QT_CONFIG(scrollbar) |
| #include <qscrollbar.h> |
| #endif |
| #include <qheaderview.h> |
| #if QT_CONFIG(spinbox) |
| #include <qspinbox.h> |
| #endif |
| #if QT_CONFIG(listview) |
| #include <qlistview.h> |
| #endif |
| #if QT_CONFIG(stackedwidget) |
| #include <qstackedwidget.h> |
| #endif |
| #if QT_CONFIG(pushbutton) |
| #include <qpushbutton.h> |
| #endif |
| #if QT_CONFIG(toolbar) |
| #include <qtoolbar.h> |
| #endif |
| #include <qlabel.h> |
| #include <qvarlengtharray.h> |
| #include <qdebug.h> |
| |
| #include <algorithm> |
| |
| QT_BEGIN_NAMESPACE |
| |
| // General const values |
| static const int windowsItemFrame = 2; // menu item frame width |
| static const int windowsItemHMargin = 3; // menu item hor text margin |
| static const int windowsItemVMargin = 0; // menu item ver text margin |
| static const int windowsArrowHMargin = 6; // arrow horizontal margin |
| static const int windowsRightBorder = 12; // right border on windows |
| |
| // External function calls |
| extern Q_WIDGETS_EXPORT HDC qt_win_display_dc(); |
| extern QRegion qt_region_from_HRGN(HRGN rgn); |
| |
| // Theme names matching the QWindowsXPStylePrivate::Theme enumeration. |
| static const wchar_t *themeNames[QWindowsXPStylePrivate::NThemes] = |
| { |
| L"BUTTON", L"COMBOBOX", L"EDIT", L"HEADER", L"LISTVIEW", |
| L"MENU", L"PROGRESS", L"REBAR", L"SCROLLBAR", L"SPIN", |
| L"TAB", L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP", L"TRACKBAR", |
| L"TREEVIEW", L"WINDOW", L"STATUS", L"TREEVIEW" |
| }; |
| |
| static inline QBackingStore *backingStoreForWidget(const QWidget *widget) |
| { |
| if (QBackingStore *backingStore = widget->backingStore()) |
| return backingStore; |
| if (const QWidget *topLevel = widget->nativeParentWidget()) |
| if (QBackingStore *topLevelBackingStore = topLevel->backingStore()) |
| return topLevelBackingStore; |
| return nullptr; |
| } |
| |
| static inline HDC hdcForWidgetBackingStore(const QWidget *widget) |
| { |
| if (QBackingStore *backingStore = backingStoreForWidget(widget)) { |
| QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); |
| if (nativeInterface) |
| return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore)); |
| } |
| return nullptr; |
| } |
| |
| // Theme data helper ------------------------------------------------------------------------------ |
| /* \internal |
| Returns \c true if the themedata is valid for use. |
| */ |
| bool XPThemeData::isValid() |
| { |
| return QWindowsXPStylePrivate::useXP() && theme >= 0 && handle(); |
| } |
| |
| |
| /* \internal |
| Returns the theme engine handle to the specific class. |
| If the handle hasn't been opened before, it opens the data, and |
| adds it to a static map, for caching. |
| */ |
| HTHEME XPThemeData::handle() |
| { |
| if (!QWindowsXPStylePrivate::useXP()) |
| return nullptr; |
| |
| if (!htheme) |
| htheme = QWindowsXPStylePrivate::createTheme(theme, QWindowsXPStylePrivate::winId(widget)); |
| return htheme; |
| } |
| |
| /* \internal |
| Converts a QRect to the native RECT structure. |
| */ |
| RECT XPThemeData::toRECT(const QRect &qr) |
| { |
| RECT r; |
| r.left = qr.x(); |
| r.right = qr.x() + qr.width(); |
| r.top = qr.y(); |
| r.bottom = qr.y() + qr.height(); |
| return r; |
| } |
| |
| /* \internal |
| Returns the native region of a part, if the part is considered |
| transparent. The region is scaled to the parts size (rect). |
| */ |
| HRGN XPThemeData::mask(QWidget *widget) |
| { |
| if (!IsThemeBackgroundPartiallyTransparent(handle(), partId, stateId)) |
| return nullptr; |
| |
| HRGN hrgn; |
| HDC dc = nullptr; |
| if (widget) |
| dc = hdcForWidgetBackingStore(widget); |
| RECT nativeRect = toRECT(rect); |
| GetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn); |
| return hrgn; |
| } |
| |
| // QWindowsXPStylePrivate ------------------------------------------------------------------------- |
| // Static initializations |
| HWND QWindowsXPStylePrivate::m_vistaTreeViewHelper = nullptr; |
| HTHEME QWindowsXPStylePrivate::m_themes[NThemes]; |
| bool QWindowsXPStylePrivate::use_xp = false; |
| QBasicAtomicInt QWindowsXPStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting |
| |
| static void qt_add_rect(HRGN &winRegion, QRect r) |
| { |
| HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height()); |
| if (rgn) { |
| HRGN dest = CreateRectRgn(0,0,0,0); |
| int result = CombineRgn(dest, winRegion, rgn, RGN_OR); |
| if (result) { |
| DeleteObject(winRegion); |
| winRegion = dest; |
| } |
| DeleteObject(rgn); |
| } |
| } |
| |
| static HRGN qt_hrgn_from_qregion(const QRegion ®ion) |
| { |
| HRGN hRegion = CreateRectRgn(0,0,0,0); |
| if (region.rectCount() == 1) { |
| qt_add_rect(hRegion, region.boundingRect()); |
| return hRegion; |
| } |
| for (const QRect &rect : region) |
| qt_add_rect(hRegion, rect); |
| return hRegion; |
| } |
| |
| /* \internal |
| Checks if the theme engine can/should be used, or if we should |
| fall back to Windows style. |
| */ |
| bool QWindowsXPStylePrivate::useXP(bool update) |
| { |
| if (!update) |
| return use_xp; |
| return use_xp = IsThemeActive() && (IsAppThemed() || !QCoreApplication::instance()); |
| } |
| |
| /* \internal |
| Handles refcounting, and queries the theme engine for usage. |
| */ |
| void QWindowsXPStylePrivate::init(bool force) |
| { |
| if (ref.ref() && !force) |
| return; |
| if (!force) // -1 based atomic refcounting |
| ref.ref(); |
| |
| useXP(true); |
| std::fill(m_themes, m_themes + NThemes, nullptr); |
| } |
| |
| /* \internal |
| Cleans up all static data. |
| */ |
| void QWindowsXPStylePrivate::cleanup(bool force) |
| { |
| if(bufferBitmap) { |
| if (bufferDC && nullBitmap) |
| SelectObject(bufferDC, nullBitmap); |
| DeleteObject(bufferBitmap); |
| bufferBitmap = nullptr; |
| } |
| |
| if(bufferDC) |
| DeleteDC(bufferDC); |
| bufferDC = nullptr; |
| |
| if (ref.deref() && !force) |
| return; |
| if (!force) // -1 based atomic refcounting |
| ref.deref(); |
| |
| use_xp = false; |
| cleanupHandleMap(); |
| } |
| |
| /* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch), |
| * we need to set the windows "explorer" theme explicitly on a native |
| * window and open the "TREEVIEW" theme handle passing its window handle |
| * in order to get Vista-style item view themes (particulary drawBackground() |
| * for selected items needs this). |
| * We invoke a service of the native Windows interface to create |
| * a non-visible window handle, open the theme on it and insert it into |
| * the cache so that it is found by XPThemeData::handle() first. |
| */ |
| |
| static inline HWND createTreeViewHelperWindow() |
| { |
| if (QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface()) { |
| void *hwnd = nullptr; |
| void *wndProc = reinterpret_cast<void *>(DefWindowProc); |
| if (QMetaObject::invokeMethod(ni, "createMessageWindow", Qt::DirectConnection, |
| Q_RETURN_ARG(void*, hwnd), |
| Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindowClass")), |
| Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindow")), |
| Q_ARG(void*, wndProc)) && hwnd) { |
| return reinterpret_cast<HWND>(hwnd); |
| } |
| } |
| return nullptr; |
| } |
| |
| bool QWindowsXPStylePrivate::initVistaTreeViewTheming() |
| { |
| if (m_vistaTreeViewHelper) |
| return true; |
| |
| m_vistaTreeViewHelper = createTreeViewHelperWindow(); |
| if (!m_vistaTreeViewHelper) { |
| qWarning("Unable to create the treeview helper window."); |
| return false; |
| } |
| if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", nullptr))) { |
| qErrnoWarning("SetWindowTheme() failed."); |
| cleanupVistaTreeViewTheming(); |
| return false; |
| } |
| return true; |
| } |
| |
| void QWindowsXPStylePrivate::cleanupVistaTreeViewTheming() |
| { |
| if (m_vistaTreeViewHelper) { |
| DestroyWindow(m_vistaTreeViewHelper); |
| m_vistaTreeViewHelper = nullptr; |
| } |
| } |
| |
| /* \internal |
| Closes all open theme data handles to ensure that we don't leak |
| resources, and that we don't refere to old handles when for |
| example the user changes the theme style. |
| */ |
| void QWindowsXPStylePrivate::cleanupHandleMap() |
| { |
| for (auto &theme : m_themes) { |
| if (theme) { |
| CloseThemeData(theme); |
| theme = nullptr; |
| } |
| } |
| QWindowsXPStylePrivate::cleanupVistaTreeViewTheming(); |
| } |
| |
| HTHEME QWindowsXPStylePrivate::createTheme(int theme, HWND hwnd) |
| { |
| if (Q_UNLIKELY(theme < 0 || theme >= NThemes || !hwnd)) { |
| qWarning("Invalid parameters #%d, %p", theme, hwnd); |
| return nullptr; |
| } |
| if (!m_themes[theme]) { |
| const wchar_t *name = themeNames[theme]; |
| if (theme == VistaTreeViewTheme && QWindowsXPStylePrivate::initVistaTreeViewTheming()) |
| hwnd = QWindowsXPStylePrivate::m_vistaTreeViewHelper; |
| m_themes[theme] = OpenThemeData(hwnd, name); |
| if (Q_UNLIKELY(!m_themes[theme])) |
| qErrnoWarning("OpenThemeData() failed for theme %d (%s).", |
| theme, qPrintable(themeName(theme))); |
| } |
| return m_themes[theme]; |
| } |
| |
| QString QWindowsXPStylePrivate::themeName(int theme) |
| { |
| return theme >= 0 && theme < NThemes ? |
| QString::fromWCharArray(themeNames[theme]) : |
| QString(); |
| } |
| |
| bool QWindowsXPStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget) |
| { |
| if (!widget) |
| return false; |
| const QWidget *parent1 = widget->parentWidget(); |
| // Exlude dialogs or other toplevels parented on item views. |
| if (!parent1 || parent1->isWindow()) |
| return false; |
| const QWidget *parent2 = parent1->parentWidget(); |
| return parent2 && widget->inherits("QLineEdit") |
| && parent2->inherits("QAbstractItemView"); |
| } |
| |
| // Returns whether base color is set for this widget |
| bool QWindowsXPStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget) |
| { |
| uint resolveMask = option->palette.resolve(); |
| if (widget) { |
| // Since spin box includes a line edit we need to resolve the palette mask also from |
| // the parent, as while the color is always correct on the palette supplied by panel, |
| // the mask can still be empty. If either mask specifies custom base color, use that. |
| #if QT_CONFIG(spinbox) |
| if (const QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget())) |
| resolveMask |= spinbox->palette().resolve(); |
| #endif // QT_CONFIG(spinbox) |
| } |
| return (resolveMask & (1 << QPalette::Base)) != 0; |
| } |
| |
| /*! \internal |
| This function will always return a valid window handle, and might |
| create a limbo widget to do so. |
| We often need a window handle to for example open theme data, so |
| this function ensures that we get one. |
| */ |
| HWND QWindowsXPStylePrivate::winId(const QWidget *widget) |
| { |
| if (widget) |
| if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast<QWidget *>(widget))) |
| return hwnd; |
| |
| // Find top level with native window (there might be dialogs that do not have one). |
| const auto allWindows = QGuiApplication::allWindows(); |
| for (const QWindow *window : allWindows) { |
| if (window->isTopLevel() && window->type() != Qt::Desktop && window->handle() != nullptr) |
| return reinterpret_cast<HWND>(window->winId()); |
| } |
| |
| return GetDesktopWindow(); |
| } |
| |
| /*! \internal |
| Returns a native buffer (DIB section) of at least the size of |
| ( \a x , \a y ). The buffer has a 32 bit depth, to not lose |
| the alpha values on proper alpha-pixmaps. |
| */ |
| HBITMAP QWindowsXPStylePrivate::buffer(int w, int h) |
| { |
| // If we already have a HBITMAP which is of adequate size, just return that |
| if (bufferBitmap) { |
| if (bufferW >= w && bufferH >= h) |
| return bufferBitmap; |
| // Not big enough, discard the old one |
| if (bufferDC && nullBitmap) |
| SelectObject(bufferDC, nullBitmap); |
| DeleteObject(bufferBitmap); |
| bufferBitmap = nullptr; |
| } |
| |
| w = qMax(bufferW, w); |
| h = qMax(bufferH, h); |
| |
| if (!bufferDC) { |
| HDC displayDC = GetDC(nullptr); |
| bufferDC = CreateCompatibleDC(displayDC); |
| ReleaseDC(nullptr, displayDC); |
| } |
| |
| // Define the header |
| BITMAPINFO bmi; |
| memset(&bmi, 0, sizeof(bmi)); |
| bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); |
| bmi.bmiHeader.biWidth = w; |
| bmi.bmiHeader.biHeight = -h; |
| bmi.bmiHeader.biPlanes = 1; |
| bmi.bmiHeader.biBitCount = 32; |
| bmi.bmiHeader.biCompression = BI_RGB; |
| |
| // Create the pixmap |
| bufferPixels = nullptr; |
| bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&bufferPixels), nullptr, 0); |
| GdiFlush(); |
| nullBitmap = static_cast<HBITMAP>(SelectObject(bufferDC, bufferBitmap)); |
| |
| if (Q_UNLIKELY(!bufferBitmap)) { |
| qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h); |
| bufferW = 0; |
| bufferH = 0; |
| return nullptr; |
| } |
| if (Q_UNLIKELY(!bufferPixels)) { |
| qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h); |
| bufferW = 0; |
| bufferH = 0; |
| return nullptr; |
| } |
| bufferW = w; |
| bufferH = h; |
| #ifdef DEBUG_XP_STYLE |
| qDebug("Creating new dib section (%d, %d)", w, h); |
| #endif |
| return bufferBitmap; |
| } |
| |
| /*! \internal |
| Returns \c true if the part contains any transparency at all. This does |
| not indicate what kind of transparency we're dealing with. It can be |
| - Alpha transparency |
| - Masked transparency |
| */ |
| bool QWindowsXPStylePrivate::isTransparent(XPThemeData &themeData) |
| { |
| return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId, |
| themeData.stateId); |
| } |
| |
| |
| /*! \internal |
| Returns a QRegion of the region of the part |
| */ |
| QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData) |
| { |
| HRGN hRgn = nullptr; |
| const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(themeData.widget); |
| RECT rect = themeData.toRECT(QRect(themeData.rect.topLeft() / factor, themeData.rect.size() / factor)); |
| if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId, |
| themeData.stateId, &rect, &hRgn))) { |
| return QRegion(); |
| } |
| |
| HRGN dest = CreateRectRgn(0, 0, 0, 0); |
| const bool success = CombineRgn(dest, hRgn, nullptr, RGN_COPY) != ERROR; |
| |
| QRegion region; |
| |
| if (success) { |
| const auto numBytes = GetRegionData(dest, 0, nullptr); |
| if (numBytes == 0) |
| return QRegion(); |
| |
| char *buf = new (std::nothrow) char[numBytes]; |
| if (!buf) |
| return QRegion(); |
| |
| RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf); |
| if (GetRegionData(dest, numBytes, rd) == 0) { |
| delete [] buf; |
| return QRegion(); |
| } |
| |
| RECT *r = reinterpret_cast<RECT*>(rd->Buffer); |
| for (uint i = 0; i < rd->rdh.nCount; ++i) { |
| QRect rect; |
| rect.setCoords(int(r->left * factor), int(r->top * factor), int((r->right - 1) * factor), int((r->bottom - 1) * factor)); |
| ++r; |
| region |= rect; |
| } |
| |
| delete [] buf; |
| } |
| |
| DeleteObject(hRgn); |
| DeleteObject(dest); |
| |
| return region; |
| } |
| |
| /*! \internal |
| Returns \c true if the native doublebuffer contains pixels with |
| varying alpha value. |
| */ |
| bool QWindowsXPStylePrivate::hasAlphaChannel(const QRect &rect) |
| { |
| const int startX = rect.left(); |
| const int startY = rect.top(); |
| const int w = rect.width(); |
| const int h = rect.height(); |
| |
| int firstAlpha = -1; |
| for (int y = startY; y < h/2; ++y) { |
| auto buffer = reinterpret_cast<const DWORD *>(bufferPixels) + (y * bufferW); |
| for (int x = startX; x < w; ++x, ++buffer) { |
| int alpha = (*buffer) >> 24; |
| if (firstAlpha == -1) |
| firstAlpha = alpha; |
| else if (alpha != firstAlpha) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /*! \internal |
| When the theme engine paints both a true alpha pixmap and a glyph |
| into our buffer, the glyph might not contain a proper alpha value. |
| The rule of thumb for premultiplied pixmaps is that the color |
| values of a pixel can never be higher than the alpha values, so |
| we use this to our advantage here, and fix all instances where |
| this occures. |
| */ |
| bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect) |
| { |
| const int startX = rect.left(); |
| const int startY = rect.top(); |
| const int w = rect.width(); |
| const int h = rect.height(); |
| bool hasFixedAlphaValue = false; |
| |
| for (int y = startY; y < h; ++y) { |
| auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW); |
| for (int x = startX; x < w; ++x, ++buffer) { |
| uint pixel = *buffer; |
| int alpha = qAlpha(pixel); |
| if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) { |
| *buffer |= 0xff000000; |
| hasFixedAlphaValue = true; |
| } |
| } |
| } |
| return hasFixedAlphaValue; |
| } |
| |
| /*! \internal |
| Swaps the alpha values on certain pixels: |
| 0xFF?????? -> 0x00?????? |
| 0x00?????? -> 0xFF?????? |
| Used to determin the mask of a non-alpha transparent pixmap in |
| the native doublebuffer, and swap the alphas so we may paint |
| the image as a Premultiplied QImage with drawImage(), and obtain |
| the mask transparency. |
| */ |
| bool QWindowsXPStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels) |
| { |
| const int startX = rect.left(); |
| const int startY = rect.top(); |
| const int w = rect.width(); |
| const int h = rect.height(); |
| bool valueChange = false; |
| |
| // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255. |
| for (int y = startY; y < h; ++y) { |
| auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW); |
| for (int x = startX; x < w; ++x, ++buffer) { |
| if (allPixels) { |
| *buffer |= 0xFF000000; |
| continue; |
| } |
| unsigned int alphaValue = (*buffer) & 0xFF000000; |
| if (alphaValue == 0xFF000000) { |
| *buffer = 0; |
| valueChange = true; |
| } else if (alphaValue == 0) { |
| *buffer |= 0xFF000000; |
| valueChange = true; |
| } |
| } |
| } |
| return valueChange; |
| } |
| |
| enum TransformType { SimpleTransform, HighDpiScalingTransform, ComplexTransform }; |
| |
| static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio) |
| { |
| if (transform.type() <= QTransform::TxTranslate) |
| return SimpleTransform; |
| if (transform.type() > QTransform::TxScale) |
| return ComplexTransform; |
| return qFuzzyCompare(transform.m11(), devicePixelRatio) |
| && qFuzzyCompare(transform.m22(), devicePixelRatio) |
| ? HighDpiScalingTransform : ComplexTransform; |
| } |
| |
| // QTBUG-60571: Exclude known fully opaque theme parts which produce values |
| // invalid in ARGB32_Premultiplied (for example, 0x00ffffff). |
| static inline bool isFullyOpaque(const XPThemeData &themeData) |
| { |
| return themeData.theme == QWindowsXPStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL; |
| } |
| |
| /*! \internal |
| Main theme drawing function. |
| Determines the correct lowlevel drawing method depending on several |
| factors. |
| Use drawBackgroundThruNativeBuffer() if: |
| - Painter does not have an HDC |
| - Theme part is flipped (mirrored horizontally) |
| else use drawBackgroundDirectly(). |
| \note drawBackgroundThruNativeBuffer() can return false for large |
| sizes due to buffer()/CreateDIBSection() failing. |
| */ |
| bool QWindowsXPStylePrivate::drawBackground(XPThemeData &themeData) |
| { |
| if (themeData.rect.isEmpty()) |
| return true; |
| |
| QPainter *painter = themeData.painter; |
| Q_ASSERT_X(painter != nullptr, "QWindowsXPStylePrivate::drawBackground()", "Trying to draw a theme part without a painter"); |
| if (!painter || !painter->isActive()) |
| return false; |
| |
| painter->save(); |
| |
| // Access paintDevice via engine since the painter may |
| // return the clip device which can still be a widget device in case of grabWidget(). |
| |
| bool translucentToplevel = false; |
| const QPaintDevice *paintDevice = painter->device(); |
| const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->devicePixelRatioF() : qreal(1); |
| if (paintDevice->devType() == QInternal::Widget) { |
| const QWidget *window = static_cast<const QWidget *>(paintDevice)->window(); |
| translucentToplevel = window->testAttribute(Qt::WA_TranslucentBackground); |
| } |
| |
| const TransformType tt = transformType(painter->deviceTransform(), aditionalDevicePixelRatio); |
| |
| bool canDrawDirectly = false; |
| if (themeData.widget && painter->opacity() == 1.0 && !themeData.rotate |
| && !isFullyOpaque(themeData) |
| && tt != ComplexTransform && !themeData.mirrorVertically |
| && !translucentToplevel) { |
| // Draw on backing store DC only for real widgets or backing store images. |
| const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice(); |
| switch (enginePaintDevice->devType()) { |
| case QInternal::Widget: |
| canDrawDirectly = true; |
| break; |
| case QInternal::Image: |
| // Ensure the backing store has received as resize and is initialized. |
| if (QBackingStore *bs = backingStoreForWidget(themeData.widget)) |
| if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice) |
| canDrawDirectly = true; |
| } |
| } |
| |
| const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : nullptr; |
| const bool result = dc |
| ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio) |
| : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio); |
| painter->restore(); |
| return result; |
| } |
| |
| static inline QRectF scaleRect(const QRectF &r, qreal factor) |
| { |
| return r.isValid() && factor > 1 |
| ? QRectF(r.topLeft() * factor, r.size() * factor) |
| : r; |
| } |
| |
| static QRegion scaleRegion(const QRegion ®ion, qreal factor) |
| { |
| if (region.isEmpty() || qFuzzyCompare(factor, qreal(1))) |
| return region; |
| QRegion result; |
| for (const QRect &rect : region) |
| result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect(); |
| return result; |
| } |
| |
| /*! \internal |
| This function draws the theme parts directly to the paintengines HDC. |
| Do not use this if you need to perform other transformations on the |
| resulting data. |
| */ |
| bool QWindowsXPStylePrivate::drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal additionalDevicePixelRatio) |
| { |
| QPainter *painter = themeData.painter; |
| |
| const auto &deviceTransform = painter->deviceTransform(); |
| const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy()); |
| const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect(); |
| |
| QRegion sysRgn = painter->paintEngine()->systemClip(); |
| if (sysRgn.isEmpty()) |
| sysRgn = area; |
| else |
| sysRgn &= area; |
| if (painter->hasClipping()) |
| sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint()); |
| HRGN hrgn = qt_hrgn_from_qregion(sysRgn); |
| SelectClipRgn(dc, hrgn); |
| |
| #ifdef DEBUG_XP_STYLE |
| printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n", |
| qPrintable(themeData.name), themeData.partId, themeData.stateId); |
| showProperties(themeData); |
| #endif |
| |
| RECT drawRECT = themeData.toRECT(area); |
| DTBGOPTS drawOptions; |
| memset(&drawOptions, 0, sizeof(drawOptions)); |
| drawOptions.dwSize = sizeof(drawOptions); |
| drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect()); |
| drawOptions.dwFlags = DTBG_CLIPRECT |
| | (themeData.noBorder ? DTBG_OMITBORDER : 0) |
| | (themeData.noContent ? DTBG_OMITCONTENT : 0) |
| | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0); |
| |
| const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions); |
| SelectClipRgn(dc, nullptr); |
| DeleteObject(hrgn); |
| return SUCCEEDED(result); |
| } |
| |
| /*! \internal |
| This function uses a secondary Native doublebuffer for painting parts. |
| It should only be used when the painteengine doesn't provide a proper |
| HDC for direct painting (e.g. when doing a grabWidget(), painting to |
| other pixmaps etc), or when special transformations are needed (e.g. |
| flips (horizonal mirroring only, vertical are handled by the theme |
| engine). |
| */ |
| bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeData, |
| qreal additionalDevicePixelRatio) |
| { |
| QPainter *painter = themeData.painter; |
| QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio); |
| |
| if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips. |
| rectF = QRectF(0, 0, rectF.height(), rectF.width()); |
| } |
| rectF.moveTo(0, 0); |
| QRect rect = rectF.toRect(); |
| int partId = themeData.partId; |
| int stateId = themeData.stateId; |
| int w = rect.width(); |
| int h = rect.height(); |
| |
| // Values initialized later, either from cached values, or from function calls |
| AlphaChannelType alphaType = UnknownAlpha; |
| bool stateHasData = true; // We assume so; |
| bool hasAlpha = false; |
| bool partIsTransparent; |
| bool potentialInvalidAlpha; |
| |
| QString pixmapCacheKey = QStringLiteral("$qt_xp_"); |
| pixmapCacheKey.append(themeName(themeData.theme)); |
| pixmapCacheKey.append(QLatin1Char('p')); |
| pixmapCacheKey.append(QString::number(partId)); |
| pixmapCacheKey.append(QLatin1Char('s')); |
| pixmapCacheKey.append(QString::number(stateId)); |
| pixmapCacheKey.append(QLatin1Char('s')); |
| pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1')); |
| pixmapCacheKey.append(QLatin1Char('b')); |
| pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1')); |
| pixmapCacheKey.append(QString::number(w)); |
| pixmapCacheKey.append(QLatin1Char('w')); |
| pixmapCacheKey.append(QString::number(h)); |
| pixmapCacheKey.append(QLatin1Char('h')); |
| pixmapCacheKey.append(QString::number(additionalDevicePixelRatio)); |
| pixmapCacheKey.append(QLatin1Char('d')); |
| |
| QPixmap cachedPixmap; |
| ThemeMapKey key(themeData); |
| ThemeMapData data = alphaCache.value(key); |
| |
| bool haveCachedPixmap = false; |
| bool isCached = data.dataValid; |
| if (isCached) { |
| partIsTransparent = data.partIsTransparent; |
| hasAlpha = data.hasAlphaChannel; |
| alphaType = data.alphaType; |
| potentialInvalidAlpha = data.hadInvalidAlpha; |
| |
| haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap); |
| |
| #ifdef DEBUG_XP_STYLE |
| char buf[25]; |
| ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h); |
| printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n", |
| haveCachedPixmap ? buf : "]-------------------", |
| qPrintable(themeData.name), themeData.partId, themeData.stateId); |
| #endif |
| } else { |
| // Not cached, so get values from Theme Engine |
| BOOL tmt_borderonly = false; |
| COLORREF tmt_transparentcolor = 0x0; |
| PROPERTYORIGIN proporigin = PO_NOTFOUND; |
| GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly); |
| GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor); |
| GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin); |
| |
| partIsTransparent = isTransparent(themeData); |
| |
| potentialInvalidAlpha = false; |
| GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin); |
| if (proporigin == PO_PART || proporigin == PO_STATE) { |
| int tmt_glyphtype = GT_NONE; |
| GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype); |
| potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH; |
| } |
| |
| #ifdef DEBUG_XP_STYLE |
| printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n", |
| qPrintable(themeData.name), themeData.partId, themeData.stateId); |
| printf("-->partIsTransparen = %d\n", partIsTransparent); |
| printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha); |
| showProperties(themeData); |
| #endif |
| } |
| bool wasAlphaSwapped = false; |
| bool wasAlphaFixed = false; |
| |
| // OLD PSDK Workaround ------------------------------------------------------------------------ |
| // See if we need extra clipping for the older PSDK, which does |
| // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER |
| // and DTGB_OMITCONTENT |
| bool addBorderContentClipping = false; |
| QRegion extraClip; |
| QRect area = rect; |
| if (themeData.noBorder || themeData.noContent) { |
| extraClip = area; |
| // We are running on a system where the uxtheme.dll does not have |
| // the DrawThemeBackgroundEx function, so we need to clip away |
| // borders or contents manually. |
| |
| int borderSize = 0; |
| PROPERTYORIGIN origin = PO_NOTFOUND; |
| GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin); |
| GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize); |
| borderSize *= additionalDevicePixelRatio; |
| |
| // Clip away border region |
| if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) { |
| if (themeData.noBorder) { |
| extraClip &= area; |
| area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize); |
| } |
| |
| // Clip away content region |
| if (themeData.noContent) { |
| QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize); |
| extraClip ^= content; |
| } |
| } |
| addBorderContentClipping = (themeData.noBorder | themeData.noContent); |
| } |
| |
| QImage img; |
| if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! ------------------------- |
| if (!buffer(w, h)) // Ensure a buffer of at least (w, h) in size |
| return false; |
| HDC dc = bufferHDC(); |
| |
| // Clear the buffer |
| if (alphaType != NoAlpha) { |
| // Consider have separate "memset" function for small chunks for more speedup |
| memset(bufferPixels, 0x00, bufferW * h * 4); |
| } |
| |
| // Difference between area and rect |
| int dx = area.x() - rect.x(); |
| int dy = area.y() - rect.y(); |
| |
| // Adjust so painting rect starts from Origo |
| rect.moveTo(0,0); |
| area.moveTo(dx,dy); |
| DTBGOPTS drawOptions; |
| drawOptions.dwSize = sizeof(drawOptions); |
| drawOptions.rcClip = themeData.toRECT(rect); |
| drawOptions.dwFlags = DTBG_CLIPRECT |
| | (themeData.noBorder ? DTBG_OMITBORDER : 0) |
| | (themeData.noContent ? DTBG_OMITCONTENT : 0); |
| |
| // Drawing the part into the backing store |
| RECT wRect(themeData.toRECT(area)); |
| DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions); |
| |
| // If not cached, analyze the buffer data to figure |
| // out alpha type, and if it contains data |
| if (!isCached) { |
| // SHORTCUT: If the part's state has no data, cache it for NOOP later |
| if (!stateHasData) { |
| memset(static_cast<void *>(&data), 0, sizeof(data)); |
| data.dataValid = true; |
| alphaCache.insert(key, data); |
| return true; |
| } |
| hasAlpha = hasAlphaChannel(rect); |
| if (!hasAlpha && partIsTransparent) |
| potentialInvalidAlpha = true; |
| #if defined(DEBUG_XP_STYLE) && 1 |
| dumpNativeDIB(w, h); |
| #endif |
| } |
| |
| // Fix alpha values, if needed |
| if (potentialInvalidAlpha) |
| wasAlphaFixed = fixAlphaChannel(rect); |
| |
| QImage::Format format; |
| if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) { |
| format = QImage::Format_ARGB32_Premultiplied; |
| alphaType = RealAlpha; |
| } else if (wasAlphaSwapped) { |
| format = QImage::Format_ARGB32_Premultiplied; |
| alphaType = MaskAlpha; |
| } else { |
| format = QImage::Format_RGB32; |
| // The image data we got from the theme engine does not have any transparency, |
| // thus the alpha channel is set to 0. |
| // However, Format_RGB32 requires the alpha part to be set to 0xff, thus |
| // we must flip it from 0x00 to 0xff |
| swapAlphaChannel(rect, true); |
| alphaType = NoAlpha; |
| } |
| #if defined(DEBUG_XP_STYLE) && 1 |
| printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha"); |
| #endif |
| img = QImage(bufferPixels, bufferW, bufferH, format); |
| img.setDevicePixelRatio(additionalDevicePixelRatio); |
| } |
| |
| // Blitting backing store |
| bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped; |
| |
| QRegion newRegion; |
| QRegion oldRegion; |
| if (useRegion) { |
| newRegion = region(themeData); |
| oldRegion = painter->clipRegion(); |
| painter->setClipRegion(newRegion); |
| #if defined(DEBUG_XP_STYLE) && 0 |
| printf("Using region:\n"); |
| for (const QRect &r : newRegion) |
| printf(" (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom()); |
| #endif |
| } |
| |
| if (addBorderContentClipping) |
| painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip); |
| |
| if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) { |
| if (!haveCachedPixmap) |
| painter->drawImage(themeData.rect, img, rect); |
| else |
| painter->drawPixmap(themeData.rect, cachedPixmap); |
| } else { |
| // This is _slow_! |
| // Make a copy containing only the necessary data, and mirror |
| // on all wanted axes. Then draw the copy. |
| // If cached, the normal pixmap is cached, instead of caching |
| // all possible orientations for each part and state. |
| QImage imgCopy; |
| if (!haveCachedPixmap) |
| imgCopy = img.copy(rect); |
| else |
| imgCopy = cachedPixmap.toImage(); |
| |
| if (themeData.rotate) { |
| QMatrix rotMatrix; |
| rotMatrix.rotate(themeData.rotate); |
| imgCopy = imgCopy.transformed(rotMatrix); |
| } |
| if (themeData.mirrorHorizontally || themeData.mirrorVertically) { |
| imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically); |
| } |
| painter->drawImage(themeData.rect, |
| imgCopy); |
| } |
| |
| if (useRegion || addBorderContentClipping) { |
| if (oldRegion.isEmpty()) |
| painter->setClipping(false); |
| else |
| painter->setClipRegion(oldRegion); |
| } |
| |
| // Cache the pixmap to avoid expensive swapAlphaChannel() calls |
| if (!haveCachedPixmap && w && h) { |
| QPixmap pix = QPixmap::fromImage(img).copy(rect); |
| QPixmapCache::insert(pixmapCacheKey, pix); |
| #ifdef DEBUG_XP_STYLE |
| printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n", |
| w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey)); |
| #endif |
| } |
| |
| // Add to theme part cache |
| if (!isCached) { |
| memset(static_cast<void *>(&data), 0, sizeof(data)); |
| data.dataValid = true; |
| data.partIsTransparent = partIsTransparent; |
| data.alphaType = alphaType; |
| data.hasAlphaChannel = hasAlpha; |
| data.wasAlphaSwapped = wasAlphaSwapped; |
| data.hadInvalidAlpha = wasAlphaFixed; |
| alphaCache.insert(key, data); |
| } |
| return true; |
| } |
| |
| |
| // ------------------------------------------------------------------------------------------------ |
| |
| /*! |
| \class QWindowsXPStyle |
| \brief The QWindowsXPStyle class provides a Microsoft Windows XP-like look and feel. |
| |
| \ingroup appearance |
| \inmodule QtWidgets |
| \internal |
| |
| \warning This style is only available on the Windows XP platform |
| because it makes use of Windows XP's style engine. |
| |
| Most of the functions are documented in the base classes |
| QWindowsStyle, QCommonStyle, and QStyle, but the |
| QWindowsXPStyle overloads of drawComplexControl(), drawControl(), |
| drawControlMask(), drawPrimitive(), proxy()->subControlRect(), and |
| sizeFromContents(), are documented here. |
| |
| \image qwindowsxpstyle.png |
| \sa QMacStyle, QWindowsStyle, QFusionStyle |
| */ |
| |
| /*! |
| Constructs a QWindowsStyle |
| */ |
| QWindowsXPStyle::QWindowsXPStyle() |
| : QWindowsStyle(*new QWindowsXPStylePrivate) |
| { |
| } |
| |
| /*! |
| Destroys the style. |
| */ |
| QWindowsXPStyle::~QWindowsXPStyle() = default; |
| |
| /*! \reimp */ |
| void QWindowsXPStyle::unpolish(QApplication *app) |
| { |
| QWindowsStyle::unpolish(app); |
| } |
| |
| /*! \reimp */ |
| void QWindowsXPStyle::polish(QApplication *app) |
| { |
| QWindowsStyle::polish(app); |
| if (!QWindowsXPStylePrivate::useXP()) |
| return; |
| } |
| |
| /*! \reimp */ |
| void QWindowsXPStyle::polish(QWidget *widget) |
| { |
| QWindowsStyle::polish(widget); |
| if (!QWindowsXPStylePrivate::useXP()) |
| return; |
| |
| if (false |
| #if QT_CONFIG(abstractbutton) |
| || qobject_cast<QAbstractButton*>(widget) |
| #endif |
| || qobject_cast<QToolButton*>(widget) |
| || qobject_cast<QTabBar*>(widget) |
| #if QT_CONFIG(combobox) |
| || qobject_cast<QComboBox*>(widget) |
| #endif // QT_CONFIG(combobox) |
| || qobject_cast<QScrollBar*>(widget) |
| || qobject_cast<QSlider*>(widget) |
| || qobject_cast<QHeaderView*>(widget) |
| #if QT_CONFIG(spinbox) |
| || qobject_cast<QAbstractSpinBox*>(widget) |
| || qobject_cast<QSpinBox*>(widget) |
| #endif // QT_CONFIG(spinbox) |
| ) { |
| widget->setAttribute(Qt::WA_Hover); |
| } |
| |
| #if QT_CONFIG(rubberband) |
| if (qobject_cast<QRubberBand*>(widget)) { |
| widget->setWindowOpacity(0.6); |
| } |
| #endif |
| if (qobject_cast<QStackedWidget*>(widget) && |
| qobject_cast<QTabWidget*>(widget->parent())) |
| widget->parentWidget()->setAttribute(Qt::WA_ContentsPropagated); |
| |
| Q_D(QWindowsXPStyle); |
| if (!d->hasInitColors) { |
| // Get text color for group box labels |
| COLORREF cref; |
| XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, 0, 0); |
| GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref); |
| d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref)); |
| GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref); |
| d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref)); |
| // Where does this color come from? |
| //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref); |
| d->sliderTickColor = qRgb(165, 162, 148); |
| d->hasInitColors = true; |
| } |
| } |
| |
| /*! \reimp */ |
| void QWindowsXPStyle::polish(QPalette &pal) |
| { |
| QWindowsStyle::polish(pal); |
| pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110)); |
| } |
| |
| /*! \reimp */ |
| void QWindowsXPStyle::unpolish(QWidget *widget) |
| { |
| #if QT_CONFIG(rubberband) |
| if (qobject_cast<QRubberBand*>(widget)) { |
| widget->setWindowOpacity(1.0); |
| } |
| #endif |
| Q_D(QWindowsXPStyle); |
| // Unpolish of widgets is the first thing that |
| // happens when a theme changes, or the theme |
| // engine is turned off. So we detect it here. |
| bool oldState = QWindowsXPStylePrivate::useXP(); |
| bool newState = QWindowsXPStylePrivate::useXP(true); |
| if ((oldState != newState) && newState) { |
| d->cleanup(true); |
| d->init(true); |
| } else { |
| // Cleanup handle map, if just changing style, |
| // or turning it on. In both cases the values |
| // already in the map might be old (other style). |
| d->cleanupHandleMap(); |
| } |
| if (false |
| #if QT_CONFIG(abstractbutton) |
| || qobject_cast<QAbstractButton*>(widget) |
| #endif |
| || qobject_cast<QToolButton*>(widget) |
| || qobject_cast<QTabBar*>(widget) |
| #if QT_CONFIG(combobox) |
| || qobject_cast<QComboBox*>(widget) |
| #endif // QT_CONFIG(combobox) |
| || qobject_cast<QScrollBar*>(widget) |
| || qobject_cast<QSlider*>(widget) |
| || qobject_cast<QHeaderView*>(widget) |
| #if QT_CONFIG(spinbox) |
| || qobject_cast<QAbstractSpinBox*>(widget) |
| || qobject_cast<QSpinBox*>(widget) |
| #endif // QT_CONFIG(spinbox) |
| ) { |
| widget->setAttribute(Qt::WA_Hover, false); |
| } |
| QWindowsStyle::unpolish(widget); |
| } |
| |
| /*! \reimp */ |
| QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option, const QWidget *widget) const |
| { |
| if (!QWindowsXPStylePrivate::useXP()) { |
| return QWindowsStyle::subElementRect(sr, option, widget); |
| } |
| |
| QRect rect(option->rect); |
| switch(sr) { |
| case SE_DockWidgetCloseButton: |
| case SE_DockWidgetFloatButton: |
| rect = QWindowsStyle::subElementRect(sr, option, widget); |
| return rect.translated(0, 1); |
| break; |
| case SE_TabWidgetTabContents: |
| if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) |
| { |
| rect = QWindowsStyle::subElementRect(sr, option, widget); |
| if (sr == SE_TabWidgetTabContents) { |
| if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) { |
| if (tabWidget->documentMode()) |
| break; |
| } |
| |
| rect.adjust(0, 0, -2, -2); |
| } |
| } |
| break; |
| case SE_TabWidgetTabBar: { |
| rect = QWindowsStyle::subElementRect(sr, option, widget); |
| const QStyleOptionTabWidgetFrame *twfOption = |
| qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option); |
| if (twfOption && twfOption->direction == Qt::RightToLeft |
| && (twfOption->shape == QTabBar::RoundedNorth |
| || twfOption->shape == QTabBar::RoundedSouth)) |
| { |
| QStyleOptionTab otherOption; |
| otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth |
| ? QTabBar::RoundedEast : QTabBar::RoundedSouth); |
| int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget); |
| int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); |
| rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0); |
| } |
| break;} |
| |
| case SE_PushButtonContents: |
| if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { |
| MARGINS borderSize; |
| if (widget) { |
| XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme); |
| HTHEME theme = buttontheme.handle(); |
| if (theme) { |
| int stateId; |
| if (!(option->state & State_Enabled)) |
| stateId = PBS_DISABLED; |
| else if (option->state & State_Sunken) |
| stateId = PBS_PRESSED; |
| else if (option->state & State_MouseOver) |
| stateId = PBS_HOT; |
| else if (btn->features & QStyleOptionButton::DefaultButton) |
| stateId = PBS_DEFAULTED; |
| else |
| stateId = PBS_NORMAL; |
| |
| int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget); |
| rect = option->rect.adjusted(border, border, -border, -border); |
| |
| if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) { |
| rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight, |
| -borderSize.cxRightWidth, -borderSize.cyBottomHeight); |
| rect = visualRect(option->direction, option->rect, rect); |
| } |
| } |
| } |
| } |
| break; |
| case SE_ProgressBarContents: |
| rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget); |
| if (option->state & QStyle::State_Horizontal) |
| rect.adjust(4, 3, -4, -3); |
| else |
| rect.adjust(3, 2, -3, -2); |
| break; |
| default: |
| rect = QWindowsStyle::subElementRect(sr, option, widget); |
| } |
| return rect; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QWindowsXPStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p, |
| const QWidget *widget) const |
| { |
| QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); |
| |
| if (!QWindowsXPStylePrivate::useXP()) { |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| |
| int themeNumber = -1; |
| int partId = 0; |
| int stateId = 0; |
| QRect rect = option->rect; |
| State flags = option->state; |
| bool hMirrored = false; |
| bool vMirrored = false; |
| bool noBorder = false; |
| bool noContent = false; |
| int rotate = 0; |
| |
| switch (pe) { |
| case PE_FrameTabBarBase: |
| if (const QStyleOptionTabBarBase *tbb |
| = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) { |
| p->save(); |
| switch (tbb->shape) { |
| case QTabBar::RoundedNorth: |
| p->setPen(QPen(tbb->palette.dark(), 0)); |
| p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight()); |
| break; |
| case QTabBar::RoundedWest: |
| p->setPen(QPen(tbb->palette.dark(), 0)); |
| p->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom()); |
| break; |
| case QTabBar::RoundedSouth: |
| p->setPen(QPen(tbb->palette.dark(), 0)); |
| p->drawLine(tbb->rect.left(), tbb->rect.top(), |
| tbb->rect.right(), tbb->rect.top()); |
| break; |
| case QTabBar::RoundedEast: |
| p->setPen(QPen(tbb->palette.dark(), 0)); |
| p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft()); |
| break; |
| case QTabBar::TriangularNorth: |
| case QTabBar::TriangularEast: |
| case QTabBar::TriangularWest: |
| case QTabBar::TriangularSouth: |
| p->restore(); |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| p->restore(); |
| } |
| return; |
| case PE_PanelButtonBevel: |
| themeNumber = QWindowsXPStylePrivate::ButtonTheme; |
| partId = BP_PUSHBUTTON; |
| if (!(flags & State_Enabled)) |
| stateId = PBS_DISABLED; |
| else if ((flags & State_Sunken) || (flags & State_On)) |
| stateId = PBS_PRESSED; |
| else if (flags & State_MouseOver) |
| stateId = PBS_HOT; |
| //else if (flags & State_ButtonDefault) |
| // stateId = PBS_DEFAULTED; |
| else |
| stateId = PBS_NORMAL; |
| break; |
| |
| case PE_PanelButtonTool: |
| if (widget && widget->inherits("QDockWidgetTitleButton")) { |
| if (const QWidget *dw = widget->parentWidget()) |
| if (dw->isWindow()) |
| return; |
| } |
| themeNumber = QWindowsXPStylePrivate::ToolBarTheme; |
| partId = TP_BUTTON; |
| if (!(flags & State_Enabled)) |
| stateId = TS_DISABLED; |
| else if (flags & State_Sunken) |
| stateId = TS_PRESSED; |
| else if (flags & State_MouseOver) |
| stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; |
| else if (flags & State_On) |
| stateId = TS_CHECKED; |
| else if (!(flags & State_AutoRaise)) |
| stateId = TS_HOT; |
| else |
| stateId = TS_NORMAL; |
| break; |
| |
| case PE_IndicatorButtonDropDown: |
| themeNumber = QWindowsXPStylePrivate::ToolBarTheme; |
| partId = TP_SPLITBUTTONDROPDOWN; |
| if (!(flags & State_Enabled)) |
| stateId = TS_DISABLED; |
| else if (flags & State_Sunken) |
| stateId = TS_PRESSED; |
| else if (flags & State_MouseOver) |
| stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; |
| else if (flags & State_On) |
| stateId = TS_CHECKED; |
| else if (!(flags & State_AutoRaise)) |
| stateId = TS_HOT; |
| else |
| stateId = TS_NORMAL; |
| if (option->direction == Qt::RightToLeft) |
| hMirrored = true; |
| break; |
| |
| case PE_IndicatorCheckBox: |
| themeNumber = QWindowsXPStylePrivate::ButtonTheme; |
| partId = BP_CHECKBOX; |
| if (!(flags & State_Enabled)) |
| stateId = CBS_UNCHECKEDDISABLED; |
| else if (flags & State_Sunken) |
| stateId = CBS_UNCHECKEDPRESSED; |
| else if (flags & State_MouseOver) |
| stateId = CBS_UNCHECKEDHOT; |
| else |
| stateId = CBS_UNCHECKEDNORMAL; |
| |
| if (flags & State_On) |
| stateId += CBS_CHECKEDNORMAL-1; |
| else if (flags & State_NoChange) |
| stateId += CBS_MIXEDNORMAL-1; |
| |
| break; |
| |
| case PE_IndicatorRadioButton: |
| themeNumber = QWindowsXPStylePrivate::ButtonTheme; |
| partId = BP_RADIOBUTTON; |
| if (!(flags & State_Enabled)) |
| stateId = RBS_UNCHECKEDDISABLED; |
| else if (flags & State_Sunken) |
| stateId = RBS_UNCHECKEDPRESSED; |
| else if (flags & State_MouseOver) |
| stateId = RBS_UNCHECKEDHOT; |
| else |
| stateId = RBS_UNCHECKEDNORMAL; |
| |
| if (flags & State_On) |
| stateId += RBS_CHECKEDNORMAL-1; |
| break; |
| |
| case PE_IndicatorDockWidgetResizeHandle: |
| return; |
| |
| case PE_Frame: |
| { |
| if (flags & State_Raised) |
| return; |
| themeNumber = QWindowsXPStylePrivate::ListViewTheme; |
| partId = LVP_LISTGROUP; |
| XPThemeData theme(widget, nullptr, themeNumber, partId); |
| |
| if (!(flags & State_Enabled)) |
| stateId = ETS_DISABLED; |
| else |
| stateId = ETS_NORMAL; |
| int fillType; |
| if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) { |
| if (fillType == BT_BORDERFILL) { |
| COLORREF bcRef; |
| GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef); |
| QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef))); |
| QPen oldPen = p->pen(); |
| // int borderSize = 1; |
| // GetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize); |
| |
| // Inner white border |
| p->setPen(QPen(option->palette.base().color(), 0)); |
| const qreal dpi = QStyleHelper::dpi(option); |
| const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi); |
| const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi); |
| p->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment, |
| bottomRightAdjustment, bottomRightAdjustment)); |
| // Outer dark border |
| p->setPen(QPen(bordercolor, 0)); |
| p->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment)); |
| p->setPen(oldPen); |
| return; |
| } |
| if (fillType == BT_NONE) |
| return; |
| } |
| break; |
| } |
| case PE_FrameLineEdit: { |
| // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class. |
| if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) { |
| QPen oldPen = p->pen(); |
| // Inner white border |
| p->setPen(QPen(option->palette.base().color(), 1)); |
| p->drawRect(option->rect.adjusted(1, 1, -2, -2)); |
| // Outer dark border |
| p->setPen(QPen(option->palette.shadow().color(), 1)); |
| p->drawRect(option->rect.adjusted(0, 0, -1, -1)); |
| p->setPen(oldPen); |
| return; |
| } |
| if (qstyleoption_cast<const QStyleOptionFrame *>(option)) { |
| themeNumber = QWindowsXPStylePrivate::EditTheme; |
| partId = EP_EDITTEXT; |
| noContent = true; |
| if (!(flags & State_Enabled)) |
| stateId = ETS_DISABLED; |
| else |
| stateId = ETS_NORMAL; |
| } |
| break; |
| } |
| |
| case PE_PanelLineEdit: |
| if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) { |
| themeNumber = QWindowsXPStylePrivate::EditTheme; |
| partId = EP_EDITTEXT; |
| noBorder = true; |
| bool isEnabled = flags & State_Enabled; |
| |
| stateId = isEnabled ? ETS_NORMAL : ETS_DISABLED; |
| |
| if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) { |
| p->fillRect(panel->rect, panel->palette.brush(QPalette::Base)); |
| } else { |
| XPThemeData theme(nullptr, p, themeNumber, partId, stateId, rect); |
| if (!theme.isValid()) { |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| int bgType; |
| GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType); |
| if( bgType == BT_IMAGEFILE ) { |
| theme.mirrorHorizontally = hMirrored; |
| theme.mirrorVertically = vMirrored; |
| theme.noBorder = noBorder; |
| theme.noContent = noContent; |
| theme.rotate = rotate; |
| d->drawBackground(theme); |
| } else { |
| QBrush fillColor = option->palette.brush(QPalette::Base); |
| |
| if (!isEnabled) { |
| PROPERTYORIGIN origin = PO_NOTFOUND; |
| GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin); |
| // Use only if the fill property comes from our part |
| if ((origin == PO_PART || origin == PO_STATE)) { |
| COLORREF bgRef; |
| GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef); |
| fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef))); |
| } |
| } |
| p->fillRect(option->rect, fillColor); |
| } |
| } |
| |
| if (panel->lineWidth > 0) |
| proxy()->drawPrimitive(PE_FrameLineEdit, panel, p, widget); |
| return; |
| } |
| break; |
| |
| case PE_FrameTabWidget: |
| if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) |
| { |
| themeNumber = QWindowsXPStylePrivate::TabTheme; |
| partId = TABP_PANE; |
| |
| if (widget) { |
| bool useGradient = true; |
| const int maxlength = 256; |
| wchar_t themeFileName[maxlength]; |
| wchar_t themeColor[maxlength]; |
| // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it |
| if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) { |
| wchar_t *offset = nullptr; |
| if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) { |
| offset++; |
| if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) { |
| useGradient = false; |
| } |
| } |
| } |
| // This should work, but currently there's an error in the ::drawBackgroundDirectly() |
| // code, when using the HDC directly.. |
| if (useGradient) { |
| QStyleOptionTabWidgetFrame frameOpt = *tab; |
| frameOpt.rect = widget->rect(); |
| QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget); |
| QRegion reg = option->rect; |
| reg -= contentsRect; |
| p->setClipRegion(reg); |
| XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); |
| theme.mirrorHorizontally = hMirrored; |
| theme.mirrorVertically = vMirrored; |
| d->drawBackground(theme); |
| p->setClipRect(contentsRect); |
| partId = TABP_BODY; |
| } |
| } |
| switch (tab->shape) { |
| case QTabBar::RoundedNorth: |
| case QTabBar::TriangularNorth: |
| break; |
| case QTabBar::RoundedSouth: |
| case QTabBar::TriangularSouth: |
| vMirrored = true; |
| break; |
| case QTabBar::RoundedEast: |
| case QTabBar::TriangularEast: |
| rotate = 90; |
| break; |
| case QTabBar::RoundedWest: |
| case QTabBar::TriangularWest: |
| rotate = 90; |
| hMirrored = true; |
| break; |
| default: |
| break; |
| } |
| } |
| break; |
| |
| case PE_FrameMenu: |
| p->save(); |
| p->setPen(option->palette.dark().color()); |
| p->drawRect(rect.adjusted(0, 0, -1, -1)); |
| p->restore(); |
| return; |
| |
| case PE_PanelMenuBar: |
| break; |
| |
| case PE_FrameDockWidget: |
| if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) |
| { |
| themeNumber = QWindowsXPStylePrivate::WindowTheme; |
| if (flags & State_Active) |
| stateId = FS_ACTIVE; |
| else |
| stateId = FS_INACTIVE; |
| |
| int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget); |
| |
| XPThemeData theme(widget, p, themeNumber, 0, stateId); |
| if (!theme.isValid()) |
| break; |
| theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth); theme.partId = WP_SMALLFRAMELEFT; |
| d->drawBackground(theme); |
| theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth); |
| theme.partId = WP_SMALLFRAMERIGHT; |
| d->drawBackground(theme); |
| theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth); |
| theme.partId = WP_SMALLFRAMEBOTTOM; |
| d->drawBackground(theme); |
| return; |
| } |
| break; |
| |
| case PE_IndicatorHeaderArrow: |
| { |
| #if 0 // XP theme engine doesn't know about this :( |
| name = QWindowsXPStylePrivate::HeaderTheme; |
| partId = HP_HEADERSORTARROW; |
| if (flags & State_Down) |
| stateId = HSAS_SORTEDDOWN; |
| else |
| stateId = HSAS_SORTEDUP; |
| #else |
| if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { |
| p->save(); |
| p->setPen(option->palette.dark().color()); |
| p->translate(0, option->rect.height()/2 - 4); |
| if (header->sortIndicator & QStyleOptionHeader::SortUp) { // invert logic to follow Windows style guide |
| p->drawLine(option->rect.x(), option->rect.y(), option->rect.x()+8, option->rect.y()); |
| p->drawLine(option->rect.x()+1, option->rect.y()+1, option->rect.x()+7, option->rect.y()+1); |
| p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2); |
| p->drawLine(option->rect.x()+3, option->rect.y()+3, option->rect.x()+5, option->rect.y()+3); |
| p->drawPoint(option->rect.x()+4, option->rect.y()+4); |
| } else if(header->sortIndicator & QStyleOptionHeader::SortDown) { |
| p->drawLine(option->rect.x(), option->rect.y()+4, option->rect.x()+8, option->rect.y()+4); |
| p->drawLine(option->rect.x()+1, option->rect.y()+3, option->rect.x()+7, option->rect.y()+3); |
| p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2); |
| p->drawLine(option->rect.x()+3, option->rect.y()+1, option->rect.x()+5, option->rect.y()+1); |
| p->drawPoint(option->rect.x()+4, option->rect.y()); |
| } |
| p->restore(); |
| return; |
| } |
| #endif |
| } |
| break; |
| |
| case PE_FrameStatusBarItem: |
| themeNumber = QWindowsXPStylePrivate::StatusTheme; |
| partId = SP_PANE; |
| break; |
| |
| case PE_FrameGroupBox: |
| themeNumber = QWindowsXPStylePrivate::ButtonTheme; |
| partId = BP_GROUPBOX; |
| if (!(flags & State_Enabled)) |
| stateId = GBS_DISABLED; |
| else |
| stateId = GBS_NORMAL; |
| if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) { |
| if (frame->features & QStyleOptionFrame::Flat) { |
| // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style |
| QRect fr = frame->rect; |
| QPoint p1(fr.x(), fr.y() + 1); |
| QPoint p2(fr.x() + fr.width(), p1.y() + 1); |
| rect = QRect(p1, p2); |
| themeNumber = -1; |
| } |
| } |
| break; |
| |
| case PE_IndicatorProgressChunk: |
| { |
| Qt::Orientation orient = Qt::Horizontal; |
| bool inverted = false; |
| if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { |
| orient = pb->orientation; |
| inverted = pb->invertedAppearance; |
| } |
| if (orient == Qt::Horizontal) { |
| partId = PP_CHUNK; |
| rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height() ); |
| if (inverted && option->direction == Qt::LeftToRight) |
| hMirrored = true; |
| } else { |
| partId = PP_CHUNKVERT; |
| rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height()); |
| } |
| themeNumber = QWindowsXPStylePrivate::ProgressTheme; |
| stateId = 1; |
| } |
| break; |
| |
| case PE_FrameWindow: |
| if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) |
| { |
| themeNumber = QWindowsXPStylePrivate::WindowTheme; |
| if (flags & State_Active) |
| stateId = FS_ACTIVE; |
| else |
| stateId = FS_INACTIVE; |
| |
| int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget)); |
| |
| XPThemeData theme(widget, p, themeNumber, 0, stateId); |
| if (!theme.isValid()) |
| break; |
| |
| // May fail due to too-large buffers for large widgets, fall back to Windows style. |
| theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth); |
| theme.partId = WP_FRAMELEFT; |
| if (!d->drawBackground(theme)) { |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth); |
| theme.partId = WP_FRAMERIGHT; |
| if (!d->drawBackground(theme)) { |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth); |
| theme.partId = WP_FRAMEBOTTOM; |
| if (!d->drawBackground(theme)) { |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth); |
| theme.partId = WP_CAPTION; |
| if (!d->drawBackground(theme)) |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| break; |
| |
| case PE_IndicatorBranch: |
| { |
| static const int decoration_size = 9; |
| int mid_h = option->rect.x() + option->rect.width() / 2; |
| int mid_v = option->rect.y() + option->rect.height() / 2; |
| int bef_h = mid_h; |
| int bef_v = mid_v; |
| int aft_h = mid_h; |
| int aft_v = mid_v; |
| QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern); |
| if (option->state & State_Item) { |
| if (option->direction == Qt::RightToLeft) |
| p->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush); |
| else |
| p->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush); |
| } |
| if (option->state & State_Sibling) |
| p->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush); |
| if (option->state & (State_Open | State_Children | State_Item | State_Sibling)) |
| p->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush); |
| if (option->state & State_Children) { |
| int delta = decoration_size / 2; |
| bef_h -= delta; |
| bef_v -= delta; |
| aft_h += delta; |
| aft_v += delta; |
| XPThemeData theme(nullptr, p, QWindowsXPStylePrivate::XpTreeViewTheme); |
| theme.rect = QRect(bef_h, bef_v, decoration_size, decoration_size); |
| theme.partId = TVP_GLYPH; |
| theme.stateId = flags & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED; |
| d->drawBackground(theme); |
| } |
| } |
| return; |
| |
| case PE_IndicatorToolBarSeparator: |
| if (option->rect.height() < 3) { |
| // XP style requires a few pixels for the separator |
| // to be visible. |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| themeNumber = QWindowsXPStylePrivate::ToolBarTheme; |
| partId = TP_SEPARATOR; |
| |
| if (option->state & State_Horizontal) |
| partId = TP_SEPARATOR; |
| else |
| partId = TP_SEPARATORVERT; |
| |
| break; |
| |
| case PE_IndicatorToolBarHandle: |
| |
| themeNumber = QWindowsXPStylePrivate::RebarTheme; |
| partId = RP_GRIPPER; |
| if (option->state & State_Horizontal) { |
| partId = RP_GRIPPER; |
| rect.adjust(0, 0, -2, 0); |
| } |
| else { |
| partId = RP_GRIPPERVERT; |
| rect.adjust(0, 0, 0, -2); |
| } |
| break; |
| |
| case PE_IndicatorItemViewItemCheck: { |
| QStyleOptionButton button; |
| button.QStyleOption::operator=(*option); |
| button.state &= ~State_MouseOver; |
| proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, p, widget); |
| return; |
| } |
| |
| default: |
| break; |
| } |
| |
| XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); |
| if (!theme.isValid()) { |
| QWindowsStyle::drawPrimitive(pe, option, p, widget); |
| return; |
| } |
| theme.mirrorHorizontally = hMirrored; |
| theme.mirrorVertically = vMirrored; |
| theme.noBorder = noBorder; |
| theme.noContent = noContent; |
| theme.rotate = rotate; |
| d->drawBackground(theme); |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *p, |
| const QWidget *widget) const |
| { |
| QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); |
| if (!QWindowsXPStylePrivate::useXP()) { |
| QWindowsStyle::drawControl(element, option, p, widget); |
| return; |
| } |
| |
| QRect rect(option->rect); |
| State flags = option->state; |
| |
| int rotate = 0; |
| bool hMirrored = false; |
| bool vMirrored = false; |
| |
| int themeNumber = -1; |
| int partId = 0; |
| int stateId = 0; |
| switch (element) { |
| case CE_SizeGrip: |
| { |
| themeNumber = QWindowsXPStylePrivate::StatusTheme; |
| partId = SP_GRIPPER; |
| XPThemeData theme(nullptr, p, themeNumber, partId); |
| QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); |
| size.rheight()--; |
| if (const QStyleOptionSizeGrip *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) { |
| switch (sg->corner) { |
| case Qt::BottomRightCorner: |
| rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size); |
| break; |
| case Qt::BottomLeftCorner: |
| rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size); |
| hMirrored = true; |
| break; |
| case Qt::TopRightCorner: |
| rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size); |
| vMirrored = true; |
| break; |
| case Qt::TopLeftCorner: |
| rect = QRect(rect.topLeft() + QPoint(1, 1), size); |
| hMirrored = vMirrored = true; |
| } |
| } |
| } |
| break; |
| |
| case CE_HeaderSection: |
| themeNumber = QWindowsXPStylePrivate::HeaderTheme; |
| partId = HP_HEADERITEM; |
| if (flags & State_Sunken) |
| stateId = HIS_PRESSED; |
| else if (flags & State_MouseOver) |
| stateId = HIS_HOT; |
| else |
| stateId = HIS_NORMAL; |
| break; |
| |
| case CE_Splitter: |
| p->eraseRect(option->rect); |
| return; |
| |
| case CE_PushButtonBevel: |
| if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) |
| { |
| themeNumber = QWindowsXPStylePrivate::ButtonTheme; |
| partId = BP_PUSHBUTTON; |
| bool justFlat = ((btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken))) |
| || ((btn->features & QStyleOptionButton::CommandLinkButton) |
| && !(flags & State_MouseOver) |
| && !(btn->features & QStyleOptionButton::DefaultButton)); |
| if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat)) |
| stateId = PBS_DISABLED; |
| else if (justFlat) |
| ; |
| else if (flags & (State_Sunken | State_On)) |
| stateId = PBS_PRESSED; |
| else if (flags & State_MouseOver) |
| stateId = PBS_HOT; |
| else if (btn->features & QStyleOptionButton::DefaultButton) |
| stateId = PBS_DEFAULTED; |
| else |
| stateId = PBS_NORMAL; |
| |
| if (!justFlat) { |
| XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); |
| d->drawBackground(theme); |
| } |
| |
| if (btn->features & QStyleOptionButton::HasMenu) { |
| int mbiw = 0, mbih = 0; |
| XPThemeData theme(widget, nullptr, |
| QWindowsXPStylePrivate::ToolBarTheme, |
| TP_SPLITBUTTONDROPDOWN); |
| if (theme.isValid()) { |
| const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); |
| mbiw = size.width(); |
| mbih = size.height(); |
| } |
| |
| QRect ir = btn->rect; |
| QStyleOptionButton newBtn = *btn; |
| newBtn.rect = QRect(ir.right() - mbiw - 1, 1 + (ir.height()/2) - (mbih/2), mbiw, mbih); |
| proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget); |
| } |
| return; |
| } |
| break; |
| case CE_TabBarTab: |
| if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) |
| { |
| stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED; |
| } |
| break; |
| |
| case CE_TabBarTabShape: |
| if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) |
| { |
| themeNumber = QWindowsXPStylePrivate::TabTheme; |
| bool isDisabled = !(tab->state & State_Enabled); |
| bool hasFocus = tab->state & State_HasFocus; |
| bool isHot = tab->state & State_MouseOver; |
| bool selected = tab->state & State_Selected; |
| bool lastTab = tab->position == QStyleOptionTab::End; |
| bool firstTab = tab->position == QStyleOptionTab::Beginning; |
| bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab; |
| bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft; |
| bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter; |
| int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); |
| int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget); |
| |
| if (isDisabled) |
| stateId = TIS_DISABLED; |
| else if (selected) |
| stateId = TIS_SELECTED; |
| else if (hasFocus) |
| stateId = TIS_FOCUSED; |
| else if (isHot) |
| stateId = TIS_HOT; |
| else |
| stateId = TIS_NORMAL; |
| |
| // Selecting proper part depending on position |
| if (firstTab || onlyOne) { |
| if (leftAligned) { |
| partId = TABP_TABITEMLEFTEDGE; |
| } else if (centerAligned) { |
| partId = TABP_TABITEM; |
| } else { // rightAligned |
| partId = TABP_TABITEMRIGHTEDGE; |
| } |
| } else { |
| partId = TABP_TABITEM; |
| } |
| |
| if (tab->direction == Qt::RightToLeft |
| && (tab->shape == QTabBar::RoundedNorth |
| || tab->shape == QTabBar::RoundedSouth)) { |
| bool temp = firstTab; |
| firstTab = lastTab; |
| lastTab = temp; |
| } |
| bool begin = firstTab || onlyOne; |
| bool end = lastTab || onlyOne; |
| switch (tab->shape) { |
| case QTabBar::RoundedNorth: |
| if (selected) |
| rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness); |
| else |
| rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0); |
| break; |
| case QTabBar::RoundedSouth: |
| //vMirrored = true; |
| rotate = 180; // Not 100% correct, but works |
| if (selected) |
| rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0); |
| else |
| rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap); |
| break; |
| case QTabBar::RoundedEast: |
| rotate = 90; |
| if (selected) { |
| rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap); |
| }else{ |
| rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0); |
| } |
| break; |
| case QTabBar::RoundedWest: |
| hMirrored = true; |
| rotate = 90; |
| if (selected) { |
| rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap); |
| }else{ |
| rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0); |
| } |
| break; |
| default: |
| themeNumber = -1; // Do our own painting for triangular |
| break; |
| } |
| |
| if (!selected) { |
| switch (tab->shape) { |
| case QTabBar::RoundedNorth: |
| rect.adjust(0,0, 0,-1); |
| break; |
| case QTabBar::RoundedSouth: |
| rect.adjust(0,1, 0,0); |
| break; |
| case QTabBar::RoundedEast: |
| rect.adjust( 1,0, 0,0); |
| break; |
| case QTabBar::RoundedWest: |
| rect.adjust(0,0, -1,0); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| break; |
| |
| case CE_ProgressBarGroove: |
| { |
| Qt::Orientation orient = Qt::Horizontal; |
| if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) |
| orient = pb->orientation; |
| partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT; |
| themeNumber = QWindowsXPStylePrivate::ProgressTheme; |
| stateId = 1; |
| } |
| break; |
| |
| case CE_MenuEmptyArea: |
| case CE_MenuItem: |
| if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) |
| { |
| int tab = menuitem->tabWidth; |
| bool dis = !(menuitem->state & State_Enabled); |
| bool act = menuitem->state & State_Selected; |
| bool checkable = menuitem->menuHasCheckableItems; |
| bool checked = checkable ? menuitem->checked : false; |
| |
| // windows always has a check column, regardless whether we have an icon or not |
| int checkcol = qMax(menuitem->maxIconWidth, 12); |
| |
| int x, y, w, h; |
| rect.getRect(&x, &y, &w, &h); |
| |
| QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button); |
| p->fillRect(rect, fill); |
| |
| if (element == CE_MenuEmptyArea) |
| break; |
| |
| // draw separator ------------------------------------------------- |
| if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) { |
| int yoff = y-1 + h / 2; |
| p->setPen(menuitem->palette.dark().color()); |
| p->drawLine(x, yoff, x+w, yoff); |
| ++yoff; |
| p->setPen(menuitem->palette.light().color()); |
| p->drawLine(x, yoff, x+w, yoff); |
| return; |
| } |
| |
| int xpos = x; |
| |
| // draw icon ------------------------------------------------------ |
| if (!menuitem->icon.isNull()) { |
| QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; |
| if (act && !dis) |
| mode = QIcon::Active; |
| QPixmap pixmap = checked ? |
| menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On) : |
| menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode); |
| const int pixw = pixmap.width() / pixmap.devicePixelRatio(); |
| const int pixh = pixmap.height() / pixmap.devicePixelRatio(); |
| QRect iconRect(0, 0, pixw, pixh); |
| iconRect.moveCenter(QRect(xpos, y, checkcol, h).center()); |
| QRect vIconRect = visualRect(option->direction, option->rect, iconRect); |
| p->setPen(menuitem->palette.text().color()); |
| p->setBrush(Qt::NoBrush); |
| if (checked) |
| p->drawRect(vIconRect.adjusted(-1, -1, 0, 0)); |
| p->drawPixmap(vIconRect.topLeft(), pixmap); |
| |
| // draw checkmark ------------------------------------------------- |
| } else if (checked) { |
| QStyleOptionMenuItem newMi = *menuitem; |
| newMi.state = State_None; |
| if (!dis) |
| newMi.state |= State_Enabled; |
| if (act) |
| newMi.state |= State_On; |
| |
| QRect checkMarkRect = QRect(menuitem->rect.x() + windowsItemFrame, |
| menuitem->rect.y() + windowsItemFrame, |
| checkcol - 2 * windowsItemFrame, |
| menuitem->rect.height() - 2*windowsItemFrame); |
| newMi.rect = visualRect(option->direction, option->rect, checkMarkRect); |
| proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget); |
| } |
| |
| QColor textColor = dis ? menuitem->palette.text().color() : |
| act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color(); |
| p->setPen(textColor); |
| |
| // draw text ------------------------------------------------------ |
| int xm = windowsItemFrame + checkcol + windowsItemHMargin; |
| xpos = menuitem->rect.x() + xm; |
| QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin); |
| QRect vTextRect = visualRect(option->direction, option->rect, textRect); |
| QString s = menuitem->text; |
| if (!s.isEmpty()) { |
| p->save(); |
| int t = s.indexOf(QLatin1Char('\t')); |
| int text_flags = Qt::AlignVCenter|Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine | Qt::AlignLeft; |
| if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) |
| text_flags |= Qt::TextHideMnemonic; |
| // draw tab text ---------------- |
| if (t >= 0) { |
| QRect vShortcutRect = visualRect(option->direction, option->rect, QRect(textRect.topRight(), menuitem->rect.bottomRight())); |
| if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) { |
| p->setPen(menuitem->palette.light().color()); |
| p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1)); |
| p->setPen(textColor); |
| } |
| p->drawText(vShortcutRect, text_flags, s.mid(t + 1)); |
| s = s.left(t); |
| } |
| QFont font = menuitem->font; |
| if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) |
| font.setBold(true); |
| p->setFont(font); |
| if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) { |
| p->setPen(menuitem->palette.light().color()); |
| p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t)); |
| p->setPen(textColor); |
| } |
| p->drawText(vTextRect, text_flags, s); |
| p->restore(); |
| } |
| |
| // draw sub menu arrow -------------------------------------------- |
| if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) { |
| int dim = (h - 2) / 2; |
| PrimitiveElement arrow; |
| arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight; |
| xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim; |
| QRect vSubMenuRect = visualRect(option->direction, option->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim)); |
| QStyleOptionMenuItem newMI = *menuitem; |
| newMI.rect = vSubMenuRect; |
| newMI.state = dis ? State_None : State_Enabled; |
| if (act) |
| newMI.palette.setColor(QPalette::ButtonText, newMI.palette.highlightedText().color()); |
| proxy()->drawPrimitive(arrow, &newMI, p, widget); |
| } |
| } |
| return; |
| |
| case CE_MenuBarItem: |
| if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) |
| { |
| if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem) |
| break; |
| |
| bool act = mbi->state & State_Selected; |
| bool dis = !(mbi->state & State_Enabled); |
| |
| QBrush fill = mbi->palette.brush(act ? QPalette::Highlight : QPalette::Button); |
| QPalette::ColorRole textRole = dis ? QPalette::Text: |
| act ? QPalette::HighlightedText : QPalette::ButtonText; |
| QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal); |
| |
| uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; |
| if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) |
| alignment |= Qt::TextHideMnemonic; |
| |
| p->fillRect(rect, fill); |
| if (!pix.isNull()) |
| drawItemPixmap(p, mbi->rect, alignment, pix); |
| else |
| drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole); |
| } |
| return; |
| #if QT_CONFIG(dockwidget) |
| case CE_DockWidgetTitle: |
| if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) |
| { |
| int buttonMargin = 4; |
| int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget); |
| int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget); |
| bool isFloating = widget && widget->isWindow(); |
| bool isActive = dwOpt->state & State_Active; |
| |
| const bool verticalTitleBar = dwOpt->verticalTitleBar; |
| |
| if (verticalTitleBar) { |
| rect = rect.transposed(); |
| |
| p->translate(rect.left() - 1, rect.top() + rect.width()); |
| p->rotate(-90); |
| p->translate(-rect.left() + 1, -rect.top()); |
| } |
| QRect r = rect.adjusted(0, 2, -1, -3); |
| QRect titleRect = r; |
| |
| if (dwOpt->closable) { |
| QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10)); |
| titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0); |
| } |
| |
| if (dwOpt->floatable) { |
| QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10)); |
| titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0); |
| } |
| |
| if (isFloating) { |
| titleRect.adjust(0, -fw, 0, 0); |
| if (widget != nullptr && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey()) |
| titleRect.adjust(titleRect.height() + mw, 0, 0, 0); |
| } else { |
| titleRect.adjust(mw, 0, 0, 0); |
| if (!dwOpt->floatable && !dwOpt->closable) |
| titleRect.adjust(0, 0, -mw, 0); |
| } |
| |
| if (!verticalTitleBar) |
| titleRect = visualRect(dwOpt->direction, r, titleRect); |
| |
| if (!isFloating) { |
| QPen oldPen = p->pen(); |
| QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); |
| p->setPen(dwOpt->palette.color(QPalette::Dark)); |
| p->drawRect(r); |
| |
| if (!titleText.isEmpty()) { |
| drawItemText(p, titleRect, |
| Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette, |
| dwOpt->state & State_Enabled, titleText, |
| QPalette::WindowText); |
| } |
| |
| p->setPen(oldPen); |
| } else { |
| themeNumber = QWindowsXPStylePrivate::WindowTheme; |
| if (isActive) |
| stateId = CS_ACTIVE; |
| else |
| stateId = CS_INACTIVE; |
| |
| int titleHeight = rect.height() - 2; |
| rect = rect.adjusted(-fw, -fw, fw, 0); |
| |
| XPThemeData theme(widget, p, themeNumber, 0, stateId); |
| if (!theme.isValid()) |
| break; |
| |
| // Draw small type title bar |
| theme.rect = rect; |
| theme.partId = WP_SMALLCAPTION; |
| d->drawBackground(theme); |
| |
| // Figure out maximal button space on title bar |
| |
| QIcon ico = widget->windowIcon(); |
| bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey()); |
| if (hasIcon) { |
| QPixmap pxIco = ico.pixmap(titleHeight); |
| if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft) |
| p->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco); |
| else |
| p->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco); |
| } |
| if (!dwOpt->title.isEmpty()) { |
| QPen oldPen = p->pen(); |
| QFont oldFont = p->font(); |
| QFont titleFont = oldFont; |
| titleFont.setBold(true); |
| p->setFont(titleFont); |
| QString titleText |
| = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); |
| |
| int result = TST_NONE; |
| GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result); |
| if (result != TST_NONE) { |
| COLORREF textShadowRef; |
| GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef); |
| QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef)); |
| p->setPen(textShadow); |
| drawItemText(p, titleRect.adjusted(1, 1, 1, 1), |
| Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette, |
| dwOpt->state & State_Enabled, titleText); |
| } |
| |
| COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); |
| QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText)); |
| p->setPen(textColor); |
| drawItemText(p, titleRect, |
| Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette, |
| dwOpt->state & State_Enabled, titleText); |
| p->setFont(oldFont); |
| p->setPen(oldPen); |
| } |
| |
| } |
| |
| return; |
| } |
| break; |
| #endif // QT_CONFIG(dockwidget) |
| #if QT_CONFIG(rubberband) |
| case CE_RubberBand: |
| if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) { |
| QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight); |
| p->save(); |
| p->setPen(highlight.darker(120)); |
| QColor dimHighlight(qMin(highlight.red()/2 + 110, 255), |
| qMin(highlight.green()/2 + 110, 255), |
| qMin(highlight.blue()/2 + 110, 255), |
| (widget && widget->isTopLevel())? 255 : 127); |
| p->setBrush(dimHighlight); |
| p->drawRect(option->rect.adjusted(0, 0, -1, -1)); |
| p->restore(); |
| return; |
| } |
| break; |
| #endif // QT_CONFIG(rubberband) |
| case CE_HeaderEmptyArea: |
| if (option->state & State_Horizontal) |
| { |
| themeNumber = QWindowsXPStylePrivate::HeaderTheme; |
| stateId = HIS_NORMAL; |
| } |
| else { |
| QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, p, widget); |
| return; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); |
| if (!theme.isValid()) { |
| QWindowsStyle::drawControl(element, option, p, widget); |
| return; |
| } |
| |
| theme.rotate = rotate; |
| theme.mirrorHorizontally = hMirrored; |
| theme.mirrorVertically = vMirrored; |
| d->drawBackground(theme); |
| } |
| |
| QRect QWindowsXPStylePrivate::scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme) |
| { |
| const bool horizontal = flags & QStyle::State_Horizontal; |
| const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); |
| const QMargins contentsMargin = |
| (theme->margins(theme->rect, TMT_SIZINGMARGINS) * factor).toMargins(); |
| theme->partId = horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT; |
| const QSize size = (theme->size() * factor).toSize(); |
| |
| const int hSpace = theme->rect.width() - size.width(); |
| const int vSpace = theme->rect.height() - size.height(); |
| const bool sufficientSpace = (horizontal && hSpace > (contentsMargin.left() + contentsMargin.right())) |
| || vSpace > contentsMargin.top() + contentsMargin.bottom(); |
| return sufficientSpace ? QRect(theme->rect.topLeft() + QPoint(hSpace, vSpace) / 2, size) : QRect(); |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, |
| QPainter *p, const QWidget *widget) const |
| { |
| QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); |
| |
| if (!QWindowsXPStylePrivate::useXP()) { |
| QWindowsStyle::drawComplexControl(cc, option, p, widget); |
| return; |
| } |
| |
| State flags = option->state; |
| SubControls sub = option->subControls; |
| QRect r = option->rect; |
| |
| int partId = 0; |
| int stateId = 0; |
| if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) |
| flags |= State_MouseOver; |
| |
| switch (cc) { |
| #if QT_CONFIG(spinbox) |
| case CC_SpinBox: |
| if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) |
| { |
| XPThemeData theme(widget, p, QWindowsXPStylePrivate::SpinTheme); |
| |
| if (sb->frame && (sub & SC_SpinBoxFrame)) { |
| partId = EP_EDITTEXT; |
| if (!(flags & State_Enabled)) |
| stateId = ETS_DISABLED; |
| else if (flags & State_HasFocus) |
| stateId = ETS_FOCUSED; |
| else |
| stateId = ETS_NORMAL; |
| |
| XPThemeData ftheme(widget, p, QWindowsXPStylePrivate::EditTheme, |
| partId, stateId, r); |
| ftheme.noContent = true; |
| d->drawBackground(ftheme); |
| } |
| if (sub & SC_SpinBoxUp) { |
| theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget); |
| partId = SPNP_UP; |
| if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled)) |
| stateId = UPS_DISABLED; |
| else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) |
| stateId = UPS_PRESSED; |
| else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver)) |
| stateId = UPS_HOT; |
| else |
| stateId = UPS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_SpinBoxDown) { |
| theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); |
| partId = SPNP_DOWN; |
| if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled)) |
| stateId = DNS_DISABLED; |
| else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) |
| stateId = DNS_PRESSED; |
| else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver)) |
| stateId = DNS_HOT; |
| else |
| stateId = DNS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| } |
| break; |
| #endif // QT_CONFIG(spinbox) |
| #if QT_CONFIG(combobox) |
| case CC_ComboBox: |
| if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) |
| { |
| if (sub & SC_ComboBoxEditField) { |
| if (cmb->frame) { |
| partId = EP_EDITTEXT; |
| if (!(flags & State_Enabled)) |
| stateId = ETS_DISABLED; |
| else if (flags & State_HasFocus) |
| stateId = ETS_FOCUSED; |
| else |
| stateId = ETS_NORMAL; |
| XPThemeData theme(widget, p, QWindowsXPStylePrivate::EditTheme, partId, stateId, r); |
| d->drawBackground(theme); |
| } else { |
| QBrush editBrush = cmb->palette.brush(QPalette::Base); |
| p->fillRect(option->rect, editBrush); |
| } |
| if (!cmb->editable) { |
| QRect re = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget); |
| if (option->state & State_HasFocus) { |
| p->fillRect(re, option->palette.highlight()); |
| p->setPen(option->palette.highlightedText().color()); |
| p->setBackground(option->palette.highlight()); |
| } else { |
| p->fillRect(re, option->palette.base()); |
| p->setPen(option->palette.text().color()); |
| p->setBackground(option->palette.base()); |
| } |
| } |
| } |
| |
| if (sub & SC_ComboBoxArrow) { |
| XPThemeData theme(widget, p, QWindowsXPStylePrivate::ComboboxTheme); |
| theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); |
| partId = CP_DROPDOWNBUTTON; |
| if (!(flags & State_Enabled)) |
| stateId = CBXS_DISABLED; |
| else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_Sunken)) |
| stateId = CBXS_PRESSED; |
| else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_MouseOver)) |
| stateId = CBXS_HOT; |
| else |
| stateId = CBXS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| } |
| break; |
| #endif // QT_CONFIG(combobox) |
| case CC_ScrollBar: |
| if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) |
| { |
| XPThemeData theme(widget, p, QWindowsXPStylePrivate::ScrollBarTheme); |
| bool maxedOut = (scrollbar->maximum == scrollbar->minimum); |
| if (maxedOut) |
| flags &= ~State_Enabled; |
| |
| bool isHorz = flags & State_Horizontal; |
| bool isRTL = option->direction == Qt::RightToLeft; |
| if (sub & SC_ScrollBarAddLine) { |
| theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); |
| partId = SBP_ARROWBTN; |
| if (!(flags & State_Enabled)) |
| stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED); |
| else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken)) |
| stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED); |
| else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver)) |
| stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT); |
| else |
| stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL); |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_ScrollBarSubLine) { |
| theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); |
| partId = SBP_ARROWBTN; |
| if (!(flags & State_Enabled)) |
| stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED); |
| else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken)) |
| stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED); |
| else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver)) |
| stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT); |
| else |
| stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL); |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (maxedOut) { |
| theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); |
| theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget)); |
| theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget)); |
| partId = scrollbar->orientation == Qt::Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; |
| stateId = SCRBS_DISABLED; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } else { |
| if (sub & SC_ScrollBarSubPage) { |
| theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget); |
| partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT; |
| if (!(flags & State_Enabled)) |
| stateId = SCRBS_DISABLED; |
| else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken)) |
| stateId = SCRBS_PRESSED; |
| else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver)) |
| stateId = SCRBS_HOT; |
| else |
| stateId = SCRBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_ScrollBarAddPage) { |
| theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget); |
| partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; |
| if (!(flags & State_Enabled)) |
| stateId = SCRBS_DISABLED; |
| else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken)) |
| stateId = SCRBS_PRESSED; |
| else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver)) |
| stateId = SCRBS_HOT; |
| else |
| stateId = SCRBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_ScrollBarSlider) { |
| theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); |
| if (!(flags & State_Enabled)) |
| stateId = SCRBS_DISABLED; |
| else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken)) |
| stateId = SCRBS_PRESSED; |
| else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver)) |
| stateId = SCRBS_HOT; |
| else |
| stateId = SCRBS_NORMAL; |
| |
| // Draw handle |
| theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| |
| const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme); |
| // Draw gripper if there is enough space |
| if (!gripperBounds.isEmpty()) { |
| p->save(); |
| theme.rect = gripperBounds; |
| p->setClipRegion(d->region(theme));// Only change inside the region of the gripper |
| d->drawBackground(theme); // Transparent gripper ontop of background |
| p->restore(); |
| } |
| } |
| } |
| } |
| break; |
| |
| #if QT_CONFIG(slider) |
| case CC_Slider: |
| if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) |
| { |
| XPThemeData theme(widget, p, QWindowsXPStylePrivate::TrackBarTheme); |
| QRect slrect = slider->rect; |
| QRegion tickreg = slrect; |
| if (sub & SC_SliderGroove) { |
| theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); |
| if (slider->orientation == Qt::Horizontal) { |
| partId = TKP_TRACK; |
| stateId = TRS_NORMAL; |
| theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4); |
| } else { |
| partId = TKP_TRACKVERT; |
| stateId = TRVS_NORMAL; |
| theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height()); |
| } |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| tickreg -= theme.rect; |
| } |
| if (sub & SC_SliderTickmarks) { |
| int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget); |
| int ticks = slider->tickPosition; |
| int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); |
| int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); |
| int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); |
| int interval = slider->tickInterval; |
| if (interval <= 0) { |
| interval = slider->singleStep; |
| if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, |
| available) |
| - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, |
| 0, available) < 3) |
| interval = slider->pageStep; |
| } |
| if (!interval) |
| interval = 1; |
| int fudge = len / 2; |
| int pos; |
| int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0; |
| p->setPen(d->sliderTickColor); |
| QVarLengthArray<QLine, 32> lines; |
| int v = slider->minimum; |
| while (v <= slider->maximum + 1) { |
| if (v == slider->maximum + 1 && interval == 1) |
| break; |
| const int v_ = qMin(v, slider->maximum); |
| int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3; |
| pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, |
| v_, available) + fudge; |
| if (slider->orientation == Qt::Horizontal) { |
| if (ticks & QSlider::TicksAbove) |
| lines.append(QLine(pos, tickOffset - 1 - bothOffset, |
| pos, tickOffset - 1 - bothOffset - tickLength)); |
| |
| if (ticks & QSlider::TicksBelow) |
| lines.append(QLine(pos, tickOffset + thickness + bothOffset, |
| pos, tickOffset + thickness + bothOffset + tickLength)); |
| } else { |
| if (ticks & QSlider::TicksAbove) |
| lines.append(QLine(tickOffset - 1 - bothOffset, pos, |
| tickOffset - 1 - bothOffset - tickLength, pos)); |
| |
| if (ticks & QSlider::TicksBelow) |
| lines.append(QLine(tickOffset + thickness + bothOffset, pos, |
| tickOffset + thickness + bothOffset + tickLength, pos)); |
| } |
| // in the case where maximum is max int |
| int nextInterval = v + interval; |
| if (nextInterval < v) |
| break; |
| v = nextInterval; |
| } |
| if (!lines.isEmpty()) { |
| p->save(); |
| p->translate(slrect.topLeft()); |
| p->drawLines(lines.constData(), lines.size()); |
| p->restore(); |
| } |
| } |
| if (sub & SC_SliderHandle) { |
| theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); |
| if (slider->orientation == Qt::Horizontal) { |
| if (slider->tickPosition == QSlider::TicksAbove) |
| partId = TKP_THUMBTOP; |
| else if (slider->tickPosition == QSlider::TicksBelow) |
| partId = TKP_THUMBBOTTOM; |
| else |
| partId = TKP_THUMB; |
| |
| if (!(slider->state & State_Enabled)) |
| stateId = TUS_DISABLED; |
| else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken)) |
| stateId = TUS_PRESSED; |
| else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver)) |
| stateId = TUS_HOT; |
| else if (flags & State_HasFocus) |
| stateId = TUS_FOCUSED; |
| else |
| stateId = TUS_NORMAL; |
| } else { |
| if (slider->tickPosition == QSlider::TicksLeft) |
| partId = TKP_THUMBLEFT; |
| else if (slider->tickPosition == QSlider::TicksRight) |
| partId = TKP_THUMBRIGHT; |
| else |
| partId = TKP_THUMBVERT; |
| |
| if (!(slider->state & State_Enabled)) |
| stateId = TUVS_DISABLED; |
| else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken)) |
| stateId = TUVS_PRESSED; |
| else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver)) |
| stateId = TUVS_HOT; |
| else if (flags & State_HasFocus) |
| stateId = TUVS_FOCUSED; |
| else |
| stateId = TUVS_NORMAL; |
| } |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (slider->state & State_HasFocus) { |
| QStyleOptionFocusRect fropt; |
| fropt.QStyleOption::operator=(*slider); |
| fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget); |
| proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget); |
| } |
| } |
| break; |
| #endif |
| #if QT_CONFIG(toolbutton) |
| case CC_ToolButton: |
| if (const QStyleOptionToolButton *toolbutton |
| = qstyleoption_cast<const QStyleOptionToolButton *>(option)) { |
| QRect button, menuarea; |
| button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton, widget); |
| menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget); |
| |
| State bflags = toolbutton->state & ~State_Sunken; |
| State mflags = bflags; |
| bool autoRaise = flags & State_AutoRaise; |
| if (autoRaise) { |
| if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) { |
| bflags &= ~State_Raised; |
| } |
| } |
| |
| if (toolbutton->state & State_Sunken) { |
| if (toolbutton->activeSubControls & SC_ToolButton) { |
| bflags |= State_Sunken; |
| mflags |= State_MouseOver | State_Sunken; |
| } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) { |
| mflags |= State_Sunken; |
| bflags |= State_MouseOver; |
| } |
| } |
| |
| QStyleOption tool = *toolbutton; |
| if (toolbutton->subControls & SC_ToolButton) { |
| if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) { |
| if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) { |
| XPThemeData theme(widget, p, QWindowsXPStylePrivate::ToolBarTheme); |
| theme.partId = TP_SPLITBUTTON; |
| theme.rect = button; |
| if (!(bflags & State_Enabled)) |
| stateId = TS_DISABLED; |
| else if (bflags & State_Sunken) |
| stateId = TS_PRESSED; |
| else if (bflags & State_MouseOver || !(flags & State_AutoRaise)) |
| stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; |
| else if (bflags & State_On) |
| stateId = TS_CHECKED; |
| else |
| stateId = TS_NORMAL; |
| if (option->direction == Qt::RightToLeft) |
| theme.mirrorHorizontally = true; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } else { |
| tool.rect = option->rect; |
| tool.state = bflags; |
| if (autoRaise) // for tool bars |
| proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p, widget); |
| else |
| proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, widget); |
| } |
| } |
| } |
| |
| if (toolbutton->state & State_HasFocus) { |
| QStyleOptionFocusRect fr; |
| fr.QStyleOption::operator=(*toolbutton); |
| fr.rect.adjust(3, 3, -3, -3); |
| if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup) |
| fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, |
| toolbutton, widget), 0); |
| proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p, widget); |
| } |
| QStyleOptionToolButton label = *toolbutton; |
| label.state = bflags; |
| int fw = 2; |
| if (!autoRaise) |
| label.state &= ~State_Sunken; |
| label.rect = button.adjusted(fw, fw, -fw, -fw); |
| proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget); |
| |
| if (toolbutton->subControls & SC_ToolButtonMenu) { |
| tool.rect = menuarea; |
| tool.state = mflags; |
| if (autoRaise) { |
| proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget); |
| } else { |
| tool.state = mflags; |
| menuarea.adjust(-2, 0, 0, 0); |
| // Draw menu button |
| if ((bflags & State_Sunken) != (mflags & State_Sunken)){ |
| p->save(); |
| p->setClipRect(menuarea); |
| tool.rect = option->rect; |
| proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, nullptr); |
| p->restore(); |
| } |
| // Draw arrow |
| p->save(); |
| p->setPen(option->palette.dark().color()); |
| p->drawLine(menuarea.left(), menuarea.top() + 3, |
| menuarea.left(), menuarea.bottom() - 3); |
| p->setPen(option->palette.light().color()); |
| p->drawLine(menuarea.left() - 1, menuarea.top() + 3, |
| menuarea.left() - 1, menuarea.bottom() - 3); |
| |
| tool.rect = menuarea.adjusted(2, 3, -2, -1); |
| proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget); |
| p->restore(); |
| } |
| } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) { |
| int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget); |
| QRect ir = toolbutton->rect; |
| QStyleOptionToolButton newBtn = *toolbutton; |
| newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5); |
| proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget); |
| } |
| } |
| break; |
| #endif // QT_CONFIG(toolbutton) |
| |
| case CC_TitleBar: |
| { |
| if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) |
| { |
| const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); |
| bool isActive = tb->titleBarState & QStyle::State_Active; |
| XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme); |
| if (sub & SC_TitleBarLabel) { |
| |
| partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION; |
| theme.rect = option->rect; |
| if (widget && !widget->isEnabled()) |
| stateId = CS_DISABLED; |
| else if (isActive) |
| stateId = CS_ACTIVE; |
| else |
| stateId = CS_INACTIVE; |
| |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| |
| QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget); |
| |
| int result = TST_NONE; |
| GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result); |
| if (result != TST_NONE) { |
| COLORREF textShadowRef; |
| GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef); |
| QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef)); |
| p->setPen(textShadow); |
| p->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor), |
| int(ir.width() - 1 * factor), ir.height(), |
| Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); |
| } |
| COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); |
| QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText)); |
| p->setPen(textColor); |
| p->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor), |
| int(ir.width() - 2 * factor), ir.height(), |
| Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); |
| } |
| if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) { |
| theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget); |
| partId = WP_SYSBUTTON; |
| if ((widget && !widget->isEnabled()) || !isActive) |
| stateId = SBS_DISABLED; |
| else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken)) |
| stateId = SBS_PUSHED; |
| else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver)) |
| stateId = SBS_HOT; |
| else |
| stateId = SBS_NORMAL; |
| if (!tb->icon.isNull()) { |
| tb->icon.paint(p, theme.rect); |
| } else { |
| theme.partId = partId; |
| theme.stateId = stateId; |
| if (theme.size().isEmpty()) { |
| int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb, widget); |
| QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize); |
| p->save(); |
| drawItemPixmap(p, theme.rect, Qt::AlignCenter, pm); |
| p->restore(); |
| } else { |
| d->drawBackground(theme); |
| } |
| } |
| } |
| |
| if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint |
| && !(tb->titleBarState & Qt::WindowMinimized)) { |
| theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarMinButton, widget); |
| partId = WP_MINBUTTON; |
| if (widget && !widget->isEnabled()) |
| stateId = MINBS_DISABLED; |
| else if (option->activeSubControls == SC_TitleBarMinButton && (option->state & State_Sunken)) |
| stateId = MINBS_PUSHED; |
| else if (option->activeSubControls == SC_TitleBarMinButton && (option->state & State_MouseOver)) |
| stateId = MINBS_HOT; |
| else if (!isActive) |
| stateId = MINBS_INACTIVE; |
| else |
| stateId = MINBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint |
| && !(tb->titleBarState & Qt::WindowMaximized)) { |
| theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarMaxButton, widget); |
| partId = WP_MAXBUTTON; |
| if (widget && !widget->isEnabled()) |
| stateId = MAXBS_DISABLED; |
| else if (option->activeSubControls == SC_TitleBarMaxButton && (option->state & State_Sunken)) |
| stateId = MAXBS_PUSHED; |
| else if (option->activeSubControls == SC_TitleBarMaxButton && (option->state & State_MouseOver)) |
| stateId = MAXBS_HOT; |
| else if (!isActive) |
| stateId = MAXBS_INACTIVE; |
| else |
| stateId = MAXBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_TitleBarContextHelpButton |
| && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) { |
| theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarContextHelpButton, widget); |
| partId = WP_HELPBUTTON; |
| if (widget && !widget->isEnabled()) |
| stateId = MINBS_DISABLED; |
| else if (option->activeSubControls == SC_TitleBarContextHelpButton && (option->state & State_Sunken)) |
| stateId = MINBS_PUSHED; |
| else if (option->activeSubControls == SC_TitleBarContextHelpButton && (option->state & State_MouseOver)) |
| stateId = MINBS_HOT; |
| else if (!isActive) |
| stateId = MINBS_INACTIVE; |
| else |
| stateId = MINBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| bool drawNormalButton = (sub & SC_TitleBarNormalButton) |
| && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint) |
| && (tb->titleBarState & Qt::WindowMinimized)) |
| || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint) |
| && (tb->titleBarState & Qt::WindowMaximized))); |
| if (drawNormalButton) { |
| theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarNormalButton, widget); |
| partId = WP_RESTOREBUTTON; |
| if (widget && !widget->isEnabled()) |
| stateId = RBS_DISABLED; |
| else if (option->activeSubControls == SC_TitleBarNormalButton && (option->state & State_Sunken)) |
| stateId = RBS_PUSHED; |
| else if (option->activeSubControls == SC_TitleBarNormalButton && (option->state & State_MouseOver)) |
| stateId = RBS_HOT; |
| else if (!isActive) |
| stateId = RBS_INACTIVE; |
| else |
| stateId = RBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint |
| && !(tb->titleBarState & Qt::WindowMinimized)) { |
| theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarShadeButton, widget); |
| partId = WP_MINBUTTON; |
| if (widget && !widget->isEnabled()) |
| stateId = MINBS_DISABLED; |
| else if (option->activeSubControls == SC_TitleBarShadeButton && (option->state & State_Sunken)) |
| stateId = MINBS_PUSHED; |
| else if (option->activeSubControls == SC_TitleBarShadeButton && (option->state & State_MouseOver)) |
| stateId = MINBS_HOT; |
| else if (!isActive) |
| stateId = MINBS_INACTIVE; |
| else |
| stateId = MINBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint |
| && tb->titleBarState & Qt::WindowMinimized) { |
| theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarUnshadeButton, widget); |
| partId = WP_RESTOREBUTTON; |
| if (widget && !widget->isEnabled()) |
| stateId = RBS_DISABLED; |
| else if (option->activeSubControls == SC_TitleBarUnshadeButton && (option->state & State_Sunken)) |
| stateId = RBS_PUSHED; |
| else if (option->activeSubControls == SC_TitleBarUnshadeButton && (option->state & State_MouseOver)) |
| stateId = RBS_HOT; |
| else if (!isActive) |
| stateId = RBS_INACTIVE; |
| else |
| stateId = RBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) { |
| theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarCloseButton, widget); |
| //partId = titlebar->testWFlags(Qt::WA_WState_Tool) ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON; |
| partId = WP_CLOSEBUTTON; |
| if (widget && !widget->isEnabled()) |
| stateId = CBS_DISABLED; |
| else if (option->activeSubControls == SC_TitleBarCloseButton && (option->state & State_Sunken)) |
| stateId = CBS_PUSHED; |
| else if (option->activeSubControls == SC_TitleBarCloseButton && (option->state & State_MouseOver)) |
| stateId = CBS_HOT; |
| else if (!isActive) |
| stateId = CBS_INACTIVE; |
| else |
| stateId = CBS_NORMAL; |
| theme.partId = partId; |
| theme.stateId = stateId; |
| d->drawBackground(theme); |
| } |
| } |
| } |
| break; |
| |
| #if QT_CONFIG(mdiarea) |
| case CC_MdiControls: |
| { |
| QRect buttonRect; |
| XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL); |
| |
| if (option->subControls & SC_MdiCloseButton) { |
| buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiCloseButton, widget); |
| if (theme.isValid()) { |
| theme.partId = WP_MDICLOSEBUTTON; |
| theme.rect = buttonRect; |
| if (!(flags & State_Enabled)) |
| theme.stateId = CBS_INACTIVE; |
| else if (flags & State_Sunken && (option->activeSubControls & SC_MdiCloseButton)) |
| theme.stateId = CBS_PUSHED; |
| else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiCloseButton)) |
| theme.stateId = CBS_HOT; |
| else |
| theme.stateId = CBS_NORMAL; |
| d->drawBackground(theme); |
| } |
| } |
| if (option->subControls & SC_MdiNormalButton) { |
| buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiNormalButton, widget); |
| if (theme.isValid()) { |
| theme.partId = WP_MDIRESTOREBUTTON; |
| theme.rect = buttonRect; |
| if (!(flags & State_Enabled)) |
| theme.stateId = CBS_INACTIVE; |
| else if (flags & State_Sunken && (option->activeSubControls & SC_MdiNormalButton)) |
| theme.stateId = CBS_PUSHED; |
| else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiNormalButton)) |
| theme.stateId = CBS_HOT; |
| else |
| theme.stateId = CBS_NORMAL; |
| d->drawBackground(theme); |
| } |
| } |
| if (option->subControls & QStyle::SC_MdiMinButton) { |
| buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiMinButton, widget); |
| if (theme.isValid()) { |
| theme.partId = WP_MDIMINBUTTON; |
| theme.rect = buttonRect; |
| if (!(flags & State_Enabled)) |
| theme.stateId = CBS_INACTIVE; |
| else if (flags & State_Sunken && (option->activeSubControls & SC_MdiMinButton)) |
| theme.stateId = CBS_PUSHED; |
| else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiMinButton)) |
| theme.stateId = CBS_HOT; |
| else |
| theme.stateId = CBS_NORMAL; |
| d->drawBackground(theme); |
| } |
| } |
| } |
| break; |
| #endif // QT_CONFIG(mdiarea) |
| #if QT_CONFIG(dial) |
| case CC_Dial: |
| if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option)) |
| QStyleHelper::drawDial(dial, p); |
| break; |
| #endif // QT_CONFIG(dial) |
| default: |
| QWindowsStyle::drawComplexControl(cc, option, p, widget); |
| break; |
| } |
| } |
| |
| static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr) |
| { |
| if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) |
| return pb->orientation; |
| return Qt::Horizontal; |
| } |
| |
| int QWindowsXPStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget) |
| { |
| switch (pm) { |
| case QStyle::PM_IndicatorWidth: |
| return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width(); |
| case QStyle::PM_IndicatorHeight: |
| return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height(); |
| case QStyle::PM_ExclusiveIndicatorWidth: |
| return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width(); |
| case QStyle::PM_ExclusiveIndicatorHeight: |
| return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height(); |
| case QStyle::PM_ProgressBarChunkWidth: |
| return progressBarOrientation(option) == Qt::Horizontal |
| ? XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNK).width() |
| : XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNKVERT).height(); |
| case QStyle::PM_SliderThickness: |
| return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::TrackBarTheme, TKP_THUMB).height(); |
| case QStyle::PM_TitleBarHeight: |
| return widget && (widget->windowType() == Qt::Tool) |
| ? GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME) |
| : GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME); |
| case QStyle::PM_MdiSubWindowFrameWidth: |
| return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width(); |
| case QStyle::PM_DockWidgetFrameWidth: |
| return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width(); |
| default: |
| break; |
| } |
| return QWindowsXPStylePrivate::InvalidMetric; |
| } |
| |
| /*! \reimp */ |
| int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const |
| { |
| if (!QWindowsXPStylePrivate::useXP()) |
| return QWindowsStyle::pixelMetric(pm, option, widget); |
| |
| int res = QWindowsXPStylePrivate::pixelMetricFromSystemDp(pm, option, widget); |
| if (res != QWindowsStylePrivate::InvalidMetric) |
| return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget)); |
| |
| res = 0; |
| switch (pm) { |
| case PM_MenuBarPanelWidth: |
| case PM_ButtonDefaultIndicator: |
| res = 0; |
| break; |
| |
| case PM_DefaultFrameWidth: |
| res = qobject_cast<const QListView*>(widget) ? 2 : 1; |
| break; |
| case PM_MenuPanelWidth: |
| case PM_SpinBoxFrameWidth: |
| res = 1; |
| break; |
| |
| case PM_TabBarTabOverlap: |
| case PM_MenuHMargin: |
| case PM_MenuVMargin: |
| res = 2; |
| break; |
| |
| case PM_TabBarBaseOverlap: |
| if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) { |
| switch (tab->shape) { |
| case QTabBar::RoundedNorth: |
| case QTabBar::TriangularNorth: |
| case QTabBar::RoundedWest: |
| case QTabBar::TriangularWest: |
| res = 1; |
| break; |
| case QTabBar::RoundedSouth: |
| case QTabBar::TriangularSouth: |
| res = 2; |
| break; |
| case QTabBar::RoundedEast: |
| case QTabBar::TriangularEast: |
| res = 3; |
| break; |
| } |
| } |
| break; |
| |
| case PM_SplitterWidth: |
| res = qMax(int(QStyleHelper::dpiScaled(5., option)), |
| QApplication::globalStrut().width()); |
| break; |
| |
| case PM_MdiSubWindowMinimizedWidth: |
| res = 160; |
| break; |
| |
| #if QT_CONFIG(toolbar) |
| case PM_ToolBarHandleExtent: |
| res = int(QStyleHelper::dpiScaled(8., option)); |
| break; |
| |
| #endif // QT_CONFIG(toolbar) |
| case PM_DockWidgetSeparatorExtent: |
| case PM_DockWidgetTitleMargin: |
| res = int(QStyleHelper::dpiScaled(4., option)); |
| break; |
| |
| case PM_ButtonShiftHorizontal: |
| case PM_ButtonShiftVertical: |
| res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0; |
| break; |
| |
| default: |
| res = QWindowsStyle::pixelMetric(pm, option, widget); |
| } |
| |
| return res; |
| } |
| |
| /* |
| This function is used by subControlRect to check if a button |
| should be drawn for the given subControl given a set of window flags. |
| */ |
| static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){ |
| |
| bool isMinimized = tb->titleBarState & Qt::WindowMinimized; |
| bool isMaximized = tb->titleBarState & Qt::WindowMaximized; |
| const uint flags = tb->titleBarFlags; |
| bool retVal = false; |
| switch (sc) { |
| case QStyle::SC_TitleBarContextHelpButton: |
| if (flags & Qt::WindowContextHelpButtonHint) |
| retVal = true; |
| break; |
| case QStyle::SC_TitleBarMinButton: |
| if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint)) |
| retVal = true; |
| break; |
| case QStyle::SC_TitleBarNormalButton: |
| if (isMinimized && (flags & Qt::WindowMinimizeButtonHint)) |
| retVal = true; |
| else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint)) |
| retVal = true; |
| break; |
| case QStyle::SC_TitleBarMaxButton: |
| if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint)) |
| retVal = true; |
| break; |
| case QStyle::SC_TitleBarShadeButton: |
| if (!isMinimized && flags & Qt::WindowShadeButtonHint) |
| retVal = true; |
| break; |
| case QStyle::SC_TitleBarUnshadeButton: |
| if (isMinimized && flags & Qt::WindowShadeButtonHint) |
| retVal = true; |
| break; |
| case QStyle::SC_TitleBarCloseButton: |
| if (flags & Qt::WindowSystemMenuHint) |
| retVal = true; |
| break; |
| case QStyle::SC_TitleBarSysMenu: |
| if (flags & Qt::WindowSystemMenuHint) |
| retVal = true; |
| break; |
| default : |
| retVal = true; |
| } |
| return retVal; |
| } |
| |
| /*! |
| \reimp |
| */ |
| QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option, |
| SubControl subControl, const QWidget *widget) const |
| { |
| if (!QWindowsXPStylePrivate::useXP()) |
| return QWindowsStyle::subControlRect(cc, option, subControl, widget); |
| |
| QRect rect; |
| |
| switch (cc) { |
| case CC_TitleBar: |
| if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { |
| if (!buttonVisible(subControl, tb)) |
| return rect; |
| const bool isToolTitle = false; |
| const int height = tb->rect.height(); |
| const int width = tb->rect.width(); |
| const int buttonMargin = int(QStyleHelper::dpiScaled(4, option)); |
| const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); |
| int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor) |
| - buttonMargin; |
| int buttonWidth = qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor) |
| - buttonMargin; |
| const int delta = buttonWidth + 2; |
| int controlTop = option->rect.bottom() - buttonHeight - 2; |
| const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget); |
| const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0; |
| const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0; |
| const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0; |
| const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0; |
| const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0; |
| bool isMinimized = tb->titleBarState & Qt::WindowMinimized; |
| bool isMaximized = tb->titleBarState & Qt::WindowMaximized; |
| int offset = 0; |
| |
| switch (subControl) { |
| case SC_TitleBarLabel: |
| rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height); |
| if (isToolTitle) { |
| if (sysmenuHint) { |
| rect.adjust(0, 0, -buttonWidth - 3, 0); |
| } |
| if (minimizeHint || maximizeHint) |
| rect.adjust(0, 0, -buttonWidth - 2, 0); |
| } else { |
| if (sysmenuHint) { |
| const int leftOffset = height - 8; |
| rect.adjust(leftOffset, 0, 0, 0); |
| } |
| if (minimizeHint) |
| rect.adjust(0, 0, -buttonWidth - 2, 0); |
| if (maximizeHint) |
| rect.adjust(0, 0, -buttonWidth - 2, 0); |
| if (contextHint) |
| rect.adjust(0, 0, -buttonWidth - 2, 0); |
| if (shadeHint) |
| rect.adjust(0, 0, -buttonWidth - 2, 0); |
| } |
| break; |
| |
| case SC_TitleBarContextHelpButton: |
| if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) |
| offset += delta; |
| Q_FALLTHROUGH(); |
| case SC_TitleBarMinButton: |
| if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) |
| offset += delta; |
| else if (subControl == SC_TitleBarMinButton) |
| break; |
| Q_FALLTHROUGH(); |
| case SC_TitleBarNormalButton: |
| if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) |
| offset += delta; |
| else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) |
| offset += delta; |
| else if (subControl == SC_TitleBarNormalButton) |
| break; |
| Q_FALLTHROUGH(); |
| case SC_TitleBarMaxButton: |
| if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) |
| offset += delta; |
| else if (subControl == SC_TitleBarMaxButton) |
| break; |
| Q_FALLTHROUGH(); |
| case SC_TitleBarShadeButton: |
| if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) |
| offset += delta; |
| else if (subControl == SC_TitleBarShadeButton) |
| break; |
| Q_FALLTHROUGH(); |
| case SC_TitleBarUnshadeButton: |
| if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) |
| offset += delta; |
| else if (subControl == SC_TitleBarUnshadeButton) |
| break; |
| Q_FALLTHROUGH(); |
| case SC_TitleBarCloseButton: |
| if (tb->titleBarFlags & Qt::WindowSystemMenuHint) |
| offset += delta; |
| else if (subControl == SC_TitleBarCloseButton) |
| break; |
| |
| rect.setRect(width - offset - controlTop + 1, controlTop, |
| buttonWidth, buttonHeight); |
| break; |
| |
| case SC_TitleBarSysMenu: |
| { |
| const int controlTop = 6; |
| const int controlHeight = height - controlTop - 3; |
| const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); |
| QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent)); |
| if (tb->icon.isNull()) |
| iconSize = QSize(controlHeight, controlHeight); |
| int hPad = (controlHeight - iconSize.height())/2; |
| int vPad = (controlHeight - iconSize.width())/2; |
| rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height()); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| break; |
| |
| case CC_ComboBox: |
| if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { |
| const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height(); |
| const int xpos = x + wi - qRound(QStyleHelper::dpiScaled(1 + 16, option)); |
| |
| switch (subControl) { |
| case SC_ComboBoxFrame: |
| rect = cmb->rect; |
| break; |
| |
| case SC_ComboBoxArrow: { |
| const qreal dpi = QStyleHelper::dpi(option); |
| rect = QRect(xpos, y + qRound(QStyleHelper::dpiScaled(1, dpi)), |
| qRound(QStyleHelper::dpiScaled(16, dpi)), |
| he - qRound(QStyleHelper::dpiScaled(2, dpi))); |
| } |
| break; |
| |
| case SC_ComboBoxEditField: { |
| const qreal dpi = QStyleHelper::dpi(option); |
| const int frame = qRound(QStyleHelper::dpiScaled(2, dpi)); |
| rect = QRect(x + frame, y + frame, |
| wi - qRound(QStyleHelper::dpiScaled(3 + 16, dpi)), |
| he - qRound(QStyleHelper::dpiScaled(4, dpi))); |
| } |
| break; |
| |
| case SC_ComboBoxListBoxPopup: |
| rect = cmb->rect; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| break; |
| #if QT_CONFIG(mdiarea) |
| case CC_MdiControls: |
| { |
| int numSubControls = 0; |
| if (option->subControls & SC_MdiCloseButton) |
| ++numSubControls; |
| if (option->subControls & SC_MdiMinButton) |
| ++numSubControls; |
| if (option->subControls & SC_MdiNormalButton) |
| ++numSubControls; |
| if (numSubControls == 0) |
| break; |
| |
| int buttonWidth = option->rect.width() / numSubControls; |
| int offset = 0; |
| switch (subControl) { |
| case SC_MdiCloseButton: |
| // Only one sub control, no offset needed. |
| if (numSubControls == 1) |
| break; |
| offset += buttonWidth; |
| Q_FALLTHROUGH(); |
| case SC_MdiNormalButton: |
| // No offset needed if |
| // 1) There's only one sub control |
| // 2) We have a close button and a normal button (offset already added in SC_MdiClose) |
| if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton))) |
| break; |
| if (option->subControls & SC_MdiNormalButton) |
| offset += buttonWidth; |
| break; |
| default: |
| break; |
| } |
| rect = QRect(offset, 0, buttonWidth, option->rect.height()); |
| break; |
| } |
| #endif // QT_CONFIG(mdiarea) |
| |
| default: |
| rect = visualRect(option->direction, option->rect, |
| QWindowsStyle::subControlRect(cc, option, subControl, widget)); |
| break; |
| } |
| return visualRect(option->direction, option->rect, rect); |
| } |
| |
| /*! |
| \reimp |
| */ |
| QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *option, |
| const QSize &contentsSize, const QWidget *widget) const |
| { |
| if (!QWindowsXPStylePrivate::useXP()) |
| return QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget); |
| |
| QSize sz(contentsSize); |
| switch (ct) { |
| case CT_LineEdit: |
| case CT_ComboBox: |
| { |
| XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL); |
| if (buttontheme.isValid()) { |
| const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget); |
| const QMarginsF borderSize = buttontheme.margins() * factor; |
| if (!borderSize.isNull()) { |
| const qreal margin = qreal(2) * factor; |
| sz.rwidth() += qRound(borderSize.left() + borderSize.right() - margin); |
| sz.rheight() += int(borderSize.bottom() + borderSize.top() - margin |
| + qreal(1) / factor - 1); |
| } |
| const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin) + 1); |
| sz += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget) |
| + textMargins, 23), 0); //arrow button |
| } |
| } |
| break; |
| case CT_SpinBox: |
| { |
| //Spinbox adds frame twice |
| sz = QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget); |
| int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget); |
| sz -= QSize(2*border, 2*border); |
| } |
| break; |
| case CT_TabWidget: |
| sz += QSize(6, 6); |
| break; |
| case CT_Menu: |
| sz += QSize(1, 0); |
| break; |
| #if QT_CONFIG(menubar) |
| case CT_MenuBarItem: |
| if (!sz.isEmpty()) |
| sz += QSize(windowsItemHMargin * 5 + 1, 6); |
| break; |
| #endif |
| case CT_MenuItem: |
| if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) |
| { |
| if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) { |
| sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); |
| sz.setHeight(sz.height() - 2); |
| return sz; |
| } |
| } |
| sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); |
| break; |
| |
| case CT_MdiControls: |
| if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) { |
| int width = 0; |
| if (styleOpt->subControls & SC_MdiMinButton) |
| width += 17 + 1; |
| if (styleOpt->subControls & SC_MdiNormalButton) |
| width += 17 + 1; |
| if (styleOpt->subControls & SC_MdiCloseButton) |
| width += 17 + 1; |
| sz = QSize(width, 19); |
| } else { |
| sz = QSize(54, 19); |
| } |
| break; |
| |
| default: |
| sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); |
| break; |
| } |
| |
| return sz; |
| } |
| |
| |
| /*! \reimp */ |
| int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, |
| QStyleHintReturn *returnData) const |
| { |
| QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); |
| if (!QWindowsXPStylePrivate::useXP()) |
| return QWindowsStyle::styleHint(hint, option, widget, returnData); |
| |
| int res = 0; |
| switch (hint) { |
| |
| case SH_EtchDisabledText: |
| res = (qobject_cast<const QLabel*>(widget) != 0); |
| break; |
| |
| case SH_SpinControls_DisableOnBounds: |
| res = 0; |
| break; |
| |
| case SH_TitleBar_AutoRaise: |
| case SH_TitleBar_NoBorder: |
| res = 1; |
| break; |
| |
| case SH_GroupBox_TextLabelColor: |
| if (!widget || (widget && widget->isEnabled())) |
| res = d->groupBoxTextColor; |
| else |
| res = d->groupBoxTextColorDisabled; |
| break; |
| |
| case SH_Table_GridLineColor: |
| res = 0xC0C0C0; |
| break; |
| |
| case SH_WindowFrame_Mask: |
| { |
| res = 1; |
| QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData); |
| const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option); |
| if (mask && titlebar) { |
| // Note certain themes will not return the whole window frame but only the titlebar part when |
| // queried This function needs to return the entire window mask, hence we will only fetch the mask for the |
| // titlebar itself and add the remaining part of the window rect at the bottom. |
| int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget); |
| QRect titleBarRect = option->rect; |
| titleBarRect.setHeight(tbHeight); |
| XPThemeData themeData; |
| if (titlebar->titleBarState & Qt::WindowMinimized) { |
| themeData = XPThemeData(widget, nullptr, |
| QWindowsXPStylePrivate::WindowTheme, |
| WP_MINCAPTION, CS_ACTIVE, titleBarRect); |
| } else |
| themeData = XPThemeData(widget, nullptr, |
| QWindowsXPStylePrivate::WindowTheme, |
| WP_CAPTION, CS_ACTIVE, titleBarRect); |
| mask->region = d->region(themeData) + |
| QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight); |
| } |
| } |
| break; |
| #if QT_CONFIG(rubberband) |
| case SH_RubberBand_Mask: |
| if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) |
| res = 0; |
| break; |
| #endif // QT_CONFIG(rubberband) |
| |
| case SH_ItemView_DrawDelegateFrame: |
| res = 1; |
| break; |
| |
| default: |
| res =QWindowsStyle::styleHint(hint, option, widget, returnData); |
| } |
| |
| return res; |
| } |
| |
| /*! \reimp */ |
| QPalette QWindowsXPStyle::standardPalette() const |
| { |
| return QWindowsXPStylePrivate::useXP() && QApplicationPrivate::sys_pal |
| ? *QApplicationPrivate::sys_pal : QWindowsStyle::standardPalette(); |
| } |
| |
| /*! |
| \reimp |
| */ |
| QPixmap QWindowsXPStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option, |
| const QWidget *widget) const |
| { |
| if (!QWindowsXPStylePrivate::useXP()) |
| return QWindowsStyle::standardPixmap(standardPixmap, option, widget); |
| |
| switch(standardPixmap) { |
| case SP_TitleBarMaxButton: |
| case SP_TitleBarCloseButton: |
| if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) |
| { |
| if (widget && widget->isWindow()) { |
| XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL); |
| if (theme.isValid()) { |
| const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); |
| return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size); |
| } |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| return QWindowsStyle::standardPixmap(standardPixmap, option, widget); |
| } |
| |
| /*! |
| \reimp |
| */ |
| QIcon QWindowsXPStyle::standardIcon(StandardPixmap standardIcon, |
| const QStyleOption *option, |
| const QWidget *widget) const |
| { |
| if (!QWindowsXPStylePrivate::useXP()) { |
| return QWindowsStyle::standardIcon(standardIcon, option, widget); |
| } |
| |
| QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); |
| switch(standardIcon) { |
| case SP_TitleBarMaxButton: |
| if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) |
| { |
| if (d->dockFloat.isNull()) { |
| XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, |
| WP_SMALLCLOSEBUTTON, CBS_NORMAL); |
| XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, |
| WP_MAXBUTTON, MAXBS_NORMAL); |
| if (theme.isValid()) { |
| const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); |
| QPixmap pm(size); |
| pm.fill(Qt::transparent); |
| QPainter p(&pm); |
| theme.painter = &p; |
| theme.rect = QRect(QPoint(0, 0), size); |
| d->drawBackground(theme); |
| d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal |
| pm.fill(Qt::transparent); |
| theme.stateId = MAXBS_PUSHED; |
| d->drawBackground(theme); |
| d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed |
| pm.fill(Qt::transparent); |
| theme.stateId = MAXBS_HOT; |
| d->drawBackground(theme); |
| d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover |
| pm.fill(Qt::transparent); |
| theme.stateId = MAXBS_INACTIVE; |
| d->drawBackground(theme); |
| d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled |
| } |
| } |
| if (widget && widget->isWindow()) |
| return d->dockFloat; |
| |
| } |
| break; |
| case SP_TitleBarCloseButton: |
| if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) |
| { |
| if (d->dockClose.isNull()) { |
| XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, |
| WP_SMALLCLOSEBUTTON, CBS_NORMAL); |
| if (theme.isValid()) { |
| const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); |
| QPixmap pm(size); |
| pm.fill(Qt::transparent); |
| QPainter p(&pm); |
| theme.painter = &p; |
| theme.partId = WP_CLOSEBUTTON; // #### |
| theme.rect = QRect(QPoint(0, 0), size); |
| d->drawBackground(theme); |
| d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal |
| pm.fill(Qt::transparent); |
| theme.stateId = CBS_PUSHED; |
| d->drawBackground(theme); |
| d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed |
| pm.fill(Qt::transparent); |
| theme.stateId = CBS_HOT; |
| d->drawBackground(theme); |
| d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover |
| pm.fill(Qt::transparent); |
| theme.stateId = CBS_INACTIVE; |
| d->drawBackground(theme); |
| d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled |
| } |
| } |
| if (widget && widget->isWindow()) |
| return d->dockClose; |
| } |
| break; |
| case SP_TitleBarNormalButton: |
| if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) |
| { |
| if (d->dockFloat.isNull()) { |
| XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, |
| WP_SMALLCLOSEBUTTON, CBS_NORMAL); |
| XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, |
| WP_RESTOREBUTTON, RBS_NORMAL); |
| if (theme.isValid()) { |
| const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); |
| QPixmap pm(size); |
| pm.fill(Qt::transparent); |
| QPainter p(&pm); |
| theme.painter = &p; |
| theme.rect = QRect(QPoint(0, 0), size); |
| d->drawBackground(theme); |
| d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal |
| pm.fill(Qt::transparent); |
| theme.stateId = RBS_PUSHED; |
| d->drawBackground(theme); |
| d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed |
| pm.fill(Qt::transparent); |
| theme.stateId = RBS_HOT; |
| d->drawBackground(theme); |
| d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover |
| pm.fill(Qt::transparent); |
| theme.stateId = RBS_INACTIVE; |
| d->drawBackground(theme); |
| d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled |
| } |
| } |
| if (widget && widget->isWindow()) |
| return d->dockFloat; |
| |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return QWindowsStyle::standardIcon(standardIcon, option, widget); |
| } |
| |
| /*! |
| \internal |
| |
| Constructs a QWindowsXPStyle object. |
| */ |
| QWindowsXPStyle::QWindowsXPStyle(QWindowsXPStylePrivate &dd) : QWindowsStyle(dd) |
| { |
| } |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| QDebug operator<<(QDebug d, const XPThemeData &t) |
| { |
| QDebugStateSaver saver(d); |
| d.nospace(); |
| d << "XPThemeData(" << t.widget << ", theme=#" << t.theme << ", " << t.htheme |
| << ", partId=" << t.partId << ", stateId=" << t.stateId << ", rect=" << t.rect |
| << ", mirrorHorizontally=" << t.mirrorHorizontally << ", mirrorVertically=" |
| << t.mirrorVertically << ", noBorder=" << t.noBorder << ", noContent=" << t.noContent |
| << ", rotate=" << t.rotate << ')'; |
| return d; |
| } |
| |
| QDebug operator<<(QDebug d, const ThemeMapKey &k) |
| { |
| QDebugStateSaver saver(d); |
| d.nospace(); |
| d << "ThemeMapKey(theme=#" << k.theme |
| << ", partId=" << k.partId << ", stateId=" << k.stateId |
| << ", noBorder=" << k.noBorder << ", noContent=" << k.noContent << ')'; |
| return d; |
| } |
| |
| QDebug operator<<(QDebug d, const ThemeMapData &td) |
| { |
| QDebugStateSaver saver(d); |
| d.nospace(); |
| d << "ThemeMapData(alphaType=" << td.alphaType |
| << ", dataValid=" << td.dataValid << ", partIsTransparent=" << td.partIsTransparent |
| << ", hasAlphaChannel=" << td.hasAlphaChannel << ", wasAlphaSwapped=" << td.wasAlphaSwapped |
| << ", hadInvalidAlpha=" << td.hadInvalidAlpha << ')'; |
| return d; |
| } |
| #endif // QT_NO_DEBUG_STREAM |
| |
| // Debugging code ---------------------------------------------------------------------[ START ]--- |
| // The code for this point on is not compiled by default, but only used as assisting |
| // debugging code when you uncomment the DEBUG_XP_STYLE define at the top of the file. |
| |
| #ifdef DEBUG_XP_STYLE |
| // The schema file expects these to be defined by the user. |
| #define TMT_ENUMDEF 8 |
| #define TMT_ENUMVAL TEXT('A') |
| #define TMT_ENUM TEXT('B') |
| #define SCHEMA_STRINGS // For 2nd pass on schema file |
| QT_BEGIN_INCLUDE_NAMESPACE |
| #include <tmschema.h> |
| QT_END_INCLUDE_NAMESPACE |
| |
| // A property's value, type and name combo |
| struct PropPair { |
| int propValue; |
| int propType; |
| LPCWSTR propName; |
| }; |
| |
| // Operator for sorting of PropPairs |
| bool operator<(PropPair a, PropPair b) { |
| return wcscmp(a.propName, b.propName) < 0; |
| } |
| |
| // Our list of all possible properties |
| static QList<PropPair> all_props; |
| |
| |
| /*! \internal |
| Dumps a portion of the full native DIB section double buffer. |
| The DIB section double buffer is only used when doing special |
| transformations to the theme part, or when the real double |
| buffer in the paintengine does not have an HDC we may use |
| directly. |
| Since we cannot rely on the pixel data we get from Microsoft |
| when drawing into the DIB section, we use this function to |
| see the actual data we got, and can determin the appropriate |
| action. |
| */ |
| void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h) |
| { |
| if (w && h) { |
| static int pCount = 0; |
| DWORD *bufPix = (DWORD*)bufferPixels; |
| |
| char *bufferDump = new char[bufferH * bufferW * 16]; |
| char *bufferPos = bufferDump; |
| |
| memset(bufferDump, 0, sizeof(bufferDump)); |
| bufferPos += sprintf(bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w); |
| bufferPos += sprintf(bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h); |
| bufferPos += sprintf(bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount); |
| for (int iy = 0; iy < h; ++iy) { |
| bufferPos += sprintf(bufferPos, "\n "); |
| bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4)); |
| for (int ix = 0; ix < w; ++ix) { |
| bufferPos += sprintf(bufferPos, "0x%08x, ", *bufPix); |
| ++bufPix; |
| } |
| } |
| bufferPos += sprintf(bufferPos, "\n};\n\n"); |
| printf(bufferDump); |
| |
| delete[] bufferDump; |
| ++pCount; |
| } |
| } |
| |
| /*! \internal |
| Shows the value of a given property for a part. |
| */ |
| static void showProperty(XPThemeData &themeData, const PropPair &prop) |
| { |
| PROPERTYORIGIN origin = PO_NOTFOUND; |
| GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); |
| const char *originStr; |
| switch(origin) { |
| case PO_STATE: |
| originStr = "State "; |
| break; |
| case PO_PART: |
| originStr = "Part "; |
| break; |
| case PO_CLASS: |
| originStr = "Class "; |
| break; |
| case PO_GLOBAL: |
| originStr = "Globl "; |
| break; |
| case PO_NOTFOUND: |
| default: |
| originStr = "Unkwn "; |
| break; |
| } |
| |
| switch(prop.propType) { |
| case TMT_STRING: |
| { |
| wchar_t buffer[512]; |
| GetThemeString(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512); |
| printf(" (%sString) %-20S: %S\n", originStr, prop.propName, buffer); |
| } |
| break; |
| case TMT_ENUM: |
| { |
| int result = -1; |
| GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); |
| printf(" (%sEnum) %-20S: %d\n", originStr, prop.propName, result); |
| } |
| break; |
| case TMT_INT: |
| { |
| int result = -1; |
| GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); |
| printf(" (%sint) %-20S: %d\n", originStr, prop.propName, result); |
| } |
| break; |
| case TMT_BOOL: |
| { |
| BOOL result = false; |
| GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); |
| printf(" (%sbool) %-20S: %d\n", originStr, prop.propName, result); |
| } |
| break; |
| case TMT_COLOR: |
| { |
| COLORREF result = 0; |
| GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); |
| printf(" (%scolor) %-20S: 0x%08X\n", originStr, prop.propName, result); |
| } |
| break; |
| case TMT_MARGINS: |
| { |
| MARGINS result; |
| memset(&result, 0, sizeof(result)); |
| GetThemeMargins(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, 0, &result); |
| printf(" (%smargins) %-20S: (%d, %d, %d, %d)\n", originStr, |
| prop.propName, result.cxLeftWidth, result.cyTopHeight, result.cxRightWidth, result.cyBottomHeight); |
| } |
| break; |
| case TMT_FILENAME: |
| { |
| wchar_t buffer[512]; |
| GetThemeFilename(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512); |
| printf(" (%sfilename)%-20S: %S\n", originStr, prop.propName, buffer); |
| } |
| break; |
| case TMT_SIZE: |
| { |
| SIZE result1; |
| SIZE result2; |
| SIZE result3; |
| memset(&result1, 0, sizeof(result1)); |
| memset(&result2, 0, sizeof(result2)); |
| memset(&result3, 0, sizeof(result3)); |
| GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_MIN, &result1); |
| GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_TRUE, &result2); |
| GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_DRAW, &result3); |
| printf(" (%ssize) %-20S: Min (%d, %d), True(%d, %d), Draw(%d, %d)\n", originStr, prop.propName, |
| result1.cx, result1.cy, result2.cx, result2.cy, result3.cx, result3.cy); |
| } |
| break; |
| case TMT_POSITION: |
| { |
| POINT result; |
| memset(&result, 0, sizeof(result)); |
| GetThemePosition(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); |
| printf(" (%sPosition)%-20S: (%d, %d)\n", originStr, prop.propName, result.x, result.y); |
| } |
| break; |
| case TMT_RECT: |
| { |
| RECT result; |
| memset(&result, 0, sizeof(result)); |
| GetThemeRect(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); |
| printf(" (%sRect) %-20S: (%d, %d, %d, %d)\n", originStr, prop.propName, result.left, result.top, result.right, result.bottom); |
| } |
| break; |
| case TMT_FONT: |
| { |
| LOGFONT result; |
| memset(&result, 0, sizeof(result)); |
| GetThemeFont(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, &result); |
| printf(" (%sFont) %-20S: %S height(%d) width(%d) weight(%d)\n", originStr, prop.propName, |
| result.lfFaceName, result.lfHeight, result.lfWidth, result.lfWeight); |
| } |
| break; |
| case TMT_INTLIST: |
| { |
| INTLIST result; |
| memset(&result, 0, sizeof(result)); |
| GetThemeIntList(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); |
| printf(" (%sInt list)%-20S: { ", originStr, prop.propName); |
| for (int i = 0; i < result.iValueCount; ++i) |
| printf("%d ", result.iValues[i]); |
| printf("}\n"); |
| } |
| break; |
| default: |
| printf(" %s%S : Unknown property type (%d)!\n", originStr, prop.propName, prop.propType); |
| } |
| } |
| |
| /*! \internal |
| Dump all valid properties for a part. |
| If it's the first time this function is called, then the name, |
| enum value and documentation of all properties are shown, as |
| well as all global properties. |
| */ |
| void QWindowsXPStylePrivate::showProperties(XPThemeData &themeData) |
| { |
| if (!all_props.count()) { |
| const TMSCHEMAINFO *infoTable = GetSchemaInfo(); |
| for (int i = 0; i < infoTable->iPropCount; ++i) { |
| int propType = infoTable->pPropTable[i].bPrimVal; |
| int propValue = infoTable->pPropTable[i].sEnumVal; |
| LPCWSTR propName = infoTable->pPropTable[i].pszName; |
| |
| switch(propType) { |
| case TMT_ENUMDEF: |
| case TMT_ENUMVAL: |
| continue; |
| default: |
| if (propType != propValue) { |
| PropPair prop; |
| prop.propValue = propValue; |
| prop.propName = propName; |
| prop.propType = propType; |
| all_props.append(prop); |
| } |
| } |
| } |
| std::sort(all_props.begin(), all_props.end()); |
| |
| {// List all properties |
| printf("part properties count = %d:\n", all_props.count()); |
| printf(" Enum Property Name Description\n"); |
| printf("-----------------------------------------------------------\n"); |
| wchar_t themeName[256]; |
| pGetCurrentThemeName(themeName, 256, 0, 0, 0, 0); |
| for (int j = 0; j < all_props.count(); ++j) { |
| PropPair prop = all_props.at(j); |
| wchar_t buf[500]; |
| GetThemeDocumentationProperty(themeName, prop.propName, buf, 500); |
| printf("%3d: (%4d) %-20S %S\n", j, prop.propValue, prop.propName, buf); |
| } |
| } |
| |
| {// Show Global values |
| printf("Global Properties:\n"); |
| for (int j = 0; j < all_props.count(); ++j) { |
| PropPair prop = all_props.at(j); |
| PROPERTYORIGIN origin = PO_NOTFOUND; |
| GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); |
| if (origin == PO_GLOBAL) { |
| showProperty(themeData, prop); |
| } |
| } |
| } |
| } |
| |
| for (int j = 0; j < all_props.count(); ++j) { |
| PropPair prop = all_props.at(j); |
| PROPERTYORIGIN origin = PO_NOTFOUND; |
| GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); |
| if (origin != PO_NOTFOUND) |
| { |
| showProperty(themeData, prop); |
| } |
| } |
| } |
| #endif |
| // Debugging code -----------------------------------------------------------------------[ END ]--- |
| |
| |
| QT_END_NAMESPACE |