Merge lp:~zsombi/ubuntu-ui-toolkit/focus-handling-on-touch into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri
Status: Merged
Approved by: Zsombor Egri
Approved revision: 1225
Merged at revision: 1211
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/focus-handling-on-touch
Merge into: lp:ubuntu-ui-toolkit/staging
Prerequisite: lp:~zsombi/ubuntu-ui-toolkit/ucfocusscope
Diff against target: 1373 lines (+712/-316)
23 files modified
modules/Ubuntu/Components/AbstractButton.qml (+4/-0)
modules/Ubuntu/Components/OptionSelector.qml (+5/-0)
modules/Ubuntu/Components/PageTreeNode.qml (+3/-0)
modules/Ubuntu/Components/Pickers/DatePicker.qml (+1/-0)
modules/Ubuntu/Components/Pickers/Dialer.qml (+1/-0)
modules/Ubuntu/Components/Pickers/DialerHand.qml (+2/-0)
modules/Ubuntu/Components/Pickers/Picker.qml (+8/-0)
modules/Ubuntu/Components/Pickers/PickerPanel.qml (+5/-0)
modules/Ubuntu/Components/Popups/Dialog.qml (+1/-0)
modules/Ubuntu/Components/Popups/Popover.qml (+1/-0)
modules/Ubuntu/Components/Popups/PopupBase.qml (+31/-1)
modules/Ubuntu/Components/Popups/SheetBase.qml (+1/-0)
modules/Ubuntu/Components/Slider.qml (+4/-0)
modules/Ubuntu/Components/TabBar.qml (+1/-0)
modules/Ubuntu/Components/TextCursor.qml (+5/-2)
modules/Ubuntu/Components/TextField.qml (+3/-0)
modules/Ubuntu/Components/UbuntuListView.qml (+18/-0)
modules/Ubuntu/Components/plugin/ucstyleditembase.cpp (+1/-2)
tests/unit_x11/tst_components/tst_datepicker.qml (+6/-2)
tests/unit_x11/tst_components/tst_focus.qml (+240/-0)
tests/unit_x11/tst_components/tst_tabs.qml (+96/-309)
tests/unit_x11/tst_components/tst_tabs_empty.qml (+41/-0)
tests/unit_x11/tst_components/tst_tabs_with_repeater.qml (+234/-0)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/focus-handling-on-touch
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Cris Dywan Approve
Review via email: mp+231356@code.launchpad.net

Commit message

Focus handling on components.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1214
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/841/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/3772/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/2901/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-amd64-ci/673
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/673
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/673/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-i386-ci/673
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/3638/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5019
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5019/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/11738
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2355/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3185
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3185/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/841/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote :

393 + // note: find Label, Picker's label uses medium font size. The lookup must be changed

Rogue fix sneaked in. Please make a separate MR for this.

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

> 393 + // note: find Label, Picker's label uses medium font size. The
> lookup must be changed
>
> Rogue fix sneaked in. Please make a separate MR for this.

As discussed, the order of children differs when focused. That's why it has been "sniffed" in here.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1216
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/866/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/3883/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/2994/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-amd64-ci/698
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/698
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/698/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-i386-ci/698
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/3740/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5130
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5130/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/11877
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2435/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3278
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3278/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/866/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1217
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/869/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/3889/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/2997/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-amd64-ci/701
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/701
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/701/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-i386-ci/701
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/3743/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5136
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5136/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/11882
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2439/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3282
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3282/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/869/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

tim@trusty:~/dev/ubuntu-ui-toolkit/r/focus-handling-on-touch/tests/unit_x11/tst_components$ qmltestrunner -input tst_tabs_empty.qml -import ../../../modules/
unity::action::ActionManager::ActionManager(QObject*):
 Could not determine application identifier. HUD will not work properly.
 Provide your application identifier in $APP_ID environment variable.
file:///home/tim/dev/ubuntu-ui-toolkit/r/focus-handling-on-touch/tests/unit_x11/tst_components/tst_tabs_empty.qml:24: ReferenceError: uniys is not defined
Test '"/home/tim/dev/ubuntu-ui-toolkit/r/focus-handling-on-touch/tests/unit_x11/tst_components/tst_tabs_empty.qml"' has invalid size QSize(400, 0) , resizing.
file:///home/tim/dev/ubuntu-ui-toolkit/r/focus-handling-on-touch/tests/unit_x11/tst_components/tst_tabs_empty.qml:24: ReferenceError: uniys is not defined
********* Start testing of qmltestrunner *********
Config: Using QtTest library 5.3.0, Qt 5.3.0
PASS : qmltestrunner::EmptyTabs::initTestCase()
PASS : qmltestrunner::EmptyTabs::test_emptyTabs()
PASS : qmltestrunner::EmptyTabs::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped
********* Finished testing of qmltestrunner *********
tim@trusty:~/dev/ubuntu-ui-toolkit/r/focus-handling-on-touch/tests/unit_x11/tst_components$

Revision history for this message
Tim Peeters (tpeeters) wrote :

^is just a typo in the code:

    height: uniys.gu(100)

Revision history for this message
Tim Peeters (tpeeters) wrote :

this program http://paste.ubuntu.com/8105902/ now gives a warning:

file:///home/tim/dev/ubuntu-ui-toolkit/m/07-tabsUnitTests/modules/Ubuntu/Components/Popups/PopupBase.qml:178: ReferenceError: window is not defined

it did not do that before. Please add a guard to check that window is valid.

Revision history for this message
Tim Peeters (tpeeters) wrote :

        function saveActiveFocus() {
            if (typeof window != 'undefined') prevFocus = window.activeFocusItem;
        }

fixes the issue in the previous comment

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1219
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/871/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/3902/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/3005/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-amd64-ci/703
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/703
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/703/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-i386-ci/703
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/3757/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5149
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5149/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/11900
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2446/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3290
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3290/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/871/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1221
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/874/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/3921/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/3016/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-amd64-ci/706
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/706
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-armhf-ci/706/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-utopic-i386-ci/706
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/3774/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5168
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5168/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/11919
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2457/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3301
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3301/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/874/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote :

214 + if (typeof window != "undefined") {

This is still racy, it won't set if the window is available later. And it's the third instance where this came up and wasn't handled correctly on the first attempt.

We need something like Context.onWindowAvailable and fix this consistently.

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

> 214 + if (typeof window != "undefined") {
>
> This is still racy, it won't set if the window is available later. And it's
> the third instance where this came up and wasn't handled correctly on the
> first attempt.
>
> We need something like Context.onWindowAvailable and fix this consistently.

I'll do a local fix with a property in between, and we can return to it in a separate MR with a proper fix.

Revision history for this message
Cris Dywan (kalikiana) wrote :

> > 214 + if (typeof window != "undefined") {
> >
> > This is still racy, it won't set if the window is available later. And it's
> > the third instance where this came up and wasn't handled correctly on the
> > first attempt.
> >
> > We need something like Context.onWindowAvailable and fix this consistently.
>
> I'll do a local fix with a property in between, and we can return to it in a
> separate MR with a proper fix.

Ack.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'modules/Ubuntu/Components/AbstractButton.qml'
--- modules/Ubuntu/Components/AbstractButton.qml 2014-06-23 09:53:09 +0000
+++ modules/Ubuntu/Components/AbstractButton.qml 2014-08-22 17:18:58 +0000
@@ -16,6 +16,7 @@
1616
17import QtQuick 2.017import QtQuick 2.0
18import QtFeedback 5.018import QtFeedback 5.0
19import Ubuntu.Components 1.1
1920
20/*!21/*!
21 \qmlabstract AbstractButton22 \qmlabstract AbstractButton
@@ -82,6 +83,8 @@
82 */83 */
83 property alias __mouseArea: mouseArea84 property alias __mouseArea: mouseArea
8485
86 activeFocusOnPress: true
87
85 HapticsEffect {88 HapticsEffect {
86 id: pressEffect89 id: pressEffect
87 attackIntensity: 0.090 attackIntensity: 0.0
@@ -100,6 +103,7 @@
100 hoverEnabled: true103 hoverEnabled: true
101104
102 onClicked: {105 onClicked: {
106 button.requestFocus();
103 if (button.__acceptEvents) {107 if (button.__acceptEvents) {
104 pressEffect.start()108 pressEffect.start()
105 button.clicked()109 button.clicked()
106110
=== modified file 'modules/Ubuntu/Components/OptionSelector.qml'
--- modules/Ubuntu/Components/OptionSelector.qml 2014-04-23 08:50:20 +0000
+++ modules/Ubuntu/Components/OptionSelector.qml 2014-08-22 17:18:58 +0000
@@ -220,6 +220,7 @@
220 StyledItem {220 StyledItem {
221 id: listContainer221 id: listContainer
222 objectName: "listContainer"222 objectName: "listContainer"
223 activeFocusOnPress: true
223224
224 readonly property url chevron: __styleInstance.chevron225 readonly property url chevron: __styleInstance.chevron
225 readonly property url tick: __styleInstance.tick226 readonly property url tick: __styleInstance.tick
@@ -279,6 +280,10 @@
279 property real itemHeight280 property real itemHeight
280 signal delegateClicked(int index)281 signal delegateClicked(int index)
281282
283 onMovementStarted: optionSelector.requestFocus(Qt.MouseFocusReason)
284 onFlickStarted: optionSelector.requestFocus(Qt.MouseFocusReason)
285 Toolkit.Mouse.onClicked: optionSelector.requestFocus(Qt.MouseFocusReason)
286
282 onDelegateClicked: optionSelector.delegateClicked(index);287 onDelegateClicked: optionSelector.delegateClicked(index);
283 interactive: listContainer.height !== list.contentHeight && listContainer.currentlyExpanded ? true : false288 interactive: listContainer.height !== list.contentHeight && listContainer.currentlyExpanded ? true : false
284 clip: true289 clip: true
285290
=== modified file 'modules/Ubuntu/Components/PageTreeNode.qml'
--- modules/Ubuntu/Components/PageTreeNode.qml 2014-06-19 12:45:37 +0000
+++ modules/Ubuntu/Components/PageTreeNode.qml 2014-08-22 17:18:58 +0000
@@ -100,6 +100,9 @@
100 */100 */
101 property bool isLeaf: false101 property bool isLeaf: false
102102
103 // turn on focusing
104 activeFocusOnPress: true
105
103 Binding {106 Binding {
104 target: node.parentNode107 target: node.parentNode
105 property: "activeLeafNode"108 property: "activeLeafNode"
106109
=== modified file 'modules/Ubuntu/Components/Pickers/DatePicker.qml'
--- modules/Ubuntu/Components/Pickers/DatePicker.qml 2014-07-18 20:12:42 +0000
+++ modules/Ubuntu/Components/Pickers/DatePicker.qml 2014-08-22 17:18:58 +0000
@@ -302,6 +302,7 @@
302302
303 implicitWidth: units.gu(36)303 implicitWidth: units.gu(36)
304 implicitHeight: units.gu(20)304 implicitHeight: units.gu(20)
305 activeFocusOnPress: true
305306
306 /*! \internal */307 /*! \internal */
307 onMinimumChanged: {308 onMinimumChanged: {
308309
=== modified file 'modules/Ubuntu/Components/Pickers/Dialer.qml'
--- modules/Ubuntu/Components/Pickers/Dialer.qml 2014-04-25 12:53:58 +0000
+++ modules/Ubuntu/Components/Pickers/Dialer.qml 2014-08-22 17:18:58 +0000
@@ -142,6 +142,7 @@
142 id: dialer142 id: dialer
143 implicitWidth: size143 implicitWidth: size
144 implicitHeight: size144 implicitHeight: size
145 activeFocusOnPress: true
145146
146 style: Theme.createStyleComponent("DialerStyle.qml", dialer)147 style: Theme.createStyleComponent("DialerStyle.qml", dialer)
147148
148149
=== modified file 'modules/Ubuntu/Components/Pickers/DialerHand.qml'
--- modules/Ubuntu/Components/Pickers/DialerHand.qml 2014-04-25 12:53:58 +0000
+++ modules/Ubuntu/Components/Pickers/DialerHand.qml 2014-08-22 17:18:58 +0000
@@ -141,6 +141,7 @@
141 anchors.centerIn: parent141 anchors.centerIn: parent
142 width: parent.width142 width: parent.width
143 height: parent.height143 height: parent.height
144 activeFocusOnPress: true
144 style: Theme.createStyleComponent("DialerHandStyle.qml", dialerHand)145 style: Theme.createStyleComponent("DialerHandStyle.qml", dialerHand)
145146
146 /*! \internal */147 /*! \internal */
@@ -193,6 +194,7 @@
193 onPositionChanged: {194 onPositionChanged: {
194 if (internalChange) return;195 if (internalChange) return;
195 internalChange = true;196 internalChange = true;
197 dialerHand.requestFocus(Qt.MouseFocusReason);
196 var point = mapToItem (dialerHand, mouse.x, mouse.y);198 var point = mapToItem (dialerHand, mouse.x, mouse.y);
197 var diffX = (point.x - centerX);199 var diffX = (point.x - centerX);
198 var diffY = -1 * (point.y - centerY);200 var diffY = -1 * (point.y - centerY);
199201
=== modified file 'modules/Ubuntu/Components/Pickers/Picker.qml'
--- modules/Ubuntu/Components/Pickers/Picker.qml 2014-04-23 08:50:20 +0000
+++ modules/Ubuntu/Components/Pickers/Picker.qml 2014-08-22 17:18:58 +0000
@@ -157,6 +157,7 @@
157157
158 implicitWidth: units.gu(8)158 implicitWidth: units.gu(8)
159 implicitHeight: units.gu(20)159 implicitHeight: units.gu(20)
160 activeFocusOnPress: true
160161
161 style: Theme.createStyleComponent("PickerStyle.qml", picker)162 style: Theme.createStyleComponent("PickerStyle.qml", picker)
162163
@@ -251,6 +252,9 @@
251 picker.selectedIndex = 0;252 picker.selectedIndex = 0;
252 }253 }
253 }254 }
255 // focus handling
256 onMoveStarted: picker.requestFocus(Qt.MouseFocusReason)
257 onFlickStarted: picker.requestFocus(Qt.MouseFocusReason)
254 }258 }
255259
256 function moveToIndex(toIndex) {260 function moveToIndex(toIndex) {
@@ -289,6 +293,8 @@
289 }293 }
290 width: parent ? parent.width : 0294 width: parent ? parent.width : 0
291 clip: true295 clip: true
296 focus: true
297 Mouse.onClicked: picker.requestFocus()
292298
293 model: picker.model299 model: picker.model
294 delegate: picker.delegate300 delegate: picker.delegate
@@ -348,6 +354,8 @@
348 }354 }
349 width: parent ? parent.width : 0355 width: parent ? parent.width : 0
350 clip: true356 clip: true
357 focus: true
358 Mouse.onClicked: picker.requestFocus(Qt.MouseFocusReason)
351359
352 model: picker.model360 model: picker.model
353 delegate: picker.delegate361 delegate: picker.delegate
354362
=== modified file 'modules/Ubuntu/Components/Pickers/PickerPanel.qml'
--- modules/Ubuntu/Components/Pickers/PickerPanel.qml 2014-04-23 08:50:20 +0000
+++ modules/Ubuntu/Components/Pickers/PickerPanel.qml 2014-08-22 17:18:58 +0000
@@ -118,6 +118,11 @@
118 "callerProperty": property118 "callerProperty": property
119 }119 }
120120
121 // hide OSK if eventually open
122 if (Qt.inputMethod.visible) {
123 Qt.inputMethod.hide();
124 }
125
121 if (!internal.isPhone) {126 if (!internal.isPhone) {
122 // we have no input panel defined, or the therefore we show the picker in a Popover127 // we have no input panel defined, or the therefore we show the picker in a Popover
123 return internal.openPopover(caller, params);128 return internal.openPopover(caller, params);
124129
=== modified file 'modules/Ubuntu/Components/Popups/Dialog.qml'
--- modules/Ubuntu/Components/Popups/Dialog.qml 2014-07-25 12:55:27 +0000
+++ modules/Ubuntu/Components/Popups/Dialog.qml 2014-08-22 17:18:58 +0000
@@ -157,6 +157,7 @@
157157
158 StyledItem {158 StyledItem {
159 id: foreground159 id: foreground
160 activeFocusOnPress: true
160 width: Math.min(minimumWidth, dialog.width)161 width: Math.min(minimumWidth, dialog.width)
161 anchors.centerIn: parent162 anchors.centerIn: parent
162163
163164
=== modified file 'modules/Ubuntu/Components/Popups/Popover.qml'
--- modules/Ubuntu/Components/Popups/Popover.qml 2014-07-16 06:31:50 +0000
+++ modules/Ubuntu/Components/Popups/Popover.qml 2014-08-22 17:18:58 +0000
@@ -204,6 +204,7 @@
204204
205 StyledItem {205 StyledItem {
206 id: foreground206 id: foreground
207 activeFocusOnPress: true
207 objectName: "popover_foreground"208 objectName: "popover_foreground"
208209
209 //styling properties210 //styling properties
210211
=== modified file 'modules/Ubuntu/Components/Popups/PopupBase.qml'
--- modules/Ubuntu/Components/Popups/PopupBase.qml 2014-07-10 11:50:13 +0000
+++ modules/Ubuntu/Components/Popups/PopupBase.qml 2014-08-22 17:18:58 +0000
@@ -163,12 +163,41 @@
163 onVisibleChanged: stateWrapper.state = (visible) ? 'opened' : 'closed'163 onVisibleChanged: stateWrapper.state = (visible) ? 'opened' : 'closed'
164 /*! \internal */164 /*! \internal */
165 onParentChanged: stateWrapper.rootItem = QuickUtils.rootItem(popupBase)165 onParentChanged: stateWrapper.rootItem = QuickUtils.rootItem(popupBase)
166 Component.onCompleted: stateWrapper.rootItem = QuickUtils.rootItem(popupBase);166 Component.onCompleted: {
167 stateWrapper.saveActiveFocus();
168 stateWrapper.rootItem = QuickUtils.rootItem(popupBase);
169 }
167170
168 Item {171 Item {
169 id: stateWrapper172 id: stateWrapper
170 property Item rootItem: QuickUtils.rootItem(popupBase)173 property Item rootItem: QuickUtils.rootItem(popupBase)
171174
175 property bool windowIsValid: typeof window != "undefined"
176 property Item prevFocus
177
178 function saveActiveFocus() {
179 // 'window' context property is exposed to QML after component completion
180 // before rendering is complete, therefore a simple 'if (window)' check is
181 // not enough.
182 if (windowIsValid) {
183 prevFocus = window.activeFocusItem;
184 windowIsValidChanged.disconnect(saveActiveFocus);
185 } else {
186 // connect the function so we can save the original focus item
187 windowIsValidChanged.connect(saveActiveFocus);
188 }
189 }
190
191 function restoreActiveFocus() {
192 if (prevFocus) {
193 if (prevFocus.hasOwnProperty("requestFocus")) {
194 prevFocus.requestFocus(Qt.PopupFocusReason);
195 } else {
196 prevFocus.forceActiveFocus(Qt.PopupFocusReason);
197 }
198 }
199 }
200
172 states: [201 states: [
173 State {202 State {
174 name: 'closed'203 name: 'closed'
@@ -211,6 +240,7 @@
211 ScriptAction {240 ScriptAction {
212 script: {241 script: {
213 popupBase.visible = false;242 popupBase.visible = false;
243 stateWrapper.restoreActiveFocus();
214 }244 }
215 }245 }
216 }246 }
217247
=== modified file 'modules/Ubuntu/Components/Popups/SheetBase.qml'
--- modules/Ubuntu/Components/Popups/SheetBase.qml 2014-06-03 13:37:22 +0000
+++ modules/Ubuntu/Components/Popups/SheetBase.qml 2014-08-22 17:18:58 +0000
@@ -86,6 +86,7 @@
8686
87 StyledItem {87 StyledItem {
88 id: foreground88 id: foreground
89 activeFocusOnPress: true
8990
90 property string title91 property string title
91 property real contentsWidth: units.gu(64)92 property real contentsWidth: units.gu(64)
9293
=== modified file 'modules/Ubuntu/Components/Slider.qml'
--- modules/Ubuntu/Components/Slider.qml 2014-08-19 05:21:56 +0000
+++ modules/Ubuntu/Components/Slider.qml 2014-08-22 17:18:58 +0000
@@ -119,6 +119,8 @@
119 /*! \internal */119 /*! \internal */
120 onValueChanged: mouseArea.liveValue = slider.value120 onValueChanged: mouseArea.liveValue = slider.value
121121
122 activeFocusOnPress: true
123
122 Binding {124 Binding {
123 target: slider125 target: slider
124 property: "value"126 property: "value"
@@ -208,6 +210,8 @@
208 var normalizedOffsetX = (mouseArea.mouseX - dragInitMouseX) / barMinusThumb;210 var normalizedOffsetX = (mouseArea.mouseX - dragInitMouseX) / barMinusThumb;
209 liveValue = valueFromNormalizedValue(dragInitNormalizedValue + normalizedOffsetX);211 liveValue = valueFromNormalizedValue(dragInitNormalizedValue + normalizedOffsetX);
210 }212 }
213 onClicked: slider.requestFocus(Qt.MouseFocusReason)
214 onLiveValueChanged: if (isPressed) slider.requestFocus(Qt.MouseFocusReason)
211 }215 }
212216
213 style: Theme.createStyleComponent("SliderStyle.qml", slider)217 style: Theme.createStyleComponent("SliderStyle.qml", slider)
214218
=== modified file 'modules/Ubuntu/Components/TabBar.qml'
--- modules/Ubuntu/Components/TabBar.qml 2014-05-09 11:22:07 +0000
+++ modules/Ubuntu/Components/TabBar.qml 2014-08-22 17:18:58 +0000
@@ -95,6 +95,7 @@
95 property bool animate: true95 property bool animate: true
9696
97 implicitHeight: units.gu(7.5)97 implicitHeight: units.gu(7.5)
98 activeFocusOnPress: true
9899
99 style: Theme.createStyleComponent("TabBarStyle.qml", tabBar)100 style: Theme.createStyleComponent("TabBarStyle.qml", tabBar)
100101
101102
=== modified file 'modules/Ubuntu/Components/TextCursor.qml'
--- modules/Ubuntu/Components/TextCursor.qml 2014-06-13 08:57:48 +0000
+++ modules/Ubuntu/Components/TextCursor.qml 2014-08-22 17:18:58 +0000
@@ -72,18 +72,21 @@
72 }72 }
73 // open context menu only for cursorPosition or selectionEnd73 // open context menu only for cursorPosition or selectionEnd
74 if (positionProperty !== "selectionStart") {74 if (positionProperty !== "selectionStart") {
75 var popup;
75 if (handler.main.popover === undefined) {76 if (handler.main.popover === undefined) {
76 // open the default one77 // open the default one
77 PopupUtils.open(Qt.resolvedUrl("TextInputPopover.qml"), cursorItem,78 popup = PopupUtils.open(Qt.resolvedUrl("TextInputPopover.qml"), cursorItem,
78 {79 {
79 "target": handler.main80 "target": handler.main
80 })81 })
81 } else {82 } else {
82 PopupUtils.open(handler.main.popover, cursorItem,83 popup = PopupUtils.open(handler.main.popover, cursorItem,
83 {84 {
84 "target": handler.main85 "target": handler.main
85 })86 })
86 }87 }
88 // do not grab focus!
89 popup.__foreground.activeFocusOnPress = false;
87 }90 }
88 }91 }
8992
9093
=== modified file 'modules/Ubuntu/Components/TextField.qml'
--- modules/Ubuntu/Components/TextField.qml 2014-08-08 07:28:49 +0000
+++ modules/Ubuntu/Components/TextField.qml 2014-08-22 17:18:58 +0000
@@ -818,6 +818,7 @@
818 // internals818 // internals
819819
820 opacity: enabled ? 1.0 : 0.3820 opacity: enabled ? 1.0 : 0.3
821 activeFocusOnPress: true
821822
822 /*! \internal */823 /*! \internal */
823 onVisibleChanged: {824 onVisibleChanged: {
@@ -904,6 +905,8 @@
904 AbstractButton {905 AbstractButton {
905 id: clearButton906 id: clearButton
906 objectName: "clear_button"907 objectName: "clear_button"
908 activeFocusOnPress: false
909
907 anchors {910 anchors {
908 top: parent.top911 top: parent.top
909 right: rightPane.left912 right: rightPane.left
910913
=== modified file 'modules/Ubuntu/Components/UbuntuListView.qml'
--- modules/Ubuntu/Components/UbuntuListView.qml 2014-04-23 08:50:20 +0000
+++ modules/Ubuntu/Components/UbuntuListView.qml 2014-08-22 17:18:58 +0000
@@ -92,8 +92,26 @@
92 }92 }
93 animation.start();93 animation.start();
94 }94 }
95
96 function requestFocus(reason) {
97 // lookup for the currentItem, and if it is a FocusScope, focus the view
98 // this will also focus the currentItem
99 if (root.currentItem && root.currentItem.hasOwnProperty("activeFocusOnPress")) {
100 root.forceActiveFocus(reason);
101 }
102 }
95 }103 }
96104
105 focus: true
106
107 /*!
108 \internal
109 Grab focus when moved, flicked or clicked
110 */
111 onMovementStarted: priv.requestFocus(Qt.MouseFocusReason)
112 onFlickStarted: priv.requestFocus(Qt.MouseFocusReason)
113 Toolkit.Mouse.onClicked: priv.requestFocus(Qt.MouseFocusReason)
114
97 /*!115 /*!
98 \preliminary116 \preliminary
99 Expand the item at the given index.117 Expand the item at the given index.
100118
=== modified file 'modules/Ubuntu/Components/plugin/ucstyleditembase.cpp'
--- modules/Ubuntu/Components/plugin/ucstyleditembase.cpp 2014-08-21 11:58:33 +0000
+++ modules/Ubuntu/Components/plugin/ucstyleditembase.cpp 2014-08-22 17:18:58 +0000
@@ -224,8 +224,7 @@
224{224{
225 QQuickItem::mousePressEvent(event);225 QQuickItem::mousePressEvent(event);
226 Q_D(UCStyledItemBase);226 Q_D(UCStyledItemBase);
227 // accept the event if the focus was possible.227 requestFocus(Qt::MouseFocusReason);
228 event->setAccepted(requestFocus(Qt::MouseFocusReason));
229}228}
230229
231#include "moc_ucstyleditembase.cpp"230#include "moc_ucstyleditembase.cpp"
232231
=== modified file 'tests/unit_x11/tst_components/tst_datepicker.qml'
--- tests/unit_x11/tst_components/tst_datepicker.qml 2014-05-09 18:06:06 +0000
+++ tests/unit_x11/tst_components/tst_datepicker.qml 2014-08-22 17:18:58 +0000
@@ -59,7 +59,11 @@
59 function getPickerLabel(picker, name) {59 function getPickerLabel(picker, name) {
60 var pickerItem = findChild(picker, name);60 var pickerItem = findChild(picker, name);
61 var pickerCurrent = findChild(pickerItem, "Picker_ViewLoader");61 var pickerCurrent = findChild(pickerItem, "Picker_ViewLoader");
62 return pickerCurrent.item.currentItem.children[2];62 // note: find Label, Picker's label uses medium font size. The lookup must be changed
63 // if the fontSize is changed!
64 var pickerLabel = findChildWithProperty(pickerCurrent.item.currentItem, "fontSize", "medium");
65 verify(pickerLabel, ("Label of %1 not accessible").arg(name));
66 return pickerLabel;
63 }67 }
64 function getPickerModel(picker, name) {68 function getPickerModel(picker, name) {
65 var pickerItem = findInvisibleChild(picker, name);69 var pickerItem = findInvisibleChild(picker, name);
@@ -318,7 +322,7 @@
318 picker.mode = "Years|Months|Days";322 picker.mode = "Years|Months|Days";
319 waitPickerMoving();323 waitPickerMoving();
320324
321 var yearLabel = getPickerLabel(picker, "PickerRow_YearPicker");325 var yearLabel = getPickerLabel(picker, "PickerRow_YearPicker", date.getFullYear());
322 var monthLabel = getPickerLabel(picker, "PickerRow_MonthPicker");326 var monthLabel = getPickerLabel(picker, "PickerRow_MonthPicker");
323 var monthModel = getPickerModel(picker, "PickerRow_MonthPicker");327 var monthModel = getPickerModel(picker, "PickerRow_MonthPicker");
324 var dayLabel = getPickerLabel(picker, "PickerRow_DayPicker");328 var dayLabel = getPickerLabel(picker, "PickerRow_DayPicker");
325329
=== added file 'tests/unit_x11/tst_components/tst_focus.qml'
--- tests/unit_x11/tst_components/tst_focus.qml 1970-01-01 00:00:00 +0000
+++ tests/unit_x11/tst_components/tst_focus.qml 2014-08-22 17:18:58 +0000
@@ -0,0 +1,240 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.2
18import QtTest 1.0
19import Ubuntu.Test 1.0
20import Ubuntu.Components 1.1
21import Ubuntu.Components.Pickers 1.0
22import Ubuntu.Components.ListItems 1.0 as ListItem
23import Ubuntu.Components.Popups 1.0
24
25Item {
26 id: main
27 width: units.gu(50); height: units.gu(100)
28
29 property bool hasOSK: QuickUtils.inputMethodProvider !== ""
30
31 Flow {
32 anchors {
33 fill: parent
34 margins: units.gu(0.5)
35 }
36 spacing: units.gu(1)
37
38 UbuntuListView {
39 id: listView
40 width: units.gu(50)
41 height: units.gu(20)
42 clip: true
43 model: 10
44 delegate: ListItem.Standard {
45 text: "Whatever"
46 }
47 }
48 TextField {
49 id: textField
50 text: "This is a text field with some text handling focus"
51 }
52 TextArea {
53 id: textArea
54 text: "This is a text area with some text handling focus"
55 }
56 Button {
57 id: button
58 text: "Press me"
59 }
60 CheckBox {
61 id: checkbox
62 text: "Check me"
63 }
64 Switch {
65 id: switchBox
66 }
67 Picker {
68 id: picker
69 model: 10
70 }
71 Picker {
72 id: roundPicker
73 model: 10
74 circular: true
75 }
76 Button {
77 id: pickerPanel
78 property date date: new Date()
79 text: Qt.formatDateTime(date, "yyyy/MMMM")
80 onClicked: PickerPanel.openDatePicker(pickerPanel, "date", "Years|Months")
81 }
82 Slider {
83 id: slider
84 }
85 ComboButton {
86 id: comboButton
87 Rectangle {
88 height: comboButton.comboListHeight
89 color: "blue"
90 }
91 }
92 Button {
93 id: popoverTest
94 text: "Popovers"
95 property Item popover
96 property Component popoverComponent
97 onClicked: {
98 popover = PopupUtils.open(popoverComponent)
99 }
100 }
101 }
102
103 Component {
104 id: dialogComponent
105 Dialog {
106 id: dialog
107 title: "TestDialog"
108 Button {
109 text: "close"
110 onClicked: PopupUtils.close(dialog)
111 }
112 }
113 }
114
115 Component {
116 id: popoverComponent
117 Popover {
118 id: popover
119 contentWidth: units.gu(20)
120 contentHeight: item.height
121 ListItem.Standard {
122 id: item
123 text: "close"
124 onClicked: PopupUtils.close(popover)
125 }
126 }
127 }
128
129 UbuntuTestCase {
130 name: "FocusingTests"
131 when: windowShown
132
133 SignalSpy {
134 id: popupCloseSpy
135 signalName: "onDestruction"
136 }
137
138 function initTestCase() {
139 textField.forceActiveFocus();
140 }
141 function cleanup() {
142 // empty event buffer
143 wait(200);
144 popupCloseSpy.clear();
145 popupCloseSpy.target = null;
146 }
147
148 // make this as the very first test executed
149 function test_0_transfer_focus_data() {
150 return [
151 {tag: "TextArea", previousFocused: textField, focusOn: textArea, clickToDismiss: false},
152 {tag: "Button", previousFocused: textArea, focusOn: button, clickToDismiss: false},
153 {tag: "Checkbox", previousFocused: button, focusOn: checkbox, clickToDismiss: false},
154 {tag: "Switch", previousFocused: checkbox, focusOn: switchBox, clickToDismiss: false},
155 {tag: "Picker - linear", previousFocused: switchBox, focusOn: picker, clickToDismiss: false},
156 {tag: "Picker - circular", previousFocused: picker, focusOn: roundPicker, clickToDismiss: false},
157 {tag: "PickerPanel", previousFocused: roundPicker, focusOn: pickerPanel, clickToDismiss: true},
158 {tag: "UbuntuListView", previousFocused: pickerPanel, focusOn: listView, clickToDismiss: false},
159 {tag: "Slider", previousFocused: listView, focusOn: slider, clickToDismiss: false},
160 {tag: "ComboButton", previousFocused: slider, focusOn: comboButton, clickToDismiss: false},
161 ];
162 }
163 function test_0_transfer_focus(data) {
164 // perform mouse press on
165 mouseClick(data.focusOn, centerOf(data.focusOn).x, centerOf(data.focusOn).y);
166 compare(data.previousFocused.focus, false, "Previous focus is still set!");
167 compare(data.focusOn.focus, true, data.tag + " is not focused!");
168 if (data.clickToDismiss) {
169 mouseClick(main, 0, 0);
170 }
171 waitForRendering(data.focusOn, 200);
172 }
173
174 function test_hide_osk_when_pickerpanel_opens() {
175 if (!main.hasOSK) {
176 skip("This functionality can be tested with OSK only.");
177 }
178
179 textField.forceActiveFocus();
180 verify(textField.focus, "TextField is not focused");
181 verify(Qt.inputMethod.visible, "OSK is hidden");
182 mouseClick(pickerPanel, centerOf(pickerPanel).x, centerOf(pickerPanel).y);
183 verify(!Qt.inputMethod.visible, "OSK is visible still!");
184 // remove panel/popover
185 mouseClick(main, 0, 0);
186 }
187
188 function test_textfield_clear_button_keeps_focus() {
189 textField.forceActiveFocus();
190 var text = textField.text;
191
192 var clearButton = findChild(textField, "clear_button");
193 verify(clearButton, "clearButton is not accessible!");
194 var center = centerOf(clearButton);
195 mouseClick(clearButton, center.x, center.y);
196 compare(textField.text, "", "Text had not been cleared?");
197 compare(textField.focus, true, "Focus had been stolen from text input");
198
199 //restore
200 textField.text = text;
201 }
202
203 function test_combo_button_dropdown_focuses_component() {
204 textField.forceActiveFocus();
205 var dropdownButton = findChild(comboButton, "combobutton_dropdown");
206 verify(dropdownButton, "dropdown button is not accessible?");
207
208 var center = centerOf(dropdownButton);
209 mouseClick(dropdownButton, center.x, center.y);
210 waitForRendering(comboButton);
211 compare(dropdownButton.focus, true, "Dropdown button hasn't got focused!");
212 compare(comboButton.focus, true, "ComboButton hasn't been focused!");
213 comboButton.expanded = false;
214 }
215
216 function test_popover_refocus_data() {
217 return [
218 {tag: "Dialog", component: dialogComponent},
219 {tag: "Popover", component: popoverComponent},
220 ];
221 }
222
223 function test_popover_refocus(data) {
224 popoverTest.popoverComponent = data.component;
225 var center = centerOf(popoverTest);
226 mouseClick(popoverTest, center.x, center.y);
227 verify(popoverTest.focus, "Button focus not gained.");
228 waitForRendering(popoverTest.popover);
229 popupCloseSpy.target = popoverTest.popover.Component;
230
231 var closeButton = findChildWithProperty(popoverTest.popover, "text", "close");
232 verify(closeButton, "Close button not accessible");
233 center = centerOf(closeButton);
234 mouseClick(closeButton, center.x, center.y);
235 verify(!popoverTest.focus, "Button focus not lost.");
236 popupCloseSpy.wait();
237 verify(popoverTest.focus, "Button focus not restored.");
238 }
239 }
240}
0241
=== modified file 'tests/unit_x11/tst_components/tst_tabs.qml'
--- tests/unit_x11/tst_components/tst_tabs.qml 2014-04-25 05:28:37 +0000
+++ tests/unit_x11/tst_components/tst_tabs.qml 2014-08-22 17:18:58 +0000
@@ -16,323 +16,108 @@
1616
17import QtQuick 2.017import QtQuick 2.0
18import QtTest 1.018import QtTest 1.0
19import Ubuntu.Test 1.0
19import Ubuntu.Components 1.120import Ubuntu.Components 1.1
2021
21Item {22MainView {
22 id: testCase23 id: mainView
23 width: units.gu(50)24 width: units.gu(50)
24 height: units.gu(80)25 height: units.gu(80)
2526
26 Tabs {27 Tabs {
27 id: emptyTabs28 id: tabs
28 }29 Tab {
2930 id: tab1
30 MainView {31 title: "tab 1"
31 id: mainView32 page: Page {
32 anchors.fill: parent33 id: page1
33 Tabs {34 Button {
34 id: tabs35 id: button
35 Tab {36 anchors.centerIn: parent
36 id: tab137 text: "click"
37 title: "tab 1"38 }
38 page: Page {39 }
39 id: page140 }
40 Button {41 Tab {
41 id: button42 id: tab2
42 anchors.centerIn: parent43 title: "tab 2"
43 text: "click"44 page: Page {
44 }45 id: page2
45 }46 }
46 }47 }
47 Tab {48 Tab {
48 id: tab249 id: tab3
49 title: "tab 2"50 title: "tab 3"
50 page: Page {51 page: Page {
51 id: page252 id: page3
52 }53 }
53 }54 }
54 Tab {55 Tab {
55 id: tab356 id: tabFlick1
56 title: "tab 3"57 title: "flick"
57 page: Page {58 page: Page {
58 id: page359 Flickable {
59 }60 id: flickable1
60 }61 anchors.fill: parent
61 Tab {62 }
62 id: tabFlick163 }
63 title: "flick"64 }
64 page: Page {65 Tab {
65 Flickable {66 id: tabFlick2
66 id: flickable167 title: "flick 2"
67 anchors.fill: parent68 page: Page {
68 }69 Flickable {
69 }70 id: flickable2
70 }71 anchors.fill: parent
71 Tab {72 }
72 id: tabFlick273 }
73 title: "flick 2"74 }
74 page: Page {75 Tab {
75 Flickable {76 id: tabFlickLoader
76 id: flickable277 title: "load"
77 anchors.fill: parent78 page: Loader {
78 }79 id: loader
79 }80 sourceComponent: tabs.selectedTabIndex != 5 ? null : pageComponent
80 }81 }
81 Tab {82 }
82 id: tabFlickLoader83 Tab {
83 title: "load"84 id: tabNoFlickLoader
84 page: Loader {85 title: "loadNoFlick"
85 id: loader86 page: Loader {
86 sourceComponent: tabs.selectedTabIndex != 5 ? null : pageComponent87 id: loaderNoFlick
87 }88 anchors {
88 }89 left: parent.left
89 Tab {90 right: parent.right
90 id: tabNoFlickLoader91 bottom: parent.bottom
91 title: "loadNoFlick"92 }
92 page: Loader {93 // height compes from the loaded Page
93 id: loaderNoFlick94 sourceComponent: tabs.selectedTabIndex === 6 ? pageComponentNoFlick : null
94 anchors {95 }
95 left: parent.left96 }
96 right: parent.right97 }
97 bottom: parent.bottom98 Component {
98 }99 id: pageComponent
99 // height compes from the loaded Page100 Page {
100 sourceComponent: tabs.selectedTabIndex === 6 ? pageComponentNoFlick : null101 title: "Loaded page"
101 }102 property Flickable flick: loadedFlickable
102 }103 Flickable {
103 }104 id: loadedFlickable
104 Component {105 anchors.fill: parent
105 id: pageComponent106 contentHeight: 1000
106 Page {107 }
107 title: "Loaded page"108 }
108 property Flickable flick: loadedFlickable109 }
109 Flickable {110 Component {
110 id: loadedFlickable111 id: pageComponentNoFlick
111 anchors.fill: parent112 Page {
112 contentHeight: 1000113 title: "Loaded page without flickable"
113 }114 }
114 }115 }
115 }116
116 Component {117 UbuntuTestCase {
117 id: pageComponentNoFlick
118 Page {
119 title: "Loaded page without flickable"
120 }
121 }
122 }
123
124 ListModel {
125 id: inputModel
126 Component.onCompleted: {
127 append({ "name": "tab 1" });
128 insert(0, { "name": "tab 0" });
129 append({ "name": "tab 3" });
130 insert(2, { "name": "tab 2" });
131 }
132 }
133
134 Tabs {
135 id: tabsWithRepeater
136 Repeater {
137 objectName: "tabsRepeater"
138 id: tabsRepeater
139 model: inputModel
140 Tab {
141 title: name
142 }
143 }
144 }
145
146 Tabs {
147 id: repeaterTabs
148
149 Repeater {
150 objectName: "repeater"
151 id: repeater
152 Tab {
153 title: modelData
154 }
155 }
156 }
157
158 Tabs {
159 id: twoRepeaters
160
161 Repeater {
162 objectName: "firstRepeater"
163 id: firstRepeater
164 model: inputModel
165 Tab {
166 title: name
167 }
168 }
169
170 Repeater {
171 objectName: "secondRepeater"
172 id: secondRepeater
173 model: testCase.listModel
174 Tab {
175 title: modelData
176 }
177 }
178 }
179
180 property var listModel: ["tab #0", "tab #1", "tab #2", "tab #3"];
181
182 Tabs {
183 id: twinRepeaters
184 ListModel {
185 id: twinModel
186 Component.onCompleted: {
187 append({ "name": "twintab 1" });
188 insert(0, { "name": "twintab 0" });
189 append({ "name": "twintab 3" });
190 insert(2, { "name": "twintab 2" });
191 }
192 }
193 Repeater {
194 objectName: "tabsRepeater"
195 id: twinRepeater1
196 model: twinModel
197 Tab {
198 title: name
199 }
200 }
201 Repeater {
202 objectName: "tabsRepeater"
203 id: twinRepeater2
204 model: twinModel
205 Tab {
206 title: name
207 }
208 }
209 }
210
211 TestCase {
212 name: "TabsAPI"118 name: "TabsAPI"
213 when: windowShown119 when: windowShown
214120
215 /*
216 The following testcases are all related to bug #1253804
217 */
218 function test_tabOrder_bug1253804() {
219 var tabsModel = tabsWithRepeater.__model;
220
221 compare(tabsRepeater.count, inputModel.count, "Incorrect number of tabs in Tabs");
222 compare(tabsModel.count, tabsRepeater.count, "Incorrect number of tabs in TabBar");
223 for (var i=0; i < tabsModel.count; i++) {
224 compare(tabsModel.get(i).title, inputModel.get(i).name, "Tab titles don't match for index "+i);
225 }
226
227 //shufle
228 inputModel.move(1, 2, 1);
229 inputModel.move(3, 0, 1);
230 inputModel.move(1, 3, 1);
231 // wait few miliseconds
232 wait(50);
233 for (i=0; i < tabsModel.count; i++) {
234 compare(tabsModel.get(i).title, inputModel.get(i).name, "Tab titles after shuffling don't match for index "+i);
235 }
236
237 // set it to null
238 tabsRepeater.model = null;
239 compare(tabsWithRepeater.__model.count, 0, "There are still tabs left after repeater model is reset");
240 }
241
242 function test_repeaterTabs() {
243 repeater.model = inputModel;
244 var tabsModel = repeaterTabs.__model;
245
246 compare(repeater.count, inputModel.count, "Incorrect number of tabs in Tabs");
247 compare(tabsModel.count, repeater.count, "Incorrect number of tabs in TabBar");
248 for (var i=0; i < tabsModel.count; i++) {
249 compare(tabsModel.get(i).title, inputModel.get(i).name, "Tab titles don't match for index "+i);
250 }
251
252 // clear repeaterTabs
253 repeater.model = null;
254 compare(repeaterTabs.__model.count, 0, "There are still tabs left after repeater model is reset");
255 }
256
257 function test_repeaterTabs_arrayAsModel() {
258 repeater.model = testCase.listModel;
259 var tabsModel = repeaterTabs.__model;
260
261 compare(repeater.count, testCase.listModel.length, "Incorrect number of tabs in Tabs");
262 compare(tabsModel.count, repeater.count, "Incorrect number of tabs in TabBar");
263 for (var i=0; i < tabsModel.count; i++) {
264 compare(tabsModel.get(i).title, testCase.listModel[i], "Tab titles don't match for index "+i);
265 }
266
267 // shuffling elements in an array is not detectable in Repeater
268 var x = testCase.listModel[1];
269 testCase.listModel[1] = testCase.listModel[0];
270 testCase.listModel[0] = x;
271 expectFailContinue("", "Array changes are not detected by repeaters");
272 compare(tabsModel.get(0).title, testCase.listModel[0], "Tab titles don't match for index 0");
273 expectFailContinue("", "Array changes are not detected by repeaters");
274 compare(tabsModel.get(1).title, testCase.listModel[1], "Tab titles don't match for index 0");
275
276 // clear repeaterTabs
277 repeater.model = null;
278 compare(repeaterTabs.__model.count, 0, "There are still tabs left after repeater model is reset");
279
280 }
281
282 function test_twoRepeaters() {
283 var tabsModel = twoRepeaters.__model;
284 var secondRepeaterModel = secondRepeater.model;
285
286 compare(tabsModel.count, firstRepeater.count + secondRepeater.count, "Incorrect number of tabs in TabBar");
287 for (var i = 0; i < firstRepeater.count; i++) {
288 compare(tabsModel.get(i).title, inputModel.get(i).name, "Tab titles don't match for index "+i);
289 }
290 for (i = firstRepeater.count; i < firstRepeater.count + secondRepeater.count; i++) {
291 compare(tabsModel.get(i).title, secondRepeaterModel[i - firstRepeater.count], "Tab titles don't match for index "+i);
292 }
293 }
294
295 function test_twinRepeaters() {
296 var tabsModel = twinRepeaters.__model;
297
298 compare(twinRepeater1.count, twinModel.count, "Incorrect number of tabs in the first repeater");
299 compare(twinRepeater2.count, twinModel.count, "Incorrect number of tabs in the second repeater");
300 compare(tabsModel.count, twinRepeater1.count + twinRepeater2.count, "Incorrect number of tabs in TabBar");
301 for (var j = 0; j < 2; j++) {
302 for (var i = 0; i < twinModel.count; i++) {
303 var index = j * twinModel.count + i;
304 compare(tabsModel.get(index).title, twinModel.get(i).name, "Tab titles don't match for Tabs index " + index);
305 }
306 }
307
308 //shuffle
309 twinModel.move(1, 2, 1);
310 twinModel.move(3, 0, 1);
311 twinModel.move(1, 3, 1);
312 // wait few miliseconds till Tabs update is realized
313 wait(50);
314
315 /* FIXME
316 for (var j = 0; j < 2; j++) {
317 for (var i = 0; i < twinModel.count; i++) {
318 var index = j * twinModel.count + i;
319 compare(tabsModel.get(index).title, twinModel.get(i).name, "Tab titles don't match for Tabs index " + index);
320 }
321 }
322 */
323
324 // set it to null
325 twinRepeater1.model = null;
326 twinRepeater2.model = null;
327 compare(twinRepeaters.__model.count, 0, "There are still tabs left after repeater model is reset");
328 }
329
330 function test_emptyTabs() {
331 compare(emptyTabs.selectedTabIndex, -1, "The default value for selectedTabIndex is -1 when there are no tabs");
332 compare(emptyTabs.selectedTab, null, "The default selected tab is null when there are no tabs");
333 compare(emptyTabs.currentPage, null, "The default currentPage is null when there are no tabs");
334 }
335
336 /* FIXME121 /* FIXME
337 function test_tabsDefaults() {122 function test_tabsDefaults() {
338 compare(tabs.selectedTabIndex, 0, "The default selectedTabIndex is 0 when Tabs has contents");123 compare(tabs.selectedTabIndex, 0, "The default selectedTabIndex is 0 when Tabs has contents");
@@ -412,8 +197,10 @@
412197
413 function test_deactivateByAppInteraction() {198 function test_deactivateByAppInteraction() {
414 tabs.tabBar.selectionMode = true;199 tabs.tabBar.selectionMode = true;
200 tabs.selectedTabIndex = 0;
201 waitForRendering(tabs.tabBar, 400);
415 compare(tabs.tabBar.selectionMode, true, "Tab bar can be put into selection mode");202 compare(tabs.tabBar.selectionMode, true, "Tab bar can be put into selection mode");
416 mouseClick(button, units.gu(1), units.gu(1), Qt.LeftButton);203 mouseClick(button, centerOf(button).x, centerOf(button).y);
417 compare(tabs.tabBar.selectionMode, false, "Tab bar deactivated by interacting with the page contents");204 compare(tabs.tabBar.selectionMode, false, "Tab bar deactivated by interacting with the page contents");
418 }205 }
419206
420207
=== added file 'tests/unit_x11/tst_components/tst_tabs_empty.qml'
--- tests/unit_x11/tst_components/tst_tabs_empty.qml 1970-01-01 00:00:00 +0000
+++ tests/unit_x11/tst_components/tst_tabs_empty.qml 2014-08-22 17:18:58 +0000
@@ -0,0 +1,41 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.2
18import Ubuntu.Components 1.1
19import Ubuntu.Test 1.0
20
21MainView {
22 width: units.gu(50)
23 height: units.gu(100)
24
25 useDeprecatedToolbar: true
26
27 Tabs {
28 id: empty
29 }
30
31 UbuntuTestCase {
32 name: "EmptyTabs"
33 when: windowShown
34
35 function test_emptyTabs() {
36 compare(empty.selectedTabIndex, -1, "The default value for selectedTabIndex is -1 when there are no tabs");
37 compare(empty.selectedTab, null, "The default selected tab is null when there are no tabs");
38 compare(empty.currentPage, null, "The default currentPage is null when there are no tabs");
39 }
40 }
41}
042
=== added file 'tests/unit_x11/tst_components/tst_tabs_with_repeater.qml'
--- tests/unit_x11/tst_components/tst_tabs_with_repeater.qml 1970-01-01 00:00:00 +0000
+++ tests/unit_x11/tst_components/tst_tabs_with_repeater.qml 2014-08-22 17:18:58 +0000
@@ -0,0 +1,234 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.2
18import Ubuntu.Components 1.1
19import Ubuntu.Test 1.0
20
21MainView {
22 id: testCase
23 width: units.gu(50)
24 height: units.gu(100)
25
26 useDeprecatedToolbar: true
27
28 ListModel {
29 id: inputModel
30 Component.onCompleted: {
31 append({ "name": "tab 1" });
32 insert(0, { "name": "tab 0" });
33 append({ "name": "tab 3" });
34 insert(2, { "name": "tab 2" });
35 }
36 }
37
38 Tabs {
39 id: tabsWithRepeater
40 Repeater {
41 objectName: "tabsRepeater"
42 id: tabsRepeater
43 model: inputModel
44 Tab {
45 title: name
46 }
47 }
48 }
49
50 Tabs {
51 id: repeaterTabs
52
53 Repeater {
54 objectName: "repeater"
55 id: repeater
56 Tab {
57 title: modelData
58 }
59 }
60 }
61
62 Tabs {
63 id: twoRepeaters
64
65 Repeater {
66 objectName: "firstRepeater"
67 id: firstRepeater
68 model: inputModel
69 Tab {
70 title: name
71 }
72 }
73
74 Repeater {
75 objectName: "secondRepeater"
76 id: secondRepeater
77 model: testCase.listModel
78 Tab {
79 title: modelData
80 }
81 }
82 }
83
84 property var listModel: ["tab #0", "tab #1", "tab #2", "tab #3"];
85
86 Tabs {
87 id: twinRepeaters
88 ListModel {
89 id: twinModel
90 Component.onCompleted: {
91 append({ "name": "twintab 1" });
92 insert(0, { "name": "twintab 0" });
93 append({ "name": "twintab 3" });
94 insert(2, { "name": "twintab 2" });
95 }
96 }
97 Repeater {
98 objectName: "tabsRepeater"
99 id: twinRepeater1
100 model: twinModel
101 Tab {
102 title: name
103 }
104 }
105 Repeater {
106 objectName: "tabsRepeater"
107 id: twinRepeater2
108 model: twinModel
109 Tab {
110 title: name
111 }
112 }
113 }
114
115 UbuntuTestCase {
116 name: "TabsWithRepeater"
117 when: windowShown
118
119 /*
120 The following testcases are all related to bug #1253804
121 */
122 function test_tabOrder_bug1253804() {
123 var tabsModel = tabsWithRepeater.__model;
124
125 compare(tabsRepeater.count, inputModel.count, "Incorrect number of tabs in Tabs");
126 compare(tabsModel.count, tabsRepeater.count, "Incorrect number of tabs in TabBar");
127 for (var i=0; i < tabsModel.count; i++) {
128 compare(tabsModel.get(i).title, inputModel.get(i).name, "Tab titles don't match for index "+i);
129 }
130
131 //shufle
132 inputModel.move(1, 2, 1);
133 inputModel.move(3, 0, 1);
134 inputModel.move(1, 3, 1);
135 // wait few miliseconds
136 wait(50);
137 for (i=0; i < tabsModel.count; i++) {
138 compare(tabsModel.get(i).title, inputModel.get(i).name, "Tab titles after shuffling don't match for index "+i);
139 }
140
141 // set it to null
142 tabsRepeater.model = null;
143 compare(tabsWithRepeater.__model.count, 0, "There are still tabs left after repeater model is reset");
144 }
145
146 function test_repeaterTabs() {
147 repeater.model = inputModel;
148 var tabsModel = repeaterTabs.__model;
149
150 compare(repeater.count, inputModel.count, "Incorrect number of tabs in Tabs");
151 compare(tabsModel.count, repeater.count, "Incorrect number of tabs in TabBar");
152 for (var i=0; i < tabsModel.count; i++) {
153 compare(tabsModel.get(i).title, inputModel.get(i).name, "Tab titles don't match for index "+i);
154 }
155
156 // clear repeaterTabs
157 repeater.model = null;
158 compare(repeaterTabs.__model.count, 0, "There are still tabs left after repeater model is reset");
159 }
160
161 function test_repeaterTabs_arrayAsModel() {
162 repeater.model = testCase.listModel;
163 var tabsModel = repeaterTabs.__model;
164
165 compare(repeater.count, testCase.listModel.length, "Incorrect number of tabs in Tabs");
166 compare(tabsModel.count, repeater.count, "Incorrect number of tabs in TabBar");
167 for (var i=0; i < tabsModel.count; i++) {
168 compare(tabsModel.get(i).title, testCase.listModel[i], "Tab titles don't match for index "+i);
169 }
170
171 // shuffling elements in an array is not detectable in Repeater
172 var x = testCase.listModel[1];
173 testCase.listModel[1] = testCase.listModel[0];
174 testCase.listModel[0] = x;
175 expectFailContinue("", "Array changes are not detected by repeaters");
176 compare(tabsModel.get(0).title, testCase.listModel[0], "Tab titles don't match for index 0");
177 expectFailContinue("", "Array changes are not detected by repeaters");
178 compare(tabsModel.get(1).title, testCase.listModel[1], "Tab titles don't match for index 0");
179
180 // clear repeaterTabs
181 repeater.model = null;
182 compare(repeaterTabs.__model.count, 0, "There are still tabs left after repeater model is reset");
183
184 }
185
186 function test_twoRepeaters() {
187 var tabsModel = twoRepeaters.__model;
188 var secondRepeaterModel = secondRepeater.model;
189
190 compare(tabsModel.count, firstRepeater.count + secondRepeater.count, "Incorrect number of tabs in TabBar");
191 for (var i = 0; i < firstRepeater.count; i++) {
192 compare(tabsModel.get(i).title, inputModel.get(i).name, "Tab titles don't match for index "+i);
193 }
194 for (i = firstRepeater.count; i < firstRepeater.count + secondRepeater.count; i++) {
195 compare(tabsModel.get(i).title, secondRepeaterModel[i - firstRepeater.count], "Tab titles don't match for index "+i);
196 }
197 }
198
199 function test_twinRepeaters() {
200 var tabsModel = twinRepeaters.__model;
201
202 compare(twinRepeater1.count, twinModel.count, "Incorrect number of tabs in the first repeater");
203 compare(twinRepeater2.count, twinModel.count, "Incorrect number of tabs in the second repeater");
204 compare(tabsModel.count, twinRepeater1.count + twinRepeater2.count, "Incorrect number of tabs in TabBar");
205 for (var j = 0; j < 2; j++) {
206 for (var i = 0; i < twinModel.count; i++) {
207 var index = j * twinModel.count + i;
208 compare(tabsModel.get(index).title, twinModel.get(i).name, "Tab titles don't match for Tabs index " + index);
209 }
210 }
211
212 //shuffle
213 twinModel.move(1, 2, 1);
214 twinModel.move(3, 0, 1);
215 twinModel.move(1, 3, 1);
216 // wait few miliseconds till Tabs update is realized
217 wait(50);
218
219 /* FIXME
220 for (var j = 0; j < 2; j++) {
221 for (var i = 0; i < twinModel.count; i++) {
222 var index = j * twinModel.count + i;
223 compare(tabsModel.get(index).title, twinModel.get(i).name, "Tab titles don't match for Tabs index " + index);
224 }
225 }
226 */
227
228 // set it to null
229 twinRepeater1.model = null;
230 twinRepeater2.model = null;
231 compare(twinRepeaters.__model.count, 0, "There are still tabs left after repeater model is reset");
232 }
233 }
234}

Subscribers

People subscribed via source and target branches