diff options
author | Liang Qi <liang.qi@qt.io> | 2016-09-22 08:44:34 +0200 |
---|---|---|
committer | J-P Nurmi <jpnurmi@qt.io> | 2016-09-22 08:55:29 +0200 |
commit | ab91c57135f7a86437a5a4341cd32df1d4682480 (patch) | |
tree | 713612fe2c27ff43ab4a3d5ed3b363c6141d5fbf | |
parent | c5a44092c8b73c540862c4a160c31487a8518992 (diff) | |
parent | 7193318330bc518255fb7ed008c144578f49b4bd (diff) |
Merge remote-tracking branch 'origin/5.7' into 5.8
Change-Id: I37aab846346692fd4bff08b0dbab66db3a8e2716
42 files changed, 1737 insertions, 271 deletions
diff --git a/src/imports/controls/Drawer.qml b/src/imports/controls/Drawer.qml index 4b4ea8c0..eca9b949 100644 --- a/src/imports/controls/Drawer.qml +++ b/src/imports/controls/Drawer.qml @@ -42,6 +42,8 @@ import QtQuick.Templates 2.1 as T T.Drawer { id: control + parent: T.ApplicationWindow.overlay + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) diff --git a/src/imports/controls/controls.pro b/src/imports/controls/controls.pro index 999245c8..9e8a3c27 100644 --- a/src/imports/controls/controls.pro +++ b/src/imports/controls/controls.pro @@ -7,8 +7,6 @@ QT_PRIVATE += core-private gui-private qml-private quick-private quicktemplates2 DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII -QMAKE_DOCS = $$PWD/doc/qtquickcontrols2.qdocconf - OTHER_FILES += \ qmldir @@ -20,6 +18,7 @@ RESOURCES += \ include(controls.pri) !static: include(designer/designer.pri) +include(doc/doc.pri) qtquickcompiler { qmlfiles.prefix = /qt-project.org/imports/QtQuick/Controls.2 diff --git a/src/imports/controls/doc/doc.pri b/src/imports/controls/doc/doc.pri new file mode 100644 index 00000000..8eaccbed --- /dev/null +++ b/src/imports/controls/doc/doc.pri @@ -0,0 +1,7 @@ +QMAKE_DOCS = $$PWD/qtquickcontrols2.qdocconf + +OTHER_FILES += \ + $$files($$PWD/snippets/*.qml) \ + $$files($$PWD/src/*.qdoc) \ + $$files($$PWD/src/calendar/*.qdoc) \ + $$files($$PWD/src/templates/*.qdoc) diff --git a/src/imports/controls/doc/images/qtquickcontrols2-control.png b/src/imports/controls/doc/images/qtquickcontrols2-control.png Binary files differindex 28fcb742..26babc9f 100644 --- a/src/imports/controls/doc/images/qtquickcontrols2-control.png +++ b/src/imports/controls/doc/images/qtquickcontrols2-control.png diff --git a/src/imports/controls/doc/images/qtquickcontrols2-control.svg b/src/imports/controls/doc/images/qtquickcontrols2-control.svg new file mode 100644 index 00000000..b0ffae7c --- /dev/null +++ b/src/imports/controls/doc/images/qtquickcontrols2-control.svg @@ -0,0 +1,392 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="679.31317" + height="357.84125" + viewBox="0 0 679.31314 357.84124" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="qtquickcontrols2-control.svg" + inkscape:export-filename="/home/mitch/Dropbox/qtquickcontrols2-control.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <defs + id="defs4"> + <marker + inkscape:stockid="TriangleOutL" + orient="auto" + refY="0" + refX="0" + id="TriangleOutL" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4327" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="scale(0.8,0.8)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="TriangleInL" + orient="auto" + refY="0" + refX="0" + id="TriangleInL" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4318" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="scale(-0.8,-0.8)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Arrow1Mstart" + orient="auto" + refY="0" + refX="0" + id="Arrow1Mstart" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4191" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="matrix(0.4,0,0,0.4,4,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Arrow2Mend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Mend" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4212" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="scale(-0.6,-0.6)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="TriangleInL" + orient="auto" + refY="0" + refX="0" + id="TriangleInL-2" + style="overflow:visible" + inkscape:isstock="true"> + <path + inkscape:connector-curvature="0" + id="path4318-4" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="scale(-0.8,-0.8)" /> + </marker> + <marker + inkscape:stockid="TriangleOutL" + orient="auto" + refY="0" + refX="0" + id="TriangleOutL-9" + style="overflow:visible" + inkscape:isstock="true"> + <path + inkscape:connector-curvature="0" + id="path4327-1" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="scale(0.8,0.8)" /> + </marker> + <marker + inkscape:stockid="TriangleInL" + orient="auto" + refY="0" + refX="0" + id="TriangleInL-3" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4318-9" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="scale(-0.8,-0.8)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="TriangleOutL" + orient="auto" + refY="0" + refX="0" + id="TriangleOutL-0" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4327-5" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="scale(0.8,0.8)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="TriangleInL" + orient="auto" + refY="0" + refX="0" + id="TriangleInL-9" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4318-2" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="scale(-0.8,-0.8)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="TriangleOutL" + orient="auto" + refY="0" + refX="0" + id="TriangleOutL-1" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4327-2" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1" + transform="scale(0.8,0.8)" + inkscape:connector-curvature="0" /> + </marker> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.8" + inkscape:cx="370.99138" + inkscape:cy="234.56612" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="3742" + inkscape:window-height="2124" + inkscape:window-x="98" + inkscape:window-y="36" + inkscape:window-maximized="1" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + units="px" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(91.482322,-255.11685)"> + <rect + style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect4136" + width="564.41473" + height="248.82799" + x="-30.289024" + y="321.69858" /> + <flowRoot + xml:space="preserve" + id="flowRoot4138" + style="font-style:normal;font-weight:normal;font-size:15.41801071px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(1.1690183,0,0,1.1690183,19.800223,-141.31953)"><flowRegion + id="flowRegion4140"><rect + id="rect4142" + width="129.78784" + height="31.668232" + x="-34.519978" + y="400.22751" + style="font-size:15.41801071px;fill:#000000" /></flowRegion><flowPara + id="flowPara4144" + style="font-size:20.55734825px;fill:#000000">Background</flowPara><flowPara + id="flowPara4146" /></flowRoot> <rect + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:27.60000038;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.58381503" + id="rect4148" + width="464.22452" + height="165.94499" + x="18.895706" + y="364.74274" /> + <flowRoot + xml:space="preserve" + id="flowRoot4138-6" + style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,144.03104,146.23557)"><flowRegion + id="flowRegion4140-6"><rect + id="rect4142-6" + width="334.36047" + height="80.812172" + x="42.426407" + y="481.62601" + style="font-size:40px;fill:#000000" /></flowRegion><flowPara + id="flowPara4146-6">Content item</flowPara></flowRoot> <path + style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.04880464;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInL);marker-end:url(#TriangleOutL)" + d="m 27.018805,516.90108 0,-133.62042" + id="path4179" + inkscape:connector-curvature="0" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.93839902;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInL-2);marker-end:url(#TriangleOutL-9)" + d="m 26.776662,538.68728 451.028788,0" + id="path4179-7" + inkscape:connector-curvature="0" /> + <flowRoot + xml:space="preserve" + id="flowRoot4138-6-2" + style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,10.003309,144.31674)"><flowRegion + id="flowRegion4140-6-6"><rect + id="rect4142-6-1" + width="117.17769" + height="101.01524" + x="42.426407" + y="481.62601" + style="font-size:20px;fill:#000000" /></flowRegion><flowPara + id="flowPara9732" + style="font-size:17.5px;fill:#000000">Available height</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot4138-6-2-7" + style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,10.811464,253.07573)"><flowRegion + id="flowRegion4140-6-6-4"><rect + id="rect4142-6-1-4" + width="252.53812" + height="61.619293" + x="42.426407" + y="481.62601" + style="font-size:17.5px;fill:#000000" /></flowRegion><flowPara + style="font-size:17.5px;fill:#000000" + id="flowPara10048">Available width</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot4138-6-1" + style="font-style:normal;font-weight:normal;font-size:25px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,210.99354,300.96896)"><flowRegion + id="flowRegion4140-6-9"><rect + id="rect4142-6-0" + width="270.72089" + height="68.690361" + x="42.426407" + y="481.62601" + style="font-size:25px;fill:#000000" /></flowRegion><flowPara + id="flowPara4146-6-2" + style="font-size:17.5px">Width</flowPara></flowRoot> <path + style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.90119678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInL-3);marker-end:url(#TriangleOutL-0)" + d="m -26.522148,583.27143 557.292688,0" + id="path4179-8" + inkscape:connector-curvature="0" /> + <path + style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.9011969;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInL-9);marker-end:url(#TriangleOutL-1)" + d="m -43.461661,566.30584 0,-239.7552" + id="path4179-9" + inkscape:connector-curvature="0" /> + <flowRoot + xml:space="preserve" + id="flowRoot4138-6-1-7" + style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,-112.00353,150.83655)"><flowRegion + id="flowRegion4140-6-9-7"><rect + id="rect4142-6-0-5" + width="270.72089" + height="68.690361" + x="42.426407" + y="481.62601" + style="font-size:17.5px;fill:#000000" /></flowRegion><flowPara + id="flowPara4146-6-2-9" + style="font-size:17.5px;fill:#000000">Height</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot4138-6-3" + style="font-style:normal;font-weight:normal;font-size:60px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,156.72375,-24.786365)"><flowRegion + id="flowRegion4140-6-67"><rect + id="rect4142-6-5" + width="334.36047" + height="80.812172" + x="42.426407" + y="481.62601" + style="font-size:60px;fill:#000000" /></flowRegion><flowPara + id="flowPara4146-6-3">Control</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot4138-6-2-7-5" + style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,186.57489,257.45801)"><flowRegion + id="flowRegion4140-6-6-4-6"><rect + id="rect4142-6-1-4-2" + width="252.53812" + height="61.619293" + x="42.426407" + y="481.62601" + style="font-size:17.5px;fill:#000000" /></flowRegion><flowPara + style="font-size:17.5px;fill:#000000" + id="flowPara10048-9">Bottom padding</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot4138-6-2-7-5-1" + style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,193.9747,48.798368)"><flowRegion + id="flowRegion4140-6-6-4-6-2"><rect + id="rect4142-6-1-4-2-7" + width="252.53812" + height="61.619293" + x="42.426407" + y="481.62601" + style="font-size:17.5px;fill:#000000" /></flowRegion><flowPara + style="font-size:17.5px;fill:#000000" + id="flowPara10048-9-0">Top padding</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot4138-6-2-7-5-9" + style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,457.80767,148.11921)"><flowRegion + id="flowRegion4140-6-6-4-6-3"><rect + id="rect4142-6-1-4-2-6" + width="81.109558" + height="93.762154" + x="42.426407" + y="481.62601" + style="font-size:17.5px;fill:#000000" /></flowRegion><flowPara + style="font-size:17.5px;line-height:125%;text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000" + id="flowPara4386">Right padding</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot4138-6-2-7-5-9-2" + style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="matrix(0.6007979,0,0,0.6007979,-54.735211,151.06168)"><flowRegion + id="flowRegion4140-6-6-4-6-3-5"><rect + id="rect4142-6-1-4-2-6-4" + width="81.109558" + height="93.762154" + x="42.426407" + y="481.62601" + style="font-size:17.5px;fill:#000000" /></flowRegion><flowPara + style="font-size:17.02554321px;line-height:125%;text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000" + id="flowPara4386-0">Left padding</flowPara></flowRoot> </g> +</svg> diff --git a/src/imports/controls/material/Drawer.qml b/src/imports/controls/material/Drawer.qml index e0b698e2..efb394d1 100644 --- a/src/imports/controls/material/Drawer.qml +++ b/src/imports/controls/material/Drawer.qml @@ -42,6 +42,8 @@ import QtQuick.Controls.Material.impl 2.1 T.Drawer { id: control + parent: T.ApplicationWindow.overlay + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) diff --git a/src/imports/controls/universal/Drawer.qml b/src/imports/controls/universal/Drawer.qml index 1ce73f41..c3dece6e 100644 --- a/src/imports/controls/universal/Drawer.qml +++ b/src/imports/controls/universal/Drawer.qml @@ -41,6 +41,8 @@ import QtQuick.Controls.Universal 2.1 T.Drawer { id: control + parent: T.ApplicationWindow.overlay + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp index 9c840493..f2442cb7 100644 --- a/src/quicktemplates2/qquickabstractbutton.cpp +++ b/src/quicktemplates2/qquickabstractbutton.cpp @@ -105,7 +105,7 @@ static const int AUTO_REPEAT_INTERVAL = 100; */ QQuickAbstractButtonPrivate::QQuickAbstractButtonPrivate() : - down(false), explicitDown(false), pressed(false), checked(false), checkable(false), + down(false), explicitDown(false), pressed(false), keepPressed(false), checked(false), checkable(false), autoExclusive(false), autoRepeat(false), wasHeld(false), holdTimer(0), delayTimer(0), repeatTimer(0), repeatButton(Qt::NoButton), indicator(nullptr), group(nullptr) { @@ -525,7 +525,7 @@ void QQuickAbstractButton::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickAbstractButton); QQuickControl::mouseMoveEvent(event); - setPressed(contains(event->pos())); + setPressed(d->keepPressed || contains(event->pos())); if (d->autoRepeat) d->stopPressRepeat(); @@ -540,7 +540,7 @@ void QQuickAbstractButton::mouseReleaseEvent(QMouseEvent *event) bool wasPressed = d->pressed; setPressed(false); - if (contains(event->pos())) + if (d->keepPressed || contains(event->pos())) nextCheckState(); if (wasPressed) { diff --git a/src/quicktemplates2/qquickabstractbutton_p_p.h b/src/quicktemplates2/qquickabstractbutton_p_p.h index a7d4d6c2..688ad804 100644 --- a/src/quicktemplates2/qquickabstractbutton_p_p.h +++ b/src/quicktemplates2/qquickabstractbutton_p_p.h @@ -82,6 +82,7 @@ public: bool down; bool explicitDown; bool pressed; + bool keepPressed; bool checked; bool checkable; bool autoExclusive; diff --git a/src/quicktemplates2/qquickapplicationwindow.cpp b/src/quicktemplates2/qquickapplicationwindow.cpp index 7f466fd7..7fb406c1 100644 --- a/src/quicktemplates2/qquickapplicationwindow.cpp +++ b/src/quicktemplates2/qquickapplicationwindow.cpp @@ -443,7 +443,7 @@ QQuickItem *QQuickApplicationWindow::contentItem() const The difference between \l Window::activeFocusItem and ApplicationWindow::activeFocusControl is that the former may point to a building block of a control, whereas the latter points to the enclosing control. For example, when SpinBox has focus, activeFocusItem points to - the editor and acticeFocusControl to the SpinBox itself. + the editor and activeFocusControl to the SpinBox itself. \sa Window::activeFocusItem */ @@ -622,47 +622,49 @@ public: void windowChange(QQuickWindow *wnd); - QQuickApplicationWindow *window; + QQuickWindow *window; }; void QQuickApplicationWindowAttachedPrivate::windowChange(QQuickWindow *wnd) { Q_Q(QQuickApplicationWindowAttached); - if (window && !QQuickApplicationWindowPrivate::get(window)) - window = nullptr; // being deleted (QTBUG-52731) + if (window == wnd) + return; - QQuickApplicationWindow *newWindow = qobject_cast<QQuickApplicationWindow *>(wnd); - if (window != newWindow) { - QQuickApplicationWindow *oldWindow = window; - if (oldWindow) { - QObject::disconnect(oldWindow, &QQuickApplicationWindow::activeFocusControlChanged, - q, &QQuickApplicationWindowAttached::activeFocusControlChanged); - QObject::disconnect(oldWindow, &QQuickApplicationWindow::headerChanged, - q, &QQuickApplicationWindowAttached::headerChanged); - QObject::disconnect(oldWindow, &QQuickApplicationWindow::footerChanged, - q, &QQuickApplicationWindowAttached::footerChanged); - } - if (newWindow) { - QObject::connect(newWindow, &QQuickApplicationWindow::activeFocusControlChanged, - q, &QQuickApplicationWindowAttached::activeFocusControlChanged); - QObject::connect(newWindow, &QQuickApplicationWindow::headerChanged, - q, &QQuickApplicationWindowAttached::headerChanged); - QObject::connect(newWindow, &QQuickApplicationWindow::footerChanged, - q, &QQuickApplicationWindowAttached::footerChanged); - } + QQuickApplicationWindow *oldWindow = qobject_cast<QQuickApplicationWindow *>(window); + if (oldWindow && !QQuickApplicationWindowPrivate::get(oldWindow)) + oldWindow = nullptr; // being deleted (QTBUG-52731) + + if (oldWindow) { + QObject::disconnect(oldWindow, &QQuickApplicationWindow::activeFocusControlChanged, + q, &QQuickApplicationWindowAttached::activeFocusControlChanged); + QObject::disconnect(oldWindow, &QQuickApplicationWindow::headerChanged, + q, &QQuickApplicationWindowAttached::headerChanged); + QObject::disconnect(oldWindow, &QQuickApplicationWindow::footerChanged, + q, &QQuickApplicationWindowAttached::footerChanged); + } - window = newWindow; - emit q->windowChanged(); - emit q->contentItemChanged(); - emit q->overlayChanged(); - - if ((oldWindow && oldWindow->activeFocusControl()) || (newWindow && newWindow->activeFocusControl())) - emit q->activeFocusControlChanged(); - if ((oldWindow && oldWindow->header()) || (newWindow && newWindow->header())) - emit q->headerChanged(); - if ((oldWindow && oldWindow->footer()) || (newWindow && newWindow->footer())) - emit q->footerChanged(); + QQuickApplicationWindow *newWindow = qobject_cast<QQuickApplicationWindow *>(wnd); + if (newWindow) { + QObject::connect(newWindow, &QQuickApplicationWindow::activeFocusControlChanged, + q, &QQuickApplicationWindowAttached::activeFocusControlChanged); + QObject::connect(newWindow, &QQuickApplicationWindow::headerChanged, + q, &QQuickApplicationWindowAttached::headerChanged); + QObject::connect(newWindow, &QQuickApplicationWindow::footerChanged, + q, &QQuickApplicationWindowAttached::footerChanged); } + + window = wnd; + emit q->windowChanged(); + emit q->contentItemChanged(); + emit q->overlayChanged(); + + if ((oldWindow && oldWindow->activeFocusControl()) || (newWindow && newWindow->activeFocusControl())) + emit q->activeFocusControlChanged(); + if ((oldWindow && oldWindow->header()) || (newWindow && newWindow->header())) + emit q->headerChanged(); + if ((oldWindow && oldWindow->footer()) || (newWindow && newWindow->footer())) + emit q->footerChanged(); } QQuickApplicationWindowAttached::QQuickApplicationWindowAttached(QObject *parent) @@ -698,7 +700,7 @@ QQuickApplicationWindowAttached::QQuickApplicationWindowAttached(QObject *parent QQuickApplicationWindow *QQuickApplicationWindowAttached::window() const { Q_D(const QQuickApplicationWindowAttached); - return d->window; + return qobject_cast<QQuickApplicationWindow *>(d->window); } /*! @@ -711,7 +713,9 @@ QQuickApplicationWindow *QQuickApplicationWindowAttached::window() const QQuickItem *QQuickApplicationWindowAttached::contentItem() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->contentItem() : nullptr; + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->contentItem(); + return nullptr; } /*! @@ -728,7 +732,9 @@ QQuickItem *QQuickApplicationWindowAttached::contentItem() const QQuickItem *QQuickApplicationWindowAttached::activeFocusControl() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->activeFocusControl() : nullptr; + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->activeFocusControl(); + return nullptr; } /*! @@ -742,7 +748,9 @@ QQuickItem *QQuickApplicationWindowAttached::activeFocusControl() const QQuickItem *QQuickApplicationWindowAttached::header() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->header() : nullptr; + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->header(); + return nullptr; } /*! @@ -756,7 +764,9 @@ QQuickItem *QQuickApplicationWindowAttached::header() const QQuickItem *QQuickApplicationWindowAttached::footer() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->footer() : nullptr; + if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(d->window)) + return window->footer(); + return nullptr; } /*! @@ -769,7 +779,7 @@ QQuickItem *QQuickApplicationWindowAttached::footer() const QQuickOverlay *QQuickApplicationWindowAttached::overlay() const { Q_D(const QQuickApplicationWindowAttached); - return d->window ? d->window->overlay() : nullptr; + return QQuickOverlay::overlay(d->window); } QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickbutton.cpp b/src/quicktemplates2/qquickbutton.cpp index 19310713..591c4316 100644 --- a/src/quicktemplates2/qquickbutton.cpp +++ b/src/quicktemplates2/qquickbutton.cpp @@ -110,6 +110,17 @@ QQuickButton::QQuickButton(QQuickButtonPrivate &dd, QQuickItem *parent) : \qmlproperty bool QtQuick.Controls::Button::checkable This property holds whether the button is checkable. + + A checkable button toggles between checked (on) and unchecked (off) when + the user clicks on it or presses the space bar while the button has active + focus. + + Setting \l {AbstractButton::}{checked} to \c true forces this property to + \c true. + + The default value is \c false. + + \sa CheckBox, Switch */ void QQuickButton::checkableChange() @@ -120,8 +131,10 @@ void QQuickButton::checkableChange() /*! \qmlproperty bool QtQuick.Controls::Button::autoRepeat - This property holds whether the button repeats pressed(), released() - and clicked() signals while the button is pressed and held down. + This property holds whether the button repeats + \l {AbstractButton::}{pressed()}, \l {AbstractButton::}{released()} + and \l {AbstractButton::}{clicked()} signals while the button is pressed + and held down. The default value is \c false. */ diff --git a/src/quicktemplates2/qquickcheckbox.cpp b/src/quicktemplates2/qquickcheckbox.cpp index 1daf69dc..a777c2b4 100644 --- a/src/quicktemplates2/qquickcheckbox.cpp +++ b/src/quicktemplates2/qquickcheckbox.cpp @@ -52,9 +52,11 @@ QT_BEGIN_NAMESPACE CheckBox presents an option button that can be toggled on (checked) or off (unchecked). Check boxes are typically used to select one or more - options from a set of options. + options from a set of options. For larger sets of options, such as those + in a list, consider using \l CheckDelegate instead. - The state of the checkbox can be set with the \l {AbstractButton::}{checked} property. + CheckBox inherits its API from \l AbstractButton. For instance, the + state of the checkbox can be set with the \l {AbstractButton::}{checked} property. In addition to the checked and unchecked states, there is a third state: partially checked. The partially checked state can be enabled using the diff --git a/src/quicktemplates2/qquickcheckdelegate.cpp b/src/quicktemplates2/qquickcheckdelegate.cpp index 09be5d60..249aaa95 100644 --- a/src/quicktemplates2/qquickcheckdelegate.cpp +++ b/src/quicktemplates2/qquickcheckdelegate.cpp @@ -54,9 +54,14 @@ QT_BEGIN_NAMESPACE CheckDelegate presents an item delegate that can be toggled on (checked) or off (unchecked). Check delegates are typically used to select one or more - options from a set of options. - - The state of the check delegate can be set with the + options from a set of options in a list. For smaller sets of options, or + for options that need to be uniquely identifiable, consider using + \l CheckBox instead. + + CheckDelegate inherits its API from \l ItemDelegate, which is inherited + from AbstractButton. For instance, you can set \l {AbstractButton::text}{text}, + and react to \l {AbstractButton::clicked}{clicks} using the AbstractButton + API. The state of the check delegate can be set with the \l {AbstractButton::}{checked} property. In addition to the checked and unchecked states, there is a third state: @@ -75,7 +80,7 @@ QT_BEGIN_NAMESPACE } \endcode - \sa {Customizing CheckDelegate}, {Delegate Controls} + \sa {Customizing CheckDelegate}, {Delegate Controls}, CheckBox */ class QQuickCheckDelegatePrivate : public QQuickItemDelegatePrivate diff --git a/src/quicktemplates2/qquickcontainer.cpp b/src/quicktemplates2/qquickcontainer.cpp index 31a92b5d..d9859b7d 100644 --- a/src/quicktemplates2/qquickcontainer.cpp +++ b/src/quicktemplates2/qquickcontainer.cpp @@ -55,9 +55,11 @@ QT_BEGIN_NAMESPACE \section2 Using Containers - Container provides an API to \l {addItem}{add}, \l {insertItem}{insert}, + Typically, items are statically declared as children of Container, but it + is also possible to \l {addItem}{add}, \l {insertItem}{insert}, \l {moveItem}{move} and \l {removeItem}{remove} items dynamically. The - items in a container can be accessed using \l itemAt() or \l contentChildren. + items in a container can be accessed using \l itemAt() or + \l contentChildren. Most containers have the concept of a "current" item. The current item is specified via the \l currentIndex property, and can be accessed using the diff --git a/src/quicktemplates2/qquickcontrol.cpp b/src/quicktemplates2/qquickcontrol.cpp index 642e2e72..518255dd 100644 --- a/src/quicktemplates2/qquickcontrol.cpp +++ b/src/quicktemplates2/qquickcontrol.cpp @@ -70,8 +70,26 @@ QT_BEGIN_NAMESPACE events from the window system, and paints a representation of itself on the screen. + \section1 Control Layout + + The following diagram illustrates the layout of a typical control: + \image qtquickcontrols2-control.png + The \l {Item::}{implicitWidth} and \l {Item::}{implicitHeight} of a control + are typically based on the implicit sizes of the background and the content + item plus any \l {Control::}{padding}. These properties determine how large + the control will be when no explicit \l {Item::}{width} or + \l {Item::}{height} is specified. + + The \l {Control::}{background} item fills the entire width and height of the + control, unless an explicit size has been given for it. + + The geometry of the \l {Control::}{contentItem} is determined by the + padding. + + \section1 Event Handling + All controls, except non-interactive indicators, do not let clicks and touches through to items below them. For example, if \l Pane is used as the \l {ApplicationWindow::}{header} or \l {ApplicationWindow::}{footer} of @@ -397,6 +415,10 @@ void QQuickControl::itemChange(QQuickItem::ItemChange change, const QQuickItem:: Q_D(QQuickControl); QQuickItem::itemChange(change, value); switch (change) { + case ItemVisibleHasChanged: + if (!value.boolValue) + setHovered(false); + break; case ItemParentHasChanged: if (value.item) { d->resolveFont(); @@ -432,6 +454,22 @@ void QQuickControl::itemChange(QQuickItem::ItemChange change, const QQuickItem:: Control propagates explicit font properties from parent to children. If you change a specific property on a control's font, that property propagates to all of the control's children, overriding any system defaults for that property. + + \code + Page { + font.family: "Courier" + + Column { + Label { + text: qsTr("This will use Courier...") + } + + Switch { + text: qsTr("... and so will this") + } + } + } + \endcode */ QFont QQuickControl::font() const { @@ -458,9 +496,10 @@ void QQuickControl::resetFont() \qmlproperty real QtQuick.Controls::Control::availableWidth \readonly - This property holds the width available after deducting horizontal padding. + This property holds the width available to the \l contentItem after + deducting horizontal padding from the \l {Item::}{width} of the control. - \sa padding, leftPadding, rightPadding + \sa {Control Layout}, padding, leftPadding, rightPadding */ qreal QQuickControl::availableWidth() const { @@ -471,9 +510,10 @@ qreal QQuickControl::availableWidth() const \qmlproperty real QtQuick.Controls::Control::availableHeight \readonly - This property holds the height available after deducting vertical padding. + This property holds the height available to the \l contentItem after + deducting vertical padding from the \l {Item::}{height} of the control. - \sa padding, topPadding, bottomPadding + \sa {Control Layout}, padding, topPadding, bottomPadding */ qreal QQuickControl::availableHeight() const { @@ -485,7 +525,19 @@ qreal QQuickControl::availableHeight() const This property holds the default padding. - \sa availableWidth, availableHeight, topPadding, leftPadding, rightPadding, bottomPadding + Padding adds a space between each edge of the content item and the + background item, effectively controlling the size of the content item. To + specify a padding value for a specific edge of the control, set its + relevant property: + + \list + \li \l {Control::}{leftPadding} + \li \l {Control::}{rightPadding} + \li \l {Control::}{topPadding} + \li \l {Control::}{bottomPadding} + \endlist + + \sa {Control Layout}, availableWidth, availableHeight, topPadding, leftPadding, rightPadding, bottomPadding */ qreal QQuickControl::padding() const { @@ -527,7 +579,7 @@ void QQuickControl::resetPadding() This property holds the top padding. - \sa padding, bottomPadding, availableHeight + \sa {Control Layout}, padding, bottomPadding, availableHeight */ qreal QQuickControl::topPadding() const { @@ -554,7 +606,7 @@ void QQuickControl::resetTopPadding() This property holds the left padding. - \sa padding, rightPadding, availableWidth + \sa {Control Layout}, padding, rightPadding, availableWidth */ qreal QQuickControl::leftPadding() const { @@ -581,7 +633,7 @@ void QQuickControl::resetLeftPadding() This property holds the right padding. - \sa padding, leftPadding, availableWidth + \sa {Control Layout}, padding, leftPadding, availableWidth */ qreal QQuickControl::rightPadding() const { @@ -608,7 +660,7 @@ void QQuickControl::resetRightPadding() This property holds the bottom padding. - \sa padding, topPadding, availableHeight + \sa {Control Layout}, padding, topPadding, availableHeight */ qreal QQuickControl::bottomPadding() const { @@ -634,6 +686,12 @@ void QQuickControl::resetBottomPadding() \qmlproperty real QtQuick.Controls::Control::spacing This property holds the spacing. + + Spacing is useful for controls that have multiple or repetitive building + blocks. For example, some styles use spacing to determine the distance + between the text and indicator of \l CheckBox. Spacing is not enforced by + Control, so each style may interpret it differently, and some may ignore it + altogether. */ qreal QQuickControl::spacing() const { @@ -766,9 +824,11 @@ void QQuickControlPrivate::updateLocaleRecur(QQuickItem *item, const QLocale &l) This property holds whether the control is mirrored. This property is provided for convenience. A control is considered mirrored - when its visual layout direction is right-to-left. + when its visual layout direction is right-to-left; that is, when using a + right-to-left locale or when \l {LayoutMirroring::enabled}{LayoutMirroring.enabled} + is \c true. - \sa locale, {LayoutMirroring}{LayoutMirroring} + \sa locale, {LayoutMirroring}{LayoutMirroring}, {Right-to-left User Interfaces} */ bool QQuickControl::isMirrored() const { @@ -917,6 +977,10 @@ void QQuickControl::setHoverEnabled(bool enabled) \qmlproperty bool QtQuick.Controls::Control::wheelEnabled This property determines whether the control handles wheel events. The default value is \c false. + + \note Care must be taken when enabling wheel events for controls within scrollable items such + as \l Flickable, as the control will consume the events and hence interrupt scrolling of the + Flickable. */ bool QQuickControl::isWheelEnabled() const { @@ -939,9 +1003,30 @@ void QQuickControl::setWheelEnabled(bool enabled) This property holds the background item. + \code + Button { + id: control + text: qsTr("Button") + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + opacity: enabled ? 1 : 0.3 + color: control.down ? "#d0d0d0" : "#e0e0e0" + } + } + \endcode + \note If the background item has no explicit size specified, it automatically follows the control's size. In most cases, there is no need to specify width or height for a background item. + + \note Most controls use the implicit size of the background item to calculate + the implicit size of the control itself. If you replace the background item + with a custom one, you should also consider providing a sensible implicit + size for it (unless it is an item like \l Image which has its own implicit + size). + + \sa {Control Layout} */ QQuickItem *QQuickControl::background() const { @@ -972,7 +1057,28 @@ void QQuickControl::setBackground(QQuickItem *background) This property holds the visual content item. - \note The content item is automatically resized inside the \l padding of the control. + \note The content item is automatically resized to fit within the + \l padding of the control. + + \note Most controls use the implicit size of the content item to calculate + the implicit size of the control itself. If you replace the content item + with a custom one, you should also consider providing a sensible implicit + size for it (unless it is an item like \l Text which has its own implicit + size). + + \code + Button { + id: control + text: qsTr("Button") + contentItem: Label { + text: control.text + font: control.font + verticalAlignment: Text.AlignVCenter + } + } + \endcode + + \sa {Control Layout}, padding */ QQuickItem *QQuickControl::contentItem() const { diff --git a/src/quicktemplates2/qquickdial.cpp b/src/quicktemplates2/qquickdial.cpp index 3b096255..48d02faa 100644 --- a/src/quicktemplates2/qquickdial.cpp +++ b/src/quicktemplates2/qquickdial.cpp @@ -300,6 +300,8 @@ qreal QQuickDial::position() const Like the \l position property, angle is continuously updated while the handle is dragged. + The range is from \c -140 degrees to \c 140 degrees. + \sa position */ qreal QQuickDial::angle() const @@ -311,7 +313,18 @@ qreal QQuickDial::angle() const /*! \qmlproperty real QtQuick.Controls::Dial::stepSize - This property holds the step size. The default value is \c 0.0. + This property holds the step size. + + The step size determines the amount by which the dial's value + is increased and decreased when interacted with via the keyboard. + For example, a step size of \c 0.2, will result in the dial's + value increasing and decreasing in increments of \c 0.2. + + The step size is only respected for touch and mouse interaction + when \l snapMode is set to a value other than \c Dial.NoSnap. + + The default value is \c 0.0, which results in an effective step + size of \c 0.1 for keyboard interaction. \sa snapMode, increase(), decrease() */ @@ -460,7 +473,7 @@ void QQuickDial::decrease() } /*! - \qmlproperty component QtQuick.Controls::Dial::handle + \qmlproperty Item QtQuick.Controls::Dial::handle This property holds the handle of the dial. diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 4c9fbb62..bc1636de 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -69,6 +69,54 @@ QT_BEGIN_NAMESPACE of the window. \endtable + \code + import QtQuick 2.7 + import QtQuick.Controls 2.0 + + ApplicationWindow { + id: window + visible: true + + Drawer { + id: drawer + width: 0.66 * window.width + height: window.height + } + } + \endcode + + Drawer is a special type of popup that resides at one of the window \l {edge}{edges}. + By default, Drawer re-parents itself to the window \l {ApplicationWindow::}{overlay}, + and therefore operates on window coordinates. It is also possible to manually set the + \l {Popup::}{parent} to something else to make the drawer operate in a specific + coordinate space. + + Drawer can be configured to cover only part of its window edge. The following example + illustrates how Drawer can be positioned to appear below a window header: + + \code + import QtQuick 2.7 + import QtQuick.Controls 2.0 + + ApplicationWindow { + id: window + visible: true + + header: ToolBar { } + + Drawer { + y: header.height + width: window.width * 0.6 + height: window.height - header.height + } + } + \endcode + + The \l position property determines how much of the drawer is visible, as + a value between \c 0.0 and \c 1.0. It is not possible to set the x-coordinate + (or horizontal margins) of a drawer at the left or right window edge, or the + y-coordinate (or vertical margins) of a drawer at the top or bottom window edge. + In the image above, the application's contents are \e "pushed" across the screen. This is achieved by applying a translation to the contents: @@ -117,6 +165,7 @@ QQuickDrawerPrivate::QQuickDrawerPrivate() : edge(Qt::LeftEdge), offset(0), position(0), dragMargin(QGuiApplication::styleHints()->startDragDistance()) { + setEdge(Qt::LeftEdge); } qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const @@ -161,6 +210,27 @@ void QQuickDrawerPrivate::reposition() popupItem->setY(window->height() - position * popupItem->height()); break; } + + QQuickPopupPrivate::reposition(); +} + +void QQuickDrawerPrivate::resizeOverlay() +{ + if (!dimmer || !window) + return; + + QRectF geometry(0, 0, window->width(), window->height()); + + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) { + geometry.setY(popupItem->y()); + geometry.setHeight(popupItem->height()); + } else { + geometry.setX(popupItem->x()); + geometry.setWidth(popupItem->width()); + } + + dimmer->setPosition(geometry.topLeft()); + dimmer->setSize(geometry.size()); } static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int threshold = -1) @@ -309,6 +379,12 @@ bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *ev Q_Q(QQuickDrawer); Q_UNUSED(item); + // Don't react to synthesized mouse move events at INF,INF coordinates. + // QQuickWindowPrivate::translateTouchToMouse() uses them to clear hover + // on touch release (QTBUG-55995). + if (qIsInf(event->screenPos().x()) || qIsInf(event->screenPos().y())) + return true; + const QPointF movePoint = event->windowPos(); if (grabMouse(event)) { @@ -374,6 +450,22 @@ bool QQuickDrawerPrivate::prepareExitTransition() return QQuickPopupPrivate::prepareExitTransition(); } +void QQuickDrawerPrivate::setEdge(Qt::Edge e) +{ + edge = e; + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) { + allowVerticalMove = true; + allowVerticalResize = true; + allowHorizontalMove = false; + allowHorizontalResize = false; + } else { + allowVerticalMove = false; + allowVerticalResize = false; + allowHorizontalMove = true; + allowHorizontalResize = true; + } +} + QQuickDrawer::QQuickDrawer(QObject *parent) : QQuickPopup(*(new QQuickDrawerPrivate), parent) { @@ -406,7 +498,7 @@ void QQuickDrawer::setEdge(Qt::Edge edge) if (d->edge == edge) return; - d->edge = edge; + d->setEdge(edge); if (isComponentComplete()) d->reposition(); emit edgeChanged(); @@ -531,4 +623,11 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) } } +void QQuickDrawer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickDrawer); + QQuickPopup::geometryChanged(newGeometry, oldGeometry); + d->resizeOverlay(); +} + QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index e694e27b..bada1344 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -87,6 +87,8 @@ protected: void mouseUngrabEvent() override; bool overlayEvent(QQuickItem *item, QEvent *event) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + private: Q_DISABLE_COPY(QQuickDrawer) Q_DECLARE_PRIVATE(QQuickDrawer) diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index f14c36dd..b7555c3e 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -68,6 +68,7 @@ public: qreal positionAt(const QPointF &point) const; void reposition() override; + void resizeOverlay() override; bool startDrag(QQuickWindow *window, QMouseEvent *event); bool grabMouse(QMouseEvent *event); @@ -80,6 +81,8 @@ public: bool prepareEnterTransition() override; bool prepareExitTransition() override; + void setEdge(Qt::Edge edge); + Qt::Edge edge; qreal offset; qreal position; diff --git a/src/quicktemplates2/qquickframe.cpp b/src/quicktemplates2/qquickframe.cpp index 2f2a5497..bcc387c1 100644 --- a/src/quicktemplates2/qquickframe.cpp +++ b/src/quicktemplates2/qquickframe.cpp @@ -54,8 +54,8 @@ QT_BEGIN_NAMESPACE or a \l ColumnLayout. Items declared as children of a Frame are automatically parented to the - Frame's contentItem. Items created dynamically need to be explicitly - parented to the contentItem. + Frame's \l {Control::}{contentItem}. Items created dynamically need to be + explicitly parented to the contentItem. If only a single item is used within a Frame, it will resize to fit the implicit size of its contained item. This makes it particularly suitable diff --git a/src/quicktemplates2/qquickgroupbox.cpp b/src/quicktemplates2/qquickgroupbox.cpp index 45fe406f..27c325ef 100644 --- a/src/quicktemplates2/qquickgroupbox.cpp +++ b/src/quicktemplates2/qquickgroupbox.cpp @@ -48,10 +48,10 @@ QT_BEGIN_NAMESPACE \inqmlmodule QtQuick.Controls \since 5.7 \ingroup qtquickcontrols2-containers - \brief A frame with a logical group of controls. + \brief A logical group of controls within a titled visual frame. GroupBox is used to layout a logical group of controls together, within - a titled visual frame. GroupBox does not provide a layout of its own, but + a \l {title}{titled} visual frame. GroupBox does not provide a layout of its own, but requires you to position its contents, for instance by creating a \l RowLayout or a \l ColumnLayout. @@ -75,8 +75,8 @@ QT_BEGIN_NAMESPACE \image qtquickcontrols2-groupbox-checkable.png It is a common pattern to enable or disable the groupbox's children when - its checkbox is toggled on or off, but it is the application that decides - on the behavior of the groupbox. + its checkbox is toggled on or off, but it is up to the application to decide + on the behavior of the checkbox. \snippet qtquickcontrols2-groupbox-checkable.qml 1 @@ -101,6 +101,9 @@ QQuickGroupBox::QQuickGroupBox(QQuickItem *parent) : \qmlproperty string QtQuick.Controls::GroupBox::title This property holds the title. + + The title is typically displayed above the groupbox to + summarize its contents. */ QString QQuickGroupBox::title() const { diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp index b8522668..bf37f801 100644 --- a/src/quicktemplates2/qquickmenu.cpp +++ b/src/quicktemplates2/qquickmenu.cpp @@ -89,6 +89,15 @@ QT_BEGIN_NAMESPACE } \endcode + Typically, menu items are statically declared as children of the menu, but + Menu also provides API to \l {addItem}{add}, \l {insertItem}{insert}, + \l {moveItem}{move} and \l {removeItem}{remove} items dynamically. The + items in a menu can be accessed using \l itemAt() or + \l {Popup::}{contentChildren}. + + Although \l {MenuItem}{MenuItems} are most commonly used with Menu, it can + contain any type of item. + \sa {Customizing Menu}, {Menu Controls}, {Popup Controls} */ @@ -350,7 +359,7 @@ void QQuickMenu::moveItem(int from, int to) /*! \qmlmethod void QtQuick.Controls::Menu::removeItem(int index) - Removes an item at \a index. + Removes the item at \a index. \note The ownership of the item is transferred to the caller. */ @@ -372,8 +381,20 @@ void QQuickMenu::removeItem(int index) This property holds the model used to display menu items. - By default, the model is an \l ObjectModel, in order to allow declaring - menu items as children of the menu. + The content model is provided for visualization purposes. It can be assigned + as a model to a content item that presents the contents of the menu. + + \code + Menu { + id: menu + contentItem: ListView { + model: menu.contentModel + } + } + \endcode + + The model allows menu items to be statically declared as children of the + menu. */ QVariant QQuickMenu::contentModel() const { @@ -387,7 +408,14 @@ QVariant QQuickMenu::contentModel() const This property holds the list of content data. - \sa Item::data + The list contains all objects that have been declared in QML as children + of the menu, and also items that have been dynamically added or + inserted using the \l addItem() and \l insertItem() methods, respectively. + + \note Unlike \c contentChildren, \c contentData does include non-visual QML + objects. It is not re-ordered when items are inserted or moved. + + \sa Item::data, contentChildren */ QQmlListProperty<QObject> QQuickMenu::contentData() { @@ -402,9 +430,11 @@ QQmlListProperty<QObject> QQuickMenu::contentData() /*! \qmlproperty string QtQuick.Controls::Menu::title - Title for the menu as a submenu or in a menubar. + This property holds the title for the menu. - Its value defaults to an empty string. + The title of a menu is often displayed in the text of a menu item when the + menu is a submenu, and in the text of a tool button when it is in a + menubar. */ QString QQuickMenu::title() const { diff --git a/src/quicktemplates2/qquickmenuitem.cpp b/src/quicktemplates2/qquickmenuitem.cpp index 35942f92..902889b0 100644 --- a/src/quicktemplates2/qquickmenuitem.cpp +++ b/src/quicktemplates2/qquickmenuitem.cpp @@ -52,26 +52,30 @@ QT_BEGIN_NAMESPACE \brief A menu item within a Menu. MenuItem is a convenience type that implements the AbstractButton API, - providing an easy way to respond to menu items being clicked, for example. + providing a familiar way to respond to menu items being \l triggered, for + example. \code Button { id: fileButton text: "File" onClicked: menu.open() - } - Menu { - id: menu - anchor.target: fileButton - MenuItem { - text: "New..." - } - MenuItem { - text: "Open..." - } - MenuItem { - text: "Save" + Menu { + id: menu + + MenuItem { + text: "New..." + onTriggered: document.reset() + } + MenuItem { + text: "Open..." + onTriggered: openDialog.open() + } + MenuItem { + text: "Save" + onTriggered: saveDialog.open() + } } } \endcode @@ -110,6 +114,11 @@ QQuickMenuItem::QQuickMenuItem(QQuickItem *parent) : \qmlproperty bool QtQuick.Controls::MenuItem::checkable This property holds whether the menu item is checkable. + + A checkable menu item toggles between checked (on) and unchecked (off) when + the user clicks on it or interacts with it via the keyboard. + + \sa {AbstractButton::}{checked} */ void QQuickMenuItem::checkableChange() diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index ccae4da6..9bdc9135 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -113,7 +113,7 @@ void QQuickOverlayPrivate::createOverlay(QQuickPopup *popup) QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); if (!p->dimmer) p->dimmer = createDimmer(popup->isModal() ? modal : modeless, popup, q); - resizeOverlay(popup); + p->resizeOverlay(); } void QQuickOverlayPrivate::destroyOverlay(QQuickPopup *popup) @@ -126,16 +126,6 @@ void QQuickOverlayPrivate::destroyOverlay(QQuickPopup *popup) } } -void QQuickOverlayPrivate::resizeOverlay(QQuickPopup *popup) -{ - Q_Q(QQuickOverlay); - QQuickPopupPrivate *p = QQuickPopupPrivate::get(popup); - if (p->dimmer) { - p->dimmer->setWidth(q->width()); - p->dimmer->setHeight(q->height()); - } -} - void QQuickOverlayPrivate::toggleOverlay() { Q_Q(QQuickOverlay); @@ -320,7 +310,7 @@ void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &old Q_D(QQuickOverlay); QQuickItem::geometryChanged(newGeometry, oldGeometry); for (QQuickPopup *popup : qAsConst(d->allPopups)) - d->resizeOverlay(popup); + QQuickPopupPrivate::get(popup)->resizeOverlay(); } void QQuickOverlay::mousePressEvent(QMouseEvent *event) @@ -404,7 +394,11 @@ bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) switch (event->type()) { case QEvent::MouseButtonPress: emit pressed(); - return popup->overlayEvent(item, event); + if (popup->overlayEvent(item, event)) { + d->mouseGrabberPopup = popup; + return true; + } + break; case QEvent::MouseMove: return popup->overlayEvent(item, event); case QEvent::MouseButtonRelease: diff --git a/src/quicktemplates2/qquickoverlay_p_p.h b/src/quicktemplates2/qquickoverlay_p_p.h index db005555..ef142f1b 100644 --- a/src/quicktemplates2/qquickoverlay_p_p.h +++ b/src/quicktemplates2/qquickoverlay_p_p.h @@ -78,7 +78,6 @@ public: void createOverlay(QQuickPopup *popup); void destroyOverlay(QQuickPopup *popup); - void resizeOverlay(QQuickPopup *popup); void toggleOverlay(); QVector<QQuickPopup *> stackingOrderPopups() const; diff --git a/src/quicktemplates2/qquickpage.cpp b/src/quicktemplates2/qquickpage.cpp index 4304c4fb..54e79fbe 100644 --- a/src/quicktemplates2/qquickpage.cpp +++ b/src/quicktemplates2/qquickpage.cpp @@ -104,6 +104,37 @@ QQuickPage::QQuickPage(QQuickItem *parent) : \qmlproperty string QtQuick.Controls::Page::title This property holds the page title. + + The title is often displayed at the top of a page to give + the user context about the page they are viewing. + + \code + ApplicationWindow { + visible: true + width: 400 + height: 400 + + header: Label { + text: view.currentItem.title + horizontalAlignment: Text.AlignHCenter + } + + SwipeView { + id: view + anchors.fill: parent + + Page { + title: qsTr("Home") + } + Page { + title: qsTr("Discover") + } + Page { + title: qsTr("Activity") + } + } + } + \endcode */ QString QQuickPage::title() const @@ -183,7 +214,13 @@ void QQuickPage::setFooter(QQuickItem *footer) This property holds the list of content data. - \sa Item::data + The list contains all objects that have been declared in QML as children + of the container. + + \note Unlike \c contentChildren, \c contentData does include non-visual QML + objects. + + \sa Item::data, contentChildren */ QQmlListProperty<QObject> QQuickPage::contentData() { @@ -200,7 +237,13 @@ QQmlListProperty<QObject> QQuickPage::contentData() This property holds the list of content children. - \sa Item::children + The list contains all items that have been declared in QML as children + of the page. + + \note Unlike \c contentData, \c contentChildren does not include non-visual + QML objects. + + \sa Item::children, contentData */ QQmlListProperty<QQuickItem> QQuickPage::contentChildren() { diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index 4143ee3f..1a5310bf 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -125,12 +125,18 @@ QQuickPopupPrivate::QQuickPopupPrivate() , hasDim(false) , visible(false) , complete(false) + , hasWidth(false) + , hasHeight(false) , hasTopMargin(false) , hasLeftMargin(false) , hasRightMargin(false) , hasBottomMargin(false) , allowVerticalFlip(false) , allowHorizontalFlip(false) + , allowVerticalMove(true) + , allowHorizontalMove(true) + , allowVerticalResize(true) + , allowHorizontalResize(true) , hadActiveFocusBeforeExitTransition(false) , x(0) , y(0) @@ -587,13 +593,19 @@ void QQuickPopupPrivate::reposition() bool widthAdjusted = false; bool heightAdjusted = false; - QRectF rect(x, y, iw > 0 ? iw : w, ih > 0 ? ih : h); + QRectF rect(allowHorizontalMove ? x : popupItem->x(), + allowVerticalMove ? y : popupItem->y(), + !hasWidth && iw > 0 ? iw : w, + !hasHeight && ih > 0 ? ih : h); if (parentItem) { rect = parentItem->mapRectToScene(rect); if (window) { const QMarginsF margins = getMargins(); - const QRectF bounds = QRectF(0, 0, window->width(), window->height()).marginsRemoved(margins); + const QRectF bounds(qMax<qreal>(0.0, margins.left()), + qMax<qreal>(0.0, margins.top()), + window->width() - qMax<qreal>(0.0, margins.left()) - qMax<qreal>(0.0, margins.right()), + window->height() - qMax<qreal>(0.0, margins.top()) - qMax<qreal>(0.0, margins.bottom())); // if the popup doesn't fit horizontally inside the window, try flipping it around (left <-> right) if (allowHorizontalFlip && (rect.left() < bounds.left() || rect.right() > bounds.right())) { @@ -610,31 +622,39 @@ void QQuickPopupPrivate::reposition() } // push inside the margins if specified - if (margins.top() >= 0 && rect.top() < bounds.top()) - rect.moveTop(margins.top()); - if (margins.bottom() >= 0 && rect.bottom() > bounds.bottom()) - rect.moveBottom(bounds.bottom()); - if (margins.left() >= 0 && rect.left() < bounds.left()) - rect.moveLeft(margins.left()); - if (margins.right() >= 0 && rect.right() > bounds.right()) - rect.moveRight(bounds.right()); + if (allowVerticalMove) { + if (margins.top() >= 0 && rect.top() < bounds.top()) + rect.moveTop(margins.top()); + if (margins.bottom() >= 0 && rect.bottom() > bounds.bottom()) + rect.moveBottom(bounds.bottom()); + } + if (allowHorizontalMove) { + if (margins.left() >= 0 && rect.left() < bounds.left()) + rect.moveLeft(margins.left()); + if (margins.right() >= 0 && rect.right() > bounds.right()) + rect.moveRight(bounds.right()); + } if (iw > 0 && (rect.left() < bounds.left() || rect.right() > bounds.right())) { // neither the flipped or pushed geometry fits inside the window, choose // whichever side (left vs. right) fits larger part of the popup - if (rect.left() < bounds.left() && bounds.left() + rect.width() <= bounds.right()) - rect.moveLeft(bounds.left()); - else if (rect.right() > bounds.right() && bounds.right() - rect.width() >= bounds.left()) - rect.moveRight(bounds.right()); + if (allowHorizontalMove && allowHorizontalFlip) { + if (rect.left() < bounds.left() && bounds.left() + rect.width() <= bounds.right()) + rect.moveLeft(bounds.left()); + else if (rect.right() > bounds.right() && bounds.right() - rect.width() >= bounds.left()) + rect.moveRight(bounds.right()); + } // as a last resort, adjust the width to fit the window - if (rect.left() < bounds.left()) { - rect.setLeft(bounds.left()); - widthAdjusted = true; - } - if (rect.right() > bounds.right()) { - rect.setRight(bounds.right()); - widthAdjusted = true; + if (allowHorizontalResize) { + if (rect.left() < bounds.left()) { + rect.setLeft(bounds.left()); + widthAdjusted = true; + } + if (rect.right() > bounds.right()) { + rect.setRight(bounds.right()); + widthAdjusted = true; + } } } else if (iw > 0 && rect.left() >= bounds.left() && rect.right() <= bounds.right() && iw != w) { @@ -646,19 +666,23 @@ void QQuickPopupPrivate::reposition() if (ih > 0 && (rect.top() < bounds.top() || rect.bottom() > bounds.bottom())) { // neither the flipped or pushed geometry fits inside the window, choose // whichever side (above vs. below) fits larger part of the popup - if (rect.top() < bounds.top() && bounds.top() + rect.height() <= bounds.bottom()) - rect.moveTop(bounds.top()); - else if (rect.bottom() > bounds.bottom() && bounds.bottom() - rect.height() >= bounds.top()) - rect.moveBottom(bounds.bottom()); + if (allowVerticalMove && allowVerticalFlip) { + if (rect.top() < bounds.top() && bounds.top() + rect.height() <= bounds.bottom()) + rect.moveTop(bounds.top()); + else if (rect.bottom() > bounds.bottom() && bounds.bottom() - rect.height() >= bounds.top()) + rect.moveBottom(bounds.bottom()); + } // as a last resort, adjust the height to fit the window - if (rect.top() < bounds.top()) { - rect.setTop(bounds.top()); - heightAdjusted = true; - } - if (rect.bottom() > bounds.bottom()) { - rect.setBottom(bounds.bottom()); - heightAdjusted = true; + if (allowVerticalResize) { + if (rect.top() < bounds.top()) { + rect.setTop(bounds.top()); + heightAdjusted = true; + } + if (rect.bottom() > bounds.bottom()) { + rect.setBottom(bounds.bottom()); + heightAdjusted = true; + } } } else if (ih > 0 && rect.top() >= bounds.top() && rect.bottom() <= bounds.bottom() && ih != h) { @@ -681,12 +705,22 @@ void QQuickPopupPrivate::reposition() emit q->yChanged(); } - if (widthAdjusted && rect.width() > 0) + if (!hasWidth && widthAdjusted && rect.width() > 0) popupItem->setWidth(rect.width()); - if (heightAdjusted && rect.height() > 0) + if (!hasHeight && heightAdjusted && rect.height() > 0) popupItem->setHeight(rect.height()); } +void QQuickPopupPrivate::resizeOverlay() +{ + if (!dimmer) + return; + + qreal w = window ? window->width() : 0; + qreal h = window ? window->height() : 0; + dimmer->setSize(QSizeF(w, h)); +} + void QQuickPopupPositioner::removeAncestorListeners(QQuickItem *item) { if (item == m_parentItem) @@ -907,13 +941,20 @@ qreal QQuickPopup::width() const void QQuickPopup::setWidth(qreal width) { Q_D(QQuickPopup); + d->hasWidth = true; d->popupItem->setWidth(width); } void QQuickPopup::resetWidth() { Q_D(QQuickPopup); + if (!d->hasWidth) + return; + + d->hasWidth = false; d->popupItem->resetWidth(); + if (d->popupItem->isVisible()) + d->reposition(); } /*! @@ -930,13 +971,20 @@ qreal QQuickPopup::height() const void QQuickPopup::setHeight(qreal height) { Q_D(QQuickPopup); + d->hasHeight = true; d->popupItem->setHeight(height); } void QQuickPopup::resetHeight() { Q_D(QQuickPopup); + if (!d->hasHeight) + return; + + d->hasHeight = false; d->popupItem->resetHeight(); + if (d->popupItem->isVisible()) + d->reposition(); } /*! diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h index 93039287..4ad8d171 100644 --- a/src/quicktemplates2/qquickpopup_p_p.h +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -157,6 +157,7 @@ public: void init(); bool tryClose(QQuickItem *item, QMouseEvent *event); virtual void reposition(); + virtual void resizeOverlay(); virtual bool prepareEnterTransition(); virtual bool prepareExitTransition(); @@ -183,12 +184,18 @@ public: bool hasDim; bool visible; bool complete; + bool hasWidth; + bool hasHeight; bool hasTopMargin; bool hasLeftMargin; bool hasRightMargin; bool hasBottomMargin; bool allowVerticalFlip; bool allowHorizontalFlip; + bool allowVerticalMove; + bool allowHorizontalMove; + bool allowVerticalResize; + bool allowHorizontalResize; bool hadActiveFocusBeforeExitTransition; qreal x; qreal y; diff --git a/src/quicktemplates2/qquickswitch.cpp b/src/quicktemplates2/qquickswitch.cpp index 37ae53f0..a7d17e86 100644 --- a/src/quicktemplates2/qquickswitch.cpp +++ b/src/quicktemplates2/qquickswitch.cpp @@ -90,103 +90,28 @@ class QQuickSwitchPrivate : public QQuickAbstractButtonPrivate public: QQuickSwitchPrivate() : position(0) { } - void updatePosition(); - qreal positionAt(const QPoint &point) const; - - bool handleMousePressEvent(QQuickItem *child, QMouseEvent *event); - bool handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event); - bool handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event); - bool handleMouseUngrabEvent(QQuickItem *child); + qreal positionAt(const QPointF &point) const; qreal position; - QPoint pressPoint; }; -void QQuickSwitchPrivate::updatePosition() -{ - Q_Q(QQuickSwitch); - q->setPosition(checked ? 1.0 : 0.0); -} - -qreal QQuickSwitchPrivate::positionAt(const QPoint &point) const +qreal QQuickSwitchPrivate::positionAt(const QPointF &point) const { Q_Q(const QQuickSwitch); - qreal pos = point.x() / indicator->width(); + qreal pos = 0.0; + if (indicator) + pos = indicator->mapFromItem(q, point).x() / indicator->width(); if (q->isMirrored()) return 1.0 - pos; return pos; } -bool QQuickSwitchPrivate::handleMousePressEvent(QQuickItem *child, QMouseEvent *event) -{ - Q_Q(QQuickSwitch); - Q_UNUSED(child); - if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && !QGuiApplication::styleHints()->setFocusOnTouchRelease()) - q->forceActiveFocus(Qt::MouseFocusReason); - - pressPoint = event->pos(); - q->setPressed(true); - emit q->pressed(); - event->accept(); - return true; -} - -bool QQuickSwitchPrivate::handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event) -{ - Q_Q(QQuickSwitch); - if (!child->keepMouseGrab()) - child->setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(event->pos().x() - pressPoint.x(), Qt::XAxis, event)); - if (child->keepMouseGrab()) { - q->setPosition(positionAt(event->pos())); - event->accept(); - } - return true; -} - -bool QQuickSwitchPrivate::handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event) -{ - Q_Q(QQuickSwitch); - if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && QGuiApplication::styleHints()->setFocusOnTouchRelease()) - q->forceActiveFocus(Qt::MouseFocusReason); - - pressPoint = QPoint(); - q->setPressed(false); - if (child->keepMouseGrab()) { - bool wasChecked = checked; - q->setChecked(position > 0.5); - q->setPosition(checked ? 1.0 : 0.0); - child->setKeepMouseGrab(false); - if (wasChecked != checked) { - emit q->released(); - emit q->clicked(); - } - event->accept(); - } else { - q->toggle(); - emit q->released(); - emit q->clicked(); - event->accept(); - } - return true; -} - -bool QQuickSwitchPrivate::handleMouseUngrabEvent(QQuickItem *child) -{ - Q_Q(QQuickSwitch); - Q_UNUSED(child); - pressPoint = QPoint(); - q->setChecked(position > 0.5); - q->setPosition(checked ? 1.0 : 0.0); - q->setPressed(false); - return true; -} - QQuickSwitch::QQuickSwitch(QQuickItem *parent) : QQuickAbstractButton(*(new QQuickSwitchPrivate), parent) { + Q_D(QQuickSwitch); + d->keepPressed = true; setCheckable(true); - setFiltersChildMouseEvents(true); - QObjectPrivate::connect(this, &QQuickAbstractButton::checkedChanged, d_func(), &QQuickSwitchPrivate::updatePosition); } /*! @@ -227,30 +152,56 @@ qreal QQuickSwitch::visualPosition() const return d->position; } +void QQuickSwitch::mousePressEvent(QMouseEvent *event) +{ + QQuickAbstractButton::mousePressEvent(event); +} + +void QQuickSwitch::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickSwitch); + QQuickAbstractButton::mouseMoveEvent(event); + + const QPointF movePoint = event->localPos(); + if (!keepMouseGrab()) { + // don't start dragging the handle unless the initial press was at the indicator, + // or the drag has reached the indicator area. this prevents unnatural jumps when + // dragging far outside the indicator. + const qreal pressPos = d->positionAt(d->pressPoint); + const qreal movePos = d->positionAt(movePoint); + if ((pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0)) + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(movePoint.x() - d->pressPoint.x(), Qt::XAxis, event)); + } + + if (keepMouseGrab()) + setPosition(d->positionAt(movePoint)); +} + +void QQuickSwitch::mouseReleaseEvent(QMouseEvent *event) +{ + QQuickAbstractButton::mouseReleaseEvent(event); + setKeepMouseGrab(false); +} + void QQuickSwitch::mirrorChange() { QQuickAbstractButton::mirrorChange(); emit visualPositionChanged(); } -bool QQuickSwitch::childMouseEventFilter(QQuickItem *child, QEvent *event) +void QQuickSwitch::nextCheckState() { Q_D(QQuickSwitch); - if (child == indicator()) { - switch (event->type()) { - case QEvent::MouseButtonPress: - return d->handleMousePressEvent(child, static_cast<QMouseEvent *>(event)); - case QEvent::MouseMove: - return d->handleMouseMoveEvent(child, static_cast<QMouseEvent *>(event)); - case QEvent::MouseButtonRelease: - return d->handleMouseReleaseEvent(child, static_cast<QMouseEvent *>(event)); - case QEvent::UngrabMouse: - return d->handleMouseUngrabEvent(child); - default: - return false; - } - } - return false; + if (keepMouseGrab()) + setChecked(d->position > 0.5); + else + QQuickAbstractButton::nextCheckState(); +} + +void QQuickSwitch::checkStateSet() +{ + Q_D(QQuickSwitch); + setPosition(d->checked ? 1.0 : 0.0); } QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickswitch_p.h b/src/quicktemplates2/qquickswitch_p.h index fa92e9f3..27a065b4 100644 --- a/src/quicktemplates2/qquickswitch_p.h +++ b/src/quicktemplates2/qquickswitch_p.h @@ -73,8 +73,14 @@ Q_SIGNALS: void visualPositionChanged(); protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mirrorChange() override; - bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; + + void nextCheckState() override; + void checkStateSet() override; private: Q_DISABLE_COPY(QQuickSwitch) diff --git a/src/quicktemplates2/qquickswitchdelegate.cpp b/src/quicktemplates2/qquickswitchdelegate.cpp index fbdae418..62b677e5 100644 --- a/src/quicktemplates2/qquickswitchdelegate.cpp +++ b/src/quicktemplates2/qquickswitchdelegate.cpp @@ -80,22 +80,17 @@ public: { } - void updatePosition(); - qreal positionAt(const QPoint &point) const; + qreal positionAt(const QPointF &point) const; qreal position; }; -void QQuickSwitchDelegatePrivate::updatePosition() -{ - Q_Q(QQuickSwitchDelegate); - q->setPosition(checked ? 1.0 : 0.0); -} - -qreal QQuickSwitchDelegatePrivate::positionAt(const QPoint &point) const +qreal QQuickSwitchDelegatePrivate::positionAt(const QPointF &point) const { Q_Q(const QQuickSwitchDelegate); - qreal pos = point.x() / indicator->width(); + qreal pos = 0.0; + if (indicator) + pos = indicator->mapFromItem(q, point).x() / indicator->width(); if (q->isMirrored()) return 1.0 - pos; return pos; @@ -104,9 +99,9 @@ qreal QQuickSwitchDelegatePrivate::positionAt(const QPoint &point) const QQuickSwitchDelegate::QQuickSwitchDelegate(QQuickItem *parent) : QQuickItemDelegate(*(new QQuickSwitchDelegatePrivate), parent) { + Q_D(QQuickSwitchDelegate); + d->keepPressed = true; setCheckable(true); - - QObjectPrivate::connect(this, &QQuickAbstractButton::checkedChanged, d_func(), &QQuickSwitchDelegatePrivate::updatePosition); } /*! @@ -147,6 +142,37 @@ qreal QQuickSwitchDelegate::visualPosition() const return d->position; } +void QQuickSwitchDelegate::mousePressEvent(QMouseEvent *event) +{ + QQuickItemDelegate::mousePressEvent(event); +} + +void QQuickSwitchDelegate::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickSwitchDelegate); + QQuickItemDelegate::mouseMoveEvent(event); + + const QPointF movePoint = event->localPos(); + if (!keepMouseGrab()) { + // don't start dragging the handle unless the initial press was at the indicator, + // or the drag has reached the indicator area. this prevents unnatural jumps when + // dragging far outside the indicator. + const qreal pressPos = d->positionAt(d->pressPoint); + const qreal movePos = d->positionAt(movePoint); + if ((pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0)) + setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(movePoint.x() - d->pressPoint.x(), Qt::XAxis, event)); + } + + if (keepMouseGrab()) + setPosition(d->positionAt(movePoint)); +} + +void QQuickSwitchDelegate::mouseReleaseEvent(QMouseEvent *event) +{ + QQuickItemDelegate::mouseReleaseEvent(event); + setKeepMouseGrab(false); +} + QFont QQuickSwitchDelegate::defaultFont() const { return QQuickControlPrivate::themeFont(QPlatformTheme::ListViewFont); @@ -158,4 +184,19 @@ void QQuickSwitchDelegate::mirrorChange() emit visualPositionChanged(); } +void QQuickSwitchDelegate::nextCheckState() +{ + Q_D(QQuickSwitchDelegate); + if (keepMouseGrab()) + setChecked(d->position > 0.5); + else + QQuickItemDelegate::nextCheckState(); +} + +void QQuickSwitchDelegate::checkStateSet() +{ + Q_D(QQuickSwitchDelegate); + setPosition(d->checked ? 1.0 : 0.0); +} + QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickswitchdelegate_p.h b/src/quicktemplates2/qquickswitchdelegate_p.h index 5126f643..c0cc21ac 100644 --- a/src/quicktemplates2/qquickswitchdelegate_p.h +++ b/src/quicktemplates2/qquickswitchdelegate_p.h @@ -73,9 +73,16 @@ Q_SIGNALS: void visualPositionChanged(); protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + QFont defaultFont() const override; void mirrorChange() override; + void nextCheckState() override; + void checkStateSet() override; + private: Q_DISABLE_COPY(QQuickSwitchDelegate) Q_DECLARE_PRIVATE(QQuickSwitchDelegate) diff --git a/tests/auto/applicationwindow/tst_applicationwindow.cpp b/tests/auto/applicationwindow/tst_applicationwindow.cpp index b712075f..fb7f96b8 100644 --- a/tests/auto/applicationwindow/tst_applicationwindow.cpp +++ b/tests/auto/applicationwindow/tst_applicationwindow.cpp @@ -318,7 +318,7 @@ void tst_applicationwindow::attachedProperties() QVERIFY(!childWindowControl->property("attached_activeFocusControl").value<QQuickItem *>()); QVERIFY(!childWindowControl->property("attached_header").value<QQuickItem *>()); QVERIFY(!childWindowControl->property("attached_footer").value<QQuickItem *>()); - QVERIFY(!childWindowControl->property("attached_overlay").value<QQuickItem *>()); + QCOMPARE(childWindowControl->property("attached_overlay").value<QQuickItem *>(), QQuickOverlay::overlay(childWindow)); QQuickItem *childWindowItem = object->property("childWindowItem").value<QQuickItem *>(); QVERIFY(childWindowItem); @@ -327,7 +327,7 @@ void tst_applicationwindow::attachedProperties() QVERIFY(!childWindowItem->property("attached_activeFocusControl").value<QQuickItem *>()); QVERIFY(!childWindowItem->property("attached_header").value<QQuickItem *>()); QVERIFY(!childWindowItem->property("attached_footer").value<QQuickItem *>()); - QVERIFY(!childWindowItem->property("attached_overlay").value<QQuickItem *>()); + QCOMPARE(childWindowItem->property("attached_overlay").value<QQuickItem *>(), QQuickOverlay::overlay(childWindow)); QObject *childWindowObject = object->property("childWindowObject").value<QObject *>(); QVERIFY(childWindowObject); diff --git a/tests/auto/controls/data/tst_control.qml b/tests/auto/controls/data/tst_control.qml index 0061440b..dc84ff93 100644 --- a/tests/auto/controls/data/tst_control.qml +++ b/tests/auto/controls/data/tst_control.qml @@ -880,6 +880,12 @@ TestCase { mouseMove(control, -10, -10) compare(control.hovered, false) + mouseMove(control, control.width / 2, control.height / 2) + compare(control.hovered, true) + + control.visible = false + compare(control.hovered, false) + control.destroy() } diff --git a/tests/auto/controls/data/tst_drawer.qml b/tests/auto/controls/data/tst_drawer.qml index bfa8dd10..2e1f32dc 100644 --- a/tests/auto/controls/data/tst_drawer.qml +++ b/tests/auto/controls/data/tst_drawer.qml @@ -60,6 +60,7 @@ TestCase { compare(control.edge, Qt.LeftEdge) compare(control.position, 0.0) compare(control.dragMargin, Qt.styleHints.startDragDistance) + compare(control.parent, ApplicationWindow.overlay) control.destroy() } diff --git a/tests/auto/controls/data/tst_popup.qml b/tests/auto/controls/data/tst_popup.qml index a86e800e..5609d47f 100644 --- a/tests/auto/controls/data/tst_popup.qml +++ b/tests/auto/controls/data/tst_popup.qml @@ -355,6 +355,48 @@ TestCase { control.destroy() } + function test_resetSize() { + var control = popupControl.createObject(testCase, {visible: true, margins: 0}) + verify(control) + + control.width = control.implicitWidth = testCase.width + 10 + control.height = control.implicitHeight = testCase.height + 10 + + compare(control.width, testCase.width + 10) + compare(control.height, testCase.height + 10) + + control.width = undefined + control.height = undefined + compare(control.width, testCase.width) + compare(control.height, testCase.height) + + control.destroy() + } + + function test_negativeMargins() { + var control = popupControl.createObject(testCase, {implicitWidth: testCase.width, implicitHeight: testCase.height}) + verify(control) + + control.open() + verify(control.visible) + + compare(control.x, 0) + compare(control.y, 0) + + compare(control.margins, -1) + compare(control.topMargin, -1) + compare(control.leftMargin, -1) + compare(control.rightMargin, -1) + compare(control.bottomMargin, -1) + + control.x = -10 + control.y = -10 + compare(control.x, 0) + compare(control.y, 0) + + control.destroy() + } + function test_margins() { var control = popupControl.createObject(testCase, {width: 100, height: 100}) verify(control) @@ -975,14 +1017,52 @@ TestCase { var control = popupControl.createObject(testCase) verify(control) - control.width = 200 - control.height = 200 - control.open() waitForRendering(control.contentItem) - compare(control.width, 200) - compare(control.height, 200) + // implicit size of the content + control.contentItem.implicitWidth = 10 + compare(control.implicitWidth, 10 + control.leftPadding + control.rightPadding) + compare(control.width, control.implicitWidth) + compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding) + + control.contentItem.implicitHeight = 20 + compare(control.implicitHeight, 20 + control.topPadding + control.bottomPadding) + compare(control.height, control.implicitHeight) + compare(control.contentItem.height, control.height - control.topPadding - control.bottomPadding) + + // implicit size of the popup + control.implicitWidth = 30 + compare(control.implicitWidth, 30) + compare(control.width, 30) + compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding) + + control.implicitHeight = 40 + compare(control.implicitHeight, 40) + compare(control.height, 40) + compare(control.contentItem.height, control.height - control.topPadding - control.bottomPadding) + + // set explicit size + control.width = 50 + compare(control.implicitWidth, 30) + compare(control.width, 50) + compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding) + + control.height = 60 + compare(control.implicitHeight, 40) + compare(control.height, 60) + compare(control.contentItem.height, control.height - control.topPadding - control.bottomPadding) + + // reset explicit size + control.width = undefined + compare(control.implicitWidth, 30) + compare(control.width, 30) + compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding) + + control.height = undefined + compare(control.implicitHeight, 40) + compare(control.height, 40) + compare(control.contentItem.height, control.height - control.topPadding - control.bottomPadding) control.destroy() } diff --git a/tests/auto/controls/data/tst_switch.qml b/tests/auto/controls/data/tst_switch.qml index 6c145fc1..f3843cf7 100644 --- a/tests/auto/controls/data/tst_switch.qml +++ b/tests/auto/controls/data/tst_switch.qml @@ -95,6 +95,29 @@ TestCase { control.destroy() } + function test_pressed_data() { + return [ + { tag: "indicator", x: 15 }, + { tag: "background", x: 5 } + ] + } + + function test_pressed(data) { + var control = swtch.createObject(testCase, {padding: 10}) + verify(control) + + // stays pressed when dragged outside + compare(control.pressed, false) + mousePress(control, data.x, control.height / 2, Qt.LeftButton) + compare(control.pressed, true) + mouseMove(control, -1, control.height / 2) + compare(control.pressed, true) + mouseRelease(control, -1, control.height / 2, Qt.LeftButton) + compare(control.pressed, false) + + control.destroy() + } + function test_mouse() { var control = swtch.createObject(testCase) verify(control) @@ -177,6 +200,110 @@ TestCase { control.destroy() } + function test_drag() { + var control = swtch.createObject(testCase, {leftPadding: 100, rightPadding: 100}) + verify(control) + + var spy = signalSequenceSpy.createObject(control, {target: control}) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, false) + + // press-drag-release inside the indicator + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed"] + mousePress(control.indicator, 0) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, true) + verify(spy.success) + + mouseMove(control.indicator, control.width) + compare(control.position, 1.0) + compare(control.checked, false) + compare(control.pressed, true) + + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] + mouseRelease(control.indicator, control.indicator.width) + compare(control.position, 1.0) + compare(control.checked, true) + compare(control.pressed, false) + verify(spy.success) + + // press-drag-release outside the indicator + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + "pressed"] + mousePress(control, 0) + compare(control.position, 1.0) + compare(control.checked, true) + compare(control.pressed, true) + verify(spy.success) + + mouseMove(control, control.width - control.rightPadding) + compare(control.position, 1.0) + compare(control.checked, true) + compare(control.pressed, true) + + mouseMove(control, control.width / 2) + compare(control.position, 0.5) + compare(control.checked, true) + compare(control.pressed, true) + + mouseMove(control, control.leftPadding) + compare(control.position, 0.0) + compare(control.checked, true) + compare(control.pressed, true) + + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }], + ["checkedChanged", { "pressed": false, "checked": false }], + "released", + "clicked"] + mouseRelease(control, control.width) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, false) + verify(spy.success) + + // press-drag-release from and to outside the indicator + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed"] + mousePress(control, control.width) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, true) + verify(spy.success) + + mouseMove(control, control.width - control.rightPadding) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, true) + + mouseMove(control, control.width / 2) + compare(control.position, 0.5) + compare(control.checked, false) + compare(control.pressed, true) + + mouseMove(control, control.width - control.rightPadding) + compare(control.position, 1.0) + compare(control.checked, false) + compare(control.pressed, true) + + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] + mouseRelease(control, control.width) + compare(control.position, 1.0) + compare(control.checked, true) + compare(control.pressed, false) + verify(spy.success) + + control.destroy() + } + function test_keys() { var control = swtch.createObject(testCase) verify(control) diff --git a/tests/auto/controls/data/tst_switchdelegate.qml b/tests/auto/controls/data/tst_switchdelegate.qml index 2d5c6089..4a5d711f 100644 --- a/tests/auto/controls/data/tst_switchdelegate.qml +++ b/tests/auto/controls/data/tst_switchdelegate.qml @@ -55,6 +55,13 @@ TestCase { SwitchDelegate {} } + Component { + id: signalSequenceSpy + SignalSequenceSpy { + signals: ["pressed", "released", "canceled", "clicked", "pressedChanged", "checkedChanged"] + } + } + // TODO: data-fy tst_checkbox (rename to tst_check?) so we don't duplicate its tests here? function test_defaults() { @@ -83,4 +90,213 @@ TestCase { compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset); control.destroy(); } + + function test_pressed_data() { + return [ + { tag: "indicator", x: 15 }, + { tag: "background", x: 5 } + ] + } + + function test_pressed(data) { + var control = switchDelegate.createObject(testCase, {padding: 10}) + verify(control) + + // stays pressed when dragged outside + compare(control.pressed, false) + mousePress(control, data.x, control.height / 2, Qt.LeftButton) + compare(control.pressed, true) + mouseMove(control, -1, control.height / 2) + compare(control.pressed, true) + mouseRelease(control, -1, control.height / 2, Qt.LeftButton) + compare(control.pressed, false) + + control.destroy() + } + + function test_mouse() { + var control = switchDelegate.createObject(testCase) + verify(control) + + // check + var spy = signalSequenceSpy.createObject(control, {target: control}) + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed"] + mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) + compare(control.pressed, true) + verify(spy.success) + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] + mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton) + compare(control.checked, true) + compare(control.pressed, false) + verify(spy.success) + + // uncheck + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + "pressed"] + mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) + compare(control.pressed, true) + verify(spy.success) + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }], + ["checkedChanged", { "pressed": false, "checked": false }], + "released", + "clicked"] + mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton) + compare(control.checked, false) + compare(control.pressed, false) + verify(spy.success) + + // release on the right + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed"] + mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) + compare(control.pressed, true) + verify(spy.success) + mouseMove(control, control.width * 2, control.height / 2, 0, Qt.LeftButton) + compare(control.pressed, true) + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] + mouseRelease(control, control.width * 2, control.height / 2, Qt.LeftButton) + compare(control.checked, true) + compare(control.pressed, false) + verify(spy.success) + + // release on the left + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + "pressed"] + mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) + compare(control.pressed, true) + verify(spy.success) + mouseMove(control, -control.width, control.height / 2, 0, Qt.LeftButton) + compare(control.pressed, true) + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }], + ["checkedChanged", { "pressed": false, "checked": false }], + "released", + "clicked"] + mouseRelease(control, -control.width, control.height / 2, Qt.LeftButton) + compare(control.checked, false) + compare(control.pressed, false) + verify(spy.success) + + // right button + spy.expectedSequence = [] + mousePress(control, control.width / 2, control.height / 2, Qt.RightButton) + compare(control.pressed, false) + verify(spy.success) + mouseRelease(control, control.width / 2, control.height / 2, Qt.RightButton) + compare(control.checked, false) + compare(control.pressed, false) + verify(spy.success) + + control.destroy() + } + + function test_drag() { + var control = switchDelegate.createObject(testCase, {leftPadding: 100, rightPadding: 100}) + verify(control) + + var spy = signalSequenceSpy.createObject(control, {target: control}) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, false) + + // press-drag-release inside the indicator + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed"] + mousePress(control.indicator, 0) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, true) + verify(spy.success) + + mouseMove(control.indicator, control.width) + compare(control.position, 1.0) + compare(control.checked, false) + compare(control.pressed, true) + + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] + mouseRelease(control.indicator, control.indicator.width) + compare(control.position, 1.0) + compare(control.checked, true) + compare(control.pressed, false) + verify(spy.success) + + // press-drag-release outside the indicator + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + "pressed"] + mousePress(control, 0) + compare(control.position, 1.0) + compare(control.checked, true) + compare(control.pressed, true) + verify(spy.success) + + mouseMove(control, control.width - control.rightPadding) + compare(control.position, 1.0) + compare(control.checked, true) + compare(control.pressed, true) + + mouseMove(control, control.width / 2) + compare(control.position, 0.5) + compare(control.checked, true) + compare(control.pressed, true) + + mouseMove(control, control.leftPadding) + compare(control.position, 0.0) + compare(control.checked, true) + compare(control.pressed, true) + + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }], + ["checkedChanged", { "pressed": false, "checked": false }], + "released", + "clicked"] + mouseRelease(control, control.width) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, false) + verify(spy.success) + + // press-drag-release from and to outside the indicator + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed"] + mousePress(control, control.width) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, true) + verify(spy.success) + + mouseMove(control, control.width - control.rightPadding) + compare(control.position, 0.0) + compare(control.checked, false) + compare(control.pressed, true) + + mouseMove(control, control.width / 2) + compare(control.position, 0.5) + compare(control.checked, false) + compare(control.pressed, true) + + mouseMove(control, control.width - control.rightPadding) + compare(control.position, 1.0) + compare(control.checked, false) + compare(control.pressed, true) + + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] + mouseRelease(control, control.width) + compare(control.position, 1.0) + compare(control.checked, true) + compare(control.pressed, false) + verify(spy.success) + + control.destroy() + } } diff --git a/tests/auto/drawer/BLACKLIST b/tests/auto/drawer/BLACKLIST new file mode 100644 index 00000000..1b06b49c --- /dev/null +++ b/tests/auto/drawer/BLACKLIST @@ -0,0 +1,2 @@ +[touch] +windows diff --git a/tests/auto/drawer/data/header.qml b/tests/auto/drawer/data/header.qml new file mode 100644 index 00000000..9a352ffc --- /dev/null +++ b/tests/auto/drawer/data/header.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +ApplicationWindow { + width: 400 + height: 400 + + property alias drawer: drawer + + header: ToolBar { } + + Drawer { + id: drawer + width: 200 + height: parent.height + } +} diff --git a/tests/auto/drawer/data/reposition.qml b/tests/auto/drawer/data/reposition.qml new file mode 100644 index 00000000..abaec5ae --- /dev/null +++ b/tests/auto/drawer/data/reposition.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +ApplicationWindow { + width: 400 + height: 400 + + property alias drawer: drawer + + header: Item { implicitHeight: 50 } + footer: Item { implicitHeight: 50 } + + Drawer { + id: drawer + width: parent.width / 2 + implicitHeight: parent.height + } +} diff --git a/tests/auto/drawer/tst_drawer.cpp b/tests/auto/drawer/tst_drawer.cpp index 8b02e95c..1ded5bf4 100644 --- a/tests/auto/drawer/tst_drawer.cpp +++ b/tests/auto/drawer/tst_drawer.cpp @@ -40,9 +40,13 @@ #include "../shared/visualtestutil.h" #include <QtGui/qstylehints.h> +#include <QtGui/qtouchdevice.h> #include <QtGui/qguiapplication.h> +#include <QtGui/qpa/qwindowsysteminterface.h> +#include <QtQuick/private/qquickwindow_p.h> #include <QtQuickTemplates2/private/qquickapplicationwindow_p.h> #include <QtQuickTemplates2/private/qquickoverlay_p.h> +#include <QtQuickTemplates2/private/qquickpopup_p_p.h> #include <QtQuickTemplates2/private/qquickdrawer_p.h> #include <QtQuickTemplates2/private/qquickbutton_p.h> #include <QtQuickTemplates2/private/qquickslider_p.h> @@ -66,6 +70,7 @@ private slots: void dragMargin(); void reposition(); + void header(); void hover_data(); void hover(); @@ -74,6 +79,9 @@ private slots: void wheel(); void multiple(); + + void touch_data(); + void touch(); }; void tst_Drawer::visible_data() @@ -323,27 +331,88 @@ void tst_Drawer::dragMargin() QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDistance, drawer->height() / 2)); } +static QRectF geometry(const QQuickItem *item) +{ + return QRectF(item->x(), item->y(), item->width(), item->height()); +} + void tst_Drawer::reposition() { - QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); + QQuickApplicationHelper helper(this, QStringLiteral("reposition.qml")); QQuickApplicationWindow *window = helper.appWindow; window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowExposed(window)); - QQuickDrawer *drawer = helper.appWindow->property("drawer").value<QQuickDrawer*>(); + QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer*>(); QVERIFY(drawer); - drawer->setEdge(Qt::RightEdge); + QQuickItem *popupItem = drawer->popupItem(); + QVERIFY(popupItem); drawer->open(); - QTRY_COMPARE(drawer->popupItem()->x(), window->width() - drawer->width()); + QQuickItem *dimmer = QQuickPopupPrivate::get(drawer)->dimmer; + QVERIFY(dimmer); + + QCOMPARE(geometry(dimmer), QRectF(0, 0, window->width(), window->height())); + QTRY_COMPARE(geometry(popupItem), QRectF(0, 0, window->width() / 2, window->height())); + + drawer->setY(100); + QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height() - 100)); + QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height() - 100)); + + drawer->setHeight(window->height()); + QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height())); + QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height())); + + drawer->resetHeight(); + QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height() - 100)); + QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height() - 100)); + + drawer->setParentItem(window->contentItem()); + QCOMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150)); + QCOMPARE(geometry(popupItem), QRectF(0, 150, window->width() / 2, window->height() - 150)); + + drawer->setEdge(Qt::RightEdge); + QCOMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150)); + QTRY_COMPARE(geometry(popupItem), QRectF(window->width() - drawer->width(), 150, window->width() / 2, window->height() - 150)); window->setWidth(window->width() + 100); - QTRY_COMPARE(drawer->popupItem()->x(), window->width() - drawer->width()); + QTRY_COMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150)); + QCOMPARE(geometry(popupItem), QRectF(window->width() - drawer->width(), 150, window->width() / 2, window->height() - 150)); drawer->close(); - QTRY_COMPARE(drawer->popupItem()->x(), static_cast<qreal>(window->width())); + QTRY_COMPARE(geometry(popupItem), QRectF(window->width(), 150, window->width() / 2, window->height() - 150)); +} + +void tst_Drawer::header() +{ + QQuickApplicationHelper helper(this, QStringLiteral("header.qml")); + + QQuickApplicationWindow *window = helper.appWindow; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickItem *content = window->contentItem(); + QVERIFY(content); + + QQuickOverlay *overlay = QQuickOverlay::overlay(window); + QVERIFY(overlay); + + QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer*>(); + QVERIFY(drawer); + QQuickItem *popupItem = drawer->popupItem(); + + drawer->open(); + QVERIFY(drawer->isVisible()); + + QCOMPARE(drawer->parentItem(), overlay); + QCOMPARE(drawer->height(), overlay->height()); + QCOMPARE(popupItem->height(), overlay->height()); + + drawer->setParentItem(content); + QCOMPARE(drawer->parentItem(), content); + QCOMPARE(drawer->height(), content->height()); + QCOMPARE(popupItem->height(), content->height()); } void tst_Drawer::hover_data() @@ -615,6 +684,58 @@ void tst_Drawer::multiple() QCOMPARE(leftDrawer->position(), 0.0); } +void tst_Drawer::touch_data() +{ + QTest::addColumn<QString>("source"); + QTest::newRow("Window") << "window.qml"; + QTest::newRow("ApplicationWindow") << "applicationwindow.qml"; +} + +void tst_Drawer::touch() +{ + QFETCH(QString, source); + QQuickApplicationHelper helper(this, source); + + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer*>(); + QVERIFY(drawer); + + struct TouchDeviceDeleter + { + static inline void cleanup(QTouchDevice *device) + { + QWindowSystemInterface::unregisterTouchDevice(device); + delete device; + } + }; + + QScopedPointer<QTouchDevice, TouchDeviceDeleter> device(new QTouchDevice); + device->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(device.data()); + + // drag to open + QTest::touchEvent(window, device.data()).press(0, QPoint(0, 100)); + QTest::touchEvent(window, device.data()).move(0, QPoint(100, 100)); + QTRY_COMPARE(drawer->position(), 0.5); + QTest::touchEvent(window, device.data()).release(0, QPoint(100, 100)); + QTRY_COMPARE(drawer->position(), 1.0); + + // drag to close + QTest::touchEvent(window, device.data()).press(0, QPoint(300, 100)); + QTest::touchEvent(window, device.data()).move(0, QPoint(300 - drawer->dragMargin(), 100)); + for (int x = 300; x > 100; x -= 10) { + QTest::touchEvent(window, device.data()).move(0, QPoint(x, 100)); + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); + } + QTest::touchEvent(window, device.data()).move(0, QPoint(100, 100)); + QTRY_COMPARE(drawer->position(), 0.5); + QTest::touchEvent(window, device.data()).release(0, QPoint(100, 100)); + QTRY_COMPARE(drawer->position(), 0.0); +} + QTEST_MAIN(tst_Drawer) #include "tst_drawer.moc" |