From 13067a7d9cb1acd74bc936c56b52aa76b7da5d38 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 29 Sep 2021 15:05:24 +0300 Subject: [PATCH 01/28] CommandLine: Find executable in PATH when it is relative Task-number: QTCREATORBUG-26329 Change-Id: Ie7b30ea5c73d11e4a9961bdaa732ee36e0e377e7 Reviewed-by: hjk --- src/libs/utils/commandline.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index 409c85f04da..ecea6934cfb 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -647,8 +647,10 @@ void ProcessArgs::addArgs(QString *args, const QStringList &inArgs) bool ProcessArgs::prepareCommand(const CommandLine &cmdLine, QString *outCmd, ProcessArgs *outArgs, const Environment *env, const FilePath *pwd) { - const FilePath executable = cmdLine.executable(); + FilePath executable = cmdLine.executable(); const QString arguments = cmdLine.arguments(); + if (env && executable.isRelativePath()) + executable = env->searchInPath(executable.toString()); ProcessArgs::SplitError err; *outArgs = ProcessArgs::prepareArgs(arguments, &err, executable.osType(), env, pwd); if (err == ProcessArgs::SplitOk) { From b8b39703290aaa23eb855c153683b0c347dd42ec Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 29 Sep 2021 18:19:15 +0300 Subject: [PATCH 02/28] Git: Fix warning when applying settings with Ok The current parentDialog is the settings window, but it is closed immediately, so the warning just disappears. Change-Id: I7f5033239bf18e758595965587947bec40facf6f Reviewed-by: hjk --- src/plugins/git/gitplugin.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index de1427e8398..b1b4362956b 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -87,6 +87,7 @@ #include #include #include +#include #include #ifdef WITH_TESTS @@ -477,8 +478,11 @@ void GitPluginPrivate::onApplySettings() bool gitFoundOk; QString errorMessage; m_settings.gitExecutable(&gitFoundOk, &errorMessage); - if (!gitFoundOk) - Core::AsynchronousMessageBox::warning(tr("Git Settings"), errorMessage); + if (!gitFoundOk) { + QTimer::singleShot(0, this, [this, errorMessage] { + Core::AsynchronousMessageBox::warning(tr("Git Settings"), errorMessage); + }); + } } void GitPluginPrivate::cleanCommitMessageFile() From c30bfde80cab950887ee38b63d88f8f8e516712c Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Thu, 30 Sep 2021 07:59:24 +0200 Subject: [PATCH 03/28] Icons: Let Inkscape 1.1.1 change the document as much as it likes New Inkscape versions (in this case 1.1.1) want to store paths, styles and transforms differently than their predecessors. Letting this happen for the whole ducument in one go makes one ugly commit, but keeps the following diffs cleaner. Change-Id: I0aeb76d0679573838db41c6935f02d6b27e0575d Reviewed-by: Alessandro Portale --- src/tools/icons/qtcreatoricons.svg | 891 ++++++++++++++--------------- 1 file changed, 445 insertions(+), 446 deletions(-) diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index af3ce0d8a03..c33350195bb 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -6,7 +6,7 @@ height="600" id="svg2" version="1.1" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" sodipodi:docname="qtcreatoricons.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" @@ -594,7 +594,6 @@ image/svg+xml - @@ -755,7 +754,7 @@ style="display:inline"> + transform="rotate(180,614,592)"> @@ -781,27 +780,27 @@ style="display:inline"> + transform="translate(102)"> + transform="translate(96)"> + style="fill:none;stroke:#232323;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.686275" /> @@ -896,7 +895,7 @@ style="opacity:0.83" height="600" width="800" - transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,503.25335,-30.961074)" + transform="rotate(45,289,591.99999)" id="use4511" xlink:href="#g4504" y="0" @@ -905,7 +904,7 @@ style="opacity:0.66" height="600" width="800" - transform="matrix(0,1,-1,0,881,303)" + transform="rotate(90,289,592)" id="use4525" xlink:href="#g4504" y="0" @@ -914,7 +913,7 @@ style="opacity:0.49" height="600" width="800" - transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,911.96107,806.25335)" + transform="rotate(135,289,592)" id="use4528" xlink:href="#g4504" y="0" @@ -923,7 +922,7 @@ style="opacity:0.32" height="600" width="800" - transform="matrix(-1,0,0,-1,578,1184)" + transform="rotate(180,289,592)" id="use4530" xlink:href="#g4504" y="0" @@ -932,7 +931,7 @@ style="opacity:0.32" height="600" width="800" - transform="matrix(-0.70710678,-0.70710678,0.70710678,-0.70710678,74.746645,1214.9611)" + transform="rotate(-135,289.00001,592.00001)" id="use4532" xlink:href="#g4504" y="0" @@ -941,7 +940,7 @@ style="opacity:0.32" height="600" width="800" - transform="matrix(0,-1,1,0,-303,881)" + transform="rotate(-90,289,592)" id="use4534" xlink:href="#g4504" y="0" @@ -950,7 +949,7 @@ style="opacity:0.32" height="600" width="800" - transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,-333.96107,377.74665)" + transform="rotate(-45,289.00001,592)" id="use4536" xlink:href="#g4504" y="0" @@ -971,7 +970,7 @@ + style="opacity:0.887" /> + style="opacity:0.773" /> + style="opacity:0.66" /> @@ -1026,64 +1025,64 @@ y="0" xlink:href="#g3383" id="use3397" - transform="matrix(-0.8660254,0.5,-0.5,-0.8660254,957.63514,879.40243)" + transform="rotate(150,360.99998,568)" width="800" height="600" - style="opacity:0.43299997" /> + style="opacity:0.433" /> + style="opacity:0.32" /> + style="opacity:0.32" /> + style="opacity:0.32" /> + style="opacity:0.32" /> + style="opacity:0.32" /> + style="opacity:0.32" /> + style="opacity:0.417;fill:none;stroke:none" /> + style="opacity:1;fill:none;fill-opacity:0.8;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:arc-type="arc" /> + transform="translate(2)"> + transform="translate(8)"> @@ -1318,8 +1318,8 @@ sodipodi:nodetypes="cccc" inkscape:connector-curvature="0" id="path6627" - d="m 485,585 8,0 -4,4 -4,-4" - style="fill:#000000;fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="m 485,585 h 8 l -4,4 -4,-4" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + transform="translate(-26)"> + transform="translate(-25)"> @@ -1400,7 +1400,7 @@ + transform="rotate(90,606,600)"> + transform="rotate(-90,622.5,567.5)"> + transform="rotate(-90,630.5,559.5)"> @@ -1482,7 +1482,7 @@ + transform="rotate(90,646,640)"> + transform="rotate(-90,598,592)" /> + transform="translate(-39)"> + transform="rotate(90)" /> + d="m 741,599 h 16 v 1 h -16 z m 2,-3 h 12 v 1 h -12 z m 1,-3 h 10 v 1 h -10 z m -3,-3 h 16 v 1 h -16 z m 1,-3 h 14 v 1 h -14 z m 1,-3 h 12 v 1 h -12 z" /> + transform="translate(9)"> + transform="rotate(90)" /> + transform="translate(25)"> + transform="rotate(90)" /> + transform="translate(41)"> + transform="rotate(90)" /> @@ -2355,14 +2355,14 @@ y="0" xlink:href="#share/qtcreator/templates/wizards/global/guiapplication" id="src/plugins/qmakeprojectmanager/wizards/images/gui" - transform="translate(60,0)" + transform="translate(60)" width="100%" height="100%" /> + sodipodi:open="true" + sodipodi:arc-type="arc" /> + style="display:inline;stroke-width:0.547721" /> + id="g3920"> + style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:none;stroke:#000000;stroke-width:0.9;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:none;stroke:#000000;stroke-width:0.9;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + points="113,255 109,258 109,264 107,262 107,258 103,255 103,253 113,253 " + style="display:inline;fill:#000000;fill-opacity:1;stroke:none" /> + transform="translate(1)"> + transform="translate(17)"> + transform="translate(1)"> + id="g3973"> + sodipodi:open="true" + sodipodi:arc-type="arc" /> @@ -4096,12 +4093,11 @@ + x="0" /> + clip-path="url(#clipPath3218)"> + clip-path="url(#clipPath4031)"> @@ -4224,7 +4217,7 @@ id="rect3817" style="fill:#ffffff" /> + transform="matrix(1,0,0,-1,233,1169)" /> + transform="translate(112)"> + transform="translate(112)"> + transform="translate(16)"> + transform="translate(16)"> @@ -4480,7 +4471,7 @@ style="fill-opacity:1" sodipodi:nodetypes="cccccsscc" /> @@ -4518,7 +4509,7 @@ @@ -4535,29 +4526,29 @@ style="fill:#ffffff;fill-opacity:1;stroke:none" /> + transform="translate(399)"> @@ -4588,7 +4579,7 @@ style="fill:#000000;fill-opacity:1;stroke:none"> @@ -4733,7 +4724,7 @@ id="polygon6686-2" transform="translate(-69.369828,-0.1767767)" style="fill:#000000;fill-opacity:1" - d="m 156,315 0,-5 -2.5,2.5 z m 3,-8 5,0 -2.5,-2.5 z m -7.99979,-5.00001 1,0 0,13 -1,0 z m 0,0 13,0 0,1 -13,0 z" + d="m 156,315 v -5 l -2.5,2.5 z m 3,-8 h 5 l -2.5,-2.5 z m -7.99979,-5.00001 h 1 v 13 h -1 z m 0,0 h 13 v 1 h -13 z" inkscape:connector-curvature="0" /> @@ -4792,7 +4783,7 @@ + transform="translate(32)"> @@ -4822,7 +4813,7 @@ + transform="translate(48)"> + transform="translate(-32)"> + transform="translate(64)"> + transform="translate(80)"> + sodipodi:open="true" + sodipodi:arc-type="arc" /> + style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:arc-type="arc" /> + transform="translate(16)"> + transform="translate(16)"> + transform="translate(32)"> @@ -5016,7 +5009,7 @@ sodipodi:nodetypes="cccc" /> @@ -5024,7 +5017,7 @@ + transform="translate(48)"> + transform="translate(16)"> + transform="translate(16)"> + transform="translate(32)"> @@ -5211,11 +5204,11 @@ + transform="translate(16)"> + style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1" /> @@ -5289,7 +5282,7 @@ + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -5342,7 +5335,7 @@ + transform="translate(16)"> @@ -5370,7 +5363,7 @@ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> @@ -5411,13 +5404,13 @@ x="0" /> + style="fill:#000000;fill-opacity:0.156863;stroke:#000000" /> + transform="translate(31)"> + transform="translate(80)" /> + transform="translate(16)"> @@ -5545,7 +5538,7 @@ + transform="translate(16)"> @@ -5572,14 +5565,14 @@ id="rect6782-96-0-0-7-8-1-0-4-4" /> @@ -5620,7 +5613,7 @@ sodipodi:nodetypes="cccc" inkscape:connector-curvature="0" id="path5163" - d="m 1233,580.5 4,0 c 6,0 6,-8 0,-8 l -5,0" + d="m 1233,580.5 h 4 c 6,0 6,-8 0,-8 h -5" style="fill:none;stroke:#000000" /> + transform="translate(48)"> @@ -5858,18 +5851,18 @@ r="2.5" /> @@ -5894,7 +5887,7 @@ y="570" /> @@ -5906,25 +5899,25 @@ sodipodi:nodetypes="cc" /> + d="m 1237.5,572 v 5 m 2.5,-2.5 h -5" /> @@ -6017,7 +6010,7 @@ width="100%" height="100%" /> + transform="rotate(90,1232.5,581)" /> @@ -6128,11 +6121,11 @@ y="0" xlink:href="#src/plugins/scxmleditor/common/images/align_bottom" id="src/plugins/scxmleditor/common/images/align_left" - transform="matrix(0,1,-1,0,2084,-884)" + transform="rotate(90,1484,600)" width="100%" height="100%" /> @@ -6179,7 +6172,7 @@ width="100%" height="100%" /> + style="stroke:#000000;stroke-width:0.6" /> + style="fill:#ffffff;fill-opacity:1" /> + style="fill:none;stroke:#000000;stroke-width:1.42;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:none;stroke:#000000;stroke-width:1.42;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -6556,7 +6549,7 @@ y="552" /> @@ -6564,7 +6557,7 @@ inkscape:connector-curvature="0" id="path4228" d="m 52,564 c 8,-8 8,-8 8,-8" - style="fill:none;stroke:#000000;stroke-width:1.42;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none" /> + style="fill:none;stroke:#000000;stroke-width:1.42;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -6667,7 +6660,7 @@ sodipodi:nodetypes="csscsccscccsc" style="fill:#000000;fill-opacity:1" inkscape:connector-curvature="0" - d="m 60.447,269.795 c -0.207,-0.04 -0.431,-0.726 0,-1.363 0.521,-0.77 1.225,-2.879 1.225,-4.146 0,-2.366 -1.645,-4.284 -3.672,-4.285 -2.027,0.001 -3.672,1.919 -3.672,4.285 0,1.268 0.701,3.377 1.225,4.146 0.43,0.638 0.206,1.323 0,1.363 -3.284,0.648 -5.053,1.389 -5.053,3.408 l 0,1.798 7.5,0 7.5,0 0,-1.798 c 0,-2.019 -1.77,-2.76 -5.053,-3.408 z" + d="m 60.447,269.795 c -0.207,-0.04 -0.431,-0.726 0,-1.363 0.521,-0.77 1.225,-2.879 1.225,-4.146 0,-2.366 -1.645,-4.284 -3.672,-4.285 -2.027,0.001 -3.672,1.919 -3.672,4.285 0,1.268 0.701,3.377 1.225,4.146 0.43,0.638 0.206,1.323 0,1.363 -3.284,0.648 -5.053,1.389 -5.053,3.408 v 1.798 h 7.5 7.5 v -1.798 c 0,-2.019 -1.77,-2.76 -5.053,-3.408 z" id="path4884-6" /> @@ -6711,14 +6704,14 @@ style="fill:#ffffff;fill-opacity:1" /> + transform="translate(16)"> @@ -6747,7 +6740,7 @@ transform="translate(144,148)" /> @@ -6780,7 +6773,7 @@ + transform="translate(-28)"> + transform="rotate(45)" /> + sodipodi:open="true" + sodipodi:arc-type="arc" /> + sodipodi:open="true" + sodipodi:arc-type="arc" /> + sodipodi:open="true" + sodipodi:arc-type="arc" /> + id="use4943" + width="100%" + height="100%" /> + transform="translate(36)"> + transform="translate(52)"> + transform="translate(36)"> + sodipodi:open="true" + sodipodi:arc-type="arc" /> + transform="translate(52)"> + style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + sodipodi:open="true" + sodipodi:arc-type="arc" /> + transform="translate(68)"> + transform="translate(84)"> + transform="translate(84)"> + transform="translate(100)"> + transform="translate(116)"> + transform="translate(148)"> + transform="translate(164)"> @@ -7188,7 +7188,7 @@ cy="560.5" cx="353.5" id="path4897" - style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.7;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> @@ -7205,12 +7205,12 @@ height="4" width="3" id="namespacedot" - style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.7;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" ry="1.5" /> + style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"> + style="fill:none;stroke:#ffffff;stroke-linecap:square" /> + style="stroke:#ffffff;stroke-linecap:square" /> + style="stroke:#ffffff;stroke-linecap:square" /> + style="stroke:#ffffff;stroke-linecap:square" /> + style="stroke:#ffffff;stroke-linecap:butt" + sodipodi:arc-type="arc" /> + transform="translate(16)"> + transform="translate(32)"> @@ -7515,7 +7516,7 @@ y="584" /> @@ -7540,7 +7541,7 @@ height="100%" /> @@ -7558,7 +7559,7 @@ x="0" /> @@ -7574,13 +7575,13 @@ style="fill:#ffffff;stroke:none" /> + transform="translate(16)"> @@ -7636,7 +7637,7 @@ x="0" /> @@ -7671,13 +7672,13 @@ style="fill:#ffffff;stroke:none" /> @@ -7728,11 +7729,11 @@ sodipodi:nodetypes="cccccccc" inkscape:connector-curvature="0" id="path4833" - d="m 145.5,542.5 6,0 0,-3 7,4.5 -7,4.5 0,-3 -6,0 z" + d="m 145.5,542.5 h 6 v -3 l 7,4.5 -7,4.5 v -3 h -6 z" style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="m 145.5,542.5 h 6 v -3 l 7,4.5 -7,4.5 v -3 h -6 z" + style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + transform="translate(-24)"> + transform="translate(-34)"> + transform="translate(-44)"> + transform="translate(-139,-80)"> + id="g4438"> + style="opacity:0.601;fill:#ffffff;fill-opacity:0" /> @@ -7988,16 +7987,16 @@ height="17" width="21" id="rect4116" - style="fill:none;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:0.4;stroke-dasharray:none" /> + style="fill:none;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.4" /> @@ -8006,12 +8005,12 @@ + style="opacity:0.83" + transform="translate(-4)"> + style="fill:#ffffff;fill-opacity:0.12549;stroke:none" /> + style="opacity:0.83" + transform="translate(-8)"> + style="opacity:0.83" + transform="translate(-12)"> + style="opacity:0.83" + transform="translate(-16)"> + style="opacity:0.83"> + style="stroke-width:0.82734" /> + style="display:inline;stroke-width:0.82734" /> + style="display:inline;stroke-width:0.82734" /> + style="display:inline;stroke-width:0.82734" /> + style="stroke-width:0.82734" /> + style="display:inline;stroke-width:0.82734" /> + style="display:inline;stroke-width:0.82734" /> + style="display:inline;stroke-width:0.82734" /> @@ -8606,7 +8605,7 @@ transform="translate(-1,-3)"> @@ -10228,7 +10227,7 @@ + transform="rotate(180,1122.5,574)"> + style="opacity:0.93" /> Date: Wed, 29 Sep 2021 16:35:52 +0200 Subject: [PATCH 04/28] ClangCodeModel: Replace FIXME comments with links to bug reports Change-Id: Ib6520781e88b8dcf63dd553e199939ff65ae1f05 Reviewed-by: David Schulz --- src/plugins/clangcodemodel/test/clangdtests.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index c71bd2d3644..7829cb0b74d 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -1627,7 +1627,7 @@ void ClangdTestCompletion::testFunctionHintsFiltered() QVERIFY(proposal); QCOMPARE(proposal->size(), 2); QVERIFY(hasItem(proposal, "func(const S &s, int j) -> void")); - QEXPECT_FAIL("", "FIXME: LanguageClient handles active parameter only in active signature", Abort); + QEXPECT_FAIL("", "QTCREATORBUG-26346", Abort); QVERIFY(hasItem(proposal, "func(const S &s, int j, int k) -> void")); } @@ -1640,7 +1640,7 @@ void ClangdTestCompletion::testFunctionHintConstructor() QVERIFY(!hasItem(proposal, "globalVariable")); QVERIFY(!hasItem(proposal, " class")); QVERIFY(hasItem(proposal, "Foo(int)")); - QEXPECT_FAIL("", "FIXME: LanguageClient handles active parameter only in active signature", Abort); + QEXPECT_FAIL("", "QTCREATORBUG-26346", Abort); QVERIFY(hasItem(proposal, "Foo(int, double)")); } @@ -1669,8 +1669,7 @@ void ClangdTestCompletion::testCompletePrivateFunctionDefinition() getProposal("privateFuncDefCompletion.cpp", proposal); QVERIFY(proposal); - QEXPECT_FAIL("", "FIXME: clangd needs to differentiate " - "between function call and function definiton", Abort); + QEXPECT_FAIL("", "https://github.com/clangd/clangd/issues/880", Abort); QCOMPARE(proposal->size(), 1); QVERIFY(hasItem(proposal, " theFunc()")); } From adc9cffab3861977859829a91dadcc7f3955e956 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 29 Sep 2021 18:18:56 +0200 Subject: [PATCH 05/28] ProjectExplorer: Drop some unnecessary QFileInfo and QDir use ... in kitinformation.cpp. And clean up a bit. Change-Id: I94c781290349e1abcc9b3a51d1a7b4164f8d3c37 Reviewed-by: Christian Kandeler --- .../projectexplorer/kitinformation.cpp | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp index 91a90a65981..781c83e5421 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitinformation.cpp @@ -51,8 +51,6 @@ #include #include -#include -#include #include #include #include @@ -133,18 +131,16 @@ Tasks SysRootKitAspect::validate(const Kit *k) const if (dir.isEmpty()) return result; - if (dir.toString().startsWith("target:") || dir.toString().startsWith("remote:")) + if (dir.startsWith("target:") || dir.startsWith("remote:")) return result; - const QFileInfo fi = dir.toFileInfo(); - - if (!fi.exists()) { + if (!dir.exists()) { result << BuildSystemTask(Task::Warning, tr("Sys Root \"%1\" does not exist in the file system.").arg(dir.toUserOutput())); - } else if (!fi.isDir()) { + } else if (!dir.isDir()) { result << BuildSystemTask(Task::Warning, tr("Sys Root \"%1\" is not a directory.").arg(dir.toUserOutput())); - } else if (QDir(dir.toString()).entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) { + } else if (dir.dirEntries(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) { result << BuildSystemTask(Task::Warning, tr("Sys Root \"%1\" is empty.").arg(dir.toUserOutput())); } @@ -626,11 +622,10 @@ QList ToolChainKitAspect::toolChains(const Kit *k) const QVariantMap value = k->value(ToolChainKitAspect::id()).toMap(); const QList tcList - = Utils::transform(ToolChainManager::allLanguages(), - [&value](Utils::Id l) -> ToolChain * { - return ToolChainManager::findToolChain(value.value(l.toString()).toByteArray()); - }); - return Utils::filtered(tcList, [](ToolChain *tc) { return tc; }); + = transform(ToolChainManager::allLanguages(), [&value](Id l) { + return ToolChainManager::findToolChain(value.value(l.toString()).toByteArray()); + }); + return filtered(tcList, [](ToolChain *tc) { return tc; }); } void ToolChainKitAspect::setToolChain(Kit *k, ToolChain *tc) @@ -664,7 +659,7 @@ void ToolChainKitAspect::setAllToolChainsToMatch(Kit *k, ToolChain *tc) QVariantMap result = k->value(ToolChainKitAspect::id()).toMap(); result.insert(tc->language().toString(), tc->id()); - for (Utils::Id l : ToolChainManager::allLanguages()) { + for (const Id l : ToolChainManager::allLanguages()) { if (l == tc->language()) continue; @@ -692,7 +687,7 @@ void ToolChainKitAspect::setAllToolChainsToMatch(Kit *k, ToolChain *tc) k->setValue(id(), result); } -void ToolChainKitAspect::clearToolChain(Kit *k, Utils::Id language) +void ToolChainKitAspect::clearToolChain(Kit *k, Id language) { QTC_ASSERT(language.isValid(), return); QTC_ASSERT(k, return); @@ -710,7 +705,7 @@ Abi ToolChainKitAspect::targetAbi(const Kit *k) QHash abiCount; foreach (ToolChain *tc, tcList) { Abi ta = tc->targetAbi(); - if (tc->language() == Utils::Id(Constants::CXX_LANGUAGE_ID)) + if (tc->language() == Id(Constants::CXX_LANGUAGE_ID)) cxxAbi = tc->targetAbi(); abiCount[ta] = (abiCount.contains(ta) ? abiCount[ta] + 1 : 1); } @@ -799,7 +794,7 @@ private: void refresh() override { - Utils::Id devType = DeviceTypeKitAspect::deviceTypeId(m_kit); + Id devType = DeviceTypeKitAspect::deviceTypeId(m_kit); if (!devType.isValid()) m_comboBox->setCurrentIndex(-1); for (int i = 0; i < m_comboBox->count(); ++i) { @@ -812,7 +807,7 @@ private: void currentTypeChanged(int idx) { - Utils::Id type = idx < 0 ? Utils::Id() : Utils::Id::fromSetting(m_comboBox->itemData(idx)); + Id type = idx < 0 ? Id() : Id::fromSetting(m_comboBox->itemData(idx)); DeviceTypeKitAspect::setDeviceTypeId(m_kit, type); } @@ -851,7 +846,7 @@ KitAspectWidget *DeviceTypeKitAspect::createConfigWidget(Kit *k) const KitAspect::ItemList DeviceTypeKitAspect::toUserOutput(const Kit *k) const { QTC_ASSERT(k, return {}); - Utils::Id type = deviceTypeId(k); + Id type = deviceTypeId(k); QString typeDisplayName = tr("Unknown device type"); if (type.isValid()) { if (IDeviceFactory *factory = IDeviceFactory::find(type)) @@ -860,33 +855,33 @@ KitAspect::ItemList DeviceTypeKitAspect::toUserOutput(const Kit *k) const return {{tr("Device type"), typeDisplayName}}; } -const Utils::Id DeviceTypeKitAspect::id() +const Id DeviceTypeKitAspect::id() { return "PE.Profile.DeviceType"; } -const Utils::Id DeviceTypeKitAspect::deviceTypeId(const Kit *k) +const Id DeviceTypeKitAspect::deviceTypeId(const Kit *k) { - return k ? Utils::Id::fromSetting(k->value(DeviceTypeKitAspect::id())) : Utils::Id(); + return k ? Id::fromSetting(k->value(DeviceTypeKitAspect::id())) : Id(); } -void DeviceTypeKitAspect::setDeviceTypeId(Kit *k, Utils::Id type) +void DeviceTypeKitAspect::setDeviceTypeId(Kit *k, Id type) { QTC_ASSERT(k, return); k->setValue(DeviceTypeKitAspect::id(), type.toSetting()); } -QSet DeviceTypeKitAspect::supportedPlatforms(const Kit *k) const +QSet DeviceTypeKitAspect::supportedPlatforms(const Kit *k) const { return {deviceTypeId(k)}; } -QSet DeviceTypeKitAspect::availableFeatures(const Kit *k) const +QSet DeviceTypeKitAspect::availableFeatures(const Kit *k) const { - Utils::Id id = DeviceTypeKitAspect::deviceTypeId(k); + Id id = DeviceTypeKitAspect::deviceTypeId(k); if (id.isValid()) return {id.withPrefix("DeviceType.")}; - return QSet(); + return {}; } // -------------------------------------------------------------------------- @@ -1225,7 +1220,7 @@ private: QComboBox *m_comboBox; QWidget *m_manageButton; DeviceManagerModel *m_model; - Utils::Id m_selectedId; + Id m_selectedId; }; } // namespace Internal From b7c15d4d8dfdbc40c89fc6ba3c6e9e7d142cdab4 Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Thu, 23 Sep 2021 17:11:19 +0300 Subject: [PATCH 06/28] Android: Make fixes to androidqmlpreview This amends 261a39cbbd2fa53d35bd4d4de8642dc341f9f6ad with fixes to issues noticed after merging the initial patch. Change-Id: I5f859374cbba3a2e020e6ca0789cc2b387d2739a Reviewed-by: Christian Kandeler Reviewed-by: Alessandro Portale Reviewed-by: Qt CI Bot --- .../android/androidqmlpreviewworker.cpp | 360 ++++++++---------- src/plugins/android/androidqmlpreviewworker.h | 34 +- 2 files changed, 184 insertions(+), 210 deletions(-) diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp index 645cabbb48a..37de94a6f94 100644 --- a/src/plugins/android/androidqmlpreviewworker.cpp +++ b/src/plugins/android/androidqmlpreviewworker.cpp @@ -23,12 +23,13 @@ ** ****************************************************************************/ +#include "androidqmlpreviewworker.h" + #include "androidavdmanager.h" #include "androiddevice.h" #include "androiddeviceinfo.h" #include "androidglobal.h" #include "androidmanager.h" -#include "androidqmlpreviewworker.h" #include @@ -44,10 +45,10 @@ #include #include +#include + +#include #include -#include -#include -#include namespace Android { namespace Internal { @@ -80,173 +81,143 @@ ApkInfo::ApkInfo() : Q_GLOBAL_STATIC(ApkInfo, apkInfo) -const char packageSuffix[] = ".qmlrc"; +static const char packageSuffix[] = ".qmlrc"; -static inline bool isMainThread() +FilePath AndroidQmlPreviewWorker::designViewerApkPath(const QString &abi) const { - return QCoreApplication::instance()->thread() == QThread::currentThread(); -} - -static FilePath viewerApkPath(const QString &avdAbi) -{ - if (avdAbi.isEmpty()) + if (abi.isEmpty()) return {}; - if (apkInfo()->abis.contains(avdAbi)) - return Core::ICore::resourcePath(QString("android/qtdesignviewer/designviewer_%1.apk"). - arg(avdAbi)); + if (apkInfo()->abis.contains(abi)) { + return Core::ICore::resourcePath(QString("android/qtdesignviewer/designviewer_%1.apk") + .arg(abi)); + } return {}; } -static SdkToolResult runAdbCommandAsyncAndWait(const QString &dev, const QStringList &arguments) +SdkToolResult AndroidQmlPreviewWorker::runAdbCommand(const QStringList &arguments) const { QStringList args; - if (!dev.isEmpty()) - args << AndroidDeviceInfo::adbSelector(dev); - args << arguments; - QFuture asyncResult = QtConcurrent::run([args] { - return AndroidManager::runAdbCommand(args);}); - - while (asyncResult.isRunning()) { - QCoreApplication::instance()->processEvents(QEventLoop::AllEvents, 100); - } - return asyncResult.result(); -} - -static SdkToolResult runAdbCommand(const QString &dev, const QStringList &arguments) -{ - if (isMainThread()) - return runAdbCommandAsyncAndWait(dev, arguments); - QStringList args; - if (!dev.isEmpty()) - args << AndroidDeviceInfo::adbSelector(dev); + if (!m_serialNumber.isEmpty()) + args << AndroidDeviceInfo::adbSelector(m_serialNumber); args << arguments; return AndroidManager::runAdbCommand(args); } -static SdkToolResult runAdbShellCommand(const QString &dev, const QStringList &arguments) +SdkToolResult AndroidQmlPreviewWorker::runAdbShellCommand(const QStringList &arguments) const { - const QStringList shellCmd{"shell"}; - return runAdbCommand(dev, shellCmd + arguments); + return runAdbCommand(QStringList() << "shell" << arguments); } -static QString startAvd(const AndroidAvdManager &avd, const QString &name) -{ - QFuture asyncRes = QtConcurrent::run([avd, name] { - return avd.startAvd(name); - }); - while (asyncRes.isRunning()) - QCoreApplication::instance()->processEvents(QEventLoop::AllEvents, 100); - return asyncRes.result(); -} - -static int pidofPreview(const QString &dev) +int AndroidQmlPreviewWorker::pidofPreview() const { const QStringList command{"pidof", apkInfo()->appId}; - const SdkToolResult res = runAdbShellCommand(dev, command); + const SdkToolResult res = runAdbShellCommand(command); return res.success() ? res.stdOut().toInt() : -1; } -static bool isPreviewRunning(const QString &dev, int lastKnownPid = -1) +bool AndroidQmlPreviewWorker::isPreviewRunning(int lastKnownPid) const { - const int pid = pidofPreview(dev); + const int pid = pidofPreview(); return (lastKnownPid > 1) ? lastKnownPid == pid : pid > 1; } -AndroidQmlPreviewWorker::AndroidQmlPreviewWorker(ProjectExplorer::RunControl *runControl) - : ProjectExplorer::RunWorker(runControl) - , m_rc(runControl) - , m_config(AndroidConfigurations::currentConfig()) +void AndroidQmlPreviewWorker::startPidWatcher() { -} - -QStringList filterAppLog(const QStringList& oldList, const QStringList& newList) -{ - QStringList list = Utils::filtered(newList, - [](const auto & arg){return arg.contains(apkInfo()->name);}); - for (const auto &oldEntry : oldList) { - list.removeAll(oldEntry); - } - return list; -} - -void AndroidQmlPreviewWorker::start() -{ - UploadInfo transfer; - const bool res = ensureAvdIsRunning() - && checkAndInstallPreviewApp() - && prepareUpload(transfer) - && uploadFiles(transfer) - && runPreviewApp(transfer); - - if (!res) { - reportFailure(); - return; - } - reportStarted(); - //Thread to monitor preview life - QtConcurrent::run([this]() { - QElapsedTimer timer; - timer.start(); - while (runControl() && runControl()->isRunning()) { + m_pidFutureWatcher.setFuture(Utils::runAsync([this]() { + // wait for started + const int sleepTimeMs = 2000; + QDeadlineTimer deadline(20000); + while (!m_pidFutureWatcher.isCanceled() && !deadline.hasExpired()) { if (m_viewerPid == -1) { - m_viewerPid = pidofPreview(m_devInfo.serialNumber); - if (m_viewerPid > 0) - QMetaObject::invokeMethod(this, &AndroidQmlPreviewWorker::startLogcat); - } else if (timer.elapsed() > 2000) { - //Get the application output - if (!isPreviewRunning(m_devInfo.serialNumber, m_viewerPid)) - QMetaObject::invokeMethod(this, &AndroidQmlPreviewWorker::stop); - - timer.restart(); + m_viewerPid = pidofPreview(); + if (m_viewerPid > 0) { + emit previewPidChanged(); + break; + } } - QThread::msleep(100); + QThread::msleep(sleepTimeMs); } - }); + + while (!m_pidFutureWatcher.isCanceled()) { + if (!isPreviewRunning(m_viewerPid)) { + stop(); + break; + } + QThread::msleep(sleepTimeMs); + } + })); } void AndroidQmlPreviewWorker::startLogcat() { - QtConcurrent::run([this]() { - QElapsedTimer timer; - timer.start(); - int initialPid = m_viewerPid; // to check if our initial process is still alive - QStringList logLines; - auto appendLogLinesCall = [&logLines, this](){ appendLogLines(logLines); }; - auto runCondition = [this, initialPid](){ return (runControl() && runControl()->isRunning()) - && initialPid == m_viewerPid;}; - QString timeFilter; - while (runCondition()) { - if (timer.elapsed() > 2000) { - //Get the application output - QStringList logcatCmd = {"logcat", QString("--pid=%1").arg(initialPid), "-t"}; - if (!timeFilter.isEmpty()) - logcatCmd.append(QString("%1").arg(timeFilter)); - else - logcatCmd.append(QString("1000")); //show last 1000 lines (but for the 1st time) + QString args = QString("logcat --pid=%1").arg(m_viewerPid); + if (!m_logcatStartTimeStamp.isEmpty()) + args += QString(" -T '%1'").arg(m_logcatStartTimeStamp); + Utils::CommandLine cmd(AndroidConfigurations::currentConfig().adbToolPath()); + cmd.setArguments(args); + m_logcatProcess.setCommand(cmd); + m_logcatProcess.setUseCtrlCStub(true); + m_logcatProcess.start(); +} - const SdkToolResult logcatResult = runAdbCommand(m_devInfo.serialNumber, logcatCmd); - if (runCondition()) { - const QStringList output = logcatResult.stdOut().split('\n'); - const QStringList filtered = filterAppLog(logLines, output); +void AndroidQmlPreviewWorker::filterLogcatAndAppendMessage(const QString &stdOut) +{ + for (const QString &line : stdOut.split('\n')) { + QStringList splittedLine = line.split(QLatin1String("%1: ").arg(apkInfo()->name)); + if (splittedLine.count() == 1) + continue; - if (!filtered.isEmpty()){ - const QString lastLine = filtered.last(); - timeFilter = lastLine.left(lastLine.indexOf(" ", lastLine.indexOf(" ") + 1)); - QMetaObject::invokeMethod(this, appendLogLinesCall); - logLines = filtered; - } - } - timer.restart(); - } - QThread::msleep(100); - } + const QString outLine = splittedLine.last(); + const QString firstPart = splittedLine.first(); + if (firstPart.contains(" I ") || firstPart.contains(" D ")) + appendMessage(outLine, NormalMessageFormat); + else + appendMessage(outLine, ErrorMessageFormat); + } +} + +AndroidQmlPreviewWorker::AndroidQmlPreviewWorker(ProjectExplorer::RunControl *runControl) + : ProjectExplorer::RunWorker(runControl), + m_rc(runControl), + m_androidConfig(AndroidConfigurations::currentConfig()) +{ + connect(this, &RunWorker::started, this, &AndroidQmlPreviewWorker::startPidWatcher); + connect(this, &RunWorker::stopped, &m_pidFutureWatcher, &QFutureWatcher::cancel); + connect(this, &AndroidQmlPreviewWorker::previewPidChanged, + this, &AndroidQmlPreviewWorker::startLogcat); + + connect(this, &RunWorker::stopped, &m_logcatProcess, &Utils::QtcProcess::stopProcess); + m_logcatProcess.setStdOutCallback([this](const QString &stdOut) { + filterLogcatAndAppendMessage(stdOut); }); } +AndroidQmlPreviewWorker::~AndroidQmlPreviewWorker() +{ + m_pidFutureWatcher.cancel(); + m_pidFutureWatcher.waitForFinished(); +} + +void AndroidQmlPreviewWorker::start() +{ + const SdkToolResult dateResult = runAdbCommand({"shell", "date", "+%s"}); + if (dateResult.success()) { + m_logcatStartTimeStamp = QDateTime::fromSecsSinceEpoch(dateResult.stdOut().toInt()) + .toString("MM-dd hh:mm:ss.mmm"); + } + const bool previewStarted = ensureAvdIsRunning() + && checkAndInstallPreviewApp() + && uploadPreviewArtefacts() + && preparePreviewArtefacts() + && startPreviewApp(); + + previewStarted ? reportStarted() : reportStopped(); +} + void AndroidQmlPreviewWorker::stop() { - if (!isPreviewRunning(m_devInfo.serialNumber, m_viewerPid) || stopPreviewApp()) + if (!isPreviewRunning(m_viewerPid) || stopPreviewApp()) appendMessage(tr("%1 has been stopped.").arg(apkInfo()->name), NormalMessageFormat); m_viewerPid = -1; reportStopped(); @@ -254,28 +225,35 @@ void AndroidQmlPreviewWorker::stop() bool AndroidQmlPreviewWorker::ensureAvdIsRunning() { - AndroidAvdManager avdMan(m_config); + AndroidAvdManager avdMananager(m_androidConfig); QString devSN = AndroidManager::deviceSerialNumber(m_rc->target()); if (devSN.isEmpty()) - devSN = m_devInfo.serialNumber; + devSN = m_serialNumber; - if (!avdMan.isAvdBooted(devSN)) { - m_devInfo = {}; + if (!avdMananager.isAvdBooted(devSN)) { using namespace ProjectExplorer; const IDevice *dev = DeviceKitAspect::device(m_rc->target()->kit()).data(); + if (!dev) { + appendMessage(tr("Selected device is invalid."), ErrorMessageFormat); + return false; + } + if (dev->deviceState() == IDevice::DeviceDisconnected) { + appendMessage(tr("Selected device is disconnected."), ErrorMessageFormat); + return false; + } AndroidDeviceInfo devInfoLocal = AndroidDevice::androidDeviceInfoFromIDevice(dev); if (devInfoLocal.isValid()) { - if (devInfoLocal.type == AndroidDeviceInfo::Emulator) { + if (dev->machineType() == IDevice::Emulator) { appendMessage(tr("Launching AVD."), NormalMessageFormat); - devInfoLocal.serialNumber = startAvd(avdMan, devInfoLocal.avdname); + devInfoLocal.serialNumber = avdMananager.startAvd(devInfoLocal.avdname); } if (devInfoLocal.serialNumber.isEmpty()) { - appendMessage(tr("Could not run AVD."), ErrorMessageFormat); + appendMessage(tr("Could not start AVD."), ErrorMessageFormat); } else { - m_devInfo = devInfoLocal; - m_avdAbis = m_config.getAbis(m_config.adbToolPath(), m_devInfo.serialNumber); + m_serialNumber = devInfoLocal.serialNumber; + m_avdAbis = m_androidConfig.getAbis(m_androidConfig.adbToolPath(), m_serialNumber); } return !devInfoLocal.serialNumber.isEmpty(); } else { @@ -283,7 +261,7 @@ bool AndroidQmlPreviewWorker::ensureAvdIsRunning() } return false; } - m_avdAbis = m_config.getAbis(m_config.adbToolPath(), m_devInfo.serialNumber); + m_avdAbis = m_androidConfig.getAbis(m_androidConfig.adbToolPath(), m_serialNumber); return true; } @@ -291,7 +269,7 @@ bool AndroidQmlPreviewWorker::checkAndInstallPreviewApp() { const QStringList command {"pm", "list", "packages", apkInfo()->appId}; appendMessage(tr("Checking if %1 app is installed.").arg(apkInfo()->name), NormalMessageFormat); - const SdkToolResult res = runAdbShellCommand(m_devInfo.serialNumber, command); + const SdkToolResult res = runAdbShellCommand(command); if (!res.success()) { appendMessage(res.stdErr(), ErrorMessageFormat); return false; @@ -303,7 +281,7 @@ bool AndroidQmlPreviewWorker::checkAndInstallPreviewApp() ErrorMessageFormat); return false; } - const FilePath apkPath = viewerApkPath(m_avdAbis.first()); + const FilePath apkPath = designViewerApkPath(m_avdAbis.first()); if (!apkPath.exists()) { appendMessage(tr("Cannot install %1 app for %2 architecture. " "The appropriate APK was not found in resources folders."). @@ -313,32 +291,29 @@ bool AndroidQmlPreviewWorker::checkAndInstallPreviewApp() appendMessage(tr("Installing %1 APK.").arg(apkInfo()->name), NormalMessageFormat); - - const SdkToolResult res = runAdbCommand(m_devInfo.serialNumber, {"install", - apkPath.toString()}); - if (!res.success()) { + const SdkToolResult res = runAdbCommand({"install", apkPath.toString()}); + if (!res.success()) appendMessage(res.stdErr(), StdErrFormat); - - return false; - } } - return true; + + return res.success(); } -bool AndroidQmlPreviewWorker::prepareUpload(UploadInfo &transfer) +bool AndroidQmlPreviewWorker::preparePreviewArtefacts() { if (m_rc->project()->id() == QmlProjectManager::Constants::QML_PROJECT_ID) { const auto bs = m_rc->target()->buildSystem(); if (bs) { - transfer.uploadPackage = FilePath::fromString( + m_uploadInfo.uploadPackage = FilePath::fromString( bs->additionalData(QmlProjectManager::Constants::mainFilePath).toString()); - transfer.projectFolder = bs->projectDirectory(); + m_uploadInfo.projectFolder = bs->projectDirectory(); return true; } } else { const FilePaths allFiles = m_rc->project()->files(m_rc->project()->SourceFiles); const FilePaths filesToExport = Utils::filtered(allFiles,[](const FilePath &path) { - return path.suffix() == "qmlproject";}); + return path.suffix() == "qmlproject"; + }); if (filesToExport.size() > 1) { appendMessage(tr("Too many .qmlproject files in your project. Open directly the " @@ -348,10 +323,9 @@ bool AndroidQmlPreviewWorker::prepareUpload(UploadInfo &transfer) appendMessage(tr("No .qmlproject file found among project files."), ErrorMessageFormat); } else { const FilePath qmlprojectFile = filesToExport.first(); - transfer.uploadPackage = transfer. - projectFolder. - resolvePath(qmlprojectFile.fileName()); - transfer.projectFolder = qmlprojectFile.parentDir(); + m_uploadInfo.uploadPackage = m_uploadInfo.projectFolder.resolvePath( + qmlprojectFile.fileName()); + m_uploadInfo.projectFolder = qmlprojectFile.parentDir(); return true; } } @@ -366,16 +340,15 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder, const FilePath rccBinary = qtVersion->rccFilePath(); QtcProcess rccProcess; FilePath qrcPath = FilePath::fromString(basename) + ".qrc4viewer"; - const FilePath qmlrcPath = FilePath::fromString(QDir::tempPath() + "/" + basename + - packageSuffix); + const FilePath qmlrcPath = FilePath::fromString(QDir::tempPath()) / basename + packageSuffix; rccProcess.setWorkingDirectory(workFolder); const QStringList arguments[2] = {{"--project", "--output", qrcPath.fileName()}, {"--binary", "--output", qmlrcPath.path(), qrcPath.fileName()}}; - for (const auto &arguments : arguments) { - rccProcess.setCommand({rccBinary, arguments}); + for (const QStringList &args : arguments) { + rccProcess.setCommand({rccBinary, args}); rccProcess.start(); if (!rccProcess.waitForStarted()) { appendMessage(tr("Could not create file for %1 \"%2\""). @@ -420,71 +393,54 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder, return qmlrcPath; } -bool AndroidQmlPreviewWorker::uploadFiles(const UploadInfo &transfer) +bool AndroidQmlPreviewWorker::uploadPreviewArtefacts() { appendMessage(tr("Uploading files."), NormalMessageFormat); - - const FilePath qresPath = createQmlrcFile(FilePath::fromString(transfer.projectFolder.path()), - transfer.uploadPackage.baseName()); + const FilePath qresPath = createQmlrcFile(m_uploadInfo.projectFolder, + m_uploadInfo.uploadPackage.baseName()); if (!qresPath.exists()) return false; - runAdbShellCommand(m_devInfo.serialNumber, {"mkdir", "-p", apkInfo()->uploadDir}); - - const SdkToolResult res = runAdbCommand(m_devInfo.serialNumber, - {"push", qresPath.resolvePath(QString()).toString(), - apkInfo()->uploadDir}); + runAdbShellCommand({"mkdir", "-p", apkInfo()->uploadDir}); + const SdkToolResult res = runAdbCommand({"push", qresPath.resolvePath(QString()).toString(), + apkInfo()->uploadDir}); if (!res.success()) { appendMessage(res.stdOut(), ErrorMessageFormat); - if (res.stdOut().contains("Permission denied")) + if (res.stdOut().contains("Permission denied")) { appendMessage("'Permission denied' error detected. Try restarting your device " "and then running the preview.", NormalMessageFormat); + } } qresPath.removeFile(); return res.success(); } -bool AndroidQmlPreviewWorker::runPreviewApp(const UploadInfo &transfer) +bool AndroidQmlPreviewWorker::startPreviewApp() { stopPreviewApp(); appendMessage(tr("Starting %1.").arg(apkInfo()->name), NormalMessageFormat); const QDir destDir(apkInfo()->uploadDir); + const QString qmlrcPath = destDir.filePath(m_uploadInfo.uploadPackage.baseName() + + packageSuffix); const QStringList command{"am", "start", "-n", apkInfo()->activityId, - "-e", "extraappparams", - QString::fromLatin1( - destDir.filePath(transfer.uploadPackage.baseName() + packageSuffix). - toUtf8(). - toBase64())}; - const SdkToolResult res = runAdbShellCommand(m_devInfo.serialNumber, command); - if (!res.success()) { - appendMessage(res.stdErr(), ErrorMessageFormat); - return res.success(); - } - appendMessage(tr("%1 is running.").arg(apkInfo()->name), NormalMessageFormat); - m_viewerPid = pidofPreview(m_devInfo.serialNumber); - return true; + "-e", "extraappparams", QLatin1String(qmlrcPath.toUtf8().toBase64())}; + const SdkToolResult result = runAdbShellCommand(command); + if (result.success()) + appendMessage(tr("%1 is running.").arg(apkInfo()->name), NormalMessageFormat); + else + appendMessage(result.stdErr(), ErrorMessageFormat); + + return result.success(); } bool AndroidQmlPreviewWorker::stopPreviewApp() { const QStringList command{"am", "force-stop", apkInfo()->appId}; - const SdkToolResult res = runAdbShellCommand(m_devInfo.serialNumber, command); - if (!res.success()) { + const SdkToolResult res = runAdbShellCommand(command); + if (!res.success()) appendMessage(res.stdErr(), ErrorMessageFormat); - return res.success(); - } - return true; -} - -void AndroidQmlPreviewWorker::appendLogLines(const QStringList & lines) -{ - for (const QString& line : lines) { - const int charsToSkip = apkInfo()->name.length() + 2; // strlen(": ") == 2 - const QString formatted = line.mid(line.indexOf(apkInfo()->name) + charsToSkip); - // TODO: See AndroidRunnerWorker::logcatProcess() - filtering for logs to decide format. - appendMessage(formatted, StdOutFormat); - } + return res.success(); } } // namespace Internal diff --git a/src/plugins/android/androidqmlpreviewworker.h b/src/plugins/android/androidqmlpreviewworker.h index e014107df54..c837570e01f 100644 --- a/src/plugins/android/androidqmlpreviewworker.h +++ b/src/plugins/android/androidqmlpreviewworker.h @@ -25,9 +25,12 @@ #pragma once #include "androidconfigurations.h" + #include #include +#include + namespace Android { class SdkToolResult; @@ -45,6 +48,10 @@ class AndroidQmlPreviewWorker : public ProjectExplorer::RunWorker Q_OBJECT public: AndroidQmlPreviewWorker(ProjectExplorer::RunControl *runControl); + ~AndroidQmlPreviewWorker(); + +signals: + void previewPidChanged(); private: void start() override; @@ -52,22 +59,33 @@ private: bool ensureAvdIsRunning(); bool checkAndInstallPreviewApp(); - bool prepareUpload(UploadInfo &transfer); - bool uploadFiles(const UploadInfo &transfer); + bool preparePreviewArtefacts(); + bool uploadPreviewArtefacts(); - bool runPreviewApp(const UploadInfo &transfer); + SdkToolResult runAdbCommand(const QStringList &arguments) const; + SdkToolResult runAdbShellCommand(const QStringList &arguments) const; + int pidofPreview() const; + bool isPreviewRunning(int lastKnownPid = -1) const; + + void startPidWatcher(); + void startLogcat(); + void filterLogcatAndAppendMessage(const QString &stdOut); + + bool startPreviewApp(); bool stopPreviewApp(); - void startLogcat(); - void appendLogLines(const QStringList &lines); - + Utils::FilePath designViewerApkPath(const QString &abi) const; Utils::FilePath createQmlrcFile(const Utils::FilePath &workFolder, const QString &basename); ProjectExplorer::RunControl *m_rc = nullptr; - AndroidConfig m_config; - AndroidDeviceInfo m_devInfo; + AndroidConfig m_androidConfig; + QString m_serialNumber; QStringList m_avdAbis; int m_viewerPid = -1; + QFutureWatcher m_pidFutureWatcher; + Utils::QtcProcess m_logcatProcess; + QString m_logcatStartTimeStamp; + UploadInfo m_uploadInfo; }; } // namespace Internal From f4a7c74daa2bb3a8e67228b38d98cd0beba3c4ab Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 30 Sep 2021 10:50:52 +0200 Subject: [PATCH 07/28] PE: Welcome: Fix displaying project paths Do not use native separators by default as the separators depend on the context. Change-Id: I28e73395ae2d7bb71774c060dd9540dc48c4c992 Reviewed-by: hjk --- src/plugins/projectexplorer/projectwelcomepage.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index 8cf3fc70f9a..e0b8795f403 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -320,14 +320,14 @@ public: QFontMetrics fm(option.widget->font()); for (const QString &project : projects) { // Project name. - QFileInfo fi(project); - QString completeBase = fi.completeBaseName(); + FilePath projectPath = FilePath::fromString(project); + QString completeBase = projectPath.completeBaseName(); painter->setPen(textColor); painter->drawText(x1, yy, completeBase); yy += 18; // Project path. - QString pathWithTilde = Utils::withTildeHomePath(QDir::toNativeSeparators(project)); + QString pathWithTilde = Utils::withTildeHomePath(projectPath.toUserOutput()); painter->setPen(foregroundColor1); painter->drawText(x1, yy, fm.elidedText(pathWithTilde, Qt::ElideMiddle, rc.width() - 40)); yy += 22; @@ -454,7 +454,7 @@ public: painter->drawPixmap(x + 11, y + 6, projectIcon); QString projectName = idx.data(Qt::DisplayRole).toString(); - QString projectPath = idx.data(ProjectModel::FilePathRole).toString(); + FilePath projectPath = FilePath::fromVariant(idx.data(ProjectModel::FilePathRole)); painter->setPen(themeColor(Theme::Welcome_ForegroundSecondaryColor)); painter->setFont(sizedFont(10, option.widget)); @@ -468,7 +468,7 @@ public: painter->setPen(themeColor(Theme::Welcome_ForegroundPrimaryColor)); painter->setFont(sizedFont(13, option.widget)); - QString pathWithTilde = Utils::withTildeHomePath(QDir::toNativeSeparators(projectPath)); + QString pathWithTilde = Utils::withTildeHomePath(projectPath.toUserOutput()); painter->drawText(x + 36, secondBase, pathWithTilde); } From 04d49fb77179a554a692a930c32afed815dd9f21 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 29 Sep 2021 17:50:39 +0200 Subject: [PATCH 08/28] LanguageClient: Disable DocumentLocatorFilter for non-applicable files Fixes: QTCREATORBUG-26338 Change-Id: Ief7a45dda74b931b26731647adba43861cb09777 Reviewed-by: David Schulz --- src/plugins/languageclient/locatorfilter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index a817b4339d8..5d2a583a27f 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -62,7 +62,9 @@ void DocumentLocatorFilter::updateCurrentClient() disconnect(m_resetSymbolsConnection); TextEditor::TextDocument *document = TextEditor::TextDocument::currentTextDocument(); - if (Client *client = LanguageClientManager::clientForDocument(document)) { + if (Client *client = LanguageClientManager::clientForDocument(document); + client && client->locatorsEnabled()) { + setEnabled(true); if (m_symbolCache != client->documentSymbolCache()) { disconnect(m_updateSymbolsConnection); m_symbolCache = client->documentSymbolCache(); @@ -76,6 +78,7 @@ void DocumentLocatorFilter::updateCurrentClient() disconnect(m_updateSymbolsConnection); m_symbolCache.clear(); m_currentUri.clear(); + setEnabled(false); } } From 32799b3a7bd0cb8cd3da99c869649dab3be83634 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Mon, 27 Sep 2021 20:21:50 +0300 Subject: [PATCH 09/28] Make additional changes in the QtCreator side required for QDS new project dialog * We need to use the CheckBox field to set the checked state of fields (TargetQtVersion, Use as default project location). * When the validation of the Project Name or Project Location fails, we need a way to be notified of what went wrong. We also need a way to force the project intro page to execute the validation of those fields whenever we need (i.e. when the user types into our QML TextField controls) Also, add a warning on loading wizards: whenever a wizard.json file cannot be parsed, we should issue a qWarning(), so that we can more easily notice when something went wrong. Task-number: QDS-4490 Change-Id: I7cfa61b4e43d731db9d0679e093e723d947b60c0 Reviewed-by: Alessandro Portale Reviewed-by: Qt CI Bot --- src/libs/utils/projectintropage.cpp | 7 +++++++ src/libs/utils/projectintropage.h | 6 ++++++ src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp | 9 +++++++++ src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h | 4 +++- .../projectexplorer/jsonwizard/jsonwizardfactory.cpp | 3 +++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp index 1a318df0d4c..6e180496375 100644 --- a/src/libs/utils/projectintropage.cpp +++ b/src/libs/utils/projectintropage.cpp @@ -195,6 +195,11 @@ bool ProjectIntroPage::validate() return false; } +void ProjectIntroPage::fieldsUpdated() +{ + slotChanged(); +} + void ProjectIntroPage::slotChanged() { const bool newComplete = validate(); @@ -286,6 +291,8 @@ void ProjectIntroPage::displayStatusMessage(InfoLabel::InfoType t, const QString { d->m_ui.stateLabel->setType(t); d->m_ui.stateLabel->setText(s); + + emit statusMessageChanged(t, s); } void ProjectIntroPage::hideStatusLabel() diff --git a/src/libs/utils/projectintropage.h b/src/libs/utils/projectintropage.h index 5de165efe11..0bccd91f2b1 100644 --- a/src/libs/utils/projectintropage.h +++ b/src/libs/utils/projectintropage.h @@ -66,8 +66,14 @@ public: bool validateProjectName(const QString &name, QString *errorMessage); + // Calls slotChanged() - i.e. tell the page that some of its fields have been updated. + // This function is useful if you programmatically update the fields of the page (i.e. from + // your client code). + void fieldsUpdated(); + signals: void activated(); + void statusMessageChanged(InfoLabel::InfoType type, const QString &message); public slots: void setFilePath(const FilePath &path); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index d7584ede0bb..d6080f4e90f 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -956,6 +956,15 @@ void CheckBoxField::setup(JsonFieldPage *page, const QString &name) }); } +void CheckBoxField::setChecked(bool value) +{ + auto w = qobject_cast(widget()); + QTC_ASSERT(w, return); + + w->setChecked(value); + w->clicked(value); +} + bool CheckBoxField::validate(MacroExpander *expander, QString *message) { if (!JsonFieldPage::Field::validate(expander, message)) diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h index 251315a510b..ed89746d0b2 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h @@ -230,11 +230,13 @@ private: Utils::PathChooser::Kind m_kind = Utils::PathChooser::ExistingDirectory; }; -class CheckBoxField : public JsonFieldPage::Field +class PROJECTEXPLORER_EXPORT CheckBoxField : public JsonFieldPage::Field { public: bool suppressName() const override { return true; } + void setChecked(bool); + private: bool parseData(const QVariant &data, QString *errorMessage) override; diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp index 579722a63d6..9aa16ff0513 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp @@ -257,12 +257,14 @@ QList JsonWizardFactory::createWizardFactories() .arg(currentFile.fileName()) .arg(line).arg(column) .arg(error.errorString())); + qWarning() << "Failed to parse wizard: " << currentFile.fileName(); continue; } if (!json.isObject()) { verboseLog.append(tr("* Did not find a JSON object in \"%1\".\n") .arg(currentFile.fileName())); + qWarning() << "Failed to parse wizard: " << currentFile.fileName(); continue; } @@ -280,6 +282,7 @@ QList JsonWizardFactory::createWizardFactories() JsonWizardFactory *factory = createWizardFactory(data, currentDir, &errorMessage); if (!factory) { verboseLog.append(tr("* Failed to create: %1\n").arg(errorMessage)); + qWarning() << "Failed to create wizard: " << currentFile.fileName(); continue; } From 7a20a8e70a46fa8b0528eb9f18b10294679c3dc0 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 29 Sep 2021 15:53:30 +0200 Subject: [PATCH 10/28] Docker: Split early return in tryCreateLocalFileAccess() For easier debugging. Change-Id: I427ec3ce10b3eae50a3fa67d5bba9951e9de4c14 Reviewed-by: Christian Stenger --- src/plugins/docker/dockerdevice.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index cbf27544079..624f3eab49b 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -897,7 +897,10 @@ void DockerDevicePrivate::startContainer() void DockerDevicePrivate::tryCreateLocalFileAccess() { - if (!m_container.isEmpty() || DockerPlugin::isDaemonRunning().value_or(true) == false) + if (!m_container.isEmpty()) + return; + + if (DockerPlugin::isDaemonRunning().value_or(true) == false) return; if (!m_shell) From 416ea51494be3047ae827e13a3e78e6460ae3e6c Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 30 Sep 2021 12:17:36 +0200 Subject: [PATCH 11/28] Android: Fix compile Amends b7c15d4d8dfdbc. Change-Id: I613476daa551cf52ef3b5e905868d027156b6826 Reviewed-by: hjk --- src/plugins/android/androidqmlpreviewworker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp index 37de94a6f94..3c2adca5727 100644 --- a/src/plugins/android/androidqmlpreviewworker.cpp +++ b/src/plugins/android/androidqmlpreviewworker.cpp @@ -48,6 +48,7 @@ #include #include +#include #include namespace Android { From 7df00776d12c3529def9d937c064a1b9e690bd4e Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 30 Sep 2021 07:23:23 +0200 Subject: [PATCH 12/28] LSP: Add support for activeParameter of SignatureInformation Task-number: QTCREATORBUG-26346 Change-Id: Ieab18f08e4f1b9fa6cacfae0a18310af4bc35165 Reviewed-by: Christian Kandeler --- src/libs/languageserverprotocol/clientcapabilities.h | 6 ++++++ src/libs/languageserverprotocol/jsonkeys.h | 1 + src/libs/languageserverprotocol/languagefeatures.h | 4 ++++ src/plugins/languageclient/client.cpp | 1 + .../languageclient/languageclientfunctionhint.cpp | 9 +++++---- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/libs/languageserverprotocol/clientcapabilities.h b/src/libs/languageserverprotocol/clientcapabilities.h index 147483c5042..c0bdae40319 100644 --- a/src/libs/languageserverprotocol/clientcapabilities.h +++ b/src/libs/languageserverprotocol/clientcapabilities.h @@ -336,6 +336,12 @@ public: Utils::optional> documentationFormat() const; void setDocumentationFormat(const QList &documentationFormat); void clearDocumentationFormat() { remove(documentationFormatKey); } + + Utils::optional activeParameterSupport() const + { return optionalValue(activeParameterSupportKey); } + void setActiveParameterSupport(bool activeParameterSupport) + { insert(activeParameterSupportKey, activeParameterSupport); } + void clearActiveParameterSupport() { remove(activeParameterSupportKey); } }; // The client supports the following `SignatureInformation` specific properties. diff --git a/src/libs/languageserverprotocol/jsonkeys.h b/src/libs/languageserverprotocol/jsonkeys.h index b0109c11349..fa0e5d63e01 100644 --- a/src/libs/languageserverprotocol/jsonkeys.h +++ b/src/libs/languageserverprotocol/jsonkeys.h @@ -29,6 +29,7 @@ namespace LanguageServerProtocol { constexpr char actionsKey[] = "actions"; constexpr char activeParameterKey[] = "activeParameter"; +constexpr char activeParameterSupportKey[] = "activeParameterSupport"; constexpr char activeSignatureKey[] = "activeSignature"; constexpr char addedKey[] = "added"; constexpr char additionalTextEditsKey[] = "additionalTextEdits"; diff --git a/src/libs/languageserverprotocol/languagefeatures.h b/src/libs/languageserverprotocol/languagefeatures.h index fc8df0bc716..ade9507d0b6 100644 --- a/src/libs/languageserverprotocol/languagefeatures.h +++ b/src/libs/languageserverprotocol/languagefeatures.h @@ -145,6 +145,10 @@ public: void setParameters(const QList ¶meters) { insertArray(parametersKey, parameters); } void clearParameters() { remove(parametersKey); } + + Utils::optional activeParameter() const { return optionalValue(activeParameterKey); } + void setActiveParameter(int activeParameter) { insert(activeParameterKey, activeParameter); } + void clearActiveParameter() { remove(activeParameterKey); } }; /** diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 220ddb42c55..5f2b44d5e91 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -248,6 +248,7 @@ static ClientCapabilities generateClientCapabilities() signatureHelp.setDynamicRegistration(true); TextDocumentClientCapabilities::SignatureHelpCapabilities::SignatureInformationCapabilities info; info.setDocumentationFormat({MarkupKind::markdown, MarkupKind::plaintext}); + info.setActiveParameterSupport(true); signatureHelp.setSignatureInformation(info); documentCapabilities.setSignatureHelp(signatureHelp); diff --git a/src/plugins/languageclient/languageclientfunctionhint.cpp b/src/plugins/languageclient/languageclientfunctionhint.cpp index 76a851d0cd4..f554d4a3b0e 100644 --- a/src/plugins/languageclient/languageclientfunctionhint.cpp +++ b/src/plugins/languageclient/languageclientfunctionhint.cpp @@ -61,11 +61,12 @@ QString FunctionHintProposalModel::text(int index) const if (index < 0 || m_sigis.signatures().size() <= index) return {}; const SignatureInformation signature = m_sigis.signatures().at(index); + int parametersIndex = signature.activeParameter().value_or(-1); + if (parametersIndex < 0) { + if (index == m_sigis.activeSignature().value_or(-1)) + parametersIndex = m_sigis.activeParameter().value_or(-1); + } QString label = signature.label(); - if (index != m_sigis.activeSignature().value_or(-1)) - return label; - - const int parametersIndex = m_sigis.activeParameter().value_or(-1); if (parametersIndex < 0) return label; From be4cfd9d07c0f6111fccb3d8f01f61ee2f7141ad Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 27 Sep 2021 14:25:26 +0200 Subject: [PATCH 13/28] Qnx: Use more FilePath for paths Change-Id: I36f00548247cd34bbb52b452bc80978f131e5270 Reviewed-by: Cristian Adam --- src/plugins/qnx/qnxbaseqtconfigwidget.cpp | 22 +++++++++---------- src/plugins/qnx/qnxbaseqtconfigwidget.h | 8 +++---- src/plugins/qnx/qnxconfiguration.cpp | 14 ++++++------ src/plugins/qnx/qnxqtversion.cpp | 10 ++++----- src/plugins/qnx/qnxqtversion.h | 6 +++--- src/plugins/qnx/qnxtoolchain.cpp | 26 +++++++++++------------ src/plugins/qnx/qnxtoolchain.h | 6 +++--- src/plugins/qnx/qnxutils.cpp | 25 +++++++++++----------- src/plugins/qnx/qnxutils.h | 6 +++--- 9 files changed, 60 insertions(+), 63 deletions(-) diff --git a/src/plugins/qnx/qnxbaseqtconfigwidget.cpp b/src/plugins/qnx/qnxbaseqtconfigwidget.cpp index 056dfa9cedf..7ac289c9bf5 100644 --- a/src/plugins/qnx/qnxbaseqtconfigwidget.cpp +++ b/src/plugins/qnx/qnxbaseqtconfigwidget.cpp @@ -33,30 +33,28 @@ #include #include +using namespace Utils; + namespace Qnx { namespace Internal { QnxBaseQtConfigWidget::QnxBaseQtConfigWidget(QnxQtVersion *version) : m_version(version), - m_sdpPathChooser(new Utils::PathChooser) + m_sdpPathChooser(new PathChooser) { QTC_ASSERT(version, return); auto layout = new QHBoxLayout(this); layout->addWidget(m_sdpPathChooser); - m_sdpPathChooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); - m_sdpPathChooser->setHistoryCompleter(QLatin1String("Qnx.Sdp.History")); - m_sdpPathChooser->setPath(version->sdpPath()); + m_sdpPathChooser->setExpectedKind(PathChooser::ExistingDirectory); + m_sdpPathChooser->setHistoryCompleter("Qnx.Sdp.History"); + m_sdpPathChooser->setFilePath(version->sdpPath()); - connect(m_sdpPathChooser, &Utils::PathChooser::rawPathChanged, - this, &QnxBaseQtConfigWidget::updateSdpPath); -} - -void QnxBaseQtConfigWidget::updateSdpPath(const QString &path) -{ - m_version->setSdpPath(path); - emit changed(); + connect(m_sdpPathChooser, &PathChooser::rawPathChanged, [this] { + m_version->setSdpPath(m_sdpPathChooser->filePath()); + emit changed(); + }); } } // namespace Internal diff --git a/src/plugins/qnx/qnxbaseqtconfigwidget.h b/src/plugins/qnx/qnxbaseqtconfigwidget.h index 28428144a71..1ab1a9d16e5 100644 --- a/src/plugins/qnx/qnxbaseqtconfigwidget.h +++ b/src/plugins/qnx/qnxbaseqtconfigwidget.h @@ -27,7 +27,10 @@ #include -namespace Utils { class PathChooser; } +namespace Utils { +class PathChooser; +class FilePath; +} // Utils namespace Qnx { namespace Internal { @@ -41,9 +44,6 @@ class QnxBaseQtConfigWidget : public QtSupport::QtConfigWidget public: explicit QnxBaseQtConfigWidget(QnxQtVersion *version); -private slots: - void updateSdpPath(const QString &path); - private: QnxQtVersion *m_version; Utils::PathChooser *m_sdpPathChooser; diff --git a/src/plugins/qnx/qnxconfiguration.cpp b/src/plugins/qnx/qnxconfiguration.cpp index 33af0f3ecb8..706551360b4 100644 --- a/src/plugins/qnx/qnxconfiguration.cpp +++ b/src/plugins/qnx/qnxconfiguration.cpp @@ -225,7 +225,7 @@ QnxQtVersion *QnxConfiguration::qnxQtVersion(const Target &target) const QtVersionManager::instance()->versions(Utils::equal(&BaseQtVersion::type, QString::fromLatin1(Constants::QNX_QNX_QT)))) { auto qnxQt = dynamic_cast(version); - if (qnxQt && FilePath::fromString(qnxQt->sdpPath()) == sdpPath()) { + if (qnxQt && qnxQt->sdpPath() == sdpPath()) { foreach (const Abi &qtAbi, version->qtAbis()) { if ((qtAbi == target.m_abi) && (qnxQt->cpuDir() == target.cpuDir())) return qnxQt; @@ -240,7 +240,7 @@ QList QnxConfiguration::autoDetect(const QList &alread { QList result; - foreach (const Target &target, m_targets) + for (const Target &target : qAsConst(m_targets)) result += findToolChain(alreadyKnown, target.m_abi); return result; @@ -286,7 +286,7 @@ QnxConfiguration::QnxToolChainMap QnxConfiguration::createToolChain(const Target "QCC for %1 (%2)") .arg(displayName()) .arg(target.shortDescription())); - toolChain->setSdpPath(sdpPath().toString()); + toolChain->setSdpPath(sdpPath()); toolChain->setCpuDir(target.cpuDir()); toolChain->resetToolChain(qccCompilerPath()); ToolChainManager::registerToolChain(toolChain); @@ -372,11 +372,11 @@ void QnxConfiguration::setVersion(const QnxVersionNumber &version) void QnxConfiguration::readInformation() { const QString qConfigPath = m_qnxConfiguration.pathAppended("qconfig").toString(); - QList installInfoList = QnxUtils::installedConfigs(qConfigPath); + const QList installInfoList = QnxUtils::installedConfigs(qConfigPath); if (installInfoList.isEmpty()) return; - foreach (const ConfigInstallInformation &info, installInfoList) { + for (const ConfigInstallInformation &info : installInfoList) { if (m_qnxHost == FilePath::fromString(info.host).canonicalPath() && m_qnxTarget == FilePath::fromString(info.target).canonicalPath()) { m_configName = info.name; @@ -386,11 +386,11 @@ void QnxConfiguration::readInformation() } } -void QnxConfiguration::setDefaultConfiguration(const Utils::FilePath &envScript) +void QnxConfiguration::setDefaultConfiguration(const FilePath &envScript) { QTC_ASSERT(!envScript.isEmpty(), return); m_envFile = envScript; - m_qnxEnv = QnxUtils::qnxEnvironmentFromEnvFile(m_envFile.toString()); + m_qnxEnv = QnxUtils::qnxEnvironmentFromEnvFile(m_envFile); foreach (const EnvironmentItem &item, m_qnxEnv) { if (item.name == QNXConfiguration) m_qnxConfiguration = FilePath::fromString(item.value).canonicalPath(); diff --git a/src/plugins/qnx/qnxqtversion.cpp b/src/plugins/qnx/qnxqtversion.cpp index 02dceac4a48..b0fcf300b35 100644 --- a/src/plugins/qnx/qnxqtversion.cpp +++ b/src/plugins/qnx/qnxqtversion.cpp @@ -109,14 +109,14 @@ QString QnxQtVersion::cpuDir() const QVariantMap QnxQtVersion::toMap() const { QVariantMap result = BaseQtVersion::toMap(); - result.insert(QLatin1String(SDP_PATH_KEY), sdpPath()); + result.insert(SDP_PATH_KEY, sdpPath().toVariant()); return result; } void QnxQtVersion::fromMap(const QVariantMap &map) { BaseQtVersion::fromMap(map); - setSdpPath(QDir::fromNativeSeparators(map.value(QLatin1String(SDP_PATH_KEY)).toString())); + setSdpPath(FilePath::fromVariant(map.value(SDP_PATH_KEY))); } Abis QnxQtVersion::detectQtAbis() const @@ -134,7 +134,7 @@ void QnxQtVersion::addToEnvironment(const Kit *k, Environment &env) const env.prependOrSetLibrarySearchPath(libraryPath().toString()); } -void QnxQtVersion::setupQmakeRunEnvironment(Utils::Environment &env) const +void QnxQtVersion::setupQmakeRunEnvironment(Environment &env) const { if (!sdpPath().isEmpty()) updateEnvironment(); @@ -160,12 +160,12 @@ QString QnxQtVersion::invalidReason() const return QtSupport::BaseQtVersion::invalidReason(); } -QString QnxQtVersion::sdpPath() const +FilePath QnxQtVersion::sdpPath() const { return m_sdpPath; } -void QnxQtVersion::setSdpPath(const QString &sdpPath) +void QnxQtVersion::setSdpPath(const FilePath &sdpPath) { if (m_sdpPath == sdpPath) return; diff --git a/src/plugins/qnx/qnxqtversion.h b/src/plugins/qnx/qnxqtversion.h index bd8876def20..637e9475f9f 100644 --- a/src/plugins/qnx/qnxqtversion.h +++ b/src/plugins/qnx/qnxqtversion.h @@ -63,15 +63,15 @@ public: bool isValid() const override; QString invalidReason() const override; - QString sdpPath() const; - void setSdpPath(const QString &sdpPath); + Utils::FilePath sdpPath() const; + void setSdpPath(const Utils::FilePath &sdpPath); private: void updateEnvironment() const; Utils::EnvironmentItems environment() const; - QString m_sdpPath; + Utils::FilePath m_sdpPath; mutable QString m_cpuDir; mutable bool m_environmentUpToDate = false; diff --git a/src/plugins/qnx/qnxtoolchain.cpp b/src/plugins/qnx/qnxtoolchain.cpp index 0fc060f51e1..984d593b4e0 100644 --- a/src/plugins/qnx/qnxtoolchain.cpp +++ b/src/plugins/qnx/qnxtoolchain.cpp @@ -41,8 +41,8 @@ using namespace Utils; namespace Qnx { namespace Internal { -static const char CompilerSdpPath[] = "Qnx.QnxToolChain.NDKPath"; -static const char CpuDirKey[] = "Qnx.QnxToolChain.CpuDir"; +const char CompilerSdpPath[] = "Qnx.QnxToolChain.NDKPath"; +const char CpuDirKey[] = "Qnx.QnxToolChain.CpuDir"; static Abis detectTargetAbis(const FilePath &sdpPath) { @@ -50,8 +50,8 @@ static Abis detectTargetAbis(const FilePath &sdpPath) FilePath qnxTarget; if (!sdpPath.fileName().isEmpty()) { - Utils::EnvironmentItems environment = QnxUtils::qnxEnvironment(sdpPath.toString()); - foreach (const Utils::EnvironmentItem &item, environment) { + const EnvironmentItems environment = QnxUtils::qnxEnvironment(sdpPath); + for (const EnvironmentItem &item : environment) { if (item.name == QLatin1String("QNX_TARGET")) qnxTarget = FilePath::fromString(item.value); } @@ -137,7 +137,7 @@ QStringList QnxToolChain::suggestedMkspecList() const QVariantMap QnxToolChain::toMap() const { QVariantMap data = GccToolChain::toMap(); - data.insert(QLatin1String(CompilerSdpPath), m_sdpPath); + data.insert(QLatin1String(CompilerSdpPath), m_sdpPath.toVariant()); data.insert(QLatin1String(CpuDirKey), m_cpuDir); return data; } @@ -147,7 +147,7 @@ bool QnxToolChain::fromMap(const QVariantMap &data) if (!GccToolChain::fromMap(data)) return false; - m_sdpPath = data.value(QLatin1String(CompilerSdpPath)).toString(); + m_sdpPath = FilePath::fromVariant(data.value(CompilerSdpPath)); m_cpuDir = data.value(QLatin1String(CpuDirKey)).toString(); // Make the ABIs QNX specific (if they aren't already). @@ -157,12 +157,12 @@ bool QnxToolChain::fromMap(const QVariantMap &data) return true; } -QString QnxToolChain::sdpPath() const +FilePath QnxToolChain::sdpPath() const { return m_sdpPath; } -void QnxToolChain::setSdpPath(const QString &sdpPath) +void QnxToolChain::setSdpPath(const FilePath &sdpPath) { if (m_sdpPath == sdpPath) return; @@ -185,7 +185,7 @@ void QnxToolChain::setCpuDir(const QString &cpuDir) GccToolChain::DetectedAbisResult QnxToolChain::detectSupportedAbis() const { - return detectTargetAbis(FilePath::fromString(m_sdpPath)); + return detectTargetAbis(m_sdpPath); } bool QnxToolChain::operator ==(const ToolChain &other) const @@ -243,7 +243,7 @@ QnxToolChainConfigWidget::QnxToolChainConfigWidget(QnxToolChain *tc) m_sdpPath->setExpectedKind(PathChooser::ExistingDirectory); m_sdpPath->setHistoryCompleter(QLatin1String("Qnx.Sdp.History")); - m_sdpPath->setPath(tc->sdpPath()); + m_sdpPath->setFilePath(tc->sdpPath()); m_sdpPath->setEnabled(!tc->isAutoDetected()); const Abis abiList = detectTargetAbis(m_sdpPath->filePath()); @@ -270,7 +270,7 @@ void QnxToolChainConfigWidget::applyImpl() Q_ASSERT(tc); QString displayName = tc->displayName(); tc->setDisplayName(displayName); // reset display name - tc->setSdpPath(m_sdpPath->filePath().toString()); + tc->setSdpPath(m_sdpPath->filePath()); tc->setTargetAbi(m_abiWidget->currentAbi()); tc->resetToolChain(m_compilerCommand->filePath()); } @@ -281,7 +281,7 @@ void QnxToolChainConfigWidget::discardImpl() QSignalBlocker blocker(this); auto tc = static_cast(toolChain()); m_compilerCommand->setFilePath(tc->compilerCommand()); - m_sdpPath->setPath(tc->sdpPath()); + m_sdpPath->setFilePath(tc->sdpPath()); m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi()); if (!m_compilerCommand->filePath().toString().isEmpty()) m_abiWidget->setEnabled(true); @@ -292,7 +292,7 @@ bool QnxToolChainConfigWidget::isDirtyImpl() const auto tc = static_cast(toolChain()); Q_ASSERT(tc); return m_compilerCommand->filePath() != tc->compilerCommand() - || m_sdpPath->filePath().toString() != tc->sdpPath() + || m_sdpPath->filePath() != tc->sdpPath() || m_abiWidget->currentAbi() != tc->targetAbi(); } diff --git a/src/plugins/qnx/qnxtoolchain.h b/src/plugins/qnx/qnxtoolchain.h index 428cc5202d1..a2d8440ddee 100644 --- a/src/plugins/qnx/qnxtoolchain.h +++ b/src/plugins/qnx/qnxtoolchain.h @@ -46,8 +46,8 @@ public: QVariantMap toMap() const override; bool fromMap(const QVariantMap &data) override; - QString sdpPath() const; - void setSdpPath(const QString &sdpPath); + Utils::FilePath sdpPath() const; + void setSdpPath(const Utils::FilePath &sdpPath); QString cpuDir() const; void setCpuDir(const QString &cpuDir); @@ -57,7 +57,7 @@ protected: DetectedAbisResult detectSupportedAbis() const override; private: - QString m_sdpPath; + Utils::FilePath m_sdpPath; QString m_cpuDir; }; diff --git a/src/plugins/qnx/qnxutils.cpp b/src/plugins/qnx/qnxutils.cpp index d90acdda803..e8fabfb959b 100644 --- a/src/plugins/qnx/qnxutils.cpp +++ b/src/plugins/qnx/qnxutils.cpp @@ -78,11 +78,11 @@ QString QnxUtils::cpuDirShortDescription(const QString &cpuDir) return cpuDir; } -EnvironmentItems QnxUtils::qnxEnvironmentFromEnvFile(const QString &fileName) +EnvironmentItems QnxUtils::qnxEnvironmentFromEnvFile(const FilePath &filePath) { EnvironmentItems items; - if (!QFileInfo::exists(fileName)) + if (!filePath.exists()) return items; const bool isWindows = HostOsInfo::isWindowsHost(); @@ -97,10 +97,10 @@ EnvironmentItems QnxUtils::qnxEnvironmentFromEnvFile(const QString &fileName) QTextStream fileContent(&tmpFile); if (isWindows) fileContent << "@echo off\n" - << "call " << fileName << '\n'; + << "call " << filePath.path() << '\n'; else fileContent << "#!/bin/bash\n" - << ". " << fileName << '\n'; + << ". " << filePath.path() << '\n'; QString linePattern = QString::fromLatin1(isWindows ? "echo %1=%%1%" : "echo %1=$%1"); for (int i = 0, len = sizeof(EVAL_ENV_VARS) / sizeof(const char *); i < len; ++i) fileContent << linePattern.arg(QLatin1String(EVAL_ENV_VARS[i])) << QLatin1Char('\n'); @@ -140,19 +140,18 @@ EnvironmentItems QnxUtils::qnxEnvironmentFromEnvFile(const QString &fileName) return items; } -QString QnxUtils::envFilePath(const QString &sdpPath) +FilePath QnxUtils::envFilePath(const FilePath &sdpPath) { - QDir sdp(sdpPath); - QStringList entries; - if (HostOsInfo::isWindowsHost()) - entries = sdp.entryList(QStringList(QLatin1String("*-env.bat"))); + FilePaths entries; + if (sdpPath.osType() == OsTypeWindows) + entries = sdpPath.dirEntries({"*-env.bat"}); else - entries = sdp.entryList(QStringList(QLatin1String("*-env.sh"))); + entries = sdpPath.dirEntries({"*-env.sh"}); if (!entries.isEmpty()) - return sdp.absoluteFilePath(entries.first()); + return entries.first(); - return QString(); + return {}; } QString QnxUtils::defaultTargetVersion(const QString &sdpPath) @@ -208,7 +207,7 @@ QList QnxUtils::installedConfigs(const QString &config return sdpList; } -EnvironmentItems QnxUtils::qnxEnvironment(const QString &sdpPath) +EnvironmentItems QnxUtils::qnxEnvironment(const FilePath &sdpPath) { return qnxEnvironmentFromEnvFile(envFilePath(sdpPath)); } diff --git a/src/plugins/qnx/qnxutils.h b/src/plugins/qnx/qnxutils.h index c918ba09ab6..a90f5ded4a4 100644 --- a/src/plugins/qnx/qnxutils.h +++ b/src/plugins/qnx/qnxutils.h @@ -68,11 +68,11 @@ class QnxUtils public: static QString cpuDirFromAbi(const ProjectExplorer::Abi &abi); static QString cpuDirShortDescription(const QString &cpuDir); - static Utils::EnvironmentItems qnxEnvironmentFromEnvFile(const QString &fileName); - static QString envFilePath(const QString &sdpPath); + static Utils::EnvironmentItems qnxEnvironmentFromEnvFile(const Utils::FilePath &filePath); + static Utils::FilePath envFilePath(const Utils::FilePath &sdpPath); static QString defaultTargetVersion(const QString &sdpPath); static QList installedConfigs(const QString &configPath = QString()); - static Utils::EnvironmentItems qnxEnvironment(const QString &sdpPath); + static Utils::EnvironmentItems qnxEnvironment(const Utils::FilePath &sdpPath); static QList findTargets(const Utils::FilePath &basePath); static ProjectExplorer::Abi convertAbi(const ProjectExplorer::Abi &abi); static ProjectExplorer::Abis convertAbis(const ProjectExplorer::Abis &abis); From d6cc280920af11fb89c7bd24817c9e31f00f5839 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 30 Sep 2021 09:55:41 +0200 Subject: [PATCH 14/28] CppEditor: use selection(Begin/End) to check local renamings Instead of relying on the fact that the position < anchor use the appropriate QTextCursor functions that return the required information. Change-Id: I019e714f9f764dea6a613f1072928437d0ebb671 Reviewed-by: Christian Kandeler --- src/plugins/cppeditor/cpplocalrenaming.cpp | 12 +++++++++--- src/plugins/cppeditor/cpplocalrenaming.h | 5 +++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/plugins/cppeditor/cpplocalrenaming.cpp b/src/plugins/cppeditor/cpplocalrenaming.cpp index 5713a02fdda..cce808e4af1 100644 --- a/src/plugins/cppeditor/cpplocalrenaming.cpp +++ b/src/plugins/cppeditor/cpplocalrenaming.cpp @@ -243,9 +243,15 @@ void CppLocalRenaming::forgetRenamingSelection() m_renameSelectionIndex = -1; } +bool CppLocalRenaming::isWithinSelection(const QTextEdit::ExtraSelection &selection, int position) +{ + return selection.cursor.selectionStart() <= position + && position <= selection.cursor.selectionEnd(); +} + bool CppLocalRenaming::isWithinRenameSelection(int position) { - return renameSelectionBegin() <= position && position <= renameSelectionEnd(); + return isWithinSelection(renameSelection(), position); } bool CppLocalRenaming::isSameSelection(int cursorPosition) const @@ -254,14 +260,14 @@ bool CppLocalRenaming::isSameSelection(int cursorPosition) const return false; const QTextEdit::ExtraSelection &sel = m_selections[m_renameSelectionIndex]; - return (sel.cursor.position() <= cursorPosition && cursorPosition <= sel.cursor.anchor()); + return isWithinSelection(sel, cursorPosition); } bool CppLocalRenaming::findRenameSelection(int cursorPosition) { for (int i = 0, total = m_selections.size(); i < total; ++i) { const QTextEdit::ExtraSelection &sel = m_selections.at(i); - if (sel.cursor.position() <= cursorPosition && cursorPosition <= sel.cursor.anchor()) { + if (isWithinSelection(sel, cursorPosition)) { m_renameSelectionIndex = i; return true; } diff --git a/src/plugins/cppeditor/cpplocalrenaming.h b/src/plugins/cppeditor/cpplocalrenaming.h index 61c1d1ca11c..5571977c503 100644 --- a/src/plugins/cppeditor/cpplocalrenaming.h +++ b/src/plugins/cppeditor/cpplocalrenaming.h @@ -72,11 +72,12 @@ private: // The "rename selection" is the local use selection on which the user started the renaming bool findRenameSelection(int cursorPosition); void forgetRenamingSelection(); + static bool isWithinSelection(const QTextEdit::ExtraSelection &selection, int position); bool isWithinRenameSelection(int position); QTextEdit::ExtraSelection &renameSelection(); - int renameSelectionBegin() { return renameSelection().cursor.position(); } - int renameSelectionEnd() { return renameSelection().cursor.anchor(); } + int renameSelectionBegin() { return renameSelection().cursor.selectionStart(); } + int renameSelectionEnd() { return renameSelection().cursor.selectionEnd(); } void updateRenamingSelectionCursor(const QTextCursor &cursor); void updateRenamingSelectionFormat(const QTextCharFormat &format); From d0a4fc4b2cc3cc2f43aab490ef85d1764f4f4c00 Mon Sep 17 00:00:00 2001 From: Miina Puuronen Date: Tue, 24 Aug 2021 12:43:51 +0300 Subject: [PATCH 15/28] QmlDesigner: Implement a horizontal layout for Component Library The view from vertical to horizontal is changed with Loader, depending on Library's width. Vertical layout (old) is mostly the same as before. Added new property categorySelected for categories. Items are also now hoverable. Task-number: QDS-4764 Change-Id: I031f3916f0d011fd76a963b247c238997d7a55d8 Reviewed-by: Miikka Heikkinen Reviewed-by: Samuel Ghinet Reviewed-by: Thomas Hartmann --- .../itemLibraryQmlSources/ItemDelegate.qml | 2 +- .../itemLibraryQmlSources/ItemsView.qml | 397 +++++++++++++----- .../itemlibrarycategoriesmodel.cpp | 45 ++ .../itemlibrary/itemlibrarycategoriesmodel.h | 4 + .../itemlibrary/itemlibrarycategory.cpp | 15 + .../itemlibrary/itemlibrarycategory.h | 5 + .../itemlibrary/itemlibraryimport.cpp | 20 + .../itemlibrary/itemlibraryimport.h | 4 + .../itemlibrary/itemlibrarymodel.cpp | 34 ++ .../components/itemlibrary/itemlibrarymodel.h | 3 + .../itemlibrary/itemlibrarywidget.cpp | 6 + .../itemlibrary/itemlibrarywidget.h | 5 + 12 files changed, 441 insertions(+), 99 deletions(-) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml index 88774a1b434..1a2775e8f1b 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml @@ -41,7 +41,7 @@ Item { anchors.topMargin: 1 anchors.fill: parent - color: StudioTheme.Values.themePanelBackground + color: mouseRegion.containsMouse ? StudioTheme.Values.themeControlBackgroundHover : StudioTheme.Values.themePanelBackground Image { id: itemIcon // to be set by model diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml index 79ab72f1233..de9d50f0429 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml @@ -73,7 +73,7 @@ itemLibraryModel [ ] */ -ScrollView { +Item { id: itemsView property string importToRemove: "" @@ -81,6 +81,11 @@ ScrollView { property var currentItem: null property var currentCategory: null property var currentImport: null + property bool isHorizontalView: false + + // horizontal component lib variables + property var selectedCategory: null + property var selectedCategoryImport: null // called from C++ to close context menu on focus out function closeContextMenu() @@ -91,15 +96,21 @@ ScrollView { function showImportCategories() { - currentImport.importCatVisibleState = true + if (itemLibraryModel.isAllCategoriesHidden()) { + itemsView.currentImport.importCatVisibleState = true + if (!itemLibraryModel.getIsAnyCategoryHidden()) + itemLibraryModel.isAnyCategoryHidden = false + + itemsView.selectedCategory = itemLibraryModel.selectImportFirstVisibleCategory() + } + + itemsView.currentImport.importCatVisibleState = true if (!itemLibraryModel.getIsAnyCategoryHidden()) itemLibraryModel.isAnyCategoryHidden = false } - onContentHeightChanged: { - var maxPosition = Math.max(contentHeight - height, 0) - if (contentY > maxPosition) - contentY = maxPosition + onWidthChanged: { + itemsView.isHorizontalView = itemsView.width > widthLimit } Item { @@ -112,66 +123,68 @@ ScrollView { property int cellVerticalMargin: 4 // the following depend on the actual shape of the item delegate - property int cellWidth: textWidth + 2 * cellHorizontalMargin - property int cellHeight: itemLibraryIconHeight + textHeight + - 2 * cellVerticalMargin + cellVerticalSpacing + property int cellWidth: styleConstants.textWidth + 2 * styleConstants.cellHorizontalMargin + property int cellHeight: itemLibraryIconHeight + styleConstants.textHeight + + 2 * styleConstants.cellVerticalMargin + styleConstants.cellVerticalSpacing StudioControls.Menu { id: moduleContextMenu StudioControls.MenuItem { text: qsTr("Remove Module") - visible: currentCategory === null + visible: itemsView.currentCategory === null height: visible ? implicitHeight : 0 - enabled: importToRemove !== "" + enabled: itemsView.importToRemove !== "" onTriggered: { showImportCategories() - rootView.removeImport(importToRemove) + rootView.removeImport(itemsView.importToRemove) } } StudioControls.MenuSeparator { - visible: currentCategory === null + visible: itemsView.currentCategory === null height: StudioTheme.Values.border } StudioControls.MenuItem { text: qsTr("Expand All") - visible: currentCategory === null + visible: itemsView.currentCategory === null height: visible ? implicitHeight : 0 onTriggered: itemLibraryModel.expandAll() } StudioControls.MenuItem { text: qsTr("Collapse All") - visible: currentCategory === null + visible: itemsView.currentCategory === null height: visible ? implicitHeight : 0 onTriggered: itemLibraryModel.collapseAll() } StudioControls.MenuSeparator { - visible: currentCategory === null + visible: itemsView.currentCategory === null height: StudioTheme.Values.border } StudioControls.MenuItem { text: qsTr("Hide Category") - visible: currentCategory + visible: itemsView.currentCategory height: visible ? implicitHeight : 0 onTriggered: { itemLibraryModel.isAnyCategoryHidden = true - currentCategory.categoryVisible = false + itemsView.currentCategory.categoryVisible = false + itemsView.currentCategory.categorySelected = false + itemsView.selectedCategory = itemLibraryModel.selectImportFirstVisibleCategory() } } StudioControls.MenuSeparator { - visible: currentCategory + visible: itemsView.currentCategory height: StudioTheme.Values.border } StudioControls.MenuItem { text: qsTr("Show Module Hidden Categories") - enabled: currentImport && !currentImport.importCatVisibleState + enabled: itemsView.currentImport && !itemsView.currentImport.importCatVisibleState onTriggered: showImportCategories() } @@ -179,6 +192,12 @@ ScrollView { text: qsTr("Show All Hidden Categories") enabled: itemLibraryModel.isAnyCategoryHidden onTriggered: { + if (itemLibraryModel.isAllCategoriesHidden()) { + itemLibraryModel.showHiddenCategories() + itemsView.selectedCategory = itemLibraryModel.selectImportFirstVisibleCategory() + itemLibraryModel.isAnyCategoryHidden = false + } + itemLibraryModel.isAnyCategoryHidden = false itemLibraryModel.showHiddenCategories() } @@ -192,94 +211,117 @@ ScrollView { StudioControls.MenuItem { id: importMenuItem - text: qsTr("Add Module: ") + importToAdd - enabled: importToAdd !== "" - onTriggered: rootView.addImportForItem(importToAdd) + text: qsTr("Add Module: ") + itemsView.importToAdd + enabled: itemsView.importToAdd !== "" + onTriggered: rootView.addImportForItem(itemsView.importToAdd) } } } - Column { - spacing: 2 - Repeater { - model: itemLibraryModel // to be set in Qml context - delegate: Section { - width: itemsView.width - - (itemsView.verticalScrollBarVisible ? itemsView.verticalThickness : 0) - caption: importName - visible: importVisible - sectionHeight: 30 - sectionFontSize: 15 - showArrow: categoryModel.rowCount() > 0 - labelColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor - : StudioTheme.Values.themeTextColor - leftPadding: 0 - rightPadding: 0 - expanded: importExpanded - expandOnClick: false - useDefaulContextMenu: false + Loader { + anchors.fill: parent + sourceComponent: itemsView.isHorizontalView ? horizontalView : verticalView + } - onToggleExpand: { - if (categoryModel.rowCount() > 0) - importExpanded = !importExpanded - } - onShowContextMenu: { - importToRemove = importRemovable ? importUrl : "" - currentImport = model - currentCategory = null - if (!rootView.isSearchActive()) - moduleContextMenu.popup() - } + Component { + id: verticalView - Column { - spacing: 2 - property var currentImportModel: model // allows accessing the import model from inside the category section - Repeater { - model: categoryModel - delegate: Section { - width: itemsView.width - - (itemsView.verticalScrollBarVisible ? itemsView.verticalThickness : 0) - sectionBackgroundColor: "transparent" - showTopSeparator: index > 0 - hideHeader: categoryModel.rowCount() <= 1 - leftPadding: 0 - rightPadding: 0 - addTopPadding: categoryModel.rowCount() > 1 - addBottomPadding: index != categoryModel.rowCount() - 1 - caption: categoryName + " (" + itemModel.rowCount() + ")" - visible: categoryVisible - expanded: categoryExpanded - expandOnClick: false - onToggleExpand: categoryExpanded = !categoryExpanded - onShowContextMenu: { - currentCategory = model - currentImport = parent.currentImportModel - if (!rootView.isSearchActive()) - moduleContextMenu.popup() - } + ScrollView { + id: verticalScrollView + width: itemsView.width + height: itemsView.height + onContentHeightChanged: { + var maxPosition = Math.max(contentHeight - verticalScrollView.height, 0) + if (contentY > maxPosition) + contentY = maxPosition + } - Grid { - id: itemGrid + Column { + spacing: 2 + Repeater { + model: itemLibraryModel // to be set in Qml context + delegate: Section { + width: itemsView.width - + (verticalScrollView.verticalScrollBarVisible + ? verticalScrollView.verticalThickness : 0) + caption: importName + visible: importVisible + sectionHeight: 30 + sectionFontSize: 15 + showArrow: categoryModel.rowCount() > 0 + labelColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor + : StudioTheme.Values.themeTextColor + leftPadding: 0 + rightPadding: 0 + expanded: importExpanded + expandOnClick: false + useDefaulContextMenu: false + onToggleExpand: { + if (categoryModel.rowCount() > 0) + importExpanded = !importExpanded + } + onShowContextMenu: { + itemsView.importToRemove = importRemovable ? importUrl : "" + itemsView.currentImport = model + itemsView.currentCategory = null + if (!rootView.isSearchActive()) + moduleContextMenu.popup() + } - property real actualWidth: parent.width - itemGrid.leftPadding -itemGrid.rightPadding - property int flexibleWidth: (itemGrid.actualWidth / columns) - styleConstants.cellWidth + Column { + spacing: 2 + property var currentImportModel: model // allows accessing the import model from inside the category section + Repeater { + model: categoryModel + delegate: Section { + width: itemsView.width - + (verticalScrollView.verticalScrollBarVisible + ? verticalScrollView.verticalThickness : 0) + sectionBackgroundColor: "transparent" + showTopSeparator: index > 0 + hideHeader: categoryModel.rowCount() <= 1 + leftPadding: 0 + rightPadding: 0 + addTopPadding: categoryModel.rowCount() > 1 + addBottomPadding: index !== categoryModel.rowCount() - 1 + caption: categoryName + " (" + itemModel.rowCount() + ")" + visible: categoryVisible + expanded: categoryExpanded + expandOnClick: false + onToggleExpand: categoryExpanded = !categoryExpanded + useDefaulContextMenu: false + onShowContextMenu: { + itemsView.currentCategory = model + itemsView.currentImport = parent.currentImportModel + if (!rootView.isSearchActive()) + moduleContextMenu.popup() + } - leftPadding: 6 - rightPadding: 6 - columns: itemGrid.actualWidth / styleConstants.cellWidth + Grid { + id: itemGrid - Repeater { - model: itemModel - delegate: ItemDelegate { - visible: itemVisible - textColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor - : StudioTheme.Values.themeTextColor - width: styleConstants.cellWidth + itemGrid.flexibleWidth - height: styleConstants.cellHeight - onShowContextMenu: { - if (!itemUsable) { - importToAdd = itemRequiredImport - itemContextMenu.popup() + property real actualWidth: parent.width - itemGrid.leftPadding -itemGrid.rightPadding + property int flexibleWidth: (itemGrid.actualWidth / columns) - styleConstants.cellWidth + + leftPadding: 6 + rightPadding: 6 + columns: itemGrid.actualWidth / styleConstants.cellWidth + rowSpacing: 7 + + Repeater { + model: itemModel + delegate: ItemDelegate { + visible: itemVisible + textColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor + : StudioTheme.Values.themeTextColor + width: styleConstants.cellWidth + itemGrid.flexibleWidth + height: styleConstants.cellHeight + onShowContextMenu: { + if (!itemUsable) { + itemsView.importToAdd = itemRequiredImport + itemContextMenu.popup() + } + } } } } @@ -291,4 +333,163 @@ ScrollView { } } } + + Component { + id: horizontalView + + Row { + padding: 5 + + ScrollView { + id: horizontalScrollView + width: 270 + height: itemsView.height + onContentHeightChanged: { + var maxPosition = Math.max(contentHeight - horizontalScrollView.height, 0) + if (contentY > maxPosition) + contentY = maxPosition + } + + Column { + width: parent.width + spacing: 2 + Repeater { + model: itemLibraryModel // to be set in Qml context + delegate: Section { + width: 265 - + (horizontalScrollView.verticalScrollBarVisible + ? horizontalScrollView.verticalThickness : 0) + caption: importName + visible: importVisible + sectionHeight: 30 + sectionFontSize: 15 + showArrow: categoryModel.rowCount() > 0 + labelColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor + : StudioTheme.Values.themeTextColor + leftPadding: 0 + rightPadding: 0 + expanded: importExpanded + expandOnClick: false + useDefaulContextMenu: false + onToggleExpand: { + if (categoryModel.rowCount() > 0) + importExpanded = !importExpanded + } + onShowContextMenu: { + itemsView.importToRemove = importRemovable ? importUrl : "" + itemsView.currentImport = model + itemsView.currentCategory = null + if (!rootView.isSearchActive()) + moduleContextMenu.popup() + } + + Column { + spacing: 2 + property var currentImportModel: model // allows accessing the import model from inside the category section + Repeater { + model: categoryModel + delegate: Rectangle { + width: 265 - + (horizontalScrollView.verticalScrollBarVisible + ? horizontalScrollView.verticalThickness : 0) + height: 25 + visible: categoryVisible + border.width: StudioTheme.Values.border + border.color: StudioTheme.Values.themeControlOutline + color: categorySelected + ? StudioTheme.Values.themeControlBackgroundHover + : categoryMouseArea.containsMouse ? Qt.darker(StudioTheme.Values.themeControlBackgroundHover, 1.5) + : StudioTheme.Values.themeControlBackground + + Text { + anchors.fill: parent + text: categoryName + color: StudioTheme.Values.themeTextColor + font.pixelSize: 13 + font.capitalization: Font.AllUppercase + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + MouseArea { + id: categoryMouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onClicked: (mouse) => { + itemLibraryModel.selectImportCategory(parent.parent.currentImportModel.importUrl, model.index) + itemsView.selectedCategory = model + itemsView.selectedCategoryImport = parent.parent.currentImportModel + + if (mouse.button === Qt.RightButton && !rootView.isSearchActive() && categoryModel.rowCount() !== 1) { + itemsView.currentCategory = model + itemsView.currentImport = parent.parent.currentImportModel + moduleContextMenu.popup() + } + } + Component.onCompleted: { + if (categorySelected) + categorySelected = !categorySelected + itemsView.selectedCategory = itemLibraryModel.selectImportFirstVisibleCategory() + if (itemsView.selectedCategory === categorySelected) + itemsView.selectedCategoryImport = itemsView.selectedCategory.parent.currentImportModel + } + } + } + } + } + } + } + } + } + + Rectangle { // separator between import/category column and item grid + id: separatingLine + height: itemsView.height - 10 + width: 1 + color: StudioTheme.Values.themeControlOutline + } + + ScrollView { + id: itemScrollView + width: itemsView.width - 275 + height: itemsView.height + onContentHeightChanged: { + var maxPosition = Math.max(contentHeight - itemScrollView.height, 0) + if (contentY > maxPosition) + contentY = maxPosition + } + + Grid { + id: hItemGrid + property real actualWidth: itemsView.width - 294 + property int flexibleWidth: (hItemGrid.actualWidth / hItemGrid.columns) - styleConstants.cellWidth + + leftPadding: 9 + rightPadding: 9 + bottomPadding: 15 + columns: hItemGrid.actualWidth / styleConstants.cellWidth + rowSpacing: 7 + + Repeater { + model: itemsView.selectedCategory ? itemsView.selectedCategory.itemModel : null + delegate: ItemDelegate { + visible: itemVisible + textColor: itemsView.selectedCategoryImport && itemsView.selectedCategoryImport.importUnimported + ? StudioTheme.Values.themeUnimportedModuleColor : StudioTheme.Values.themeTextColor + width: styleConstants.cellWidth + hItemGrid.flexibleWidth + height: styleConstants.cellHeight + onShowContextMenu: { + if (!itemUsable) { + itemsView.importToAdd = itemRequiredImport + itemContextMenu.popup() + } + } + } + } + } + } + } + } } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp index 2677eb94ae9..d351232e36b 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp @@ -144,6 +144,17 @@ void ItemLibraryCategoriesModel::resetModel() endResetModel(); } +bool ItemLibraryCategoriesModel::isAllCategoriesHidden() const +{ + for (const auto &category : std::as_const(m_categoryList)) { + // ignore "All Other Components" as its categoryVisible is always true + if (category->isCategoryVisible() && category->categoryName() != "All Other Components") + return false; + } + + return true; +} + void ItemLibraryCategoriesModel::showAllCategories(bool show) { for (const auto &category : std::as_const(m_categoryList)) { @@ -153,9 +164,43 @@ void ItemLibraryCategoriesModel::showAllCategories(bool show) category->ownerImport()->importName()); } } + emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categoryVisible")}); } +QObject *ItemLibraryCategoriesModel::selectFirstVisibleCategory() +{ + for (int i = 0; i < m_categoryList.length(); ++i) { + const auto category = m_categoryList.at(i); + + if (category->isCategoryVisible()) { + category->setCategorySelected(true); + emit dataChanged(index(i),index(i), {m_roleNames.key("categorySelected")}); + return category; + } + } + + return nullptr; +} + +void ItemLibraryCategoriesModel::clearSelectedCategories() +{ + for (const auto &category : std::as_const(m_categoryList)) + category->setCategorySelected(false); + + emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categorySelected")}); +} + +void ItemLibraryCategoriesModel::selectCategory(int categoryIndex) +{ + const auto category = m_categoryList.at(categoryIndex); + if (!category->categorySelected()) { + clearSelectedCategories(); + category->setCategorySelected(true); + emit dataChanged(index(categoryIndex),index(categoryIndex), {m_roleNames.key("categorySelected")}); + } +} + void ItemLibraryCategoriesModel::addRoleNames() { int role = 0; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h index 276d47ac480..9433af804f9 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h @@ -52,9 +52,13 @@ public: const QList> &categorySections() const; + bool isAllCategoriesHidden() const; void sortCategorySections(); void resetModel(); void showAllCategories(bool show = true); + void clearSelectedCategories(); + QObject *selectFirstVisibleCategory(); + void selectCategory(int categoryIndex); private: void addRoleNames(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp index 464f56ca103..0744b7b2b3f 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp @@ -26,6 +26,7 @@ #include "itemlibrarycategory.h" #include "itemlibraryitem.h" +#include "itemlibrarywidget.h" namespace QmlDesigner { @@ -46,6 +47,11 @@ bool ItemLibraryCategory::categoryExpanded() const return m_categoryExpanded; } +bool ItemLibraryCategory::categorySelected() const +{ + return m_categorySelected; +} + QString ItemLibraryCategory::sortingName() const { if (ItemLibraryModel::categorySortingHash.contains(categoryName())) @@ -84,6 +90,10 @@ bool ItemLibraryCategory::updateItemVisibility(const QString &searchText, bool * hasVisibleItems = true; } + // update item model in horizontal view so search text matches item grid + if (ItemLibraryWidget::isHorizontalLayout) + m_itemModel.resetModel(); + // expand category if it has an item matching search criteria if (!searchText.isEmpty() && hasVisibleItems && !categoryExpanded()) setExpanded(true); @@ -124,4 +134,9 @@ void ItemLibraryCategory::setExpanded(bool expanded) m_categoryExpanded = expanded; } +void ItemLibraryCategory::setCategorySelected(bool selected) +{ + m_categorySelected = selected; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.h index 6e5da446108..e1af69e5fa6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.h @@ -39,6 +39,7 @@ class ItemLibraryCategory : public QObject Q_PROPERTY(QString categoryName READ categoryName FINAL) Q_PROPERTY(bool categoryVisible READ isCategoryVisible WRITE setCategoryVisible NOTIFY categoryVisibilityChanged FINAL) Q_PROPERTY(bool categoryExpanded READ categoryExpanded WRITE setExpanded NOTIFY expandedChanged FINAL) + Q_PROPERTY(bool categorySelected READ categorySelected WRITE setCategorySelected NOTIFY categorySelectedChanged FINAL) Q_PROPERTY(QObject *itemModel READ itemModel NOTIFY itemModelChanged FINAL) public: @@ -46,6 +47,7 @@ public: QString categoryName() const; bool categoryExpanded() const; + bool categorySelected() const; QString sortingName() const; void addItem(ItemLibraryItem *item); @@ -60,6 +62,7 @@ public: void sortItems(); void setExpanded(bool expanded); + void setCategorySelected(bool selected); ItemLibraryImport *ownerImport() const { return m_ownerImport; } @@ -68,6 +71,7 @@ signals: void visibilityChanged(); void expandedChanged(); void categoryVisibilityChanged(); + void categorySelectedChanged(); private: ItemLibraryItemsModel m_itemModel; @@ -75,6 +79,7 @@ private: QString m_name; bool m_categoryExpanded = true; bool m_isVisible = true; + bool m_categorySelected = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp index 76138ed0745..7e696e43ef4 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp @@ -138,6 +138,26 @@ void ItemLibraryImport::showAllCategories(bool show) m_categoryModel.showAllCategories(show); } +void ItemLibraryImport::selectCategory(int categoryIndex) +{ + m_categoryModel.selectCategory(categoryIndex); +} + +QObject *ItemLibraryImport::selectFirstVisibleCategory() +{ + return m_categoryModel.selectFirstVisibleCategory(); +} + +void ItemLibraryImport::clearSelectedCategories() +{ + m_categoryModel.clearSelectedCategories(); +} + +bool ItemLibraryImport::isAllCategoriesHidden() const +{ + return m_categoryModel.isAllCategoriesHidden(); +} + Import ItemLibraryImport::importEntry() const { return m_import; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h index 81717439f9a..f5a1cd02791 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h @@ -68,6 +68,7 @@ public: bool importCatVisibleState() const; bool hasCategories() const; bool hasSingleCategory() const; + bool isAllCategoriesHidden() const; ItemLibraryCategory *getCategorySection(const QString &categoryName) const; void addCategory(ItemLibraryCategory *category); @@ -80,6 +81,9 @@ public: void setImportCatVisibleState(bool show); void expandCategories(bool expand = true); void showAllCategories(bool show = true); + void selectCategory(int categoryIndex); + QObject *selectFirstVisibleCategory(); + void clearSelectedCategories(); static QString userComponentsTitle(); static QString quick3DAssetsTitle(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 85d367fb77c..ebef71f63f5 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -90,6 +90,40 @@ bool ItemLibraryModel::getIsAnyCategoryHidden() const return false; } +void ItemLibraryModel::selectImportCategory(const QString importUrl, int categoryIndex) +{ + ItemLibraryImport *selectedCategoryImport = importByUrl(importUrl); + + for (int i = 0; i < m_importList.length(); ++i) { + const auto importToSelect = m_importList.at(i); + + if (selectedCategoryImport == importToSelect) + importToSelect->selectCategory(categoryIndex); + else + importToSelect->clearSelectedCategories(); + } +} + +bool ItemLibraryModel::isAllCategoriesHidden() const +{ + for (int i = 0; i < m_importList.length(); ++i) { + if (!m_importList.at(i)->isAllCategoriesHidden()) + return false; + } + + return true; +} + +QObject *ItemLibraryModel::selectImportFirstVisibleCategory() +{ + for (const QPointer &import : std::as_const(m_importList)) { + if (!import->isAllCategoriesHidden()) + return import->selectFirstVisibleCategory(); + } + + return nullptr; +} + bool ItemLibraryModel::isAnyCategoryHidden() const { return m_isAnyCategoryHidden; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h index f2e5786f302..ad9803821ff 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h @@ -77,6 +77,9 @@ public: Q_INVOKABLE void collapseAll(); Q_INVOKABLE void showHiddenCategories(); Q_INVOKABLE bool getIsAnyCategoryHidden() const; + Q_INVOKABLE void selectImportCategory(const QString importUrl, int categoryIndex); + Q_INVOKABLE QObject *selectImportFirstVisibleCategory(); + Q_INVOKABLE bool isAllCategoriesHidden() const; Import entryToImport(const ItemLibraryEntry &entry); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 3c8a76597bd..de15fde7cf3 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -143,6 +143,11 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) return QObject::eventFilter(obj, event); } +void ItemLibraryWidget::resizeEvent(QResizeEvent *event) +{ + isHorizontalLayout = event->size().width() >= HORIZONTAL_LAYOUT_WIDTH_LIMIT; +} + ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, AsynchronousImageCache &asynchronousFontImageCache, SynchronousImageCache &synchronousFontImageCache) @@ -196,6 +201,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, {{"itemLibraryIconWidth"}, m_itemIconSize.width()}, {{"itemLibraryIconHeight"}, m_itemIconSize.height()}, {{"rootView"}, QVariant::fromValue(this)}, + {{"widthLimit"}, HORIZONTAL_LAYOUT_WIDTH_LIMIT}, {{"highlightColor"}, Utils::StyleHelper::notTooBrightHighlightColor()}, }); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 4910fedc182..6695c01da5a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -92,6 +92,8 @@ public: void setFlowMode(bool b); static QPair getAssetTypeAndData(const QString &assetPath); + inline static bool isHorizontalLayout = false; + Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos); Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos); Q_INVOKABLE void removeImport(const QString &importUrl); @@ -110,6 +112,7 @@ signals: protected: bool eventFilter(QObject *obj, QEvent *event) override; + void resizeEvent(QResizeEvent *event) override; private: void reloadQmlSource(); @@ -149,6 +152,8 @@ private: bool m_updateRetry = false; QString m_filterText; QPoint m_dragStartPoint; + + inline static int HORIZONTAL_LAYOUT_WIDTH_LIMIT = 600; }; } // namespace QmlDesigner From fcfdfdf823a42470f2a47e515e6fcb7cad5d73c9 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 30 Sep 2021 12:44:55 +0200 Subject: [PATCH 16/28] ProjectExplorer: Settingspage code cosmetics FilePath, namespaces, indentation. Change-Id: I77a3595bad01c1984bf23cd12ba06d90d58a71ea Reviewed-by: Christian Kandeler --- .../projectexplorersettingspage.cpp | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettingspage.cpp index 1cf66764a68..22163a9f451 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp +++ b/src/plugins/projectexplorer/projectexplorersettingspage.cpp @@ -34,10 +34,13 @@ #include +using namespace Core; +using namespace Utils; + namespace ProjectExplorer { namespace Internal { - enum { UseCurrentDirectory, UseProjectDirectory }; +enum { UseCurrentDirectory, UseProjectDirectory }; class ProjectExplorerSettingsWidget : public QWidget { @@ -49,8 +52,8 @@ public: ProjectExplorerSettings settings() const; void setSettings(const ProjectExplorerSettings &s); - QString projectsDirectory() const; - void setProjectsDirectory(const QString &pd); + FilePath projectsDirectory() const; + void setProjectsDirectory(const FilePath &pd); bool useProjectsDirectory(); void setUseProjectsDirectory(bool v); @@ -68,7 +71,7 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) : QWidget(parent) { m_ui.setupUi(this); - setJomVisible(Utils::HostOsInfo::isWindowsHost()); + setJomVisible(HostOsInfo::isWindowsHost()); m_ui.stopBeforeBuildComboBox->addItem(tr("None"), int(StopBeforeBuild::None)); m_ui.stopBeforeBuildComboBox->addItem(tr("All"), int(StopBeforeBuild::All)); m_ui.stopBeforeBuildComboBox->addItem(tr("Same Project"), int(StopBeforeBuild::SameProject)); @@ -135,14 +138,14 @@ void ProjectExplorerSettingsWidget::setSettings(const ProjectExplorerSettings & m_ui.lowBuildPriorityCheckBox->setChecked(m_settings.lowBuildPriority); } -QString ProjectExplorerSettingsWidget::projectsDirectory() const +FilePath ProjectExplorerSettingsWidget::projectsDirectory() const { - return m_ui.projectsDirectoryPathChooser->filePath().toString(); + return m_ui.projectsDirectoryPathChooser->filePath(); } -void ProjectExplorerSettingsWidget::setProjectsDirectory(const QString &pd) +void ProjectExplorerSettingsWidget::setProjectsDirectory(const FilePath &pd) { - m_ui.projectsDirectoryPathChooser->setPath(pd); + m_ui.projectsDirectoryPathChooser->setFilePath(pd); } bool ProjectExplorerSettingsWidget::useProjectsDirectory() @@ -179,8 +182,8 @@ QWidget *ProjectExplorerSettingsPage::widget() if (!m_widget) { m_widget = new ProjectExplorerSettingsWidget; m_widget->setSettings(ProjectExplorerPlugin::projectExplorerSettings()); - m_widget->setProjectsDirectory(Core::DocumentManager::projectsDirectory().toString()); - m_widget->setUseProjectsDirectory(Core::DocumentManager::useProjectsDirectory()); + m_widget->setProjectsDirectory(DocumentManager::projectsDirectory()); + m_widget->setUseProjectsDirectory(DocumentManager::useProjectsDirectory()); } return m_widget; } @@ -189,9 +192,8 @@ void ProjectExplorerSettingsPage::apply() { if (m_widget) { ProjectExplorerPlugin::setProjectExplorerSettings(m_widget->settings()); - Core::DocumentManager::setProjectsDirectory( - Utils::FilePath::fromString(m_widget->projectsDirectory())); - Core::DocumentManager::setUseProjectsDirectory(m_widget->useProjectsDirectory()); + DocumentManager::setProjectsDirectory(m_widget->projectsDirectory()); + DocumentManager::setUseProjectsDirectory(m_widget->useProjectsDirectory()); } } From 442bf3993990ce78ff43655b8d0d7d66c02d7fc8 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 29 Sep 2021 09:18:04 +0200 Subject: [PATCH 17/28] Utils: swap anchor and position of Utils::Text::selectAt selecting now from column to column+length Change-Id: I34fdf2d3db7fb15687b973094b2ca063b5c6587c Reviewed-by: Eike Ziller --- src/libs/utils/textutils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/textutils.cpp b/src/libs/utils/textutils.cpp index 3b475aef3cd..4e0764fffb7 100644 --- a/src/libs/utils/textutils.cpp +++ b/src/libs/utils/textutils.cpp @@ -88,9 +88,9 @@ QTextCursor selectAt(QTextCursor textCursor, int line, int column, uint length) if (column < 1) column = 1; - const int anchorPosition = positionInText(textCursor.document(), line, column + length); + const int anchorPosition = positionInText(textCursor.document(), line, column); textCursor.setPosition(anchorPosition); - textCursor.setPosition(anchorPosition - length, QTextCursor::KeepAnchor); + textCursor.setPosition(anchorPosition + int(length), QTextCursor::KeepAnchor); return textCursor; } From c75df591ed0884f08e9352d6d2c5d285a4b31300 Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Thu, 30 Sep 2021 14:57:35 +0300 Subject: [PATCH 18/28] Doc: Update info about the light components Task-number: QDS-4888 Change-Id: I2a25ab5c6e8c8ce985ad73d3f52a638857d0e127 Reviewed-by: Leena Miettinen --- .../images/studio-3d-area-light.png | Bin 21349 -> 21349 bytes .../images/studio-3d-directional-light.png | Bin 19122 -> 19122 bytes .../images/studio-3d-point-light.png | Bin 21503 -> 21503 bytes .../images/studio-3d-spot-light.png | Bin 25519 -> 25519 bytes .../qtdesignstudio-3d-lights.qdoc | 87 +++++++++++++----- 5 files changed, 64 insertions(+), 23 deletions(-) diff --git a/doc/qtdesignstudio/images/studio-3d-area-light.png b/doc/qtdesignstudio/images/studio-3d-area-light.png index d31661e992335c5c2547e06d63fb9483a0bd1ae9..24fbfcbe33e6db0d89483aa42fa17de252a7e460 100644 GIT binary patch delta 17163 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 C)e@=z delta 16444 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 Date: Thu, 30 Sep 2021 17:51:56 +0200 Subject: [PATCH 19/28] CMake: Fix clang build type mismatch detection with Clang 13.0 Clang 13.0 has made CommonOptionsParser's constructor protected and the test fails to build, and clang is considered as being not found. Change-Id: I7b28b5104bcb6541f3ee9c4bf56e9bb1898d58af Reviewed-by: Qt CI Bot Reviewed-by: Eike Ziller --- cmake/FindClang.cmake | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmake/FindClang.cmake b/cmake/FindClang.cmake index 1ae02e38c84..41b7669f56f 100644 --- a/cmake/FindClang.cmake +++ b/cmake/FindClang.cmake @@ -34,7 +34,13 @@ if (WIN32 AND TARGET clangTooling) llvm::cl::OptionCategory CheckToolCategory("check tool options"); int main(int argc, const char **argv) { - CommonOptionsParser OptionsParser(argc, argv, CheckToolCategory); + class Parser : public CommonOptionsParser { + public: + Parser(int &argc, const char **argv, llvm::cl::OptionCategory &Category) : + CommonOptionsParser(argc, argv, Category) {} + }; + + Parser OptionsParser(argc, argv, CheckToolCategory); ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); return 0; From 0665a9151c0423cc77aab12440114f852ac5aed0 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Mon, 27 Sep 2021 17:26:40 +0200 Subject: [PATCH 20/28] CMakePM: Scan files only on CMake configuration failure The file tree scanner will only be started on CMake configuration failure to generate the fall back project node. Change-Id: I873ef1189fd43dc9bffa03aeafabb00bb3b8b6af Reviewed-by: Qt CI Bot Reviewed-by: Eike Ziller --- .../cmakeprojectmanager/cmakebuildsystem.cpp | 118 +++++++++--------- .../cmakeprojectmanager/cmakebuildsystem.h | 16 +-- .../fileapidataextractor.cpp | 26 ---- .../fileapidataextractor.h | 4 - .../cmakeprojectmanager/fileapireader.cpp | 36 +++--- .../cmakeprojectmanager/fileapireader.h | 6 +- 6 files changed, 83 insertions(+), 123 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index d3a95645304..d1c9d9a22b4 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -35,6 +35,7 @@ #include "cmakeprojectnodes.h" #include "cmakeprojectplugin.h" #include "cmakespecificsettings.h" +#include "projecttreehelper.h" #include "utils/algorithm.h" #include @@ -158,8 +159,7 @@ CMakeBuildSystem::CMakeBuildSystem(CMakeBuildConfiguration *bc) m_treeScanner.setFilter([this](const MimeType &mimeType, const FilePath &fn) { // Mime checks requires more resources, so keep it last in check list - auto isIgnored = fn.toString().startsWith(projectFilePath().toString() + ".user") - || TreeScanner::isWellKnownBinary(mimeType, fn); + auto isIgnored = TreeScanner::isWellKnownBinary(mimeType, fn); // Cache mime check result for speed up if (!isIgnored) { @@ -213,7 +213,6 @@ CMakeBuildSystem::~CMakeBuildSystem() delete m_cppCodeModelUpdater; qDeleteAll(m_extraCompilers); - qDeleteAll(m_allFiles.allFiles); } void CMakeBuildSystem::triggerParsing() @@ -244,28 +243,11 @@ void CMakeBuildSystem::triggerParsing() qCDebug(cmakeBuildSystemLog) << "ParseGuard acquired."; - if (m_allFiles.allFiles.isEmpty()) { - qCDebug(cmakeBuildSystemLog) - << "No treescanner information available, forcing treescanner run."; - updateReparseParameters(REPARSE_SCAN); - } - int reparseParameters = takeReparseParameters(); - m_waitingForScan = (reparseParameters & REPARSE_SCAN) != 0; m_waitingForParse = true; m_combinedScanAndParseResult = true; - if (m_waitingForScan) { - qCDebug(cmakeBuildSystemLog) << "Starting TreeScanner"; - QTC_CHECK(m_treeScanner.isFinished()); - if (m_treeScanner.asyncScanForFiles(projectDirectory())) - Core::ProgressManager::addTask(m_treeScanner.future(), - tr("Scan \"%1\" project tree") - .arg(project()->displayName()), - "CMake.Scan.Tree"); - } - QTC_ASSERT(m_parameters.isValid(), return ); TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); @@ -365,8 +347,6 @@ QString CMakeBuildSystem::reparseParametersString(int reparseFlags) result += " FORCE_CMAKE_RUN"; if (reparseFlags & REPARSE_FORCE_INITIAL_CONFIGURATION) result += " FORCE_CONFIG"; - if (reparseFlags & REPARSE_SCAN) - result += " SCAN"; } return result.trimmed(); } @@ -442,7 +422,7 @@ void CMakeBuildSystem::runCMakeAndScanProjectTree() BuildDirParameters parameters(cmakeBuildConfiguration()); qCDebug(cmakeBuildSystemLog) << "Requesting parse due to \"Rescan Project\" command"; setParametersAndRequestParse(parameters, - REPARSE_FORCE_CMAKE_RUN | REPARSE_SCAN | REPARSE_URGENT); + REPARSE_FORCE_CMAKE_RUN | REPARSE_URGENT); } void CMakeBuildSystem::runCMakeWithExtraArguments() @@ -461,18 +441,6 @@ void CMakeBuildSystem::buildCMakeTarget(const QString &buildTarget) cmakeBuildConfiguration()->buildTarget(buildTarget); } -void CMakeBuildSystem::handleTreeScanningFinished() -{ - QTC_CHECK(m_waitingForScan); - - qDeleteAll(m_allFiles.allFiles); - m_allFiles = m_treeScanner.release(); - - m_waitingForScan = false; - - combineScanAndParse(m_reader.lastCMakeExitCode() != 0); -} - bool CMakeBuildSystem::persistCMakeState() { BuildDirParameters parameters(cmakeBuildConfiguration()); @@ -524,17 +492,10 @@ void CMakeBuildSystem::clearCMakeCache() path.removeRecursively(); } -std::unique_ptr CMakeBuildSystem::generateProjectTree( - const TreeScanner::Result &allFiles, bool failedToParse) -{ - auto root = m_reader.generateProjectTree(allFiles, failedToParse); - return root; -} - void CMakeBuildSystem::combineScanAndParse(bool restoredFromBackup) { if (cmakeBuildConfiguration()->isActive()) { - if (m_waitingForParse || m_waitingForScan) + if (m_waitingForParse) return; if (m_combinedScanAndParseResult) { @@ -548,6 +509,15 @@ void CMakeBuildSystem::combineScanAndParse(bool restoredFromBackup) "

The backup of the previous configuration has been restored.

" "

Have a look at the Issues pane or in the \"Projects > Build\" settings " "for more information about the failure.(m_parameters.sourceDirectory); + newRoot->setDisplayName(m_parameters.sourceDirectory.fileName()); + + if (!m_reader.topCmakeFile().isEmpty()) { + auto node = std::make_unique(m_reader.topCmakeFile(), FileType::Project); + node->setIsGenerated(false); + + std::vector> fileNodes; + fileNodes.emplace_back(std::move(node)); + + addCMakeLists(newRoot.get(), std::move(fileNodes)); + } + + addFileSystemNodes(newRoot.get(), m_allFiles); + setRootProjectNode(std::move(newRoot)); + + m_reader.resetData(); + + m_currentGuard = {}; + emitBuildSystemUpdated(); + + qCDebug(cmakeBuildSystemLog) << "All fallback CMake project data up to date."; +} + void CMakeBuildSystem::updateFallbackProjectData() { qCDebug(cmakeBuildSystemLog) << "Updating fallback CMake project data"; - - QTC_ASSERT(m_treeScanner.isFinished() && !m_reader.isParsing(), return ); - - auto newRoot = generateProjectTree(m_allFiles, true); - setRootProjectNode(std::move(newRoot)); - - qCDebug(cmakeBuildSystemLog) << "All fallback CMake project data up to date."; + qCDebug(cmakeBuildSystemLog) << "Starting TreeScanner"; + QTC_CHECK(m_treeScanner.isFinished()); + if (m_treeScanner.asyncScanForFiles(projectDirectory())) + Core::ProgressManager::addTask(m_treeScanner.future(), + tr("Scan \"%1\" project tree") + .arg(project()->displayName()), + "CMake.Scan.Tree"); } void CMakeBuildSystem::updateCMakeConfiguration(QString &errorMessage) @@ -899,7 +895,7 @@ void CMakeBuildSystem::becameDirty() if (isParsing()) return; - setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), REPARSE_SCAN); + setParametersAndRequestParse(BuildDirParameters(cmakeBuildConfiguration()), REPARSE_DEFAULT); } void CMakeBuildSystem::updateReparseParameters(const int parameters) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index 4ed4342b01d..542568b5a77 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -37,7 +37,10 @@ #include namespace CppEditor { class CppProjectUpdater; } -namespace ProjectExplorer { class ExtraCompiler; } +namespace ProjectExplorer { + class ExtraCompiler; + class FolderNode; +} namespace CMakeProjectManager { @@ -113,8 +116,7 @@ private: REPARSE_FORCE_INITIAL_CONFIGURATION = (1 << 1), // Force initial configuration arguments to cmake REPARSE_FORCE_EXTRA_CONFIGURATION = (1 << 2), // Force extra configuration arguments to cmake - REPARSE_SCAN = (1 << 3), // Run filesystem scan - REPARSE_URGENT = (1 << 4), // Do not delay the parser run by 1s + REPARSE_URGENT = (1 << 3), // Do not delay the parser run by 1s }; QString reparseParametersString(int reparseFlags); void setParametersAndRequestParse(const BuildDirParameters ¶meters, @@ -132,9 +134,6 @@ private: // Combining Treescanner and Parser states: void combineScanAndParse(bool restoredFromBackup); - - std::unique_ptr generateProjectTree( - const ProjectExplorer::TreeScanner::Result &allFiles, bool failedToParse); void checkAndReportError(QString &errorMessage); void updateCMakeConfiguration(QString &errorMessage); @@ -146,6 +145,8 @@ private: const QList &moduleMappings); void updateInitialCMakeExpandableVars(); + void updateFileSystemNodes(); + void handleParsingSucceeded(bool restoredFromBackup); void handleParsingFailed(const QString &msg); @@ -161,10 +162,9 @@ private: void runCTest(); ProjectExplorer::TreeScanner m_treeScanner; - ProjectExplorer::TreeScanner::Result m_allFiles; + std::shared_ptr m_allFiles; QHash m_mimeBinaryCache; - bool m_waitingForScan = false; bool m_waitingForParse = false; bool m_combinedScanAndParseResult = false; diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index 5c50ce0c977..d893370028e 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -721,31 +721,5 @@ FileApiQtcData extractData(FileApiData &input, return result; } -FileApiQtcData generateFallbackData(const FilePath &topCmakeFile, - const FilePath &sourceDirectory, - const FilePath &buildDirectory, - QString errorMessage) -{ - Q_UNUSED(buildDirectory) - - FileApiQtcData result; - - result.rootProjectNode.reset(new CMakeProjectNode{sourceDirectory}); - result.rootProjectNode->setDisplayName(sourceDirectory.fileName()); - result.errorMessage = errorMessage; - - if (!topCmakeFile.isEmpty()) { - auto node = std::make_unique(topCmakeFile, FileType::Project); - node->setIsGenerated(false); - - std::vector> fileNodes; - fileNodes.emplace_back(std::move(node)); - - addCMakeLists(result.rootProjectNode.get(), std::move(fileNodes)); - } - - return result; -} - } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.h b/src/plugins/cmakeprojectmanager/fileapidataextractor.h index f0ba89d0428..2b65c76e079 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.h +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.h @@ -75,10 +75,6 @@ public: FileApiQtcData extractData(FileApiData &data, const Utils::FilePath &sourceDirectory, const Utils::FilePath &buildDirectory); -FileApiQtcData generateFallbackData(const Utils::FilePath &topCmakeFile, - const Utils::FilePath &sourceDirectory, - const Utils::FilePath &buildDirectory, - QString errorMessage); } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/fileapireader.cpp b/src/plugins/cmakeprojectmanager/fileapireader.cpp index 24c3c29fe47..faa1717e71c 100644 --- a/src/plugins/cmakeprojectmanager/fileapireader.cpp +++ b/src/plugins/cmakeprojectmanager/fileapireader.cpp @@ -27,7 +27,6 @@ #include "fileapidataextractor.h" #include "fileapiparser.h" -#include "projecttreehelper.h" #include @@ -214,15 +213,6 @@ bool FileApiReader::usesAllCapsTargets() const return m_usesAllCapsTargets; } -std::unique_ptr FileApiReader::generateProjectTree( - const ProjectExplorer::TreeScanner::Result &allFiles, bool failedToParse) -{ - if (failedToParse) - addFileSystemNodes(m_rootProjectNode.get(), allFiles.folderNode); - - return std::exchange(m_rootProjectNode, {}); -} - RawProjectParts FileApiReader::createRawProjectParts(QString &errorMessage) { Q_UNUSED(errorMessage) @@ -250,37 +240,29 @@ void FileApiReader::endState(const FilePath &replyFilePath, bool restoredFromBac const FilePath sourceDirectory = m_parameters.sourceDirectory; const FilePath buildDirectory = m_parameters.buildDirectory; - const FilePath topCmakeFile = m_cmakeFiles.size() == 1 ? (*m_cmakeFiles.begin()).path : FilePath{}; const QString cmakeBuildType = m_parameters.cmakeBuildType == "Build" ? "" : m_parameters.cmakeBuildType; QTC_CHECK(!replyFilePath.needsDevice()); m_lastReplyTimestamp = replyFilePath.lastModified(); m_future = runAsync(ProjectExplorerPlugin::sharedThreadPool(), - [replyFilePath, sourceDirectory, buildDirectory, topCmakeFile, cmakeBuildType]( + [replyFilePath, sourceDirectory, buildDirectory, cmakeBuildType]( QFutureInterface> &fi) { auto result = std::make_shared(); FileApiData data = FileApiParser::parseData(fi, replyFilePath, cmakeBuildType, result->errorMessage); - if (!result->errorMessage.isEmpty()) { - *result = generateFallbackData(topCmakeFile, - sourceDirectory, - buildDirectory, - result->errorMessage); - } else { + if (result->errorMessage.isEmpty()) *result = extractData(data, sourceDirectory, buildDirectory); - } - if (!result->errorMessage.isEmpty()) { + else qWarning() << result->errorMessage; - } fi.reportResult(result); }); onResultReady(m_future.value(), this, - [this, topCmakeFile, sourceDirectory, buildDirectory, restoredFromBackup]( + [this, sourceDirectory, buildDirectory, restoredFromBackup]( const std::shared_ptr &value) { m_isParsing = false; m_cache = std::move(value->cache); @@ -348,6 +330,16 @@ void FileApiReader::writeConfigurationIntoBuildDirectory(const QStringList &conf QTC_CHECK(settingsFile.writeFileContents(contents)); } +std::unique_ptr FileApiReader::rootProjectNode() +{ + return std::exchange(m_rootProjectNode, {}); +} + +FilePath FileApiReader::topCmakeFile() const +{ + return m_cmakeFiles.size() == 1 ? (*m_cmakeFiles.begin()).path : FilePath{}; +} + int FileApiReader::lastCMakeExitCode() const { return m_lastCMakeExitCode; diff --git a/src/plugins/cmakeprojectmanager/fileapireader.h b/src/plugins/cmakeprojectmanager/fileapireader.h index a0fd38c491d..8e0c77278d2 100644 --- a/src/plugins/cmakeprojectmanager/fileapireader.h +++ b/src/plugins/cmakeprojectmanager/fileapireader.h @@ -71,8 +71,6 @@ public: QList takeBuildTargets(QString &errorMessage); CMakeConfig takeParsedConfiguration(QString &errorMessage); QString ctestPath() const; - std::unique_ptr generateProjectTree( - const ProjectExplorer::TreeScanner::Result &allFiles, bool failedToParse); ProjectExplorer::RawProjectParts createRawProjectParts(QString &errorMessage); bool isMultiConfig() const; @@ -80,6 +78,10 @@ public: int lastCMakeExitCode() const; + std::unique_ptr rootProjectNode(); + + Utils::FilePath topCmakeFile() const; + signals: void configurationStarted() const; void dataAvailable(bool restoredFromBackup) const; From ec7c60cd673e012c0decc7905867dcfe707c730a Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 29 Sep 2021 13:00:36 +0200 Subject: [PATCH 21/28] ClangCodeModel: Do not report non-type template parameters as types ... with clangd. Change-Id: I9edad375eacc6c33a833c88da64dc29f79334157 Reviewed-by: David Schulz --- src/plugins/clangcodemodel/clangdclient.cpp | 4 +++- src/plugins/clangcodemodel/test/clangdtests.cpp | 10 ++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 6103fada825..7a6c1045ec3 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -2616,7 +2616,9 @@ static void semanticHighlighter(QFutureInterface &future, } else if (token.type == "type") { styles.mainStyle = C_TYPE; } else if (token.type == "typeParameter") { - styles.mainStyle = C_TYPE; + // clangd reports both type and non-type template parameters as type parameters, + // but the latter can be distinguished by the readonly modifier. + styles.mainStyle = token.modifiers.contains("readonly") ? C_PARAMETER : C_TYPE; } if (token.modifiers.contains("declaration")) styles.mixinStyles.push_back(C_DECLARATION); diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index 7829cb0b74d..d0726af4a49 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -909,7 +909,7 @@ void ClangdTestHighlighting::test_data() QTest::newRow("template parameter default argument") << 265 << 41 << 265 << 44 << QList{C_TYPE} << 0; QTest::newRow("template non-type parameter") << 265 << 50 << 265 << 74 - << QList{C_LOCAL, C_DECLARATION} << 0; + << QList{C_PARAMETER, C_DECLARATION} << 0; QTest::newRow("template non-type parameter default argument") << 265 << 77 << 265 << 78 << QList{C_NUMBER} << 0; QTest::newRow("template template parameter") << 265 << 103 << 265 << 128 @@ -935,7 +935,7 @@ void ClangdTestHighlighting::test_data() QTest::newRow("local var declaration of template parameter type") << 268 << 27 << 268 << 57 << QList{C_LOCAL, C_DECLARATION} << 0; QTest::newRow("reference to non-type template parameter") << 269 << 46 << 269 << 70 - << QList{C_LOCAL} << 0; + << QList{C_PARAMETER} << 0; QTest::newRow("local var declaration initialized with non-type template parameter") << 269 << 10 << 269 << 43 << QList{C_LOCAL, C_DECLARATION} << 0; @@ -1331,12 +1331,6 @@ void ClangdTestHighlighting::test() QEXPECT_FAIL("non-final virtual function call via pointer", "clangd < 14 does not send virtual modifier", Continue); } - QEXPECT_FAIL("template non-type parameter", - "FIXME: clangd reports non-type template parameters at \"typeParameter\"", - Continue); - QEXPECT_FAIL("reference to non-type template parameter", - "FIXME: clangd reports non-type template parameters at \"typeParameter\"", - Continue); QEXPECT_FAIL("non-const reference via member function call as output argument (function)", "Without punctuation and comment tokens from clangd, it's not possible " "to highlight entire expressions. But do we really want this? What about nested " From d52e7b939890cacf59d8252f5546f9f1f64a7d29 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 30 Sep 2021 16:02:39 +0200 Subject: [PATCH 22/28] LanguageClient: Make sure to run callback in all code paths ... when receiving a reply for textDocument/definition. Change-Id: I08f3a22f7c39a33b7b84539125a76d0c0d625692 Reviewed-by: David Schulz --- src/plugins/clangcodemodel/test/clangdtests.cpp | 3 --- .../languageclient/languageclientsymbolsupport.cpp | 10 +++++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index d0726af4a49..20938701157 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -506,9 +506,6 @@ void ClangdTestLocalReferences::test() client()->findLocalUsages(doc, cursor, std::move(handler)); timer.start(10000); loop.exec(); - QEXPECT_FAIL("cursor not on identifier", "clangd bug: go to definition does not return", Abort); - QEXPECT_FAIL("template parameter member access", - "clangd bug: go to definition does not return", Abort); QVERIFY(timer.isActive()); timer.stop(); diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp index fabb628bebc..8199084c416 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.cpp +++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp @@ -78,14 +78,18 @@ static void handleGotoDefinitionResponse(const GotoDefinitionRequest::Response & { if (Utils::optional _result = response.result()) { const GotoResult result = _result.value(); - if (Utils::holds_alternative(result)) - return; - if (auto ploc = Utils::get_if(&result)) { + if (Utils::holds_alternative(result)) { + callback({}); + } else if (auto ploc = Utils::get_if(&result)) { callback(linkUnderCursor.value_or(ploc->toLink())); } else if (auto plloc = Utils::get_if>(&result)) { if (!plloc->isEmpty()) callback(linkUnderCursor.value_or(plloc->value(0).toLink())); + else + callback({}); } + } else { + callback({}); } } From a07112258b47d4953f6e6b2f486dba75c4fc6159 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 28 Sep 2021 17:54:31 +0200 Subject: [PATCH 23/28] QmlDesigner: Disable Timeview and CurveEditorView when widget is invisble Task-number: QDS-5159 Change-Id: Ibae0b9742d4aaac6a1def1c84342a031baf75256 Reviewed-by: Knud Dollereder Reviewed-by: Qt CI Bot --- .../components/curveeditor/curveeditor.cpp | 12 ++++++++++ .../components/curveeditor/curveeditor.h | 7 ++++++ .../curveeditor/curveeditorview.cpp | 22 +++++++++++++++---- .../components/curveeditor/curveeditorview.h | 1 + .../timelineeditor/timelineview.cpp | 1 + .../timelineeditor/timelinewidget.cpp | 12 +++++----- 6 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp index 23623ae3095..acc77acf0a5 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp @@ -81,6 +81,18 @@ void CurveEditor::clearCanvas() m_view->reset({}); } +void CurveEditor::showEvent(QShowEvent *event) +{ + emit viewEnabledChanged(true); + QWidget::showEvent(event); +} + +void CurveEditor::hideEvent(QHideEvent *event) +{ + emit viewEnabledChanged(false); + QWidget::hideEvent(event); +} + QToolBar *CurveEditor::createToolBar(CurveEditorModel *model) { auto *bar = new QToolBar; diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.h b/src/plugins/qmldesigner/components/curveeditor/curveeditor.h index f248a0ed080..e297e31a9bc 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.h +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.h @@ -49,6 +49,13 @@ public: void clearCanvas(); +signals: + void viewEnabledChanged(const bool); + +protected: + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; + private: QToolBar *createToolBar(CurveEditorModel *model); diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp index 5172d10d8db..bddb6536c7c 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp @@ -51,6 +51,13 @@ CurveEditorView::CurveEditorView(QObject *parent) connect(m_model, &CurveEditorModel::commitStartFrame, this, &CurveEditorView::commitStartFrame); connect(m_model, &CurveEditorModel::commitEndFrame, this, &CurveEditorView::commitEndFrame); connect(m_model, &CurveEditorModel::curveChanged, this, &CurveEditorView::commitKeyframes); + + connect(m_editor, &CurveEditor::viewEnabledChanged, this, [this](bool enabled){ + setEnabled(enabled); + if (enabled) + init(); + }); + setEnabled(false); } CurveEditorView::~CurveEditorView() {} @@ -70,10 +77,8 @@ void CurveEditorView::modelAttached(Model *model) { AbstractView::modelAttached(model); - QmlTimeline timeline = activeTimeline(); - if (timeline.isValid()) { - m_model->setTimeline(timeline); - } + if (isEnabled()) + init(); } void CurveEditorView::modelAboutToBeDetached(Model *model) @@ -389,4 +394,13 @@ void CurveEditorView::commitEndFrame(int frame) timeline.modelNode().variantProperty("endFrame").setValue(frame); } +void CurveEditorView::init() +{ + QmlTimeline timeline = activeTimeline(); + if (timeline.isValid()) { + m_model->setTimeline(timeline); + } + +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.h b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.h index e774d2bc434..cdfc762191a 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.h +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.h @@ -85,6 +85,7 @@ private: void commitCurrentFrame(int frame); void commitStartFrame(int frame); void commitEndFrame(int frame); + void init(); private: bool m_block; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index cb9ed8f0cd4..1f1bef923d5 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -71,6 +71,7 @@ TimelineView::TimelineView(QObject *parent) , m_timelineWidget(nullptr) { EasingCurve::registerStreamOperators(); + setEnabled(false); } TimelineView::~TimelineView() = default; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index 149d1fd7e97..b583863015f 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -606,13 +606,7 @@ void TimelineWidget::showEvent(QShowEvent *event) { Q_UNUSED(event) - /* m_timelineView->setEnabled(true); - TODO See QDS-4191 - */ - - if (m_timelineView->model()) - init(); graphicsScene()->setWidth(m_graphicsView->viewport()->width()); graphicsScene()->invalidateLayout(); @@ -620,6 +614,10 @@ void TimelineWidget::showEvent(QShowEvent *event) graphicsScene()->onShow(); QWidget::showEvent(event); + + //All the events have to be fully processed before we call init() + if (m_timelineView->model()) + QTimer::singleShot(0, [this]() { init(); }); } void TimelineWidget::resizeEvent(QResizeEvent *event) @@ -630,7 +628,7 @@ void TimelineWidget::resizeEvent(QResizeEvent *event) void TimelineWidget::hideEvent(QHideEvent *event) { - /* m_timelineView->setEnabled(false); TODO See QDS-4191 */ + m_timelineView->setEnabled(false); QWidget::hideEvent(event); } From 9e38e710d3fe5f27c4531fef3aadd078a5afdb91 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 30 Sep 2021 13:42:48 +0300 Subject: [PATCH 24/28] QmlDesigner: Allow dropping external assets External assets can be dropped to Form Editor, 3D Editor, Text Editor, and Navigator. Fixes: QDS-5045 Change-Id: I2de06ab118350a8d0809b286c16d06e7edea92e4 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../componentcore/designeractionmanager.cpp | 51 +++++++++++++++++++ .../componentcore/designeractionmanager.h | 3 ++ .../components/edit3d/edit3dwidget.cpp | 18 +++++++ .../components/edit3d/edit3dwidget.h | 4 ++ .../formeditor/formeditorwidget.cpp | 18 +++++++ .../components/formeditor/formeditorwidget.h | 3 ++ .../components/navigator/navigatorwidget.cpp | 17 +++++++ .../components/navigator/navigatorwidget.h | 8 ++- .../texteditor/texteditorwidget.cpp | 18 ++++++- .../components/texteditor/texteditorwidget.h | 2 + 10 files changed, 139 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 5292c2f1999..d4528893637 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include @@ -241,6 +242,56 @@ ModelNodePreviewImageOperation DesignerActionManager::modelNodePreviewOperation( return op; } +bool DesignerActionManager::externalDragHasSupportedAssets(const QMimeData *mimeData) const +{ + if (!mimeData->hasUrls()) + return false; + + QSet filtersSet; + const QList handlers = addResourceHandler(); + for (const AddResourceHandler &handler : handlers) + filtersSet.insert(handler.filter); + + const QList urls = mimeData->urls(); + for (const QUrl &url : urls) { + QString suffix = "*." + url.fileName().split('.').last().toLower(); + if (filtersSet.contains(suffix)) // accept drop if it contains a valid file + return true; + } + + return false; +} + +void DesignerActionManager::handleExternalAssetsDrop(const QMimeData *mimeData) const +{ + const QList handlers = addResourceHandler(); + // create suffix to categry and category to operation hashes + QHash suffixCategory; + QHash categoryOperation; + for (const AddResourceHandler &handler : handlers) { + suffixCategory.insert(handler.filter, handler.category); + categoryOperation.insert(handler.category, handler.operation); + } + + // add files grouped by categories (so that files under same category run under 1 operation) + QHash categoryFiles; + const QList urls = mimeData->urls(); + for (const QUrl &url : urls) { + QString suffix = "*." + url.fileName().split('.').last().toLower(); + QString category = suffixCategory.value(suffix); + if (!category.isEmpty()) + categoryFiles[category].append(url.toLocalFile()); + } + + // run operations + const QStringList categories = categoryFiles.keys(); + for (const QString &category : categories) { + AddResourceOperation operation = categoryOperation.value(category); + QStringList files = categoryFiles.value(category); + operation(files, {}); + } +} + class VisiblityModelNodeAction : public ModelNodeContextMenuAction { public: diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index 36253fc4453..e0fe93f601d 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -38,6 +38,7 @@ QT_BEGIN_NAMESPACE class QGraphicsItem; class QGraphicsWidget; +class QMimeData; QT_END_NAMESPACE namespace QmlDesigner { @@ -135,6 +136,8 @@ public: void registerModelNodePreviewHandler(const ModelNodePreviewImageHandler &handler); bool hasModelNodePreviewHandler(const ModelNode &node) const; ModelNodePreviewImageOperation modelNodePreviewOperation(const ModelNode &node) const; + bool externalDragHasSupportedAssets(const QMimeData *data) const; + void handleExternalAssetsDrop(const QMimeData *data) const; private: void addTransitionEffectAction(const TypeName &typeName); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index ec8a1a69d9b..025b6227893 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -40,6 +40,7 @@ #include #include +#include #include namespace QmlDesigner { @@ -47,6 +48,8 @@ namespace QmlDesigner { Edit3DWidget::Edit3DWidget(Edit3DView *view) : m_view(view) { + setAcceptDrops(true); + Core::Context context(Constants::C_QMLEDITOR3D); m_context = new Core::IContext(this); m_context->setContext(context); @@ -159,4 +162,19 @@ Edit3DView *Edit3DWidget::view() const return m_view.data(); } +void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) +{ + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() + ->viewManager().designerActionManager(); + if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())) + dragEnterEvent->acceptProposedAction(); } + +void Edit3DWidget::dropEvent(QDropEvent *dropEvent) +{ + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() + ->viewManager().designerActionManager(); + actionManager.handleExternalAssetsDrop(dropEvent->mimeData()); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index ca44a955f5e..e7223ceb8c9 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -48,6 +48,10 @@ public: void showCanvas(bool show); +protected: + void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; + void dropEvent(QDropEvent *dropEvent) override; + private: void linkActivated(const QString &link); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index b6716939f96..1f9fc4487b6 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -63,6 +64,8 @@ namespace QmlDesigner { FormEditorWidget::FormEditorWidget(FormEditorView *view) : m_formEditorView(view) { + setAcceptDrops(true); + Core::Context context(Constants::C_QMLFORMEDITOR); m_context = new Core::IContext(this); m_context->setContext(context); @@ -582,4 +585,19 @@ void FormEditorWidget::showEvent(QShowEvent *event) } } +void FormEditorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) +{ + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() + ->viewManager().designerActionManager(); + if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())) + dragEnterEvent->acceptProposedAction(); +} + +void FormEditorWidget::dropEvent(QDropEvent *dropEvent) +{ + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() + ->viewManager().designerActionManager(); + actionManager.handleExternalAssetsDrop(dropEvent->mimeData()); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h index d0c1fa7c5ee..64b1e1a4eeb 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h @@ -49,6 +49,7 @@ class QmlItemNode; class FormEditorWidget : public QWidget { Q_OBJECT + public: FormEditorWidget(FormEditorView *view); @@ -93,6 +94,8 @@ protected: DocumentWarningWidget *errorWidget(); void hideEvent(QHideEvent *event) override; void showEvent(QShowEvent *event) override; + void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; + void dropEvent(QDropEvent *dropEvent) override; private: void changeTransformTool(bool checked); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp index 453865966be..4e77b7d4b1d 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp @@ -49,6 +49,8 @@ NavigatorWidget::NavigatorWidget(NavigatorView *view) : m_treeView(new NavigatorTreeView) , m_navigatorView(view) { + setAcceptDrops(true); + m_treeView->setDragEnabled(true); m_treeView->setAcceptDrops(true); m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -184,4 +186,19 @@ NavigatorView *NavigatorWidget::navigatorView() const return m_navigatorView.data(); } +void NavigatorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) +{ + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() + ->viewManager().designerActionManager(); + if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())) + dragEnterEvent->acceptProposedAction(); +} + +void NavigatorWidget::dropEvent(QDropEvent *dropEvent) +{ + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() + ->viewManager().designerActionManager(); + actionManager.handleExternalAssetsDrop(dropEvent->mimeData()); +} + } diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.h b/src/plugins/qmldesigner/components/navigator/navigatorwidget.h index 9c1c962c435..d8dc0938566 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.h @@ -43,6 +43,7 @@ class NavigatorView; class NavigatorWidget: public QFrame { Q_OBJECT + public: NavigatorWidget(NavigatorView *view); @@ -63,10 +64,13 @@ signals: void filterToggled(bool); void reverseOrderToggled(bool); -private: // functions +protected: + void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; + void dropEvent(QDropEvent *dropEvent) override; + +private: NavigatorView *navigatorView() const; -private: // variables NavigatorTreeView *m_treeView; QPointer m_navigatorView; }; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index b8747f7102d..6f45bbb5636 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -53,6 +53,8 @@ TextEditorWidget::TextEditorWidget(TextEditorView *textEditorView) , m_textEditorView(textEditorView) , m_statusBar(new TextEditorStatusBar(this)) { + setAcceptDrops(true); + QBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); @@ -179,7 +181,7 @@ void TextEditorWidget::setBlockCursorSelectionSynchronisation(bool b) m_blockCursorSelectionSynchronisation = b; } -bool TextEditorWidget::eventFilter( QObject *, QEvent *event) +bool TextEditorWidget::eventFilter(QObject *, QEvent *event) { static std::vector overrideKeys = { Qt::Key_Delete, Qt::Key_Backspace, Qt::Key_Insert, Qt::Key_Escape }; @@ -216,5 +218,19 @@ bool TextEditorWidget::eventFilter( QObject *, QEvent *event) return false; } +void TextEditorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) +{ + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() + ->viewManager().designerActionManager(); + if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())) + dragEnterEvent->acceptProposedAction(); +} + +void TextEditorWidget::dropEvent(QDropEvent *dropEvent) +{ + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() + ->viewManager().designerActionManager(); + actionManager.handleExternalAssetsDrop(dropEvent->mimeData()); +} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h index 8f0ba40e47f..96a8361069f 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h @@ -63,6 +63,8 @@ public: protected: bool eventFilter(QObject *object, QEvent *event) override; + void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; + void dropEvent(QDropEvent *dropEvent) override; private: void updateSelectionByCursorPosition(); From 2373c69e47c8e3386fe6a23932476f1f9943624d Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 30 Sep 2021 13:09:18 +0200 Subject: [PATCH 25/28] QmakeProjectManager: Proliferate FilePath use Change-Id: Ife92980a179a2872e4dd5083b60bbd0561be55bc Reviewed-by: Jarek Kobus --- .../qmakeprojectmanager/addlibrarywizard.cpp | 16 ++-- .../qmakeprojectmanager/addlibrarywizard.h | 8 +- .../librarydetailscontroller.cpp | 96 ++++++++++--------- .../librarydetailscontroller.h | 16 ++-- .../qmakeprojectmanagerplugin.cpp | 46 ++++----- 5 files changed, 93 insertions(+), 89 deletions(-) diff --git a/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp b/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp index e2fc82358af..8839a89e8ab 100644 --- a/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp +++ b/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp @@ -38,8 +38,8 @@ #include #include -using namespace QmakeProjectManager; -using namespace QmakeProjectManager::Internal; +namespace QmakeProjectManager { +namespace Internal { const char qt_file_dialog_filter_reg_exp[] = "^(.*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; @@ -79,8 +79,8 @@ static bool validateLibraryPath(const Utils::FilePath &filePath, return false; } -AddLibraryWizard::AddLibraryWizard(const QString &fileName, QWidget *parent) : - Utils::Wizard(parent), m_proFile(fileName) +AddLibraryWizard::AddLibraryWizard(const Utils::FilePath &proFile, QWidget *parent) : + Utils::Wizard(parent), m_proFile(proFile) { setWindowTitle(tr("Add Library")); m_libraryTypePage = new LibraryTypePage(this); @@ -93,7 +93,7 @@ AddLibraryWizard::AddLibraryWizard(const QString &fileName, QWidget *parent) : AddLibraryWizard::~AddLibraryWizard() = default; -QString AddLibraryWizard::proFile() const +Utils::FilePath AddLibraryWizard::proFile() const { return m_proFile; } @@ -294,10 +294,9 @@ SummaryPage::SummaryPage(AddLibraryWizard *parent) void SummaryPage::initializePage() { m_snippet = m_libraryWizard->snippet(); - QFileInfo fi(m_libraryWizard->proFile()); m_summaryLabel->setText( tr("The following snippet will be added to the
%1 file:") - .arg(fi.fileName())); + .arg(m_libraryWizard->proFile().fileName())); QString richSnippet; { QTextStream str(&richSnippet); @@ -316,3 +315,6 @@ QString SummaryPage::snippet() const { return m_snippet; } + +} // Internal +} // QmakeProjectManager diff --git a/src/plugins/qmakeprojectmanager/addlibrarywizard.h b/src/plugins/qmakeprojectmanager/addlibrarywizard.h index ad2372f0072..5825fae4d99 100644 --- a/src/plugins/qmakeprojectmanager/addlibrarywizard.h +++ b/src/plugins/qmakeprojectmanager/addlibrarywizard.h @@ -76,20 +76,18 @@ public: Q_DECLARE_FLAGS(Platforms, Platform) - explicit AddLibraryWizard(const QString &fileName, QWidget *parent = nullptr); + explicit AddLibraryWizard(const Utils::FilePath &proFile, QWidget *parent = nullptr); ~AddLibraryWizard() override; LibraryKind libraryKind() const; - QString proFile() const; + Utils::FilePath proFile() const; QString snippet() const; -signals: - private: LibraryTypePage *m_libraryTypePage = nullptr; DetailsPage *m_detailsPage = nullptr; SummaryPage *m_summaryPage = nullptr; - QString m_proFile; + Utils::FilePath m_proFile; }; class LibraryTypePage : public QWizardPage diff --git a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp index 04f6e131bce..6a1e405b369 100644 --- a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp +++ b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp @@ -40,22 +40,24 @@ #include using namespace ProjectExplorer; -using namespace QmakeProjectManager; -using namespace QmakeProjectManager::Internal; +using namespace Utils; + +namespace QmakeProjectManager { +namespace Internal { static void fillLibraryPlatformTypes(QComboBox *comboBox) { comboBox->clear(); - comboBox->addItem("Windows (*.lib lib*.a)", int(Utils::OsTypeWindows)); - comboBox->addItem("Linux (lib*.so lib*.a)", int(Utils::OsTypeLinux)); - comboBox->addItem("macOS (*.dylib *.a *.framework)", int(Utils::OsTypeMac)); - const int currentIndex = comboBox->findData(int(Utils::HostOsInfo::hostOs())); + comboBox->addItem("Windows (*.lib lib*.a)", int(OsTypeWindows)); + comboBox->addItem("Linux (lib*.so lib*.a)", int(OsTypeLinux)); + comboBox->addItem("macOS (*.dylib *.a *.framework)", int(OsTypeMac)); + const int currentIndex = comboBox->findData(int(HostOsInfo::hostOs())); comboBox->setCurrentIndex(std::max(0, currentIndex)); } LibraryDetailsController::LibraryDetailsController( Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, QObject *parent) : + const FilePath &proFile, QObject *parent) : QObject(parent), m_proFile(proFile), m_libraryDetailsWidget(libraryDetails) @@ -65,12 +67,12 @@ LibraryDetailsController::LibraryDetailsController( setLinkageGroupVisible(true); setMacLibraryGroupVisible(true); setPackageLineEditVisible(false); - const bool isMacOs = libraryPlatformType() == Utils::OsTypeMac; - const bool isWindows = libraryPlatformType() == Utils::OsTypeWindows; + const bool isMacOs = libraryPlatformType() == OsTypeMac; + const bool isWindows = libraryPlatformType() == OsTypeWindows; setMacLibraryRadiosVisible(!isMacOs); setLinkageRadiosVisible(isWindows); - connect(m_libraryDetailsWidget->includePathChooser, &Utils::PathChooser::rawPathChanged, + connect(m_libraryDetailsWidget->includePathChooser, &PathChooser::rawPathChanged, this, &LibraryDetailsController::slotIncludePathChanged); connect(m_libraryDetailsWidget->frameworkRadio, &QAbstractButton::clicked, this, &LibraryDetailsController::slotMacLibraryTypeChanged); @@ -108,9 +110,9 @@ AddLibraryWizard::MacLibraryType LibraryDetailsController::macLibraryType() cons return m_macLibraryType; } -Utils::OsType LibraryDetailsController::libraryPlatformType() const +OsType LibraryDetailsController::libraryPlatformType() const { - return Utils::OsType(m_libraryDetailsWidget->libraryTypeComboBox->currentData().value()); + return OsType(m_libraryDetailsWidget->libraryTypeComboBox->currentData().value()); } QString LibraryDetailsController::libraryPlatformFilter() const @@ -198,7 +200,7 @@ void LibraryDetailsController::updateGui() // UGLY HACK END } -QString LibraryDetailsController::proFile() const +FilePath LibraryDetailsController::proFile() const { return m_proFile; } @@ -397,7 +399,7 @@ static QString smartQuote(const QString &aString) { // The OS type is not important in that case, but use always the same // in order not to generate different quoting depending on host platform - return Utils::ProcessArgs::quoteArg(aString, Utils::OsTypeLinux); + return ProcessArgs::quoteArg(aString, OsTypeLinux); } static QString appendSeparator(const QString &aString) @@ -622,15 +624,15 @@ static QString generatePreTargetDepsSnippet(AddLibraryWizard::Platforms platform NonInternalLibraryDetailsController::NonInternalLibraryDetailsController( Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, QObject *parent) : + const FilePath &proFile, QObject *parent) : LibraryDetailsController(libraryDetails, proFile, parent) { setLibraryComboBoxVisible(false); setLibraryPathChooserVisible(true); - connect(libraryDetailsWidget()->libraryPathChooser, &Utils::PathChooser::validChanged, + connect(libraryDetailsWidget()->libraryPathChooser, &PathChooser::validChanged, this, &LibraryDetailsController::completeChanged); - connect(libraryDetailsWidget()->libraryPathChooser, &Utils::PathChooser::rawPathChanged, + connect(libraryDetailsWidget()->libraryPathChooser, &PathChooser::rawPathChanged, this, &NonInternalLibraryDetailsController::slotLibraryPathChanged); connect(libraryDetailsWidget()->removeSuffixCheckBox, &QAbstractButton::toggled, this, &NonInternalLibraryDetailsController::slotRemoveSuffixChanged); @@ -646,7 +648,7 @@ NonInternalLibraryDetailsController::NonInternalLibraryDetailsController( AddLibraryWizard::LinkageType NonInternalLibraryDetailsController::suggestedLinkageType() const { AddLibraryWizard::LinkageType type = AddLibraryWizard::NoLinkage; - if (libraryPlatformType() != Utils::OsTypeWindows) { + if (libraryPlatformType() != OsTypeWindows) { if (libraryDetailsWidget()->libraryPathChooser->isValid()) { QFileInfo fi(libraryDetailsWidget()->libraryPathChooser->filePath().toString()); if (fi.suffix() == QLatin1String("a")) @@ -661,7 +663,7 @@ AddLibraryWizard::LinkageType NonInternalLibraryDetailsController::suggestedLink AddLibraryWizard::MacLibraryType NonInternalLibraryDetailsController::suggestedMacLibraryType() const { AddLibraryWizard::MacLibraryType type = AddLibraryWizard::NoLibraryType; - if (libraryPlatformType() == Utils::OsTypeMac) { + if (libraryPlatformType() == OsTypeMac) { if (libraryDetailsWidget()->libraryPathChooser->isValid()) { QFileInfo fi(libraryDetailsWidget()->libraryPathChooser->filePath().toString()); if (fi.suffix() == QLatin1String("framework")) @@ -695,7 +697,7 @@ QString NonInternalLibraryDetailsController::suggestedIncludePath() const void NonInternalLibraryDetailsController::updateWindowsOptionsEnablement() { bool ena = platforms() & (AddLibraryWizard::WindowsMinGWPlatform | AddLibraryWizard::WindowsMSVCPlatform); - if (libraryPlatformType() == Utils::OsTypeWindows) { + if (libraryPlatformType() == OsTypeWindows) { libraryDetailsWidget()->addSuffixCheckBox->setEnabled(ena); ena = true; } @@ -732,10 +734,10 @@ void NonInternalLibraryDetailsController::slotRemoveSuffixChanged(bool ena) void NonInternalLibraryDetailsController::handleLibraryTypeChange() { libraryDetailsWidget()->libraryPathChooser->setPromptDialogFilter(libraryPlatformFilter()); - const bool isMacOs = libraryPlatformType() == Utils::OsTypeMac; - const bool isWindows = libraryPlatformType() == Utils::OsTypeWindows; - libraryDetailsWidget()->libraryPathChooser->setExpectedKind(isMacOs ? Utils::PathChooser::Any - : Utils::PathChooser::File); + const bool isMacOs = libraryPlatformType() == OsTypeMac; + const bool isWindows = libraryPlatformType() == OsTypeWindows; + libraryDetailsWidget()->libraryPathChooser->setExpectedKind(isMacOs ? PathChooser::Any + : PathChooser::File); setMacLibraryRadiosVisible(!isMacOs); setLinkageRadiosVisible(isWindows); setRemoveSuffixVisible(isWindows); @@ -752,7 +754,7 @@ void NonInternalLibraryDetailsController::slotLibraryTypeChanged() void NonInternalLibraryDetailsController::handleLibraryPathChange() { - if (libraryPlatformType() == Utils::OsTypeWindows) { + if (libraryPlatformType() == OsTypeWindows) { bool subfoldersEnabled = true; bool removeSuffixEnabled = true; if (libraryDetailsWidget()->libraryPathChooser->isValid()) { @@ -797,13 +799,13 @@ QString NonInternalLibraryDetailsController::snippet() const QString libName; const bool removeSuffix = isWindowsGroupVisible() && libraryDetailsWidget()->removeSuffixCheckBox->isChecked(); - if (libraryPlatformType() == Utils::OsTypeWindows) { + if (libraryPlatformType() == OsTypeWindows) { libName = fi.completeBaseName(); if (removeSuffix && !libName.isEmpty()) // remove last letter which needs to be "d" libName = libName.left(libName.size() - 1); if (fi.completeSuffix() == QLatin1String("a")) // the mingw lib case libName = libName.mid(3); // cut the "lib" prefix - } else if (libraryPlatformType() == Utils::OsTypeMac) { + } else if (libraryPlatformType() == OsTypeMac) { if (macLibraryType() == AddLibraryWizard::FrameworkType) libName = fi.completeBaseName(); else @@ -817,7 +819,7 @@ QString NonInternalLibraryDetailsController::snippet() const if (isWindowsGroupVisible()) { // when we are on Win but we don't generate the code for Win // we still need to remove "debug" or "release" subfolder - const bool useSubfoldersCondition = (libraryPlatformType() == Utils::OsTypeWindows) + const bool useSubfoldersCondition = (libraryPlatformType() == OsTypeWindows) ? true : platforms() & (AddLibraryWizard::WindowsMinGWPlatform | AddLibraryWizard::WindowsMSVCPlatform); if (useSubfoldersCondition) @@ -829,10 +831,10 @@ QString NonInternalLibraryDetailsController::snippet() const QString targetRelativePath; QString includeRelativePath; if (isIncludePathVisible()) { // generate also the path to lib - QFileInfo pfi(proFile()); + QFileInfo pfi = proFile().toFileInfo(); QDir pdir = pfi.absoluteDir(); QString absoluteLibraryPath = fi.absolutePath(); - if (libraryPlatformType() == Utils::OsTypeWindows && useSubfolders) { // drop last subfolder which needs to be "debug" or "release" + if (libraryPlatformType() == OsTypeWindows && useSubfolders) { // drop last subfolder which needs to be "debug" or "release" QFileInfo libfi(absoluteLibraryPath); absoluteLibraryPath = libfi.absolutePath(); } @@ -862,7 +864,7 @@ QString NonInternalLibraryDetailsController::snippet() const PackageLibraryDetailsController::PackageLibraryDetailsController( Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, QObject *parent) + const FilePath &proFile, QObject *parent) : NonInternalLibraryDetailsController(libraryDetails, proFile, parent) { setPlatformsVisible(false); @@ -897,11 +899,11 @@ QString PackageLibraryDetailsController::snippet() const bool PackageLibraryDetailsController::isLinkPackageGenerated() const { - const Project *project = SessionManager::projectForFile(Utils::FilePath::fromString(proFile())); + const Project *project = SessionManager::projectForFile(proFile()); if (!project) return false; - const ProjectNode *projectNode = project->findNodeForBuildKey(proFile()); + const ProjectNode *projectNode = project->findNodeForBuildKey(proFile().toString()); if (!projectNode) return false; @@ -921,7 +923,7 @@ bool PackageLibraryDetailsController::isLinkPackageGenerated() const SystemLibraryDetailsController::SystemLibraryDetailsController( Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, QObject *parent) + const FilePath &proFile, QObject *parent) : NonInternalLibraryDetailsController(libraryDetails, proFile, parent) { setIncludePathVisible(false); @@ -934,7 +936,7 @@ SystemLibraryDetailsController::SystemLibraryDetailsController( ExternalLibraryDetailsController::ExternalLibraryDetailsController( Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, QObject *parent) + const FilePath &proFile, QObject *parent) : NonInternalLibraryDetailsController(libraryDetails, proFile, parent) { setIncludePathVisible(true); @@ -949,7 +951,7 @@ void ExternalLibraryDetailsController::updateWindowsOptionsEnablement() bool subfoldersEnabled = true; bool removeSuffixEnabled = true; - if (libraryPlatformType() == Utils::OsTypeWindows + if (libraryPlatformType() == OsTypeWindows && libraryDetailsWidget()->libraryPathChooser->isValid()) { QFileInfo fi(libraryDetailsWidget()->libraryPathChooser->filePath().toString()); QFileInfo dfi(fi.absolutePath()); @@ -968,9 +970,8 @@ void ExternalLibraryDetailsController::updateWindowsOptionsEnablement() ///////////// -InternalLibraryDetailsController::InternalLibraryDetailsController( - Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, QObject *parent) +InternalLibraryDetailsController::InternalLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, + const FilePath &proFile, QObject *parent) : LibraryDetailsController(libraryDetails, proFile, parent) { setLinkageRadiosVisible(false); @@ -980,7 +981,7 @@ InternalLibraryDetailsController::InternalLibraryDetailsController( setWindowsGroupVisible(true); setRemoveSuffixVisible(false); - if (Utils::HostOsInfo::isWindowsHost()) + if (HostOsInfo::isWindowsHost()) libraryDetailsWidget()->useSubfoldersCheckBox->setEnabled(true); connect(libraryDetailsWidget()->libraryComboBox, @@ -1034,7 +1035,7 @@ QString InternalLibraryDetailsController::suggestedIncludePath() const void InternalLibraryDetailsController::updateWindowsOptionsEnablement() { - if (Utils::HostOsInfo::isWindowsHost()) + if (HostOsInfo::isWindowsHost()) libraryDetailsWidget()->addSuffixCheckBox->setEnabled(true); libraryDetailsWidget()->winGroupBox->setEnabled(platforms() & (AddLibraryWizard::WindowsMinGWPlatform | AddLibraryWizard::WindowsMSVCPlatform)); @@ -1047,7 +1048,7 @@ void InternalLibraryDetailsController::updateProFile() libraryDetailsWidget()->libraryComboBox->clear(); const QmakeProject *project - = dynamic_cast(SessionManager::projectForFile(Utils::FilePath::fromString(proFile()))); + = dynamic_cast(SessionManager::projectForFile(proFile())); if (!project) return; @@ -1091,7 +1092,7 @@ void InternalLibraryDetailsController::slotCurrentLibraryChanged() currentIndex, Qt::ToolTipRole).toString()); QmakeProFile *proFile = m_proFiles.at(currentIndex); const QStringList configVar = proFile->variableValue(Variable::Config); - if (Utils::HostOsInfo::isWindowsHost()) { + if (HostOsInfo::isWindowsHost()) { bool useSubfolders = false; if (configVar.contains(QLatin1String("debug_and_release")) && configVar.contains(QLatin1String("debug_and_release_target"))) @@ -1130,10 +1131,10 @@ QString InternalLibraryDetailsController::snippet() const // relative path for the project for which we insert the snippet, // it's relative to the root project - const QString proRelavitePath = rootDir.relativeFilePath(proFile()); + const QString proRelavitePath = rootDir.relativeFilePath(proFile().toString()); // project for which we insert the snippet - const Project *project = SessionManager::projectForFile(Utils::FilePath::fromString(proFile())); + const Project *project = SessionManager::projectForFile(proFile()); // the build directory of the active build configuration QDir rootBuildDir = rootDir; // If the project is unconfigured use the project dir @@ -1147,7 +1148,7 @@ QString InternalLibraryDetailsController::snippet() const QDir projectBuildDir(pfi.absolutePath()); // current project node from combobox - QFileInfo fi(proFile()); + QFileInfo fi = proFile().toFileInfo(); QDir projectSrcDir(fi.absolutePath()); // project node which we want to link against @@ -1175,3 +1176,6 @@ QString InternalLibraryDetailsController::snippet() const useSubfolders, addSuffix); return snippetMessage; } + +} // Internal +} // QmakeProjectManager diff --git a/src/plugins/qmakeprojectmanager/librarydetailscontroller.h b/src/plugins/qmakeprojectmanager/librarydetailscontroller.h index 70d64757ed7..4e7571adbf8 100644 --- a/src/plugins/qmakeprojectmanager/librarydetailscontroller.h +++ b/src/plugins/qmakeprojectmanager/librarydetailscontroller.h @@ -38,7 +38,7 @@ class LibraryDetailsController : public QObject Q_OBJECT public: explicit LibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, + const Utils::FilePath &proFile, QObject *parent = nullptr); virtual bool isComplete() const = 0; virtual QString snippet() const = 0; @@ -54,7 +54,7 @@ protected: AddLibraryWizard::MacLibraryType macLibraryType() const; Utils::OsType libraryPlatformType() const; QString libraryPlatformFilter() const; - QString proFile() const; + Utils::FilePath proFile() const; bool isIncludePathChanged() const; bool guiSignalsIgnored() const; @@ -99,7 +99,7 @@ private: AddLibraryWizard::LinkageType m_linkageType = AddLibraryWizard::NoLinkage; AddLibraryWizard::MacLibraryType m_macLibraryType = AddLibraryWizard::NoLibraryType; - QString m_proFile; + Utils::FilePath m_proFile; bool m_ignoreGuiSignals = false; bool m_includePathChanged = false; @@ -118,7 +118,7 @@ class NonInternalLibraryDetailsController : public LibraryDetailsController Q_OBJECT public: explicit NonInternalLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, + const Utils::FilePath &proFile, QObject *parent = nullptr); bool isComplete() const override; QString snippet() const override; @@ -143,7 +143,7 @@ class PackageLibraryDetailsController : public NonInternalLibraryDetailsControll Q_OBJECT public: explicit PackageLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, + const Utils::FilePath &proFile, QObject *parent = nullptr); bool isComplete() const override; QString snippet() const override; @@ -160,7 +160,7 @@ class SystemLibraryDetailsController : public NonInternalLibraryDetailsControlle Q_OBJECT public: explicit SystemLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, + const Utils::FilePath &proFile, QObject *parent = nullptr); protected: void updateWindowsOptionsEnablement() override final { @@ -173,7 +173,7 @@ class ExternalLibraryDetailsController : public NonInternalLibraryDetailsControl Q_OBJECT public: explicit ExternalLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, + const Utils::FilePath &proFile, QObject *parent = nullptr); protected: void updateWindowsOptionsEnablement() override final; @@ -184,7 +184,7 @@ class InternalLibraryDetailsController : public LibraryDetailsController Q_OBJECT public: explicit InternalLibraryDetailsController(Ui::LibraryDetailsWidget *libraryDetails, - const QString &proFile, + const Utils::FilePath &proFile, QObject *parent = nullptr); bool isComplete() const override; QString snippet() const override; diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp index 603f683f304..3268d16a529 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp @@ -73,6 +73,7 @@ using namespace Core; using namespace ProjectExplorer; using namespace TextEditor; +using namespace Utils; namespace QmakeProjectManager { namespace Internal { @@ -90,7 +91,7 @@ public: void buildStateChanged(Project *pro); void updateBuildFileAction(); void disableBuildFileMenus(); - void enableBuildFileMenus(const Utils::FilePath &file); + void enableBuildFileMenus(const FilePath &file); Core::Context projectContext; @@ -114,15 +115,15 @@ public: QAction *m_runQMakeAction = nullptr; QAction *m_runQMakeActionContextMenu = nullptr; - Utils::ParameterAction *m_buildSubProjectContextMenu = nullptr; + ParameterAction *m_buildSubProjectContextMenu = nullptr; QAction *m_subProjectRebuildSeparator = nullptr; QAction *m_rebuildSubProjectContextMenu = nullptr; QAction *m_cleanSubProjectContextMenu = nullptr; QAction *m_buildFileContextMenu = nullptr; - Utils::ParameterAction *m_buildSubProjectAction = nullptr; + ParameterAction *m_buildSubProjectAction = nullptr; QAction *m_rebuildSubProjectAction = nullptr; QAction *m_cleanSubProjectAction = nullptr; - Utils::ParameterAction *m_buildFileAction = nullptr; + ParameterAction *m_buildFileAction = nullptr; QAction *m_addLibraryAction = nullptr; QAction *m_addLibraryActionContextMenu = nullptr; @@ -140,7 +141,7 @@ public: void buildFile(); void handleSubDirContextMenu(QmakeBuildSystem::Action action, bool isFileBuild); - void addLibraryImpl(const QString &fileName, TextEditor::BaseTextEditor *editor); + void addLibraryImpl(const FilePath &filePath, TextEditor::BaseTextEditor *editor); void runQMakeImpl(Project *p, ProjectExplorer::Node *node); }; @@ -181,8 +182,8 @@ bool QmakeProjectManagerPlugin::initialize(const QStringList &arguments, QString //register actions Command *command = nullptr; - d->m_buildSubProjectContextMenu = new Utils::ParameterAction(tr("Build"), tr("Build \"%1\""), - Utils::ParameterAction::AlwaysEnabled/*handled manually*/, + d->m_buildSubProjectContextMenu = new ParameterAction(tr("Build"), tr("Build \"%1\""), + ParameterAction::AlwaysEnabled/*handled manually*/, this); command = ActionManager::registerAction(d->m_buildSubProjectContextMenu, Constants::BUILDSUBDIRCONTEXTMENU, projectContext); command->setAttribute(Command::CA_Hide); @@ -227,8 +228,8 @@ bool QmakeProjectManagerPlugin::initialize(const QStringList &arguments, QString connect(d->m_buildFileContextMenu, &QAction::triggered, d, &QmakeProjectManagerPluginPrivate::buildFileContextMenu); - d->m_buildSubProjectAction = new Utils::ParameterAction(tr("Build &Subproject"), tr("Build &Subproject \"%1\""), - Utils::ParameterAction::AlwaysEnabled, this); + d->m_buildSubProjectAction = new ParameterAction(tr("Build &Subproject"), tr("Build &Subproject \"%1\""), + ParameterAction::AlwaysEnabled, this); command = ActionManager::registerAction(d->m_buildSubProjectAction, Constants::BUILDSUBDIR, projectContext); command->setAttribute(Command::CA_Hide); command->setAttribute(Command::CA_UpdateText); @@ -244,8 +245,7 @@ bool QmakeProjectManagerPlugin::initialize(const QStringList &arguments, QString connect(d->m_runQMakeAction, &QAction::triggered, d, &QmakeProjectManagerPluginPrivate::runQMake); - d->m_rebuildSubProjectAction = new QAction(Icons::REBUILD.icon(), tr("Rebuild"), - this); + d->m_rebuildSubProjectAction = new QAction(ProjectExplorer::Icons::REBUILD.icon(), tr("Rebuild"), this); d->m_rebuildSubProjectAction->setWhatsThis(tr("Rebuild Subproject")); command = ActionManager::registerAction(d->m_rebuildSubProjectAction, Constants::REBUILDSUBDIR, projectContext); command->setAttribute(Command::CA_Hide); @@ -265,8 +265,8 @@ bool QmakeProjectManagerPlugin::initialize(const QStringList &arguments, QString connect(d->m_cleanSubProjectAction, &QAction::triggered, d, &QmakeProjectManagerPluginPrivate::cleanSubDirContextMenu); - d->m_buildFileAction = new Utils::ParameterAction(tr("Build File"), tr("Build File \"%1\""), - Utils::ParameterAction::AlwaysEnabled, this); + d->m_buildFileAction = new ParameterAction(tr("Build File"), tr("Build File \"%1\""), + ParameterAction::AlwaysEnabled, this); command = ActionManager::registerAction(d->m_buildFileAction, Constants::BUILDFILE, projectContext); command->setAttribute(Command::CA_Hide); command->setAttribute(Command::CA_UpdateText); @@ -361,33 +361,33 @@ static QmakeProFileNode *buildableFileProFile(Node *node) void QmakeProjectManagerPluginPrivate::addLibrary() { if (auto editor = qobject_cast(Core::EditorManager::currentEditor())) - addLibraryImpl(editor->document()->filePath().toString(), editor); + addLibraryImpl(editor->document()->filePath(), editor); } void QmakeProjectManagerPluginPrivate::addLibraryContextMenu() { - QString projectPath; + FilePath projectPath; Node *node = ProjectTree::currentNode(); if (ContainerNode *cn = node->asContainerNode()) - projectPath = cn->project()->projectFilePath().toString(); + projectPath = cn->project()->projectFilePath(); else if (dynamic_cast(node)) - projectPath = node->filePath().toString(); + projectPath = node->filePath(); addLibraryImpl(projectPath, nullptr); } -void QmakeProjectManagerPluginPrivate::addLibraryImpl(const QString &fileName, BaseTextEditor *editor) +void QmakeProjectManagerPluginPrivate::addLibraryImpl(const FilePath &filePath, BaseTextEditor *editor) { - if (fileName.isEmpty()) + if (filePath.isEmpty()) return; - Internal::AddLibraryWizard wizard(fileName, Core::ICore::dialogParent()); + Internal::AddLibraryWizard wizard(filePath, Core::ICore::dialogParent()); if (wizard.exec() != QDialog::Accepted) return; if (!editor) - editor = qobject_cast(Core::EditorManager::openEditor(fileName, + editor = qobject_cast(Core::EditorManager::openEditor(filePath, Constants::PROFILE_EDITOR_ID, Core::EditorManager::DoNotMakeVisible)); if (!editor) return; @@ -448,7 +448,7 @@ void QmakeProjectManagerPluginPrivate::buildFile() if (!currentDocument) return; - const Utils::FilePath file = currentDocument->filePath(); + const FilePath file = currentDocument->filePath(); Node *n = ProjectTree::nodeForFile(file); FileNode *node = n ? n->asFileNode() : nullptr; if (!node) @@ -599,7 +599,7 @@ void QmakeProjectManagerPluginPrivate::disableBuildFileMenus() m_buildFileContextMenu->setEnabled(false); } -void QmakeProjectManagerPluginPrivate::enableBuildFileMenus(const Utils::FilePath &file) +void QmakeProjectManagerPluginPrivate::enableBuildFileMenus(const FilePath &file) { bool visible = false; bool enabled = false; From 4d3374d23a1e593fcf3c132f0c38b78a6386cfb6 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 30 Sep 2021 16:27:55 +0200 Subject: [PATCH 26/28] Utils: Drop .exists() check in some FilePath functions ... when it is implicit by other checks in the same expression. Change-Id: I10c6bd92e4890bdf611f3a222dc6c2d271e98f37 Reviewed-by: Christian Stenger --- src/libs/utils/filepath.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 3b099e422e4..3602a6c304f 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -616,7 +616,7 @@ bool FilePath::isWritableFile() const return s_deviceHooks.isWritableFile(*this); } const QFileInfo fi{m_data}; - return fi.exists() && fi.isWritable() && !fi.isDir(); + return fi.isWritable() && !fi.isDir(); } bool FilePath::ensureWritableDir() const @@ -626,7 +626,7 @@ bool FilePath::ensureWritableDir() const return s_deviceHooks.ensureWritableDir(*this); } const QFileInfo fi{m_data}; - if (exists() && fi.isDir() && fi.isWritable()) + if (fi.isDir() && fi.isWritable()) return true; return QDir().mkpath(m_data); } @@ -652,7 +652,7 @@ bool FilePath::isExecutableFile() const return s_deviceHooks.isExecutableFile(*this); } const QFileInfo fi{m_data}; - return fi.exists() && fi.isExecutable() && !fi.isDir(); + return fi.isExecutable() && !fi.isDir(); } bool FilePath::isReadableFile() const @@ -662,7 +662,7 @@ bool FilePath::isReadableFile() const return s_deviceHooks.isReadableFile(*this); } const QFileInfo fi{m_data}; - return fi.exists() && fi.isReadable() && !fi.isDir(); + return fi.isReadable() && !fi.isDir(); } bool FilePath::isReadableDir() const @@ -672,7 +672,7 @@ bool FilePath::isReadableDir() const return s_deviceHooks.isReadableDir(*this); } const QFileInfo fi{m_data}; - return fi.exists() && fi.isReadable() && fi.isDir(); + return fi.isReadable() && fi.isDir(); } bool FilePath::isFile() const @@ -682,7 +682,7 @@ bool FilePath::isFile() const return s_deviceHooks.isFile(*this); } const QFileInfo fi{m_data}; - return fi.exists() && fi.isFile(); + return fi.isFile(); } bool FilePath::isDir() const @@ -692,7 +692,7 @@ bool FilePath::isDir() const return s_deviceHooks.isDir(*this); } const QFileInfo fi{m_data}; - return fi.exists() && fi.isDir(); + return fi.isDir(); } bool FilePath::createDir() const From 6842745a68aebc670bd9625328223edc4793f647 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 30 Sep 2021 16:40:56 +0200 Subject: [PATCH 27/28] Debugger: Avoid some repeated file accesses on startup Change-Id: Id0d8458a872519156b859c40762ebc7837ef434b Reviewed-by: Christian Stenger --- src/plugins/debugger/debuggerkitinformation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp index edc355469de..e7f1c5f2d7f 100644 --- a/src/plugins/debugger/debuggerkitinformation.cpp +++ b/src/plugins/debugger/debuggerkitinformation.cpp @@ -308,7 +308,8 @@ DebuggerKitAspect::ConfigurationErrors DebuggerKitAspect::configurationErrors(co ConfigurationErrors result = NoConfigurationError; const FilePath debugger = item->command(); - if (!debugger.exists() || debugger.isDir()) + const bool found = debugger.exists() && !debugger.isDir(); + if (!found) result |= DebuggerNotFound; else if (!debugger.isExecutableFile()) result |= DebuggerNotExecutable; @@ -321,7 +322,7 @@ DebuggerKitAspect::ConfigurationErrors DebuggerKitAspect::configurationErrors(co result |= DebuggerDoesNotMatch; } - if (!debugger.exists() || debugger.isDir()) { + if (!found) { if (item->engineType() == NoEngineType) return NoDebugger; From 9def0165acde64365de6bf998772681d33b91e48 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 30 Sep 2021 16:46:42 +0200 Subject: [PATCH 28/28] Replace a few PathChooser::setPath by setFilePath Change-Id: I4120aa2fe9584334e7c19ca15a647027e496008e Reviewed-by: Qt CI Bot Reviewed-by: Eike Ziller --- src/libs/utils/aspects.cpp | 2 +- .../baremetal/debugservers/uvsc/uvtargetdeviceviewer.cpp | 2 +- src/plugins/clearcase/settingspage.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index 2d8e1b641d7..6a6c9d8e91d 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -1181,7 +1181,7 @@ void StringAspect::setVolatileValue(const QVariant &val) switch (d->m_displayStyle) { case PathChooserDisplay: if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setPath(val.toString()); + d->m_pathChooserDisplay->setFilePath(FilePath::fromVariant(val)); break; case LineEditDisplay: if (d->m_lineEditDisplay) diff --git a/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceviewer.cpp b/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceviewer.cpp index 7d01bd14401..0a54774d3ee 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceviewer.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceviewer.cpp @@ -123,7 +123,7 @@ void DeviceSelectorDetailsPanel::refresh() m_memoryView->refresh(); m_algorithmView->refresh(); m_algorithmView->setAlgorithm(m_selection.algorithmIndex); - m_peripheralDescriptionFileChooser->setPath(m_selection.svd); + m_peripheralDescriptionFileChooser->setFilePath(Utils::FilePath::fromString(m_selection.svd)); } // DeviceSelector diff --git a/src/plugins/clearcase/settingspage.cpp b/src/plugins/clearcase/settingspage.cpp index a8b808980dd..11ff5b8d9cf 100644 --- a/src/plugins/clearcase/settingspage.cpp +++ b/src/plugins/clearcase/settingspage.cpp @@ -67,7 +67,7 @@ SettingsPageWidget::SettingsPageWidget() const ClearCaseSettings &s = ClearCasePlugin::settings(); - m_ui.commandPathChooser->setPath(s.ccCommand); + m_ui.commandPathChooser->setFilePath(FilePath::fromString(s.ccCommand)); m_ui.timeOutSpinBox->setValue(s.timeOutS); m_ui.autoCheckOutCheckBox->setChecked(s.autoCheckOut); m_ui.noCommentCheckBox->setChecked(s.noComment);