From 8ca4f909e8c4dd29ee1465b39605d71455a77ce6 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 24 May 2023 12:38:31 +0200 Subject: [PATCH 1/3] Examples: Move categorization to examplesparser For easier testing. Change-Id: I11f9de3f4fbcc2c85c196f1b59b2147e73ea8209 Reviewed-by: Christian Stenger Reviewed-by: Qt CI Bot --- src/plugins/qtsupport/exampleslistmodel.cpp | 52 -------------------- src/plugins/qtsupport/examplesparser.cpp | 54 +++++++++++++++++++++ src/plugins/qtsupport/examplesparser.h | 3 ++ src/plugins/qtsupport/qtsupport_global.h | 12 +++++ 4 files changed, 69 insertions(+), 52 deletions(-) diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp index d3cf3535dd9..f18fefc34f2 100644 --- a/src/plugins/qtsupport/exampleslistmodel.cpp +++ b/src/plugins/qtsupport/exampleslistmodel.cpp @@ -4,7 +4,6 @@ #include "exampleslistmodel.h" #include "examplesparser.h" -#include "qtsupporttr.h" #include #include @@ -326,57 +325,6 @@ static bool isValidExampleOrDemo(ExampleItem *item) return ok || debugExamples(); } -static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second) -{ - if (first->isHighlighted && !second->isHighlighted) - return true; - if (!first->isHighlighted && second->isHighlighted) - return false; - return first->name.compare(second->name, Qt::CaseInsensitive) < 0; -} - -static QList>> getCategories( - const QList &items, bool sortIntoCategories) -{ - static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples"); - const bool useCategories = sortIntoCategories - || qtcEnvironmentVariableIsSet("QTC_USE_EXAMPLE_CATEGORIES"); - QList other; - QMap> categoryMap; - if (useCategories) { - for (ExampleItem *item : items) { - const QStringList itemCategories = item->metaData.value("category"); - for (const QString &category : itemCategories) - categoryMap[category].append(item); - if (itemCategories.isEmpty()) - other.append(item); - } - } - QList>> categories; - if (categoryMap.isEmpty()) { - // The example set doesn't define categories. Consider the "highlighted" ones as "featured" - QList featured; - QList allOther; - std::tie(featured, allOther) = Utils::partition(items, [](ExampleItem *i) { - return i->isHighlighted; - }); - if (!featured.isEmpty()) - categories.append({Tr::tr("Featured", "Category for highlighted examples"), featured}); - if (!allOther.isEmpty()) - categories.append({otherDisplayName, allOther}); - } else { - const auto end = categoryMap.constKeyValueEnd(); - for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) - categories.append(*it); - if (!other.isEmpty()) - categories.append({otherDisplayName, other}); - } - const auto end = categories.end(); - for (auto it = categories.begin(); it != end; ++it) - sort(it->second, sortByHighlightedAndName); - return categories; -} - void ExamplesViewController::updateExamples() { QString examplesInstallPath; diff --git a/src/plugins/qtsupport/examplesparser.cpp b/src/plugins/qtsupport/examplesparser.cpp index 5fd3161777c..bd6ff08918f 100644 --- a/src/plugins/qtsupport/examplesparser.cpp +++ b/src/plugins/qtsupport/examplesparser.cpp @@ -3,7 +3,10 @@ #include "examplesparser.h" +#include "qtsupporttr.h" + #include +#include #include #include @@ -298,4 +301,55 @@ expected_str> parseExamples(const QByteArray &manifestData, return items; } +static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second) +{ + if (first->isHighlighted && !second->isHighlighted) + return true; + if (!first->isHighlighted && second->isHighlighted) + return false; + return first->name.compare(second->name, Qt::CaseInsensitive) < 0; +} + +QList>> getCategories(const QList &items, + bool sortIntoCategories) +{ + static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples"); + const bool useCategories = sortIntoCategories + || qtcEnvironmentVariableIsSet("QTC_USE_EXAMPLE_CATEGORIES"); + QList other; + QMap> categoryMap; + if (useCategories) { + for (ExampleItem *item : items) { + const QStringList itemCategories = item->metaData.value("category"); + for (const QString &category : itemCategories) + categoryMap[category].append(item); + if (itemCategories.isEmpty()) + other.append(item); + } + } + QList>> categories; + if (categoryMap.isEmpty()) { + // The example set doesn't define categories. Consider the "highlighted" ones as "featured" + QList featured; + QList allOther; + std::tie(featured, allOther) = Utils::partition(items, [](ExampleItem *i) { + return i->isHighlighted; + }); + if (!featured.isEmpty()) + categories.append({Tr::tr("Featured", "Category for highlighted examples"), featured}); + if (!allOther.isEmpty()) + categories.append({otherDisplayName, allOther}); + } else { + const auto end = categoryMap.constKeyValueEnd(); + for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) + categories.append(*it); + if (!other.isEmpty()) + categories.append({otherDisplayName, other}); + } + const auto end = categories.end(); + for (auto it = categories.begin(); it != end; ++it) + sort(it->second, sortByHighlightedAndName); + return categories; +} + } // namespace QtSupport::Internal diff --git a/src/plugins/qtsupport/examplesparser.h b/src/plugins/qtsupport/examplesparser.h index 2d1afa54838..a7d5501bc51 100644 --- a/src/plugins/qtsupport/examplesparser.h +++ b/src/plugins/qtsupport/examplesparser.h @@ -44,6 +44,9 @@ QTSUPPORT_EXPORT Utils::expected_str> parseExamples( const Utils::FilePath &demosInstallPath, bool examples); +QTSUPPORT_TEST_EXPORT QList>> getCategories( + const QList &items, bool sortIntoCategories); + } // namespace QtSupport::Internal Q_DECLARE_METATYPE(QtSupport::Internal::ExampleItem *) diff --git a/src/plugins/qtsupport/qtsupport_global.h b/src/plugins/qtsupport/qtsupport_global.h index aca20f5aa27..60c9bf0c128 100644 --- a/src/plugins/qtsupport/qtsupport_global.h +++ b/src/plugins/qtsupport/qtsupport_global.h @@ -12,3 +12,15 @@ #else # define QTSUPPORT_EXPORT Q_DECL_IMPORT #endif + +#if defined(WITH_TESTS) +# if defined(QTSUPPORT_LIBRARY) +# define QTSUPPORT_TEST_EXPORT Q_DECL_EXPORT +# elif defined(QTSUPPORT_STATIC_LIBRARY) +# define QTSUPPORT_TEST_EXPORT +# else +# define QTSUPPORT_TEST_EXPORT Q_DECL_IMPORT +# endif +#else +# define QTSUPPORT_TEST_EXPORT +#endif From dd1a69e05f12656f9260fb8c7941405043804326 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 24 May 2023 13:47:27 +0200 Subject: [PATCH 2/3] Fix crash when examples are in multiple categories When items are added into multiple categories, we got into a double delete when cleaning up, e.g. when changing the shown examples to a different Qt version. Sort copies into categories and delete the originals as a quickfix. Cleaner would be if we didn't allocate them on the heap in the first place. Fixes: QTCREATORBUG-29197 Change-Id: I6490111aba87335b0f7189c4957ed1da8681806f Reviewed-by: Qt CI Bot Reviewed-by: Christian Stenger --- src/plugins/qtsupport/examplesparser.cpp | 14 +++++++--- tests/auto/examples/tst_examples.cpp | 33 ++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/plugins/qtsupport/examplesparser.cpp b/src/plugins/qtsupport/examplesparser.cpp index bd6ff08918f..e5853a0f7cb 100644 --- a/src/plugins/qtsupport/examplesparser.cpp +++ b/src/plugins/qtsupport/examplesparser.cpp @@ -319,16 +319,22 @@ QList>> getCategories(const QList other; QMap> categoryMap; if (useCategories) { + // Append copies of the items and delete the original ones, + // because items might be added to multiple categories and that needs individual items for (ExampleItem *item : items) { - const QStringList itemCategories = item->metaData.value("category"); + const QStringList itemCategories = Utils::filteredUnique( + item->metaData.value("category")); for (const QString &category : itemCategories) - categoryMap[category].append(item); + categoryMap[category].append(new ExampleItem(*item)); if (itemCategories.isEmpty()) - other.append(item); + other.append(new ExampleItem(*item)); } } QList>> categories; if (categoryMap.isEmpty()) { + // If we tried sorting into categories, but none were defined, we copied the items + // into "other", which we don't use here. Get rid of them again. + qDeleteAll(other); // The example set doesn't define categories. Consider the "highlighted" ones as "featured" QList featured; QList allOther; @@ -340,6 +346,8 @@ QList>> getCategories(const QList("videoLength"); QTest::addColumn("platforms"); QTest::addColumn("metaData"); + QTest::addColumn("categories"); QTest::addRow("example") << QByteArray(R"raw( @@ -102,6 +103,8 @@ void tst_Examples::parsing_data() widgets/widgets/analogclock/analogclock.cpp Graphics + Graphics + Foobar widgets @@ -120,13 +123,29 @@ void tst_Examples::parsing_data() "manifest/widgets/widgets/analogclock/analogclock.cpp")} << FilePath::fromUserInput("manifest/widgets/widgets/analogclock/analogclock.cpp") << FilePaths() << Example << true << false << false << "" - << "" << QStringList() << MetaData({{"category", {"Graphics"}}, {"tags", {"widgets"}}}); + << "" << QStringList() + << MetaData({{"category", {"Graphics", "Graphics", "Foobar"}}, {"tags", {"widgets"}}}) + << QStringList{"Foobar", "Graphics"}; + + QTest::addRow("no category, highlighted") + << QByteArray(R"raw( + + + + + )raw") << /*isExamples=*/true + << "No Category, highlighted" << QString() << QString() << QStringList() + << FilePath("manifest") << QString() << FilePaths() << FilePath() << FilePaths() << Example + << /*hasSourceCode=*/false << false << /*isHighlighted=*/true << "" + << "" << QStringList() << MetaData() << QStringList{"Featured"}; } void tst_Examples::parsing() { QFETCH(QByteArray, data); QFETCH(bool, isExamples); + QFETCH(QStringList, categories); const ExampleItem expected = fetchItem(); const expected_str> result = parseExamples(data, @@ -154,7 +173,17 @@ void tst_Examples::parsing() QCOMPARE(item.videoLength, expected.videoLength); QCOMPARE(item.platforms, expected.platforms); QCOMPARE(item.metaData, expected.metaData); - qDeleteAll(*result); + + const QList>> resultCategories = getCategories(*result, + true); + QCOMPARE(resultCategories.size(), categories.size()); + for (int i = 0; i < resultCategories.size(); ++i) { + QCOMPARE(resultCategories.at(i).first, categories.at(i)); + QCOMPARE(resultCategories.at(i).second.size(), 1); + } + + for (const auto &category : resultCategories) + qDeleteAll(category.second); } QTEST_APPLESS_MAIN(tst_Examples) From 50f277ef8ac4edb944f7a26d25f5fb7dcefd050d Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 11 May 2023 11:42:01 +0200 Subject: [PATCH 3/3] German translation: Fossil MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Icc9ffe9f992619e5f4bd1a8689f11aa998f9c8ab Reviewed-by: Robert Löhning Reviewed-by: --- share/qtcreator/translations/qtcreator_de.ts | 260 ++++++++++--------- 1 file changed, 131 insertions(+), 129 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts index 65fec424e4a..f04a40e4d9a 100644 --- a/share/qtcreator/translations/qtcreator_de.ts +++ b/share/qtcreator/translations/qtcreator_de.ts @@ -11934,7 +11934,7 @@ Lokale Commits werden nicht zum Master-Branch gepusht, bis ein normaler Commit e Local filesystem: - Dateisystem: + Lokales Dateisystem: Options @@ -11942,7 +11942,7 @@ Lokale Commits werden nicht zum Master-Branch gepusht, bis ein normaler Commit e Remember specified location as default - Obige Einstellung als Vorgabe übernehmen + Angegebenen Ort als Vorgabe übernehmen Overwrite @@ -12124,7 +12124,7 @@ Lokale Pull-Operationen werden nicht auf den Master-Branch angewandt. Update... - Auf aktuellen Stand bringen... + Aktualisieren... Commit... @@ -12160,7 +12160,7 @@ Lokale Pull-Operationen werden nicht auf den Master-Branch angewandt. Unable to create a commit editor. - Es konnte kein Editor für den Commit angelegt werden. + Es konnte kein Commit-Editor angelegt werden. Commit changes for "%1". @@ -27079,497 +27079,499 @@ zu deaktivieren, deaktiviert auch die folgenden Plugins: QtC::Fossil Commit Editor - Commit-Editor + Commit-Editor Configure Repository - + Repository konfigurieren Existing user to become an author of changes made to the repository. - + Vorhandener Benutzer, der Autor der Änderungen im Repository werden soll. SSL/TLS Identity Key - + SSL/TLS Identity Key SSL/TLS client identity key to use if requested by the server. - + SSL/TLS Client Identity Key, der benutzt werden soll, wenn der Server diesen anfordert. Disable auto-sync - + Automatisches Synchronisieren (autosync) deaktivieren Disable automatic pull prior to commit or update and automatic push after commit or tag or branch creation. - + Deaktiviert die automatische Pull-Operation vor Commits oder Aktualisierungen und die automatische Push-Operation nach Commits oder dem Erstellen von Tags oder Branches. Repository User - + Benutzer des Repositorys User: - Nutzer: + Nutzer: Repository Settings - + Repository-Einstellungen SSL/TLS identity: - + SSL/TLS Identity: Ignore All Whitespace - + Alle Leerzeichen ignorieren Strip Trailing CR - + CR-Zeichen am Zeilenende entfernen Show Committers - + Committer anzeigen List Versions - + Versionen anzeigen Ancestors - + Vorfahren Descendants - + Nachfahren Unfiltered - Ungefiltert + Ungefiltert Lineage - + Abstammung Verbose - Ausführlich + Ausführlich Show files changed in each revision - Geänderte Dateien jeder Revision anzeigen + Geänderte Dateien jeder Revision anzeigen All Items - + Alle Elemente File Commits - + Datei-Commits Technical Notes - + Technische Anmerkungen (technotes) Tags - Tags + Tags Tickets - + Tickets Wiki Commits - + Wiki-Commits Item Types - + Element-Typen Private - + Privat Create a private check-in that is never synced. Children of private check-ins are automatically private. Private check-ins are not pushed to the remote repository by default. - + Erstelle einen privaten Check-In, der niemals synchronisiert wird. +Kinder von privaten Check-Ins sind automatisch privat. +Private Check-Ins werden standardmäßig nicht zum entfernten Repository gepusht. Tag names to apply; comma-separated. - + Kommaseparierte Liste von Tag-Namen, die angewendet werden sollen. Current Information - + Aktuelle Informationen Local root: - + Lokale Root: Branch: - Branch: + Branch: Tags: - Schlüsselworte: + Tags: Commit Information - Informationen zu Commit + Informationen zum Commit New branch: - + Neuer Branch: Author: - Autor: + Autor: Message check failed. - + Die Überprüfung der Beschreibung schlug fehl. &Annotate %1 - + &Annotation für %1 Annotate &Parent Revision %1 - + Annotation der über&geordneten Revision %1 &Fossil - + &Fossil Annotate Current File - Annotation für Datei + Annotation für Datei Annotate "%1" - Annotation für "%1" + Annotation für "%1" Diff Current File - + Diff für aktuelle Datei Diff "%1" - Diff für "%1" + Diff für "%1" Meta+I,Meta+D - + Meta+I,Meta+D ALT+I,Alt+D - + ALT+I,Alt+D Timeline Current File - + Zeitleiste für aktuelle Datei Timeline "%1" - + Zeitleiste für "%1" Meta+I,Meta+L - + Meta+I,Meta+L ALT+I,Alt+L - + ALT+I,Alt+L Status Current File - Status der Datei + Status der aktuellen Datei Status "%1" - Status von "%1" + Status von "%1" Meta+I,Meta+S - + Meta+I,Meta+S ALT+I,Alt+S - + ALT+I,Alt+S Add Current File - + Aktuelle Datei hinzufügen Add "%1" - "%1" hinzufügen + "%1" hinzufügen Delete Current File... - + Aktuelle Datei löschen... Delete "%1"... - Lösche "%1"... + Lösche "%1"... Revert Current File... - Änderungen der Datei rückgängig machen... + Änderungen der aktuellen Datei rückgängig machen... Revert "%1"... - Änderungen in "%1" rückgängig machen... + Änderungen in "%1" rückgängig machen... Diff - Diff + Diff Timeline - + Zeitleiste Meta+I,Meta+T - + Meta+I,Meta+T ALT+I,Alt+T - + ALT+I,Alt+T Revert... - Rückgängig machen... + Rückgängig machen... Status - Status + Status Revert - Rückgängig machen + Rückgängig machen Pull... - Pull... + Pull... Push... - Push... + Push... Update... - Auf aktuellen Stand bringen... + Aktualisieren... Meta+I,Meta+U - + Meta+I,Meta+U ALT+I,Alt+U - + ALT+I,Alt+U Commit... - Commit... + Commit... Meta+I,Meta+C - + Meta+I,Meta+C ALT+I,Alt+C - + ALT+I,Alt+C Settings ... - + Einstellungen... Create Repository... - Repository erzeugen... + Repository erzeugen... Remote repository is not defined. - + Es ist kein entferntes Repository definiert. Update - Aktualisieren + Aktualisieren There are no changes to commit. - Es sind keine ausstehenden Änderungen vorhanden. + Es sind keine ausstehenden Änderungen vorhanden. Unable to create an editor for the commit. - Es konnte kein Editor für den Commit angelegt werden. + Es konnte kein Editor für den Commit angelegt werden. Unable to create a commit editor. - Es konnte kein Editor für den Commit angelegt werden. + Es konnte kein Commit-Editor angelegt werden. Commit changes for "%1". - Commit der Änderungen in "%1". + Commit der Änderungen in "%1". Choose Checkout Directory - + Verzeichnis für Checkout wählen The directory "%1" is already managed by a version control system (%2). Would you like to specify another directory? - Das Verzeichnis "%1" steht bereits unter Verwaltung eines Versionskontrollsystems (%2). Möchten Sie einen anderes Verzeichnis angeben? + Das Verzeichnis "%1" steht bereits unter Verwaltung eines Versionskontrollsystems (%2). Möchten Sie ein anderes Verzeichnis angeben? Repository already under version control - Repository bereits unter Versionskontrolle + Repository bereits unter Versionskontrolle Repository Created - Repository erstellt + Repository erstellt A version control repository has been created in %1. - Ein Repository für Versionskontrolle wurde im Verzeichnis %1 erstellt. + Ein Repository für Versionskontrolle wurde im Verzeichnis %1 erstellt. Repository Creation Failed - Fehlschlag bei Erstellung des Repositorys + Fehlschlag bei Erstellung des Repositorys A version control repository could not be created in %1. - Im Verzeichnis %1 konnte kein Repository für die Versionskontrolle erstellt werden. + Im Verzeichnis %1 konnte kein Repository für die Versionskontrolle erstellt werden. Fossil - + Fossil Specify a revision other than the default? - Möchten Sie eine Revision angeben? + Möchten Sie eine Revision angeben? Checkout revision, can also be a branch or a tag name. - + Checkout der Revision erstellen, kann auch der Name eines Branches oder Tags sein. Revision - Revision + Revision Fossil Command - + Fossil-Kommando Command: - + Kommando: Fossil Repositories - + Fossil-Repositorys Default path: - + Vorgabeverzeichnis: Directory to store local repositories by default. - + Vorgabeverzeichnis für lokale Repositorys. Default user: - + Vorgabe-Benutzer: Log width: - + Breite des Logs: The width of log entry line (>20). Choose 0 to see a single line per entry. - + Die Breite der Zeilen in Logs (>20). Wählen Sie 0, um eine einzelne Zeile pro Eintrag anzuzeigen. Timeout: - Zeitlimit: + Zeitlimit: s - s + s Log count: - Log-Anzeige beschränken auf: + Log-Anzeige beschränken auf: The number of recent commit log entries to show. Choose 0 to see all entries. - + Zahl der anzuzeigenden Logeinträge, 0 für unbegrenzt. Configuration - Konfiguration + Konfiguration Local Repositories - + Lokale Repositorys User - Nutzer + Nutzer Miscellaneous - Sonstige Einstellungen + Sonstige Einstellungen Pull Source - + Quelle für Pull-Operation Push Destination - + Ziel für Push-Operation Default location - Vorgabe + Vorgabe Local filesystem: - Dateisystem: + Lokales Dateisystem: Specify URL: - URL: + URL: For example: https://[user[:pass]@]host[:port]/[path] - + Zum Beispiel: https://[user[:pass]@]host[:port]/[path] Remember specified location as default - Obige Einstellung als Vorgabe übernehmen + Angegebenen Ort als Vorgabe übernehmen Include private branches - + Private Branches einbeziehen Allow transfer of private branches. - + Übertragen von privaten Branches erlauben. Remote Location - + Entfernter Ort Options - Einstellungen + Einstellungen @@ -32653,7 +32655,7 @@ Beispiel: *.cpp%1*.h Local filesystem: - Dateisystem: + Lokales Dateisystem: Default Location @@ -32813,7 +32815,7 @@ Beispiel: *.cpp%1*.h Update... - Auf aktuellen Stand bringen... + Aktualisieren... Import...