From c830979fbb176e6e48b3ff45c84fa0742aaa186e Mon Sep 17 00:00:00 2001 From: Oliver Eftevaag Date: Mon, 15 Mar 2021 16:05:11 +0100 Subject: Add FontDialog to QtQuick.Dialogs Adding non-native FontDialog to QtQuick. This is a native FontDialog on platforms that support it, and a non-native Qt Quick FontDialog on platforms that don't. Fixes: QTBUG-87799 Change-Id: I43a59e3668a8a40f1d0c04a3c2506283d552a22b Reviewed-by: Mitch Curtis --- tests/auto/dialogs/CMakeLists.txt | 1 + tests/auto/dialogs/qquickfontdialogimpl/BLACKLIST | 4 + .../dialogs/qquickfontdialogimpl/CMakeLists.txt | 39 ++ .../qquickfontdialogimpl/data/fontDialog.qml | 65 +++ .../tst_qquickfontdialogimpl.cpp | 520 +++++++++++++++++++++ tests/manual/dialogs/CMakeLists.txt | 1 + tests/manual/dialogs/FontDialogPage.qml | 222 +++++++++ tests/manual/dialogs/dialogs.pro | 1 + tests/manual/dialogs/dialogs.qml | 3 +- 9 files changed, 855 insertions(+), 1 deletion(-) create mode 100644 tests/auto/dialogs/qquickfontdialogimpl/BLACKLIST create mode 100644 tests/auto/dialogs/qquickfontdialogimpl/CMakeLists.txt create mode 100644 tests/auto/dialogs/qquickfontdialogimpl/data/fontDialog.qml create mode 100644 tests/auto/dialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp create mode 100644 tests/manual/dialogs/FontDialogPage.qml (limited to 'tests') diff --git a/tests/auto/dialogs/CMakeLists.txt b/tests/auto/dialogs/CMakeLists.txt index 26eb933f..94485f87 100644 --- a/tests/auto/dialogs/CMakeLists.txt +++ b/tests/auto/dialogs/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(qquickfiledialogimpl) +add_subdirectory(qquickfontdialogimpl) diff --git a/tests/auto/dialogs/qquickfontdialogimpl/BLACKLIST b/tests/auto/dialogs/qquickfontdialogimpl/BLACKLIST new file mode 100644 index 00000000..acabf6c5 --- /dev/null +++ b/tests/auto/dialogs/qquickfontdialogimpl/BLACKLIST @@ -0,0 +1,4 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format +# QTBUG-94225 +[listViews] +b2qt diff --git a/tests/auto/dialogs/qquickfontdialogimpl/CMakeLists.txt b/tests/auto/dialogs/qquickfontdialogimpl/CMakeLists.txt new file mode 100644 index 00000000..0250c64e --- /dev/null +++ b/tests/auto/dialogs/qquickfontdialogimpl/CMakeLists.txt @@ -0,0 +1,39 @@ +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_qquickfontdialogimpl + SOURCES + ../../shared/qtest_quickcontrols.h + ../../shared/util.cpp ../../shared/util.h + ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h + tst_qquickfontdialogimpl.cpp + DEFINES + QQC2_IMPORT_PATH=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/imports\\\" + PUBLIC_LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickControls2 + Qt::QuickControls2Private + Qt::QuickDialogs2Private + Qt::QuickDialogs2QuickImplPrivate + Qt::QuickPrivate + Qt::QuickTemplates2Private + Qt::QuickTest + Qt::TestPrivate + TESTDATA ${test_data} +) + +qt_internal_extend_target(tst_qquickfontdialogimpl CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=\\\":/data\\\" +) + +qt_internal_extend_target(tst_qquickfontdialogimpl CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" +) diff --git a/tests/auto/dialogs/qquickfontdialogimpl/data/fontDialog.qml b/tests/auto/dialogs/qquickfontdialogimpl/data/fontDialog.qml new file mode 100644 index 00000000..4979d473 --- /dev/null +++ b/tests/auto/dialogs/qquickfontdialogimpl/data/fontDialog.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +ApplicationWindow { + width: 640 + height: 480 + + property alias dialog: dialog + + FontDialog { + id: dialog + objectName: "FontDialog" + } +} diff --git a/tests/auto/dialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp b/tests/auto/dialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp new file mode 100644 index 00000000..42f60f1b --- /dev/null +++ b/tests/auto/dialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp @@ -0,0 +1,520 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later 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 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../../shared/dialogtestutil.h" +#include "../../shared/util.h" +#include "../../shared/visualtestutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace QQuickDialogTestUtil; +using namespace QQuickVisualTestUtil; + +class tst_QQuickFontDialogImpl : public QQmlDataTest +{ + Q_OBJECT + +private slots: + void writingSystem(); + void listViews(); + void effects(); + void changeFontSize(); + void changeDialogTitle(); + void searchFamily(); + +private: + QQuickAbstractButton *findDialogButton(QQuickDialogButtonBox *box, const QString &buttonText) + { + for (int i = 0; i < box->count(); ++i) { + auto button = qobject_cast(box->itemAt(i)); + if (button && button->text().toUpper() == buttonText.toUpper()) + return button; + } + return nullptr; + } + + bool closePopup(DialogTestHelper *dialogHelper, + QString dialogButton, QString &failureMessage) + { + auto dialogButtonBox = + dialogHelper->quickDialog->footer()->findChild(); + + if (!dialogButtonBox) { + failureMessage = QLatin1String("dialogButtonBox is null"); + return false; + } + + QQuickAbstractButton *openButton = findDialogButton(dialogButtonBox, dialogButton); + + if (!openButton) { + failureMessage = + QLatin1String("Couldn't find a button with text '%1'").arg(dialogButton); + return false; + } + + bool clicked = clickButton(openButton); + + if (!clicked) { + failureMessage = QLatin1String("'%1' button was never clicked").arg(dialogButton); + return false; + } + + bool visible = dialogHelper->dialog->isVisible(); + if (visible) { + failureMessage = QLatin1String("Dialog is still visible after clicking the '%1' button") + .arg(dialogButton); + return false; + } + return true; + } +}; + +#define CREATE_DIALOG_TEST_HELPER \ + DialogTestHelper dialogHelper(this, "fontDialog.qml"); \ + QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage()); \ + QVERIFY(dialogHelper.waitForWindowActive()); + +#define CLOSE_DIALOG(BUTTON) \ + QString errorMessage; \ + QVERIFY2(closePopup(&dialogHelper, BUTTON, errorMessage), qPrintable(errorMessage)); \ + QTRY_VERIFY(!dialogHelper.quickDialog->isVisible()); + +void tst_QQuickFontDialogImpl::writingSystem() +{ + CREATE_DIALOG_TEST_HELPER + + // Open the dialog. + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + const QQuickTextEdit *sampleEdit = + dialogHelper.quickDialog->findChild("sampleEdit"); + QVERIFY(sampleEdit); + + QCOMPARE(sampleEdit->text(), QFontDatabase::writingSystemSample(QFontDatabase::Any)); + + // Check that the font family list view exist and add signal spy. + const QQuickListView *fontFamilyListView = + dialogHelper.quickDialog->findChild("familyListView"); + QVERIFY(fontFamilyListView); + QSignalSpy fontFamilyModelSpy(fontFamilyListView, SIGNAL(modelChanged())); + + // Open the ComboBox's popup. + const QQuickComboBox *writingSystemComboBox = dialogHelper.quickDialog->findChild(); + QVERIFY(writingSystemComboBox); + const QPoint comboBoxCenterPos = writingSystemComboBox->mapToScene({ writingSystemComboBox->width() / 2, writingSystemComboBox->height() / 2 }).toPoint(); + QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, comboBoxCenterPos); + QTRY_VERIFY(writingSystemComboBox->popup()->isOpened()); + + // "Any" should be selected by default. + QQuickListView *comboBoxPopupListView = qobject_cast(writingSystemComboBox->popup()->contentItem()); + QVERIFY(comboBoxPopupListView); + const int anyIndex = QFontDatabase::Any; + QQuickAbstractButton *anyDelegate = qobject_cast(findViewDelegateItem(comboBoxPopupListView, anyIndex)); + QVERIFY(anyDelegate); + QCOMPARE(anyDelegate->text(), QFontDatabase::writingSystemName(QFontDatabase::Any)); + + QCOMPARE(fontFamilyModelSpy.count(), 0); + + // Select "Japanese" from the ComboBox. + const int japaneseIndex = QFontDatabase::Japanese; + QQuickAbstractButton *japaneseDelegate = qobject_cast(findViewDelegateItem(comboBoxPopupListView, japaneseIndex)); + QVERIFY(japaneseDelegate); + QCOMPARE(japaneseDelegate->text(), QFontDatabase::writingSystemName(QFontDatabase::Japanese)); + QVERIFY(clickButton(japaneseDelegate)); + QTRY_VERIFY(!writingSystemComboBox->popup()->isVisible()); + + // Check that the contents of the font family listview changed + QCOMPARE(fontFamilyModelSpy.count(), 1); + + // And that the sample text is correctly set + QCOMPARE(sampleEdit->text(), QFontDatabase::writingSystemSample(QFontDatabase::Japanese)); + + // Then accept it to close it. + auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild(); + QVERIFY(dialogButtonBox); + QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Ok"); + QVERIFY(openButton); + QVERIFY(clickButton(openButton)); + QVERIFY(!dialogHelper.dialog->isVisible()); + QTRY_VERIFY(!dialogHelper.quickDialog->isVisible()); +} + +void tst_QQuickFontDialogImpl::listViews() +{ + CREATE_DIALOG_TEST_HELPER + + // Open the dialog. + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + QQuickListView *fontFamilyListView = + dialogHelper.quickDialog->findChild("familyListView"); + QVERIFY(fontFamilyListView); + + const QQuickListView *fontStyleListView = + dialogHelper.quickDialog->findChild("styleListView"); + QVERIFY(fontStyleListView); + + const QQuickListView *fontSizeListView = + dialogHelper.quickDialog->findChild("sizeListView"); + QVERIFY(fontSizeListView); + + QSignalSpy currentFontSpy(dialogHelper.dialog, SIGNAL(currentFontChanged())); + QSignalSpy styleModelSpy(fontStyleListView, SIGNAL(modelChanged())); + QSignalSpy sizeModelSpy(fontSizeListView, SIGNAL(modelChanged())); + + QCOMPARE(fontFamilyListView->currentIndex(), 0); + QCOMPARE(fontStyleListView->currentIndex(), 0); + QCOMPARE(fontSizeListView->currentIndex(), 0); + + auto fontListModel = QFontDatabase::families(QFontDatabase::WritingSystem::Any); + fontListModel.removeIf(QFontDatabase::isPrivateFamily); + + // In case the font database contains a significant number of font families + const int maxNumberOfFamiliesToTest = 10; + + for (auto i = 1; i < qMin(fontListModel.size(), maxNumberOfFamiliesToTest); ++i) { + currentFontSpy.clear(); + styleModelSpy.clear(); + sizeModelSpy.clear(); + + const QString err = QString("LOOP INDEX %1, EXPECTED %2, ACTUAL %3").arg(i); + + const QFont currentFont = dialogHelper.dialog->currentFont(); + + const auto oldStyleModel = fontStyleListView->model(); + const auto oldSizeModel = fontSizeListView->model(); + + const QString expected1 = fontListModel[i - 1], actual1 = currentFont.family(); + QVERIFY2(expected1 == actual1, qPrintable(err.arg(expected1, actual1))); + + QQuickAbstractButton *fontDelegate = + qobject_cast(findViewDelegateItem(fontFamilyListView, i)); + QVERIFY2(fontDelegate, qPrintable(QString("LOOP INDEX %1").arg(i))); + + QVERIFY2(clickButton(fontDelegate), qPrintable(QString("LOOP INDEX %1").arg(i))); + + const QString expected2 = fontListModel[i], + actual2 = dialogHelper.dialog->currentFont().family(); + QVERIFY2(expected2 == actual2, qPrintable(err.arg(expected2, actual2).append(" font: ").append(fontDelegate->text()))); + QVERIFY2(currentFontSpy.count() == 1, qPrintable(err.arg(1, currentFontSpy.count()))); + QVERIFY2((oldStyleModel == fontStyleListView->model()) != (styleModelSpy.count() == 1), + qPrintable(QString("LOOP INDEX %1").arg(i))); + QVERIFY2((oldSizeModel == fontSizeListView->model()) != (sizeModelSpy.count() == 1), + qPrintable(QString("LOOP INDEX %1").arg(i))); + } + + // Then accept it to close it. + QVERIFY(dialogHelper.dialog->currentFont() != dialogHelper.dialog->selectedFont()); + + CLOSE_DIALOG("Ok"); + + QVERIFY(dialogHelper.dialog->currentFont() == dialogHelper.dialog->selectedFont()); +} + +void tst_QQuickFontDialogImpl::effects() +{ + CREATE_DIALOG_TEST_HELPER + + // Open the dialog. + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + QSignalSpy currentFontSpy(dialogHelper.dialog, SIGNAL(currentFontChanged())); + + QQuickCheckBox *underlineCheckBox = + dialogHelper.quickDialog->findChild("underlineEffect"); + QVERIFY(underlineCheckBox); + + QQuickCheckBox *strikeoutCheckBox = + dialogHelper.quickDialog->findChild("strikeoutEffect"); + QVERIFY(strikeoutCheckBox); + + QVERIFY(!dialogHelper.dialog->currentFont().underline()); + QVERIFY(!dialogHelper.dialog->currentFont().strikeOut()); + + QVERIFY(clickButton(underlineCheckBox)); + + QCOMPARE(currentFontSpy.count(), 1); + QVERIFY(dialogHelper.dialog->currentFont().underline()); + QVERIFY(!dialogHelper.dialog->currentFont().strikeOut()); + + QVERIFY(clickButton(underlineCheckBox)); + + QCOMPARE(currentFontSpy.count(), 2); + QVERIFY(!dialogHelper.dialog->currentFont().underline()); + QVERIFY(!dialogHelper.dialog->currentFont().strikeOut()); + + QVERIFY(clickButton(strikeoutCheckBox)); + + QCOMPARE(currentFontSpy.count(), 3); + QVERIFY(!dialogHelper.dialog->currentFont().underline()); + QVERIFY(dialogHelper.dialog->currentFont().strikeOut()); + + QVERIFY(clickButton(strikeoutCheckBox)); + + QCOMPARE(currentFontSpy.count(), 4); + QVERIFY(!dialogHelper.dialog->currentFont().underline()); + QVERIFY(!dialogHelper.dialog->currentFont().strikeOut()); + + // Then accept it to close it. + CLOSE_DIALOG("Ok"); +} + +void tst_QQuickFontDialogImpl::changeFontSize() +{ + CREATE_DIALOG_TEST_HELPER + + // Open the dialog. + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + QQuickTextField *sizeEdit = dialogHelper.quickDialog->findChild("sizeEdit"); + QVERIFY(sizeEdit); + + QQuickListView *fontSizeListView = + dialogHelper.quickDialog->findChild("sizeListView"); + QVERIFY(fontSizeListView); + + const QQuickItemDelegate *firstSizeDelegate = + qobject_cast(findViewDelegateItem(fontSizeListView, 0)); + + QCOMPARE(dialogHelper.dialog->currentFont().pointSize(), firstSizeDelegate->text().toInt()); + + sizeEdit->setText("15"); + QCOMPARE(dialogHelper.dialog->currentFont().pointSize(), 15); + + sizeEdit->setText("22"); + QCOMPARE(dialogHelper.dialog->currentFont().pointSize(), 22); + + QVERIFY(dialogHelper.dialog->currentFont() != dialogHelper.dialog->selectedFont()); + + // Then accept it to close it. + CLOSE_DIALOG("Ok"); + + QVERIFY(dialogHelper.dialog->currentFont() == dialogHelper.dialog->selectedFont()); +} + +void tst_QQuickFontDialogImpl::changeDialogTitle() +{ + CREATE_DIALOG_TEST_HELPER + + // Open the dialog. + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + const auto dialogButtonBox = + dialogHelper.quickDialog->footer()->findChild(); + QVERIFY(dialogButtonBox); + QQuickAbstractButton *cancelButton = findDialogButton(dialogButtonBox, "Cancel"); + QVERIFY(cancelButton); + + const QQuickLabel *titleLabel = dialogHelper.quickDialog->header()->findChild(); + QVERIFY(titleLabel); + + QCOMPARE(titleLabel->text(), QString()); + + const QString newTitle1 = QLatin1String("Some random title #1"); + + // Dialog must be closed for the title to update + QVERIFY(clickButton(cancelButton)); + QVERIFY(!dialogHelper.dialog->isVisible()); + QTRY_VERIFY(!dialogHelper.quickDialog->isVisible()); + + dialogHelper.dialog->setTitle(newTitle1); + + // Reopen the dialog + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + QVERIFY(dialogHelper.dialog->isVisible()); + QTRY_VERIFY(dialogHelper.quickDialog->isVisible()); + + QCOMPARE(titleLabel->text(), newTitle1); + + const QString newTitle2 = QLatin1String("Some random other title #2"); + + dialogHelper.dialog->setTitle(newTitle2); + + // Shouldn't update unless you reopen the dialog + QCOMPARE(titleLabel->text(), newTitle1); + + QVERIFY(clickButton(cancelButton)); + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + // Should now be updated + QCOMPARE(titleLabel->text(), newTitle2); + + CLOSE_DIALOG("Ok"); +} + +/* + Represents the expected search logic we use when the user types text into + one of the read-only text edits. + + As we test against real fonts installed on the system and hence cannot use + a hard-coded list, we need this helper to ensure that the (non-trivial) + searching behavior matches what we expect. +*/ +class ListSearchHelper +{ +public: + ListSearchHelper(const QStringList &model) + : m_model(model) + { + } + + int expectedCurrentIndexForSearch(const QString &searchText) + { + bool redo = false; + + do { + m_searchText.append(searchText); + + for (int i = 0; i < m_model.count(); ++i) { + if (m_model.at(i).startsWith(m_searchText, Qt::CaseInsensitive)) + return i; + } + + m_searchText.clear(); + + redo = !redo; + } while (redo); + + return -1; + } + +private: + QStringList m_model; + QString m_searchText; +}; + +void tst_QQuickFontDialogImpl::searchFamily() +{ + CREATE_DIALOG_TEST_HELPER + + // Open the dialog. + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + const QQuickTextField *familyEdit = + dialogHelper.quickDialog->findChild("familyEdit"); + QVERIFY(familyEdit); + + QQuickListView *fontFamilyListView = + dialogHelper.quickDialog->findChild("familyListView"); + QVERIFY(fontFamilyListView); + + const QPoint familyEditCenterPos = + familyEdit->mapToScene({ familyEdit->width() / 2, familyEdit->height() / 2 }).toPoint(); + QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, familyEditCenterPos); + QVERIFY(familyEdit->hasActiveFocus()); + + QSignalSpy familyListViewCurrentIndexSpy(fontFamilyListView, SIGNAL(currentIndexChanged())); + QVERIFY(familyListViewCurrentIndexSpy.isValid()); + + const QString alphabet("abcdefghijklmnopqrstuvwxyz"); + QStringList model = QFontDatabase::families(QFontDatabase::WritingSystem::Any); + model.removeIf(QFontDatabase::isPrivateFamily); + + ListSearchHelper listSearchHelper(model); + + // For each letter in the alphabet, press the corresponding key + // and check that the relevant delegate item in familyListView is selected. + for (auto alphabet_it = alphabet.cbegin(); alphabet_it != alphabet.cend(); alphabet_it++) { + const int previousIndex = fontFamilyListView->currentIndex(); + + const char keyEntered = alphabet_it->toLatin1(); + QTest::keyClick(dialogHelper.window(), keyEntered); + + int expectedListViewIndex = listSearchHelper.expectedCurrentIndexForSearch(QString(keyEntered)); + if (expectedListViewIndex == -1) { + // There was no match, so the currentIndex should remain unchanged. + expectedListViewIndex = previousIndex; + } + if (fontFamilyListView->currentIndex() == expectedListViewIndex) { + // Working as expected; keep testing. + continue; + } + + // Get the actual text of the current delegate item and the expected text. + auto currentDelegateItem = findViewDelegateItem(fontFamilyListView, fontFamilyListView->currentIndex()); + QVERIFY(currentDelegateItem); + const auto actualDelegateText = currentDelegateItem->property("text").toString(); + const auto expectedDelegateText = expectedListViewIndex != -1 ? model.at(expectedListViewIndex) : "(none)"; + + QFAIL(qPrintable(QString::fromLatin1("Expected fontFamilyListView to" + " change its currentIndex to %1 (%2) after typing '%3', but it is %4 (%5)") + .arg(expectedListViewIndex) + .arg(expectedDelegateText) + .arg(keyEntered) + .arg(fontFamilyListView->currentIndex()) + .arg(actualDelegateText))); + } + + CLOSE_DIALOG("Ok"); +} + +int main(int argc, char *argv[]) +{ + // We need to set this attribute, and this (defining main() ourselves and + // calling QTEST_MAIN_IMPL) seems to be the nicest way to do it without + // duplicating too much code. + // We also don't want to run this for every style, as each one will have + // different ways of implementing the dialogs. + QCoreApplication::setAttribute(Qt::AA_DontUseNativeDialogs); + // For now we only test one style. + // TODO: use Basic + QQuickStyle::setStyle("Fusion"); + QTEST_MAIN_IMPL(tst_QQuickFontDialogImpl) +} + +#include "tst_qquickfontdialogimpl.moc" diff --git a/tests/manual/dialogs/CMakeLists.txt b/tests/manual/dialogs/CMakeLists.txt index d99d64b9..cc132f36 100644 --- a/tests/manual/dialogs/CMakeLists.txt +++ b/tests/manual/dialogs/CMakeLists.txt @@ -18,6 +18,7 @@ qt_internal_add_manual_test(dialogs set(qmake_immediate_resource_files "dialogs.qml" "FileDialogPage.qml" + "FontDialogPage.qml" "StringListView.qml" "qmldir" "Theme.qml" diff --git a/tests/manual/dialogs/FontDialogPage.qml b/tests/manual/dialogs/FontDialogPage.qml new file mode 100644 index 00000000..5b5d1034 --- /dev/null +++ b/tests/manual/dialogs/FontDialogPage.qml @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Layouts + +import "." + +ColumnLayout { + property alias dialog: fontDialog + + // Put it all in another ColumnLayout so we can easily add margins. + ColumnLayout { + Layout.leftMargin: 12 + Layout.rightMargin: 12 + Layout.topMargin: 12 + Layout.bottomMargin: 12 + + GroupBox { + title: qsTr("Dialog properties") + + Layout.fillWidth: true + + GridLayout { + columns: 2 + anchors.fill: parent + + Label { + text: qsTr("modality") + + Layout.alignment: Qt.AlignTop + Layout.minimumWidth: ApplicationWindow.window.width * 0.2 + Layout.maximumWidth: ApplicationWindow.window.width * 0.2 + } + ButtonGroup { + id: modalityButtonGroup + buttons: modalityColumnLayout.children + } + ColumnLayout { + id: modalityColumnLayout + + RadioButton { + text: qsTr("Qt.NonModal") + + readonly property int modality: Qt.NonModal + } + RadioButton { + text: qsTr("Qt.WindowModal") + checked: true + + readonly property int modality: Qt.WindowModal + } + RadioButton { + text: qsTr("Qt.ApplicationModal") + + readonly property int modality: Qt.ApplicationModal + } + } + + Label { + text: qsTr("result") + } + TextField { + id: resultTextField + text: fontDialog.result === 1 ? qsTr("Accepted") : qsTr("Rejected") + readOnly: true + enabled: false + } + + Label { + text: qsTr("title") + } + TextField { + id: titleTextField + text: qsTr("Pick a font") + } + } + } + + GroupBox { + title: qsTr("FontDialog properties") + + Layout.fillWidth: true + + GridLayout { + columns: 2 + anchors.fill: parent + + Label { + Layout.minimumWidth: ApplicationWindow.window.width * 0.2 + Layout.maximumWidth: ApplicationWindow.window.width * 0.2 + text: qsTr("currentFont") + } + TextField { + id: currentFontTextField + text: qsTr("AaBbYyZz") + font: fontDialog.currentFont + readOnly: true + selectByMouse: true + + Layout.fillWidth: true + } + + Label { + text: qsTr("selectedFont") + } + TextField { + id: selectedFontTextField + text: qsTr("AaBbYyZz") + font: fontDialog.selectedFont + readOnly: true + selectByMouse: true + + Layout.fillWidth: true + } + + Label { + text: qsTr("fontOptions") + + Layout.alignment: Qt.AlignTop + } + ColumnLayout { + id: fontOptionsColumnLayout + + CheckBox { + id: noButtons + text: qsTr("NoButtons") + + readonly property int fontOption: checked ? FontDialog.NoButtons : 0 + } + CheckBox { + id: scalableFonts + text: qsTr("ScalableFonts") + + readonly property int fontOption: checked ? FontDialog.ScalableFonts : 0 + } + CheckBox { + id: nonScalableFonts + text: qsTr("NonScalableFonts") + + readonly property int fontOption: checked ? FontDialog.NonScalableFonts : 0 + } + CheckBox { + id: monospacedFonts + text: qsTr("MonospacedFonts") + + readonly property int fontOption: checked ? FontDialog.MonospacedFonts : 0 + } + CheckBox { + id: proportionalFonts + text: qsTr("ProportionalFonts") + + readonly property int fontOption: checked ? FontDialog.ProportionalFonts : 0 + } + } + } + } + + FontDialog { + id: fontDialog + + modality: modalityButtonGroup.checkedButton.modality + title: titleTextField.text + options: noButtons.fontOption + | scalableFonts.fontOption + | nonScalableFonts.fontOption + | monospacedFonts.fontOption + | proportionalFonts.fontOption + } + } +} diff --git a/tests/manual/dialogs/dialogs.pro b/tests/manual/dialogs/dialogs.pro index abb31492..7dc1b8fd 100644 --- a/tests/manual/dialogs/dialogs.pro +++ b/tests/manual/dialogs/dialogs.pro @@ -6,6 +6,7 @@ SOURCES += dialogs.cpp RESOURCES += \ dialogs.qml \ FileDialogPage.qml \ + FontDialogPage.qml \ StringListView.qml \ qmldir \ Theme.qml diff --git a/tests/manual/dialogs/dialogs.qml b/tests/manual/dialogs/dialogs.qml index d1f54c08..6e64659e 100644 --- a/tests/manual/dialogs/dialogs.qml +++ b/tests/manual/dialogs/dialogs.qml @@ -84,7 +84,7 @@ ApplicationWindow { text: qsTr("FileDialog") } TabButton { - text: qsTr("Coming Soon...") + text: qsTr("FontDialog") } } @@ -99,6 +99,7 @@ ApplicationWindow { width: scrollView.width FileDialogPage {} + FontDialogPage {} } } } -- cgit v1.2.3