/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include "qstyle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WINCE_WM #include static bool qt_wince_is_smartphone() { wchar_t tszPlatform[64]; if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(tszPlatform)/sizeof(*tszPlatform),tszPlatform,0)) if (0 == _tcsicmp(reinterpret_cast (QString::fromLatin1("Smartphone").utf16()), tszPlatform)) return true; return false; } #endif #include class tst_QStyle : public QObject { Q_OBJECT public: tst_QStyle(); virtual ~tst_QStyle(); private: bool testAllFunctions(QStyle *); bool testScrollBarSubControls(QStyle *); void testPainting(QStyle *style, const QString &platform); private slots: void drawItemPixmap(); void initTestCase(); void cleanup(); void cleanupTestCase(); void init(); #ifndef QT_NO_STYLE_FUSION void testFusionStyle(); #endif void testWindowsStyle(); #if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSXP) void testWindowsXPStyle(); #endif #if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSVISTA) void testWindowsVistaStyle(); #endif #ifdef Q_OS_MAC void testMacStyle(); #endif #ifdef Q_OS_WINCE void testWindowsCEStyle(); #endif #ifdef Q_OS_WINCE_WM void testWindowsMobileStyle(); #endif void testStyleFactory(); void testProxyStyle(); void pixelMetric(); #if !defined(QT_NO_STYLE_WINDOWS) && !defined(QT_NO_STYLE_FUSION) void progressBarChangeStyle(); #endif void defaultFont(); void testDrawingShortcuts(); void testFrameOnlyAroundContents(); private: void lineUpLayoutTest(QStyle *); QWidget *testWidget; }; tst_QStyle::tst_QStyle() { testWidget = 0; } tst_QStyle::~tst_QStyle() { } class MyWidget : public QWidget { public: MyWidget( QWidget* QWidget=0, const char* name=0 ); protected: void paintEvent( QPaintEvent* ); }; void tst_QStyle::init() { testWidget = new MyWidget( 0, "testObject"); } void tst_QStyle::cleanup() { delete testWidget; testWidget = 0; } void tst_QStyle::initTestCase() { } void tst_QStyle::cleanupTestCase() { } void tst_QStyle::testStyleFactory() { QStringList keys = QStyleFactory::keys(); #ifndef QT_NO_STYLE_FUSION QVERIFY(keys.contains("Fusion")); #endif #ifndef QT_NO_STYLE_WINDOWS QVERIFY(keys.contains("Windows")); #endif #ifdef Q_OS_WIN if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) QVERIFY(keys.contains("WindowsXP")); if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) QVERIFY(keys.contains("WindowsVista")); #endif foreach (QString styleName , keys) { QStyle *style = QStyleFactory::create(styleName); QVERIFY2(style != 0, qPrintable(QString::fromLatin1("Fail to load style '%1'").arg(styleName))); delete style; } } class CustomProxy : public QProxyStyle { virtual int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const { if (metric == QStyle::PM_ButtonIconSize) return 13; return QProxyStyle::pixelMetric(metric, option, widget); } }; void tst_QStyle::testProxyStyle() { QProxyStyle *proxyStyle = new QProxyStyle(); QVERIFY(proxyStyle->baseStyle()); QStyle *style = QStyleFactory::create("Windows"); QVERIFY(style->proxy() == style); proxyStyle->setBaseStyle(style); QVERIFY(style->proxy() == proxyStyle); QVERIFY(style->parent() == proxyStyle); QVERIFY(proxyStyle->baseStyle() == style); QVERIFY(testAllFunctions(proxyStyle)); proxyStyle->setBaseStyle(0); QVERIFY(proxyStyle->baseStyle()); qApp->setStyle(proxyStyle); QProxyStyle doubleProxy(new QProxyStyle(QStyleFactory::create("Windows"))); QVERIFY(testAllFunctions(&doubleProxy)); CustomProxy customStyle; QLineEdit edit; edit.setStyle(&customStyle); QVERIFY(!customStyle.parent()); QVERIFY(edit.style()->pixelMetric(QStyle::PM_ButtonIconSize) == 13); } void tst_QStyle::drawItemPixmap() { testWidget->resize(300, 300); testWidget->show(); QPixmap p(QString(SRCDIR) + "/task_25863.png", "PNG"); const QPixmap actualPix = testWidget->grab(); QCOMPARE(actualPix, p); testWidget->hide(); } bool tst_QStyle::testAllFunctions(QStyle *style) { QStyleOption opt; opt.init(testWidget); testWidget->setStyle(style); //Tests styleHint with default arguments for potential crashes for ( int hint = 0 ; hint < int(QStyle::SH_Menu_Mask); ++hint) { style->styleHint(QStyle::StyleHint(hint)); style->styleHint(QStyle::StyleHint(hint), &opt, testWidget); } //Tests pixelMetric with default arguments for potential crashes for ( int pm = 0 ; pm < int(QStyle::PM_LayoutVerticalSpacing); ++pm) { style->pixelMetric(QStyle::PixelMetric(pm)); style->pixelMetric(QStyle::PixelMetric(pm), &opt, testWidget); } //Tests drawControl with default arguments for potential crashes for ( int control = 0 ; control < int(QStyle::CE_ColumnViewGrip); ++control) { QPixmap surface(QSize(200, 200)); QPainter painter(&surface); style->drawControl(QStyle::ControlElement(control), &opt, &painter, 0); } //Tests drawComplexControl with default arguments for potential crashes { QPixmap surface(QSize(200, 200)); QPainter painter(&surface); QStyleOptionComboBox copt1; copt1.init(testWidget); QStyleOptionGroupBox copt2; copt2.init(testWidget); QStyleOptionSizeGrip copt3; copt3.init(testWidget); QStyleOptionSlider copt4; copt4.init(testWidget); copt4.minimum = 0; copt4.maximum = 100; copt4.tickInterval = 25; copt4.sliderValue = 50; QStyleOptionSpinBox copt5; copt5.init(testWidget); QStyleOptionTitleBar copt6; copt6.init(testWidget); QStyleOptionToolButton copt7; copt7.init(testWidget); QStyleOptionComplex copt9; copt9.initFrom(testWidget); style->drawComplexControl(QStyle::CC_SpinBox, &copt5, &painter, 0); style->drawComplexControl(QStyle::CC_ComboBox, &copt1, &painter, 0); style->drawComplexControl(QStyle::CC_ScrollBar, &copt4, &painter, 0); style->drawComplexControl(QStyle::CC_Slider, &copt4, &painter, 0); style->drawComplexControl(QStyle::CC_ToolButton, &copt7, &painter, 0); style->drawComplexControl(QStyle::CC_TitleBar, &copt6, &painter, 0); style->drawComplexControl(QStyle::CC_GroupBox, &copt2, &painter, 0); style->drawComplexControl(QStyle::CC_Dial, &copt4, &painter, 0); } //Check standard pixmaps/icons for ( int i = 0 ; i < int(QStyle::SP_ToolBarVerticalExtensionButton); ++i) { QPixmap pixmap = style->standardPixmap(QStyle::StandardPixmap(i)); if (pixmap.isNull()) { qWarning("missing StandardPixmap: %d", i); } QIcon icn = style->standardIcon(QStyle::StandardPixmap(i)); if (icn.isNull()) { qWarning("missing StandardIcon: %d", i); } } style->itemPixmapRect(QRect(0, 0, 100, 100), Qt::AlignHCenter, QPixmap(200, 200)); style->itemTextRect(QFontMetrics(qApp->font()), QRect(0, 0, 100, 100), Qt::AlignHCenter, true, QString("Test")); return testScrollBarSubControls(style); } bool tst_QStyle::testScrollBarSubControls(QStyle* style) { // WinCE SmartPhone doesn't have scrollbar subcontrols, so skip the rest of the test. #ifdef Q_OS_WINCE_WM if (style->inherits("QWindowsMobileStyle") && qt_wince_is_smartphone()) return true; #else Q_UNUSED(style); #endif QScrollBar scrollBar; scrollBar.show(); const QStyleOptionSlider opt = qt_qscrollbarStyleOption(&scrollBar); foreach (int subControl, QList() << 1 << 2 << 4 << 8) { QRect sr = testWidget->style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SubControl(subControl), &scrollBar); if (sr.isNull()) { qWarning("Null rect for subcontrol %d", subControl); return false; } } return true; } #ifndef QT_NO_STYLE_FUSION void tst_QStyle::testFusionStyle() { QStyle *fstyle = QStyleFactory::create("Fusion"); QVERIFY(testAllFunctions(fstyle)); lineUpLayoutTest(fstyle); delete fstyle; } #endif void tst_QStyle::testWindowsStyle() { QStyle *wstyle = QStyleFactory::create("Windows"); QVERIFY(testAllFunctions(wstyle)); lineUpLayoutTest(wstyle); // Tests drawing indeterminate progress with 0 size: QTBUG-15973 QStyleOptionProgressBar pb; pb.rect = QRect(0,0,-9,0); QPixmap surface(QSize(200, 200)); QPainter painter(&surface); wstyle->drawControl(QStyle::CE_ProgressBar, &pb, &painter, 0); delete wstyle; } #if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSXP) // WindowsXP style void tst_QStyle::testWindowsXPStyle() { QStyle *xpstyle = QStyleFactory::create("WindowsXP"); QVERIFY(testAllFunctions(xpstyle)); lineUpLayoutTest(xpstyle); delete xpstyle; } #endif void writeImage(const QString &fileName, QImage image) { QImageWriter imageWriter(fileName); imageWriter.setFormat("png"); qDebug() << "result " << imageWriter.write(image); } QImage readImage(const QString &fileName) { QImageReader reader(fileName); return reader.read(); } #if defined(Q_OS_WIN) && !defined(QT_NO_STYLE_WINDOWSVISTA) void tst_QStyle::testWindowsVistaStyle() { QStyle *vistastyle = QStyleFactory::create("WindowsVista"); QVERIFY(testAllFunctions(vistastyle)); if (QSysInfo::WindowsVersion == QSysInfo::WV_VISTA) testPainting(vistastyle, "vista"); else if (QSysInfo::WindowsVersion == QSysInfo::WV_XP) testPainting(vistastyle, "xp"); delete vistastyle; } #endif void comparePixmap(const QString &filename, const QPixmap &pixmap) { QImage oldFile = readImage(filename); QPixmap oldPixmap = QPixmap::fromImage(oldFile); if (!oldFile.isNull()) QCOMPARE(pixmap, oldPixmap); else writeImage(filename, pixmap.toImage()); } void tst_QStyle::testPainting(QStyle *style, const QString &platform) { qDebug("TEST PAINTING"); //Test Menu QString fileName = "images/" + platform + "/menu.png"; QMenu menu; menu.setStyle(style); menu.show(); menu.addAction(new QAction("Test 1", &menu)); menu.addAction(new QAction("Test 2", &menu)); QPixmap pixmap = menu.grab(); comparePixmap(fileName, pixmap); //Push button fileName = "images/" + platform + "/button.png"; QPushButton button("OK"); button.setStyle(style); button.show(); pixmap = button.grab(); button.hide(); comparePixmap(fileName, pixmap); //Push button fileName = "images/" + platform + "/radiobutton.png"; QRadioButton radiobutton("Check"); radiobutton.setStyle(style); radiobutton.show(); pixmap = radiobutton.grab(); radiobutton.hide(); comparePixmap(fileName, pixmap); //Combo box fileName = "images/" + platform + "/combobox.png"; QComboBox combobox; combobox.setStyle(style); combobox.addItem("Test 1"); combobox.addItem("Test 2"); combobox.show(); pixmap = combobox.grab(); combobox.hide(); comparePixmap(fileName, pixmap); //Spin box fileName = "images/" + platform + "/spinbox.png"; QDoubleSpinBox spinbox; spinbox.setLocale(QLocale(QLocale::English, QLocale::UnitedStates)); spinbox.setStyle(style); spinbox.show(); pixmap = spinbox.grab(); spinbox.hide(); comparePixmap(fileName, pixmap); QLocale::setDefault(QLocale::system()); //Slider fileName = "images/" + platform + "/slider.png"; QSlider slider; slider.setStyle(style); slider.show(); pixmap = slider.grab(); slider.hide(); comparePixmap(fileName, pixmap); //Line edit fileName = "images/" + platform + "/lineedit.png"; QLineEdit lineedit("Test text"); lineedit.setStyle(style); lineedit.show(); pixmap = lineedit.grab(); lineedit.hide(); comparePixmap(fileName, pixmap); //MDI fileName = "images/" + platform + "/mdi.png"; QMdiArea mdiArea; mdiArea.addSubWindow(new QWidget(&mdiArea)); mdiArea.resize(200, 200); mdiArea.setStyle(style); mdiArea.show(); pixmap = mdiArea.grab(); mdiArea.hide(); comparePixmap(fileName, pixmap); // QToolButton fileName = "images/" + platform + "/toolbutton.png"; QToolButton tb; tb.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); tb.setText("AaQqPpXx"); tb.setIcon(style->standardPixmap(QStyle::SP_DirHomeIcon)); tb.setStyle(style); tb.show(); pixmap = tb.grab(); tb.hide(); comparePixmap(fileName, pixmap); } #ifdef Q_OS_MAC void tst_QStyle::testMacStyle() { QStyle *mstyle = QStyleFactory::create("Macintosh"); QVERIFY(testAllFunctions(mstyle)); delete mstyle; } #endif #ifdef Q_OS_WINCE // WindowsCEStyle style void tst_QStyle::testWindowsCEStyle() { QStyle *cstyle = QStyleFactory::create("WindowsCE"); QVERIFY(testAllFunctions(&cstyle)); delete cstyle; } #endif #ifdef Q_OS_WINCE_WM // WindowsMobileStyle style void tst_QStyle::testWindowsMobileStyle() { QStyle *cstyle = QStyleFactory::create("WindowsMobile"); QVERIFY(testAllFunctions(&cstyle)); delete cstyle; } #endif // Helper class... MyWidget::MyWidget( QWidget* parent, const char* name ) : QWidget( parent ) { setObjectName(name); } void MyWidget::paintEvent( QPaintEvent* ) { QPainter p(this); QPixmap big(400,400); big.fill(Qt::green); style()->drawItemPixmap(&p, rect(), Qt::AlignCenter, big); } class Qt42Style : public QCommonStyle { Q_OBJECT public: Qt42Style() : QCommonStyle() { margin_toplevel = 10; margin = 5; spacing = 0; } virtual int pixelMetric(PixelMetric metric, const QStyleOption * option = 0, const QWidget * widget = 0 ) const; int margin_toplevel; int margin; int spacing; }; int Qt42Style::pixelMetric(PixelMetric metric, const QStyleOption * option /*= 0*/, const QWidget * widget /*= 0*/ ) const { switch (metric) { case QStyle::PM_DefaultTopLevelMargin: return margin_toplevel; break; case QStyle::PM_DefaultChildMargin: return margin; break; case QStyle::PM_DefaultLayoutSpacing: return spacing; break; default: break; } return -1; } void tst_QStyle::pixelMetric() { Qt42Style *style = new Qt42Style(); QCOMPARE(style->pixelMetric(QStyle::PM_DefaultTopLevelMargin), 10); QCOMPARE(style->pixelMetric(QStyle::PM_DefaultChildMargin), 5); QCOMPARE(style->pixelMetric(QStyle::PM_DefaultLayoutSpacing), 0); style->margin_toplevel = 0; style->margin = 0; style->spacing = 0; QCOMPARE(style->pixelMetric(QStyle::PM_DefaultTopLevelMargin), 0); QCOMPARE(style->pixelMetric(QStyle::PM_DefaultChildMargin), 0); QCOMPARE(style->pixelMetric(QStyle::PM_DefaultLayoutSpacing), 0); style->margin_toplevel = -1; style->margin = -1; style->spacing = -1; QCOMPARE(style->pixelMetric(QStyle::PM_DefaultTopLevelMargin), -1); QCOMPARE(style->pixelMetric(QStyle::PM_DefaultChildMargin), -1); QCOMPARE(style->pixelMetric(QStyle::PM_DefaultLayoutSpacing), -1); delete style; } #if !defined(QT_NO_STYLE_WINDOWS) && !defined(QT_NO_STYLE_FUSION) void tst_QStyle::progressBarChangeStyle() { //test a crashing situation (task 143530) //where changing the styles and deleting a progressbar would crash QStyle *style1 = QStyleFactory::create("Windows"); QStyle *style2 = QStyleFactory::create("Fusion"); QProgressBar *progress=new QProgressBar; progress->setStyle(style1); progress->show(); progress->setStyle(style2); QTest::qWait(100); delete progress; QTest::qWait(100); //before the correction, there would be a crash here delete style1; delete style2; } #endif void tst_QStyle::lineUpLayoutTest(QStyle *style) { QWidget widget; QHBoxLayout layout; QFont font; font.setPointSize(9); //Plastique is lined up for odd numbers... widget.setFont(font); QSpinBox spinbox(&widget); QLineEdit lineedit(&widget); QComboBox combo(&widget); combo.setEditable(true); layout.addWidget(&spinbox); layout.addWidget(&lineedit); layout.addWidget(&combo); widget.setLayout(&layout); widget.setStyle(style); // propagate the style. foreach (QWidget *w, qFindChildren(&widget)) w->setStyle(style); widget.show(); QTest::qWait( 500 ); QVERIFY(qAbs(spinbox.height() - lineedit.height()) <= 1); QVERIFY(qAbs(spinbox.height() - combo.height()) <= 1); } void tst_QStyle::defaultFont() { QFont defaultFont = qApp->font(); QFont pointFont = defaultFont; pointFont.setPixelSize(9); qApp->setFont(pointFont); QPushButton button; button.show(); qApp->processEvents(); qApp->setFont(defaultFont); } class DrawTextStyle : public QProxyStyle { Q_OBJECT public: DrawTextStyle(QStyle *base = 0) : QProxyStyle(), alignment(0) { setBaseStyle(base); } void drawItemText(QPainter *painter, const QRect &rect, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const { DrawTextStyle *that = (DrawTextStyle *)this; that->alignment = flags; QProxyStyle::drawItemText(painter, rect, flags, pal, enabled, text, textRole); } int alignment; }; void tst_QStyle::testDrawingShortcuts() { { QWidget w; QToolButton *tb = new QToolButton(&w); tb->setText("&abc"); DrawTextStyle *dts = new DrawTextStyle; w.show(); tb->setStyle(dts); tb->grab(); QStyleOptionToolButton sotb; sotb.initFrom(tb); bool showMnemonic = dts->styleHint(QStyle::SH_UnderlineShortcut, &sotb, tb); QVERIFY(dts->alignment & (showMnemonic ? Qt::TextShowMnemonic : Qt::TextHideMnemonic)); delete dts; } { QToolBar w; QToolButton *tb = new QToolButton(&w); tb->setText("&abc"); DrawTextStyle *dts = new DrawTextStyle; w.addWidget(tb); w.show(); tb->setStyle(dts); tb->grab(); QStyleOptionToolButton sotb; sotb.initFrom(tb); bool showMnemonic = dts->styleHint(QStyle::SH_UnderlineShortcut, &sotb, tb); QVERIFY(dts->alignment & (showMnemonic ? Qt::TextShowMnemonic : Qt::TextHideMnemonic)); delete dts; } } #define SCROLLBAR_SPACING 33 class FrameTestStyle : public QProxyStyle { public: FrameTestStyle() : QProxyStyle(QStyleFactory::create("Windows")) { } int styleHint(StyleHint hint, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *returnData) const { if (hint == QStyle::SH_ScrollView_FrameOnlyAroundContents) return 1; return QProxyStyle ::styleHint(hint, opt, widget, returnData); } int pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const { if (pm == QStyle::PM_ScrollView_ScrollBarSpacing) return SCROLLBAR_SPACING; return QProxyStyle ::pixelMetric(pm, option ,widget); } }; void tst_QStyle::testFrameOnlyAroundContents() { QScrollArea area; area.setGeometry(0, 0, 200, 200); QStyle *winStyle = QStyleFactory::create("Windows"); FrameTestStyle frameStyle; QWidget *widget = new QWidget(&area); widget->setGeometry(0, 0, 400, 400); area.setStyle(winStyle); area.verticalScrollBar()->setStyle(winStyle); area.setWidget(widget); area.setVisible(true); int viewPortWidth = area.viewport()->width(); area.verticalScrollBar()->setStyle(&frameStyle); area.setStyle(&frameStyle); // Test that we reserve space for scrollbar spacing QVERIFY(viewPortWidth == area.viewport()->width() + SCROLLBAR_SPACING); delete winStyle; } QTEST_MAIN(tst_QStyle) #include "tst_qstyle.moc"