diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts index 033acd385d0..2d1a13ead00 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... diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp index 84e1b57ab1c..5beb9c80a35 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,61 +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"), 0}, featured}); - if (!allOther.isEmpty()) - categories.append({{otherDisplayName, 1}, allOther}); - } else { - int index = 0; - const auto end = categoryMap.constKeyValueEnd(); - for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) { - categories.append({{it->first, index, /*maxRows=*/index == 0 ? 2 : 1}, it->second}); - ++index; - } - if (!other.isEmpty()) - categories.append({{otherDisplayName, index, /*maxRows=*/1}, 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 c44919f2e12..84469ca2c4d 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,68 @@ 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) { + // 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 = Utils::filteredUnique( + item->metaData.value("category")); + for (const QString &category : itemCategories) + categoryMap[category].append(new ExampleItem(*item)); + if (itemCategories.isEmpty()) + 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; + std::tie(featured, allOther) = Utils::partition(items, [](ExampleItem *i) { + return i->isHighlighted; + }); + if (!featured.isEmpty()) { + categories.append( + {{Tr::tr("Featured", "Category for highlighted examples"), 0}, featured}); + } + if (!allOther.isEmpty()) + categories.append({{otherDisplayName, 1}, allOther}); + } else { + // All original items have been copied into a category or other, delete. + qDeleteAll(items); + int index = 0; + const auto end = categoryMap.constKeyValueEnd(); + for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) { + categories.append({{it->first, index, /*maxRows=*/index == 0 ? 2 : 1}, it->second}); + ++index; + } + if (!other.isEmpty()) + categories.append({{otherDisplayName, index, /*maxRows=*/1}, 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..bfe65721c22 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 diff --git a/tests/auto/examples/tst_examples.cpp b/tests/auto/examples/tst_examples.cpp index eef0429d29f..d6639663adf 100644 --- a/tests/auto/examples/tst_examples.cpp +++ b/tests/auto/examples/tst_examples.cpp @@ -6,6 +6,7 @@ #include +using namespace Core; using namespace Utils; using namespace QtSupport::Internal; @@ -88,6 +89,7 @@ void tst_Examples::parsing_data() QTest::addColumn("videoLength"); QTest::addColumn("platforms"); QTest::addColumn("metaData"); + QTest::addColumn("categories"); QTest::addRow("example") << QByteArray(R"raw( @@ -102,6 +104,8 @@ void tst_Examples::parsing_data() widgets/widgets/analogclock/analogclock.cpp Graphics + Graphics + Foobar widgets @@ -120,13 +124,29 @@ void tst_Examples::parsing_data() "examples/widgets/widgets/analogclock/analogclock.cpp")} << FilePath::fromUserInput("examples/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("examples") << 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 +174,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.name, categories.at(i)); + QCOMPARE(resultCategories.at(i).second.size(), 1); + } + + for (const auto &category : resultCategories) + qDeleteAll(category.second); } QTEST_APPLESS_MAIN(tst_Examples)