Merge lp:~zsombi/ubuntu-ui-toolkit/textinput-text-selection into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri
Status: Merged
Approved by: Cris Dywan
Approved revision: 1030
Merged at revision: 1020
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/textinput-text-selection
Merge into: lp:ubuntu-ui-toolkit/staging
Prerequisite: lp:~zsombi/ubuntu-ui-toolkit/toolkit-version
Diff against target: 2236 lines (+1257/-431)
12 files modified
components.api (+0/-2)
modules/Ubuntu/Components/InputHandler.qml (+305/-0)
modules/Ubuntu/Components/TextArea.qml (+45/-130)
modules/Ubuntu/Components/TextField.qml (+81/-133)
modules/Ubuntu/Components/Themes/Ambiance/TextAreaStyle.qml (+5/-5)
modules/Ubuntu/Components/qmldir (+1/-0)
modules/Ubuntu/Test/UbuntuTestCase.qml (+1/-0)
tests/resources/inputs/TextInputs.qml (+85/-0)
tests/unit_x11/tst_components/tst_textarea.qml (+318/-60)
tests/unit_x11/tst_components/tst_textarea_in_flickable.qml (+64/-38)
tests/unit_x11/tst_components/tst_textfield.qml (+314/-63)
tests/unit_x11/tst_mousefilters/HoverEvent.qml.moved (+38/-0)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/textinput-text-selection
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Cris Dywan Approve
Review via email: mp+216851@code.launchpad.net

This proposal supersedes a proposal from 2014-04-15.

Commit message

Fixes TextField and TextArea selection and scrolling behaviors.

Description of the change

Superseeded due to 1.0 version introduction.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal

I see the same test failure here as J locally.
> tests/unit_x11/tst_components/tst_textarea_in_flickable.qml(72)
> tryCompare(moveSpy, "count", 1, 200);
Why not moveSpy.wait(); here? That seems to fix it for me.

When using select and context menu a few times it's possible to change the selection after the menu opens but before letting go of the mouse button. So I don't get what I selected or worst case nothing.

What I manually checked is that I can select, including scrolling, get a menu, copy, paste and so on, double click to select a word, all of these seem sold aside from the above issue.

FTR toggleFlickablesInteractive looks rather hackish… I guess Repeater doesn't support it, and PropertyChanges doesn't take a list either… but let's have it as-is for now.

Should maybe selectionTimeout.interval be added to modules/Ubuntu/Components/Themes/Ambiance/TextCursorStyle.qml:31

test_press_and_hold_over_selected_text etc. doesn't test the context menu. Especially given the above problem I found while using it, I think we need to test that it can be clicked and catches the right text. Possibly AP.

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote : Posted in a previous version of this proposal

> I see the same test failure here as J locally.
> > tests/unit_x11/tst_components/tst_textarea_in_flickable.qml(72)
> > tryCompare(moveSpy, "count", 1, 200);
> Why not moveSpy.wait(); here? That seems to fix it for me.

Ahh, right :) I'll fix it.

>
> When using select and context menu a few times it's possible to change the
> selection after the menu opens but before letting go of the mouse button. So I
> don't get what I selected or worst case nothing.

Let's do this in a separate branch..

>
> What I manually checked is that I can select, including scrolling, get a menu,
> copy, paste and so on, double click to select a word, all of these seem sold
> aside from the above issue.
>
> FTR toggleFlickablesInteractive looks rather hackish… I guess Repeater doesn't
> support it, and PropertyChanges doesn't take a list either… but let's have it
> as-is for now.

Well, limitation of PropertyChange, maybe we propose some Change component upstream to deal with a list of targets :).

>
> Should maybe selectionTimeout.interval be added to
> modules/Ubuntu/Components/Themes/Ambiance/TextCursorStyle.qml:31

I will put this in the TextFieldStyle/TextAreaStyle components rather than the cursor style.

>
> test_press_and_hold_over_selected_text etc. doesn't test the context menu.
> Especially given the above problem I found while using it, I think we need to
> test that it can be clicked and catches the right text. Possibly AP.

Yep, but let's get this in a separate branch as well.

Revision history for this message
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal

Filed bug 1304952 for testing the context menu.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:1012
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/3/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/162
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/4721
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/3
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/3
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/3/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/3
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/155
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4304
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4304/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/5875
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4074
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/4850
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/4850/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/3/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:1014
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/5/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/166
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/4728
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/5
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/5
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/5/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/5
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/159
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4311
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4311/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/5883
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4081
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/4857
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/4857/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/5/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal

The menu now stays in place the way it should. Nice!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:1015
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/10/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/174
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/4738
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/10
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/10
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/10/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/10
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/167
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4321
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4321/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/5893
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4091
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/4867
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/4867/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/10/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Autolanding.
More details in the following jenkins job:
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-autolanding/3/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/178
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/4744
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-autolanding/3
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-autolanding/3
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-autolanding/3/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-autolanding/3
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/171
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4327
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4327/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/5899
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4096
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/4873
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/4873/artifact/work/output/*zip*/output.zip

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal

Seems to work fine, albeit I get a weird error message now:
QIODevice::write: device not open

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal

FAIL! : components::TextAreaInFlickableAPI::test_DoNotStealFlickEvents() 'wait for signal onMovementEnded' returned FALSE. ()
   Loc: [/tmp/buildd/ubuntu-ui-toolkit-0.1.46+14.04.20140408.1bzr1024pkg0trusty43/tests/unit_x11/tst_components/tst_textarea_in_flickable.qml(72)]

review: Needs Fixing
Revision history for this message
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal

FAIL! : components::TextAreaInFlickableAPI::test_DoNotStealFlickEvents() 'wait for signal onMovementEnded' returned FALSE. ()
   Loc: [/tmp/buildd/ubuntu-ui-toolkit-0.1.46+14.04.20140408.1bzr1024pkg0trusty43/tests/unit_x11/tst_components/tst_textarea_in_flickable.qml(72)]

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:1025
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/55/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/338
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/4961
    FAILURE: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/55/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/55
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/55/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/55/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/311
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4559
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4559/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/6240
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4279
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5132
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5132/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/55/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

PASSED: Continuous integration, rev:1026
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/60/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/354
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/4976
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/60
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/60
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/60/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/60
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/326
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4584
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4584/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/6275
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4292
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5155
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5155/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/60/rebuild

review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:1026
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/58/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/351
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/4973
    FAILURE: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/58/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/58
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/58/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/58
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/323
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4577
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4577/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/6263
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4290
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5148
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5148/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/58/rebuild

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

FAILED: Continuous integration, rev:1027
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/67/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/374
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/4996
    FAILURE: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/67/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/67
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/67/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/67
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/345
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4605
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4605/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/6296
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4306
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5176
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5176/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/67/rebuild

review: Needs Fixing (continuous-integration)
1028. By Zsombor Egri

tests reworked for tst_textarea_in_flickable.qml

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

PASSED: Continuous integration, rev:1028
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/84/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/401
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/5026
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/84
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/84
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/84/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/84
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/364
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4645
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4645/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/6359
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4326
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5214
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5214/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/84/rebuild

review: Approve (continuous-integration)
1029. By Zsombor Egri

staging merge

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

FAILED: Continuous integration, rev:1029
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/88/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/414
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/5038
    FAILURE: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/88/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/88
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/88/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/88
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/376
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4660
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4660/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/6380
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4337
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5227
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5227/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/88/rebuild

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

The DatePicker failure must be a hickup of the CI - again... It has nothing to do with the changes from this MR.

1030. By Zsombor Egri

staging merge

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

PASSED: Continuous integration, rev:1030
http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-ci/97/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-trusty-touch/430
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/5054
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-amd64-ci/97
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/97
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-armhf-ci/97/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-sdk-team-ubuntu-ui-toolkit-staging-trusty-i386-ci/97
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/387
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4677
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/4677/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/6398
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/4348
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5243
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/5243/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/97/rebuild

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

(Still) working fine, and tests finally seem to pass. Nice.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2014-04-24 18:39:13 +0000
3+++ components.api 2014-04-25 05:29:38 +0000
4@@ -483,7 +483,6 @@
5 function remove(start, end)
6 function undo()
7 function forceActiveFocus()
8- property internal __internal
9 TextField 0.1 1.0
10 ActionItem
11 property bool highlighted
12@@ -549,7 +548,6 @@
13 function undo()
14 function remove(start, end)
15 function getText(start, end)
16- property internal __internal
17 Palette 0.1
18 QtObject
19 property PaletteValues normal
20
21=== added file 'modules/Ubuntu/Components/InputHandler.qml'
22--- modules/Ubuntu/Components/InputHandler.qml 1970-01-01 00:00:00 +0000
23+++ modules/Ubuntu/Components/InputHandler.qml 2014-04-25 05:29:38 +0000
24@@ -0,0 +1,305 @@
25+/*
26+ * Copyright 2014 Canonical Ltd.
27+ *
28+ * This program is free software; you can redistribute it and/or modify
29+ * it under the terms of the GNU Lesser General Public License as published by
30+ * the Free Software Foundation; version 3.
31+ *
32+ * This program is distributed in the hope that it will be useful,
33+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
34+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35+ * GNU Lesser General Public License for more details.
36+ *
37+ * You should have received a copy of the GNU Lesser General Public License
38+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
39+ */
40+
41+import QtQuick 2.0
42+import Ubuntu.Components 0.1 as Ubuntu
43+
44+/*
45+ This component is a unified text selection and scrolling handler for both
46+ TextField and TextArea components.
47+ */
48+
49+Item {
50+ id: inputHandler
51+ objectName: "input_handler"
52+ // the root control
53+ property Item main
54+ // the input instance
55+ property Item input
56+ // the Flickable holdiong the input instance
57+ property Flickable flickable
58+ // selection cursor mode
59+ property bool selectionCursor: input && input.selectedText !== ""
60+ // True if mouse handlig is enabled, false if flicking mode is enabled
61+ readonly property bool mouseHandlingEnabled: !flickable.interactive
62+ // property holding the selection mode timeout
63+ property int selectionModeTimeout: 200
64+
65+ // signal triggered when popup shoudl be opened
66+ signal pressAndHold(int pos)
67+
68+ function activateInput() {
69+ if (!input.activeFocus) {
70+ input.forceActiveFocus();
71+ } else {
72+ showInputPanel();
73+ }
74+ }
75+
76+ function showInputPanel() {
77+ if (!Qt.inputMethod.visible) {
78+ Qt.inputMethod.show();
79+ }
80+ textChanged = false;
81+ }
82+ function hideInputPanel() {
83+ Qt.inputMethod.hide();
84+ // emit accepted signal if changed
85+ if (textChanged && input.hasOwnProperty("accepted")) {
86+ input.accepted();
87+ }
88+ }
89+
90+ // internal properties/functions
91+ readonly property bool singleLine: input && input.hasOwnProperty("validator")
92+ property var flickableList: new Array()
93+ property bool textChanged: false
94+ property int pressedPosition: -1
95+ // move properties
96+ property int moveStarts: -1
97+ property int moveEnds: -1
98+ // set scroller to the first Flickable that scrolls the input
99+ // this can be the internal Flickable if the full autosize is disabled
100+ // or one of the input's parent Flickable
101+ readonly property bool scrollingDisabled: main && main.hasOwnProperty("autoSize") ?
102+ (main.autoSize && (main.maximumLineCount <= 0)) : false
103+ onScrollingDisabledChanged: if (state == "") flickable.interactive = !scrollingDisabled
104+ readonly property Flickable grandScroller: firstFlickableParent(main)
105+ readonly property Flickable scroller: (scrollingDisabled && grandScroller) ? grandScroller : flickable
106+
107+ // ensures the text cusrorRectangle is always in the internal Flickable's visible area
108+ function ensureVisible()
109+ {
110+ var rect = input.cursorRectangle;
111+ if (flickable.moving || flickable.flicking)
112+ return;
113+ if (flickable.contentX >= rect.x)
114+ flickable.contentX = rect.x;
115+ else if (flickable.contentX + flickable.width <= rect.x + rect.width)
116+ flickable.contentX = rect.x + rect.width - flickable.width;
117+ if (flickable.contentY >= rect.y)
118+ flickable.contentY = rect.y;
119+ else if (flickable.contentY + flickable.height <= rect.y + rect.height)
120+ flickable.contentY = rect.y + rect.height - flickable.height;
121+ }
122+ // returns the mouse position
123+ function mousePosition(mouse) {
124+ return singleLine ? input.positionAt(mouse.x) : input.positionAt(mouse.x, mouse.y);
125+ }
126+ // checks whether the position is in the selected text
127+ function positionInSelection(pos) {
128+ return (input.selectionStart !== input.selectionEnd)
129+ && (pos >= Math.min(input.selectionStart, input.selectionEnd))
130+ && (pos <= Math.max(input.selectionStart, input.selectionEnd));
131+ }
132+
133+ // check whether the mouse is inside a selected text area
134+ function mouseInSelection(mouse) {
135+ var pos = mousePosition(mouse);
136+ return positionInSelection(pos);
137+ }
138+ // selects text
139+ function selectText(mouse) {
140+ moveEnds = mousePosition(mouse);
141+ if (moveStarts < 0) {
142+ moveStarts = moveEnds;
143+ }
144+ input.select(moveStarts, moveEnds);
145+ }
146+ // returns the first Flickable parent of a given item
147+ function firstFlickableParent(item) {
148+ var p = item ? item.parent : null;
149+ while (p && !p.hasOwnProperty("flicking")) {
150+ p = p.parent;
151+ }
152+ return p;
153+ }
154+ // focuses the input if not yet focused, and shows the context menu
155+ function openContextMenu(mouse) {
156+ var pos = mousePosition(mouse);
157+ if (!main.focus || !mouseInSelection(mouse)) {
158+ activateInput();
159+ input.cursorPosition = pressedPosition = mousePosition(mouse);
160+ }
161+ // open context menu at the cursor position
162+ inputHandler.pressAndHold(input.cursorPosition);
163+ // if opened with left press (touch falls into this criteria as well), we need to set state to inactive
164+ // so the mouse moves won't result in selected text loss/change
165+ if (mouse.button === Qt.LeftButton) {
166+ state = "inactive";
167+ }
168+ }
169+
170+ // disables interactive Flickable parents, stops at the first non-interactive flickable.
171+ function toggleFlickablesInteractive(turnOn) {
172+ var p;
173+ if (!turnOn) {
174+ // handle the scroller separately
175+ p = firstFlickableParent(scroller)
176+ while (p) {
177+ if (p.interactive) {
178+ flickableList.push(p);
179+ p.interactive = false;
180+ } else {
181+ break;
182+ }
183+ p = firstFlickableParent(p);
184+ }
185+ } else {
186+ while (flickableList.length > 0) {
187+ p = flickableList.pop();
188+ p.interactive = true;
189+ }
190+ }
191+ }
192+
193+ Component.onCompleted: {
194+ state = (main.focus) ? "" : "inactive";
195+ }
196+
197+ // states
198+ states: [
199+ // override default state to turn on the saved Flickable interactive mode
200+ State {
201+ name: ""
202+ StateChangeScript {
203+ // restore interactive for all Flickable parents
204+ script: toggleFlickablesInteractive(true);
205+ }
206+ },
207+ State {
208+ name: "inactive"
209+ // we do not disable scroller here as in case the internal scrolling
210+ // is disabled (scrollingDisabled = true) the outer scroller (grandScroller)
211+ // would be blocked as well, which we don't want to
212+ PropertyChanges {
213+ target: flickable
214+ interactive: false
215+ }
216+ },
217+ State {
218+ name: "scrolling"
219+ StateChangeScript {
220+ script: {
221+ // stop scrolling all the parents
222+ toggleFlickablesInteractive(false);
223+ // stop selection timeout
224+ selectionTimeout.running = false;
225+ }
226+ }
227+ },
228+ State {
229+ name: "select"
230+ // during select state all the flickables are blocked (interactive = false)
231+ // we can use scroller here as we need to disable the outer scroller too!
232+ PropertyChanges {
233+ target: scroller
234+ interactive: false
235+ }
236+ StateChangeScript {
237+ script: {
238+ // turn off interactive for all parent flickables
239+ toggleFlickablesInteractive(false);
240+ if (!positionInSelection(pressedPosition)) {
241+ input.cursorPosition = pressedPosition;
242+ }
243+ }
244+ }
245+ }
246+ ]
247+
248+ // brings the state back to default when the component looses focuse
249+ Connections {
250+ target: main
251+ ignoreUnknownSignals: true
252+ onFocusChanged: {
253+ state = (main.focus) ? "" : "inactive";
254+ }
255+ }
256+
257+ // input specific signals
258+ Connections {
259+ target: input
260+ onCursorRectangleChanged: if (!scrollingDisabled) ensureVisible()
261+ onTextChanged: textChanged = true;
262+ }
263+
264+ // inner or outer Flickable controlling
265+ Connections {
266+ target: scroller
267+ // turn scrolling state on
268+ onFlickStarted: if (!scrollingDisabled) state = "scrolling"
269+ onMovementStarted: if (!scrollingDisabled) state = "scrolling"
270+ // reset to default state
271+ onMovementEnded: state = ""
272+ }
273+
274+ // switches the state to selection
275+ Timer {
276+ id: selectionTimeout
277+ interval: selectionModeTimeout
278+ onTriggered: {
279+ if (scroller && !scroller.moving) {
280+ state = "select";
281+ }
282+ }
283+ }
284+
285+ // Mouse handling
286+ Ubuntu.Mouse.forwardTo: [main]
287+ Ubuntu.Mouse.onPressed: {
288+ if (input.activeFocus) {
289+ // start selection timeout
290+ selectionTimeout.restart();
291+ }
292+ // remember pressed position as we need it when entering into selection state
293+ pressedPosition = mousePosition(mouse);
294+ // consume event so it does not get forwarded to the input
295+ mouse.accepted = true;
296+ }
297+ Ubuntu.Mouse.onReleased: {
298+ if (!main.focus && !main.activeFocusOnPress) {
299+ return;
300+ }
301+
302+ activateInput();
303+ // stop text selection timer
304+ selectionTimeout.running = false;
305+ if (state === "") {
306+ input.cursorPosition = mousePosition(mouse);
307+ }
308+ moveStarts = moveEnds = -1;
309+ state = "";
310+ }
311+ Ubuntu.Mouse.onPositionChanged: {
312+ // leave if not focus, not the left button or not in select state
313+ if (!input.activeFocus || (mouse.button !== Qt.LeftButton) || (state !== "select") || !main.selectByMouse) {
314+ return;
315+ }
316+ // stop text selection timer
317+ selectionTimeout.running = false;
318+ selectText(mouse);
319+ }
320+ Ubuntu.Mouse.onDoubleClicked: {
321+ if (main.selectByMouse) {
322+ input.selectWord();
323+ // turn selection state temporarily so the selection is not cleared on release
324+ state = "selection";
325+ }
326+ }
327+ Ubuntu.Mouse.onPressAndHold: openContextMenu(mouse)
328+
329+}
330
331=== modified file 'modules/Ubuntu/Components/TextArea.qml'
332--- modules/Ubuntu/Components/TextArea.qml 2014-04-20 19:25:12 +0000
333+++ modules/Ubuntu/Components/TextArea.qml 2014-04-25 05:29:38 +0000
334@@ -15,6 +15,7 @@
335 */
336
337 import QtQuick 2.0
338+import Ubuntu.Components 0.1 as Ubuntu
339 import "mathUtils.js" as MathUtils
340
341 /*!
342@@ -60,19 +61,34 @@
343 mode and 4 lines on fixed-mode. The line size is calculated from the font size and the
344 ovarlay and frame spacing specified in the style.
345
346+ \section2 Scrolling and text selection
347+ The input is activated when the tap or mouse is released after being pressed
348+ over the component.
349+
350 Scrolling the editing area can happen when the size is fixed or in auto-sizing mode when
351 the content size is bigger than the visible area. The scrolling is realized by swipe
352 gestures, or by navigating the cursor.
353
354- The item enters in selection mode when the user performs a long tap (or long mouse press)
355- or a double tap/press on the text area. The mode is visualized by two selection cursors
356- (pins) which can be used to select the desired text. The text can also be selected by
357- moving the finger/mouse towards the desired area right after entering in selection mode.
358- The way the text is selected is driven by the mouseSelectionMode value, which is either
359- character or word. The editor leaves the selection mode by pressing/tapping again on it
360- or by losing focus.
361-
362- \b{This component is under heavy development.}
363+ The content can be selected in the following ways:
364+ \list
365+ \li - double tapping/left mouse clicking over the content, when the word that
366+ had been tapped over will be selected
367+ \li - by pressing and dragging the selection cursor over the text input. Note
368+ that there has to be a delay of approx. 200 ms between the press and drag
369+ gesture, time when the input switches from scroll mode to selection mode
370+ \endlist
371+
372+ The input is focused (activated) upon tap/left mouse button release. The cursor
373+ will be placed at the position the mouse/tap point at release time. If the click
374+ is happening on a selected area, the selection will be cleared. Long press above
375+ a selected area brings up the clipboard context menu. When the long press happens
376+ over a non-selected area, the cursor will be moved to the position and the component
377+ enters in selection mode. The selection mode can also be activated by tapping and
378+ keeping the tap/mouse over for approx 300 ms. If there is a move during this time,
379+ the component enters into scrolling mode. The mode is exited once the scrolling
380+ finishes. During the scrolling mode the selected text is preserved.
381+
382+ \note During text selection all interactive parent Flickables are turned off.
383 */
384
385 StyledItem {
386@@ -708,7 +724,7 @@
387 */
388 function forceActiveFocus()
389 {
390- internal.activateEditor();
391+ inputHandler.activateInput();
392 }
393
394 // logic
395@@ -721,6 +737,13 @@
396 // activation area on mouse click
397 // the editor activates automatically when pressed in the editor control,
398 // however that one can be slightly spaced to the main control area
399+ MouseArea {
400+ anchors.fill: parent
401+ enabled: internal.frameSpacing > 0
402+ // activate input when pressed on the frame
403+ preventStealing: false
404+ Ubuntu.Mouse.forwardTo: [inputHandler]
405+ }
406
407 //internals
408
409@@ -744,8 +767,6 @@
410 LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
411 LayoutMirroring.childrenInherit: true
412
413- /*!\internal */
414- property alias __internal: internal
415 QtObject {
416 id: internal
417 // public property locals enabling aliasing
418@@ -757,23 +778,8 @@
419 property real inputAreaWidth: control.width - 2 * frameSpacing
420 property real inputAreaHeight: control.height - 2 * frameSpacing
421 //selection properties
422- property bool draggingMode: false
423- property bool selectionMode: false
424 property bool prevShowCursor
425
426- signal popupTriggered(int pos)
427-
428- onDraggingModeChanged: {
429- if (draggingMode) selectionMode = false;
430- }
431- onSelectionModeChanged: {
432- if (selectionMode)
433- draggingMode = false;
434- else {
435- toggleSelectionCursors(false);
436- }
437- }
438-
439 function toggleSelectionCursors(show)
440 {
441 if (!show) {
442@@ -788,25 +794,6 @@
443 }
444 }
445
446- function activateEditor()
447- {
448- if (!editor.activeFocus)
449- editor.forceActiveFocus();
450- else
451- showInputPanel();
452-
453- }
454-
455- function showInputPanel()
456- {
457- if (!Qt.inputMethod.visible)
458- Qt.inputMethod.show();
459- }
460- function hideInputPanel()
461- {
462- Qt.inputMethod.hide();
463- }
464-
465 function linesHeight(lines)
466 {
467 var lineHeight = editor.font.pixelSize * lines + lineSpacing * lines
468@@ -822,24 +809,6 @@
469 control.height = linesHeight(MathUtils.clamp(control.lineCount, 1, max));
470 }
471 }
472-
473- function enterSelectionMode(x, y)
474- {
475- if (undefined !== x && undefined !== y) {
476- control.cursorPosition = control.positionAt(x, y);
477- control.moveCursorSelection(control.cursorPosition + 1);
478- }
479- toggleSelectionCursors(true);
480- }
481-
482- function positionCursor(x, y) {
483- var cursorPos = control.positionAt(x, y);
484- if (control.selectedText === "")
485- control.cursorPosition = cursorPos;
486- else if (control.selectionStart > cursorPos || control.selectionEnd < cursorPos) {
487- control.cursorPosition = cursorPos;
488- }
489- }
490 }
491
492 // grab Enter/Return keys which may be stolen from parent components of TextArea
493@@ -869,7 +838,7 @@
494 popover: control.popover
495 visible: editor.cursorVisible
496
497- Component.onCompleted: internal.popupTriggered.connect(cursorItem.openPopover)
498+ Component.onCompleted: inputHandler.pressAndHold.connect(cursorItem.openPopover)
499 }
500 }
501 // selection cursor loader
502@@ -922,6 +891,7 @@
503 }
504 Flickable {
505 id: flicker
506+ objectName: "textarea_scroller"
507 anchors {
508 fill: parent
509 margins: internal.frameSpacing
510@@ -929,38 +899,23 @@
511 clip: true
512 contentWidth: editor.paintedWidth
513 contentHeight: editor.paintedHeight
514- interactive: !autoSize || (autoSize && maximumLineCount > 0)
515 // do not allow rebounding
516 boundsBehavior: Flickable.StopAtBounds
517- pressDelay: 500
518-
519- function ensureVisible(r)
520- {
521- if (moving || flicking)
522- return;
523- if (contentX >= r.x)
524- contentX = r.x;
525- else if (contentX+width <= r.x+r.width)
526- contentX = r.x+r.width-width;
527- if (contentY >= r.y)
528- contentY = r.y;
529- else if (contentY+height <= r.y+r.height)
530- contentY = r.y+r.height-height;
531- }
532
533 // editor
534 // Images are not shown when text contains <img> tags
535 // bug to watch: https://bugreports.qt-project.org/browse/QTBUG-27071
536 TextEdit {
537+ objectName: "textarea_input"
538 readOnly: false
539 id: editor
540 focus: true
541- onCursorRectangleChanged: flicker.ensureVisible(cursorRectangle)
542 width: internal.inputAreaWidth
543 height: Math.max(internal.inputAreaHeight, editor.contentHeight)
544 wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
545 mouseSelectionMode: TextEdit.SelectWords
546 selectByMouse: false
547+ activeFocusOnPress: false
548 cursorDelegate: cursor
549 color: control.__styleInstance.color
550 selectedTextColor: Theme.palette.selected.foregroundText
551@@ -972,55 +927,15 @@
552 // autosize handling
553 onLineCountChanged: internal.frameSize()
554
555- // remove selection when typing starts or input method start entering text
556- onInputMethodComposingChanged: {
557- if (inputMethodComposing)
558- internal.selectionMode = false;
559- }
560- Keys.onPressed: {
561- if ((event.text !== ""))
562- internal.selectionMode = false;
563- }
564- Keys.onReleased: {
565- // selection positioners are updated after the keypress
566- if (selectionStart == selectionEnd)
567- internal.selectionMode = false;
568- }
569-
570- // handling text selection
571- MouseArea {
572- id: handler
573- enabled: control.enabled && control.activeFocusOnPress
574+ // input selection and navigation handling
575+ Ubuntu.Mouse.forwardTo: [inputHandler]
576+ InputHandler {
577+ id: inputHandler
578 anchors.fill: parent
579- propagateComposedEvents: true
580-
581- onPressed: {
582- internal.activateEditor();
583- internal.draggingMode = true;
584- }
585- onPressAndHold: {
586- // move mode gets false if there was a mouse move after the press;
587- // this is needed as Flickable will send a pressAndHold in case of
588- // press -> move-pressed ->stop-and-hold-pressed gesture is performed
589- if (!internal.draggingMode)
590- return;
591- internal.draggingMode = false;
592- // open popup
593- internal.positionCursor(mouse.x, mouse.y);
594- internal.popupTriggered(editor.cursorPosition);
595- }
596- onReleased: {
597- internal.draggingMode = false;
598- }
599- onDoubleClicked: {
600- internal.activateEditor();
601- if (control.selectByMouse)
602- control.selectWord();
603- }
604- onClicked: {
605- internal.activateEditor();
606- internal.positionCursor(mouse.x, mouse.y);
607- }
608+ main: control
609+ input: editor
610+ flickable: flicker
611+ selectionModeTimeout: control.__styleInstance.selectionModeTimeout
612 }
613 }
614 }
615
616=== modified file 'modules/Ubuntu/Components/TextField.qml'
617--- modules/Ubuntu/Components/TextField.qml 2014-04-20 19:25:12 +0000
618+++ modules/Ubuntu/Components/TextField.qml 2014-04-25 05:29:38 +0000
619@@ -16,6 +16,7 @@
620
621 import QtQuick 2.0
622 import Ubuntu.Unity.Action 1.1 as UnityActions
623+import Ubuntu.Components 0.1 as Ubuntu
624
625 /*!
626 \qmltype TextField
627@@ -70,6 +71,34 @@
628 }
629 }
630 \endqml
631+
632+ \section2 Scrolling and text selection
633+ The input is activated when the tap or mouse is released after being pressed
634+ over the component.
635+
636+ The text can be scrolled horizontally by swiping over the content both when
637+ the component is active or inactive.
638+
639+ The content can be selected in the following ways:
640+ \list
641+ \li - double tapping/left mouse clicking over the content, when the word that
642+ had been tapped over will be selected
643+ \li - by pressing and dragging the selection cursor over the text input. Note
644+ that there has to be a delay of approx. 200 ms between the press and drag
645+ gesture, time when the input switches from scroll mode to selection mode
646+ \endlist
647+
648+ The input is focused (activated) upon tap/left mouse button release. The cursor
649+ will be placed at the position the mouse/tap point at release time. If the click
650+ is happening on a selected area, the selection will be cleared. Long press above
651+ a selected area brings up the clipboard context menu. When the long press happens
652+ over a non-selected area, the cursor will be moved to the position and the component
653+ enters in selection mode. The selection mode can also be activated by tapping and
654+ keeping the tap/mouse over for approx 300 ms. If there is a move during this time,
655+ the component enters into scrolling mode. The mode is exited once the scrolling
656+ finishes. During the scrolling mode the selected text is preserved.
657+
658+ \note During text selection all interactive parent Flickables are turned off.
659 */
660
661 ActionItem {
662@@ -98,10 +127,11 @@
663 property bool hasClearButton: true
664
665 /*!
666- \preliminary
667+ \deprecated
668 Component to be shown and used instead of the default On Screen Keyboard.
669 */
670 property Component customSoftwareInputPanel
671+ onCustomSoftwareInputPanelChanged: console.error("customSoftwareInputPanel property deprecated.")
672
673 /*!
674 The property overrides the default popover of the TextField. When set, the TextField
675@@ -146,10 +176,8 @@
676 /*!
677 Whether the TextField should gain active focus on a mouse press. By default
678 this is set to true.
679-
680- \qmlproperty bool activeFocusOnPress
681 */
682- property alias activeFocusOnPress: editor.activeFocusOnPress
683+ property bool activeFocusOnPress: true
684
685 /*!
686 Whether the TextField should scroll when the text is longer than the width.
687@@ -446,11 +474,8 @@
688
689 If false, the user cannot use the mouse to select text, only can use it to
690 focus the input.
691-
692- \qmlproperty bool selectByMouse
693- \preliminary
694 */
695- property alias selectByMouse: virtualKbdHandler.enabled
696+ property bool selectByMouse: true
697
698 /*!
699 This read-only property provides the text currently selected in the text input.
700@@ -719,7 +744,7 @@
701 */
702 function forceActiveFocus()
703 {
704- internal.activateEditor();
705+ inputHandler.activateInput();
706 }
707
708 /*!
709@@ -817,25 +842,20 @@
710 anchors.fill: parent
711 // us it only when there is space between the frame and input
712 enabled: internal.spacing > 0
713- onClicked: internal.activateEditor()
714+ preventStealing: false
715+ // forward mouse events to input so we can handle those uniformly
716+ Ubuntu.Mouse.forwardTo: [inputHandler]
717 }
718
719 Text { id: fontHolder }
720
721 //internals
722- /*! \internal */
723- property alias __internal: internal
724 QtObject {
725 id: internal
726 // array of borders in left, top, right, bottom order
727- property bool textChanged: false
728 property real spacing: control.__styleInstance.overlaySpacing
729 property real lineSpacing: units.dp(3)
730 property real lineSize: editor.font.pixelSize + lineSpacing
731- //selection properties
732- property bool selectionMode: false
733-
734- signal popupTriggered()
735
736 property int type: action ? action.parameterType : 0
737 onTypeChanged: {
738@@ -847,54 +867,6 @@
739 || type == UnityActions.Action.Real)
740 inputMethodHints = Qt.ImhDigitsOnly
741 }
742-
743- function activateEditor()
744- {
745- if (!editor.activeFocus)
746- editor.forceActiveFocus();
747- else
748- showInputPanel();
749- }
750-
751- function showInputPanel()
752- {
753- if (control.customSoftwareInputPanel != undefined) {
754- // TODO implement once we have the SIP ready
755- } else {
756- if (!Qt.inputMethod.visible)
757- Qt.inputMethod.show();
758- }
759- textChanged = false;
760- }
761- function hideInputPanel()
762- {
763- if (control.customSoftwareInputPanel != undefined) {
764- // TODO implement once we have the SIP ready
765- } else {
766- Qt.inputMethod.hide();
767- }
768- // emit accepted signal if changed
769- if (textChanged)
770- control.accepted();
771- }
772- // reset selection
773- function resetEditorSelection(mouseX)
774- {
775- editor.cursorPosition = editor.positionAt(mouseX);
776- }
777-
778- // positions the cursor depending on whether there is a selection active or not
779- function positionCursor(x) {
780-
781- var cursorPos = control.positionAt(x);
782- if (control.selectedText === "") {
783- control.cursorPosition = cursorPos;
784- }
785- // If target cursor position is outside selection then cancel selection and move cursor
786- else if (control.selectionStart > cursorPos || control.selectionEnd < cursorPos) {
787- control.cursorPosition = cursorPos;
788- }
789- }
790 }
791
792 //left pane
793@@ -947,7 +919,7 @@
794 popover: control.popover
795 visible: editor.cursorVisible
796
797- Component.onCompleted: internal.popupTriggered.connect(openPopover)
798+ Component.onCompleted: inputHandler.pressAndHold.connect(openPopover)
799 }
800 }
801
802@@ -999,79 +971,55 @@
803
804
805 // text input
806- TextInput {
807- id: editor
808- // FocusScope will forward focus to this component
809- focus: true
810+ Flickable {
811+ id: flicker
812+ objectName: "textfield_scroller"
813 anchors {
814 left: leftPane.right
815 right: clearButton.left
816+ top: parent.top
817+ bottom: parent.bottom
818 margins: internal.spacing
819- verticalCenter: parent.verticalCenter
820 }
821- // get the control's style
822+ topMargin: internal.spacing
823+ // do not allow rebounding
824+ boundsBehavior: Flickable.StopAtBounds
825+ // need to forward events as events occurred on topMargin area are not grabbed by the MouseArea.
826+ Ubuntu.Mouse.forwardTo: [inputHandler]
827+
828 clip: true
829- onTextChanged: internal.textChanged = true
830- cursorDelegate: cursor
831- color: control.__styleInstance.color
832- selectedTextColor: Theme.palette.selected.foregroundText
833- selectionColor: Theme.palette.selected.foreground
834- font.pixelSize: FontUtils.sizeToPixels("medium")
835- passwordCharacter: "\u2022"
836- // forward keys to the root element so it can be captured outside of it
837- Keys.forwardTo: [control]
838-
839- // handle virtual keyboard and cursor positioning, as the MouseArea overrides
840- // those functionalities of the TextInput
841- MouseArea {
842- id: virtualKbdHandler
843- anchors.fill: parent
844- hoverEnabled: true
845- preventStealing: true
846-
847- onClicked: {
848- // activate control
849- if (!control.activeFocus) {
850- internal.activateEditor();
851- // set cursor position if no selection was previously set
852- internal.positionCursor(mouse.x)
853- } else if (!internal.selectionMode){
854- // reset selection and move cursor unde mouse click
855- internal.resetEditorSelection(mouse.x);
856- } else if (internal.selectionMode) {
857- // reset selection mode (onReleased is triggered prior to onClicked
858- // and resetting selection mode there would cause to enter in the\
859- // previous if-clause
860- internal.selectionMode = false;
861- }
862- }
863-
864- onPressAndHold: {
865- internal.activateEditor();
866- internal.positionCursor(mouse.x);
867- internal.popupTriggered();
868- }
869-
870- onDoubleClicked: {
871- // select word under doubletap
872- if (!control.activeFocus)
873- return;
874- editor.selectWord();
875- internal.selectionMode = false;
876- }
877- onPressed: {
878- // don't do anything while the control is inactive
879- if (!control.activeFocus || (pressedButtons != Qt.LeftButton))
880- return;
881- internal.activateEditor();
882- if (control.selectedText === "") {
883- internal.resetEditorSelection(mouse.x);
884- internal.selectionMode = true;
885- }
886- }
887- onReleased: {
888- if (!containsMouse)
889- internal.selectionMode = false;
890+ contentWidth: editor.contentWidth
891+ contentHeight: editor.contentHeight
892+
893+ TextInput {
894+ id: editor
895+ // FocusScope will forward focus to this component
896+ focus: true
897+ anchors.verticalCenter: parent.verticalCenter
898+ // get the control's style
899+ clip: true
900+ cursorDelegate: cursor
901+ color: control.__styleInstance.color
902+ selectedTextColor: Theme.palette.selected.foregroundText
903+ selectionColor: Theme.palette.selected.foreground
904+ font.pixelSize: FontUtils.sizeToPixels("medium")
905+ passwordCharacter: "\u2022"
906+ // forward keys to the root element so it can be captured outside of it
907+ Keys.forwardTo: [control]
908+
909+ // overrides
910+ selectByMouse: false
911+ activeFocusOnPress: false
912+
913+ // input selection and navigation handling
914+ Ubuntu.Mouse.forwardTo: [inputHandler]
915+ InputHandler {
916+ id: inputHandler
917+ anchors.fill: parent
918+ main: control
919+ input: editor
920+ flickable: flicker
921+ selectionModeTimeout: control.__styleInstance.selectionModeTimeout
922 }
923 }
924 }
925
926=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/TextAreaStyle.qml'
927--- modules/Ubuntu/Components/Themes/Ambiance/TextAreaStyle.qml 2014-04-20 19:25:12 +0000
928+++ modules/Ubuntu/Components/Themes/Ambiance/TextAreaStyle.qml 2014-04-25 05:29:38 +0000
929@@ -38,6 +38,11 @@
930 property real frameSpacing: units.gu(1)
931 property real overlaySpacing: units.gu(0.5)
932
933+ /*!
934+ Property holding the timeout in milliseconds the component enters into selection mode.
935+ */
936+ property int selectionModeTimeout: 300
937+
938 anchors.fill: parent
939
940 z: -1
941@@ -47,11 +52,6 @@
942 onErrorChanged: (error) ? visuals.errorColor : visuals.backgroundColor;
943 color: visuals.backgroundColor;
944 anchors.fill: parent
945-
946- MouseArea {
947- anchors.fill: parent
948- onPressed: if (!styledItem.activeFocus && styledItem.activeFocusOnPress) styledItem.forceActiveFocus()
949- }
950 }
951
952 Loader {
953
954=== modified file 'modules/Ubuntu/Components/qmldir'
955--- modules/Ubuntu/Components/qmldir 2014-04-20 19:25:12 +0000
956+++ modules/Ubuntu/Components/qmldir 2014-04-25 05:29:38 +0000
957@@ -47,6 +47,7 @@
958
959 internal TextCursor TextCursor.qml
960 internal TextInputPopover TextInputPopover.qml
961+internal InputHandler InputHandler.qml
962
963 #version 1.0
964 Action 1.0 Action.qml
965
966=== modified file 'modules/Ubuntu/Test/UbuntuTestCase.qml'
967--- modules/Ubuntu/Test/UbuntuTestCase.qml 2014-04-20 19:25:12 +0000
968+++ modules/Ubuntu/Test/UbuntuTestCase.qml 2014-04-25 05:29:38 +0000
969@@ -125,6 +125,7 @@
970 if (pressTimeout !== undefined && pressTimeout > 0) {
971 wait(pressTimeout);
972 }
973+ mouseMove(item, x, y, button, modifiers, delay);
974 for (var i = 1; i <= steps; i++) {
975 // mouse moves are all processed immediately, without delay in between events
976 mouseMove(item, x + i * ddx, y + i * ddy, -1, button);
977
978=== added directory 'tests/resources/inputs'
979=== added file 'tests/resources/inputs/TextInputs.qml'
980--- tests/resources/inputs/TextInputs.qml 1970-01-01 00:00:00 +0000
981+++ tests/resources/inputs/TextInputs.qml 2014-04-25 05:29:38 +0000
982@@ -0,0 +1,85 @@
983+/*
984+ * Copyright 2014 Canonical Ltd.
985+ *
986+ * This program is free software; you can redistribute it and/or modify
987+ * it under the terms of the GNU Lesser General Public License as published by
988+ * the Free Software Foundation; version 3.
989+ *
990+ * This program is distributed in the hope that it will be useful,
991+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
992+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
993+ * GNU Lesser General Public License for more details.
994+ *
995+ * You should have received a copy of the GNU Lesser General Public License
996+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
997+ */
998+
999+import QtQuick 2.0
1000+import Ubuntu.Components 0.1
1001+import Ubuntu.Components.ListItems 0.1
1002+import Ubuntu.Components.Popups 0.1
1003+
1004+MainView {
1005+ id: main
1006+ width: units.gu(40)
1007+ height: units.gu(71)
1008+
1009+ applicationName: "TextInputs"
1010+
1011+ Column {
1012+ anchors.fill: parent
1013+ TextArea {
1014+ id: topLine
1015+ autoSize: true
1016+ maximumLineCount: 0
1017+ text: "Lorem Ipsum is simply dummy text\nof the printing and typesetting\nindustry.\n"
1018+ }
1019+ Flickable {
1020+ width: parent.width
1021+ height: parent.height - topLine.height
1022+ objectName: "MainView_Flickable"
1023+ contentHeight: column.childrenRect.height
1024+ onMovingChanged: print(objectName, "moving")
1025+ Column {
1026+ id: column
1027+ anchors.horizontalCenter: parent.horizontalCenter
1028+ spacing: units.gu(1)
1029+ TextField {
1030+ id: field
1031+ objectName: "Standard"
1032+ width: units.gu(30)
1033+ text: "The orange (specifically, the sweet orange) is the fruit of the citrus species Citrus × ​sinensis in the family Rutaceae."
1034+ }
1035+ Button {
1036+ text: "home"
1037+ onClicked: field.cursorPosition = 0;
1038+ }
1039+
1040+ TextField {
1041+ objectName: "Preserving"
1042+ width: units.gu(30)
1043+ text: "Second line."
1044+ selectByMouse: false
1045+ placeholderText: "yeeeeewww!"
1046+ persistentSelection: true
1047+ }
1048+ TextArea {
1049+ text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
1050+ }
1051+ TextArea {
1052+ text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
1053+ persistentSelection: true
1054+ }
1055+ TextArea {
1056+ autoSize: true
1057+ maximumLineCount: 0
1058+ text: "Lorem Ipsum is simply dummy text\nof the printing and typesetting\nindustry.\n"
1059+ }
1060+ TextArea {
1061+ autoSize: true
1062+ maximumLineCount: 5
1063+ }
1064+ }
1065+ }
1066+ }
1067+}
1068
1069=== modified file 'tests/unit_x11/tst_components/tst_textarea.qml'
1070--- tests/unit_x11/tst_components/tst_textarea.qml 2014-04-20 19:25:12 +0000
1071+++ tests/unit_x11/tst_components/tst_textarea.qml 2014-04-25 05:29:38 +0000
1072@@ -16,56 +16,58 @@
1073
1074 import QtQuick 2.0
1075 import QtTest 1.0
1076+import Ubuntu.Test 1.0
1077 import Ubuntu.Components 1.0
1078 import Ubuntu.Components.ListItems 1.0 as ListItem
1079
1080 Item {
1081- width: 200; height: 200
1082+ id: main
1083+ width: units.gu(50); height: units.gu(100)
1084
1085 property bool hasOSK: QuickUtils.inputMethodProvider !== ""
1086
1087- TextArea {
1088- id: textArea
1089- SignalSpy {
1090- id: signalSpy
1091- target: parent
1092- }
1093-
1094- property int keyPressData
1095- property int keyReleaseData
1096- Keys.onPressed: keyPressData = event.key
1097- Keys.onReleased: keyReleaseData = event.key
1098- }
1099-
1100- TextArea {
1101- id: colorTest
1102- color: colorTest.text.length < 4 ? "#0000ff" : "#00ff00"
1103- }
1104-
1105- TextEdit {
1106- id: textEdit
1107- }
1108-
1109- ListItem.Empty {
1110- id: listItem
1111- height: 200
1112- anchors.left: parent.left
1113-
1114- anchors.right: parent.right
1115- SignalSpy {
1116- id: listItemSpy
1117- signalName: "clicked"
1118- target: listItem
1119- }
1120-
1121- TextArea {
1122- id: input
1123- anchors.fill: parent
1124- Component.onCompleted: forceActiveFocus()
1125- }
1126- }
1127-
1128- Item {
1129+ Column {
1130+ TextArea {
1131+ id: textArea
1132+ SignalSpy {
1133+ id: signalSpy
1134+ target: parent
1135+ }
1136+
1137+ property int keyPressData
1138+ property int keyReleaseData
1139+ Keys.onPressed: keyPressData = event.key
1140+ Keys.onReleased: keyReleaseData = event.key
1141+ }
1142+
1143+ TextArea {
1144+ id: colorTest
1145+ color: colorTest.text.length < 4 ? "#0000ff" : "#00ff00"
1146+ }
1147+
1148+ TextEdit {
1149+ id: textEdit
1150+ }
1151+
1152+ ListItem.Empty {
1153+ id: listItem
1154+ height: 200
1155+ anchors.left: parent.left
1156+
1157+ anchors.right: parent.right
1158+ SignalSpy {
1159+ id: listItemSpy
1160+ signalName: "clicked"
1161+ target: listItem
1162+ }
1163+
1164+ TextArea {
1165+ id: input
1166+ anchors.fill: parent
1167+ Component.onCompleted: forceActiveFocus()
1168+ }
1169+ }
1170+
1171 TextArea {
1172 id: t1
1173 objectName: "t1"
1174@@ -74,13 +76,42 @@
1175 id: t2
1176 objectName: "t2"
1177 }
1178+
1179+ TextArea {
1180+ id: longText
1181+ text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."
1182+ }
1183 }
1184
1185- TestCase {
1186+ UbuntuTestCase {
1187 name: "TextAreaAPI"
1188 when: windowShown
1189
1190- function test_1_activate() {
1191+ function cleanup() {
1192+ textArea.focus =
1193+ colorTest.focus =
1194+ textEdit.focus =
1195+ input.focus =
1196+ t1.focus =
1197+ t2.focus =
1198+ longText.focus = false;
1199+ longText.cursorPosition = 0;
1200+ var scroller = findChild(longText, "textarea_scroller");
1201+ scroller.contentY = 0;
1202+ scroller.contentX = 0;
1203+
1204+ textArea.text = "";
1205+ textArea.textFormat = TextEdit.PlainText;
1206+ input.textFormat = TextEdit.PlainText;
1207+ input.text = "";
1208+ input.cursorPosition = 0;
1209+
1210+ // empty event buffer
1211+ wait(200);
1212+ }
1213+
1214+
1215+ function test_activate() {
1216 textArea.forceActiveFocus();
1217 compare(textArea.activeFocus, true, "TextArea is active");
1218 }
1219@@ -150,7 +181,7 @@
1220 compare(textArea.lineCount,textEdit.lineCount,"TextArea.lineCount is same as TextEdit.lineCount")
1221 }
1222
1223- function test_1_mouseSelectionMode() {
1224+ function test_0_mouseSelectionMode() {
1225 compare(textArea.mouseSelectionMode, TextEdit.SelectWords,"TextArea.mouseSelectionMode is SelectWords")
1226 }
1227
1228@@ -206,45 +237,45 @@
1229 compare(textArea.wrapMode,TextEdit.Wrap,"TextArea.wrapMode is TextEdit.Wrap")
1230 }
1231
1232- // TextArea specific properties
1233- function test_1_highlighted() {
1234+ // TextArea specific properties
1235+ function test_0_highlighted() {
1236 compare(textArea.highlighted, textArea.focus, "highlighted is the same as focused");
1237 }
1238
1239- function test_1_contentHeight() {
1240+ function test_contentHeight() {
1241 compare(textArea.contentHeight>0,true,"contentHeight over 0 units on default")
1242 var newValue = 200;
1243 textArea.contentHeight = newValue;
1244 compare(textArea.contentHeight,newValue,"set/get");
1245 }
1246
1247- function test_1_contentWidth() {
1248+ function test_contentWidth() {
1249 compare(textArea.contentWidth,units.gu(30),"contentWidth is 30 units on default")
1250 var newValue = 200;
1251 textArea.contentWidth = newValue;
1252 compare(textArea.contentWidth,newValue,"set/get");
1253 }
1254
1255- function test_1_placeholderText() {
1256+ function test_placeholderText() {
1257 compare(textArea.placeholderText,"","placeholderText is '' on default")
1258 var newValue = "Hello Placeholder";
1259 textArea.placeholderText = newValue;
1260 compare(textArea.placeholderText,newValue,"set/get");
1261 }
1262
1263- function test_1_autoSize() {
1264+ function test_autoSize() {
1265 compare(textArea.autoSize,false,"TextArea.autoSize is set to false");
1266 var newValue = true;
1267 textArea.autoSize = newValue;
1268 compare(textArea.autoSize, newValue,"set/get");
1269 }
1270
1271- function test_1_baseUrl() {
1272+ function test_0_baseUrl() {
1273 expectFail("","TODO")
1274 compare(textArea.baseUrl,"tst_textarea.qml","baseUrl is QML file instantiating the TextArea item on default")
1275 }
1276
1277- function test_1_displayText() {
1278+ function test_displayText() {
1279 compare(textArea.displayText,"","displayText is '' on default")
1280 var newValue = "Hello Display Text";
1281 try {
1282@@ -256,11 +287,11 @@
1283
1284 }
1285
1286- function test_1_popover() {
1287+ function test_0_popover() {
1288 compare(textArea.popover, undefined, "Uses default popover");
1289 }
1290
1291- function test_1_maximumLineCount() {
1292+ function test_maximumLineCount() {
1293 compare(textArea.maximumLineCount,5,"maximumLineCount is 0 on default")
1294 var newValue = 10;
1295 textArea.maximumLineCount = newValue;
1296@@ -272,7 +303,7 @@
1297 compare(textArea.activeFocus, false, "TextArea is inactive");
1298 }
1299
1300- // functions
1301+ // functions
1302 function test_copy() {
1303 textArea.copy();
1304 }
1305@@ -360,13 +391,13 @@
1306 }
1307
1308
1309- // signals
1310+ // signals
1311 function test_linkActivated() {
1312 signalSpy.signalName = "linkActivated";
1313 compare(signalSpy.valid,true,"linkActivated signal exists")
1314 }
1315
1316- // filters
1317+ // filters
1318 function test_keyPressAndReleaseFilter() {
1319 textArea.visible = true;
1320 textArea.forceActiveFocus();
1321@@ -451,7 +482,7 @@
1322 }
1323
1324 // make it to b ethe last test case executed
1325- function test_zz_TextareaInListItem_RichTextEnterCaptured() {
1326+ function test_TextareaInListItem_RichTextEnterCaptured() {
1327 textArea.text = "a<br />b";
1328 textArea.textFormat = TextEdit.RichText;
1329 input.forceActiveFocus();
1330@@ -461,5 +492,232 @@
1331 keyClick(Qt.Key_Return);
1332 compare(input.text, textArea.text, "Formatted text split");
1333 }
1334+
1335+ // selection and scrolling
1336+ SignalSpy {
1337+ id: flickSpy
1338+ signalName: "onMovementEnded"
1339+ }
1340+
1341+ function test_scroll_when_not_focused() {
1342+ var handler = findChild(longText, "input_handler");
1343+ verify(handler);
1344+
1345+ flickSpy.target = findChild(longText, "textarea_scroller");
1346+ flickSpy.clear();
1347+ verify(longText.focus == false);
1348+ var y = longText.height - units.gu(2);
1349+ var my = y / 2;
1350+ var x = longText.width / 2;
1351+ var dy = units.gu(3);
1352+ flick(longText, x, y, 0, -dy);
1353+ verify(longText.focus);
1354+ compare(flickSpy.count, 0, "The input had scrolled while inactive");
1355+ }
1356+
1357+ function test_scroll_when_focused() {
1358+ longText.focus = true;
1359+ var handler = findChild(longText, "input_handler");
1360+ verify(handler);
1361+
1362+ flickSpy.target = findChild(longText, "textarea_scroller");
1363+ flickSpy.clear();
1364+ var y = longText.height - units.gu(2);
1365+ var my = y / 2;
1366+ var x = longText.width / 2;
1367+ var dy = units.gu(3);
1368+ compare(handler.state, "", "The input is not in default state before selection");
1369+ flick(longText, x, my, 0, -dy);
1370+ flickSpy.wait();
1371+ compare(handler.state, "", "The input has not returned to default state.");
1372+ }
1373+
1374+ function test_select_by_pressAndDrag() {
1375+ longText.focus = true;
1376+ var handler = findChild(longText, "input_handler");
1377+ verify(handler);
1378+ var dx = longText.width / 4;
1379+ var x = units.gu(5);
1380+ var y = longText.height / 2;
1381+ compare(handler.state, "", "The input is not in default state before selection");
1382+ flick(longText, x, y, 2*dx, 0, handler.selectionModeTimeout + 50, 10);
1383+ verify(longText.selectedText !== "");
1384+ compare(handler.state, "", "The input has not returned to default state.");
1385+ }
1386+
1387+ function test_select_text_doubletap() {
1388+ longText.focus = true;
1389+ var x = units.gu(2);
1390+ var y = longText.height / 4;
1391+ mouseDoubleClick(longText, x, y);
1392+ expectFail("", "mouseDoubleClick fails to trigger");
1393+ verify(longText.selectedText !== "");
1394+ }
1395+
1396+ function test_scroll_with_selected_text() {
1397+ longText.focus = true;
1398+ var handler = findChild(longText, "input_handler");
1399+ verify(handler);
1400+ var y = longText.height / 2;
1401+ var x = longText.width / 2;
1402+ flickSpy.target = findChild(longText, "textarea_scroller");
1403+ flickSpy.clear();
1404+
1405+ // select text
1406+ compare(handler.state, "", "The input is not in default state before selection");
1407+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
1408+ verify(longText.selectedText !== "");
1409+ compare(handler.state, "", "The input has not returned to default state.");
1410+
1411+ // flick upwards
1412+ flick(longText, x, y, 0, -units.gu(2));
1413+ flickSpy.wait();
1414+ compare(handler.state, "", "The input has not returned to default state.");
1415+ }
1416+
1417+ SignalSpy {
1418+ id: popoverSpy
1419+ signalName: "onPressAndHold"
1420+ }
1421+
1422+ function test_press_and_hold_moves_cursor_position_and_opens_context_menu() {
1423+ longText.focus = true;
1424+ var handler = findChild(longText, "input_handler");
1425+ var y = longText.height / 2;
1426+ flickSpy.target = findChild(longText, "textarea_scroller");
1427+ flickSpy.clear();
1428+ popoverSpy.target = handler;
1429+ popoverSpy.clear();
1430+
1431+ // long press
1432+ compare(handler.state, "", "The input is not in default state before long press");
1433+ mouseLongPress(longText, units.gu(8), y);
1434+ waitForRendering(longText);
1435+ popoverSpy.wait();
1436+ verify(longText.cursorPosition != 0);
1437+ compare(handler.state, "inactive", "The input is not in inactive state while context menu is open");
1438+
1439+ // cleanup, release the mouse, that should bring the handler back to defautl state
1440+ mouseRelease(longText, units.gu(8), y);
1441+ compare(handler.state, "", "The input has not returned to default state.");
1442+ // dismiss popover
1443+ mouseClick(main, 10, 10);
1444+ }
1445+
1446+ function test_press_and_hold_moves_cursor_position_and_opens_context_menu_when_not_focus() {
1447+ var handler = findChild(longText, "input_handler");
1448+ var y = longText.height / 2;
1449+ flickSpy.target = findChild(longText, "textarea_scroller");
1450+ flickSpy.clear();
1451+ popoverSpy.target = handler;
1452+ popoverSpy.clear();
1453+
1454+ // long press
1455+ compare(handler.state, "inactive", "The input is not in inactive state before long press");
1456+ mouseLongPress(longText, units.gu(8), y);
1457+ waitForRendering(longText);
1458+ popoverSpy.wait();
1459+ verify(longText.focus, "The input is not focused");
1460+ verify(longText.cursorPosition != 0, "The cursor wasn't moved");
1461+ compare(handler.state, "inactive", "The input is not in inactive state while the context menu is open");
1462+
1463+ // cleanup, release the mouse, that should bring the handler back to defautl state
1464+ mouseRelease(longText, units.gu(8), y);
1465+ compare(handler.state, "", "The input has not returned to default state.");
1466+ // dismiss popover
1467+ mouseClick(main, 10, 10);
1468+ }
1469+
1470+ function test_press_and_hold_over_selected_text() {
1471+ longText.focus = true;
1472+ var handler = findChild(longText, "input_handler");
1473+ var y = longText.height / 2;
1474+ flickSpy.target = findChild(longText, "textarea_scroller");
1475+ flickSpy.clear();
1476+ popoverSpy.target = handler;
1477+ popoverSpy.clear();
1478+
1479+ // select text
1480+ compare(handler.state, "", "The input is not in default state before long press");
1481+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
1482+ waitForRendering(longText);
1483+ compare(handler.state, "", "The input has not returned to default state.");
1484+ verify(longText.selectedText !== "");
1485+
1486+ mouseLongPress(longText, units.gu(4), y);
1487+ // wait till popover is shown
1488+ waitForRendering(longText);
1489+ popoverSpy.wait();
1490+ // cleanup, release the mouse, that should bring the handler back to default state
1491+ mouseRelease(longText, units.gu(4), y);
1492+ compare(handler.state, "", "The input has not returned to default state.");
1493+ // dismiss popover
1494+ mouseClick(main, 10, 10);
1495+ }
1496+
1497+ function test_move_mouse_while_context_menu_open_does_not_move_selection() {
1498+ longText.focus = true;
1499+ var handler = findChild(longText, "input_handler");
1500+ var y = longText.height / 2;
1501+ flickSpy.target = findChild(longText, "textarea_scroller");
1502+ flickSpy.clear();
1503+ popoverSpy.target = handler;
1504+ popoverSpy.clear();
1505+
1506+ // select text
1507+ compare(handler.state, "", "The input is not in default state before long press");
1508+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
1509+ verify(longText.selectedText !== "");
1510+ var selection = longText.selectedText;
1511+ compare(handler.state, "", "The input has not returned to default state.");
1512+
1513+ mouseLongPress(longText, units.gu(4), y);
1514+ // wait till popover is shown
1515+ waitForRendering(longText);
1516+ popoverSpy.wait();
1517+ // do some mouse moves and compare whether we have the same selection
1518+ mouseMoveSlowly(longText, units.gu(4), y, units.gu(4), units.gu(1), 3, 100);
1519+ compare(selection, longText.selectedText, "Selection differs");
1520+ // cleanup, release the mouse, that should bring the handler back to default state
1521+ mouseRelease(longText, units.gu(8), y + units.gu(1));
1522+ compare(handler.state, "", "The input has not returned to default state.");
1523+ mouseClick(main, 10, 10);
1524+ }
1525+
1526+ function test_clear_selection_by_click_on_selection() {
1527+ longText.focus = true;
1528+ var handler = findChild(longText, "input_handler");
1529+ var y = longText.height / 2;
1530+ flickSpy.target = findChild(longText, "textarea_scroller");
1531+ flickSpy.clear();
1532+
1533+ // select text
1534+ compare(handler.state, "", "The input is not in default state before long press");
1535+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
1536+ compare(handler.state, "", "The input has not returned to default state.");
1537+ verify(longText.selectedText !== "", "There is no selected text");
1538+
1539+ // click on selection
1540+ mouseClick(longText, units.gu(4), y);
1541+ verify(longText.selectedText === "", "There is still selected text");
1542+ }
1543+
1544+ function test_clear_selection_by_click_beside_selection() {
1545+ longText.focus = true;
1546+ var handler = findChild(longText, "input_handler");
1547+ var y = longText.height / 2;
1548+ flickSpy.target = findChild(longText, "textarea_scroller");
1549+ flickSpy.clear();
1550+
1551+ // select text
1552+ compare(handler.state, "", "The input is not in default state before long press");
1553+ flick(longText, 0, y, units.gu(8), units.gu(4), handler.selectionModeTimeout + 50);
1554+ compare(handler.state, "", "The input has not returned to default state.");
1555+ verify(longText.selectedText !== "", "There is no text selected");
1556+
1557+ // click on selection
1558+ mouseClick(longText, units.gu(10), y);
1559+ verify(longText.selectedText === "", "There is still selected text");
1560+ }
1561 }
1562 }
1563
1564=== modified file 'tests/unit_x11/tst_components/tst_textarea_in_flickable.qml'
1565--- tests/unit_x11/tst_components/tst_textarea_in_flickable.qml 2014-04-20 19:25:12 +0000
1566+++ tests/unit_x11/tst_components/tst_textarea_in_flickable.qml 2014-04-25 05:29:38 +0000
1567@@ -16,68 +16,94 @@
1568
1569 import QtQuick 2.0
1570 import QtTest 1.0
1571+import Ubuntu.Test 1.0
1572 import Ubuntu.Components 1.0
1573
1574 Item {
1575 id: root
1576- width: 200; height: 200
1577+ width: units.gu(50); height: units.gu(100)
1578
1579 property bool hasOSK: QuickUtils.inputMethodProvider !== ""
1580
1581 Flickable {
1582 id: flickable
1583 anchors.fill: parent
1584- contentWidth: inFlickable.width
1585- contentHeight: inFlickable.height
1586+ contentHeight: column.childrenRect.height
1587 clip: true
1588
1589- TextArea {
1590- id: inFlickable
1591- width: flickable.width
1592- autoSize: true
1593- maximumLineCount: 0
1594- text: "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1"
1595+ Column {
1596+ id: column
1597+ Text {
1598+ text: "This is a simple label on top of the Flickable"
1599+ }
1600+
1601+ TextArea {
1602+ id: inFlickable
1603+ width: flickable.width
1604+ autoSize: true
1605+ maximumLineCount: 0
1606+ text: "1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1"
1607+ }
1608 }
1609 }
1610
1611 SignalSpy {
1612- id: flickableSpy
1613+ id: moveSpy
1614 target: flickable
1615- signalName: "movingChanged"
1616+ signalName: "onMovementEnded"
1617 }
1618
1619- TestCase {
1620+ UbuntuTestCase {
1621 name: "TextAreaInFlickableAPI"
1622 when: windowShown
1623- // simulates a flick event between \b from and \b to points both relative to the item
1624- // with a given speed
1625- function flick(item, from, to, speed) {
1626- var pointCount = 5;
1627- if (from === undefined)
1628- qtest_fail("source point not defined", 2);
1629- if (to === undefined)
1630- qtest_fail("destination point not defined", 2);
1631- if (speed === undefined)
1632- speed = -1;
1633- else
1634- speed /= pointCount;
1635-
1636- var dx = to.x - from.x;
1637- var dy = to.y - from.y;
1638-
1639- mousePress(item, from.x, from.y);
1640- for (var i = 0; i < pointCount; i++) {
1641- mouseMove(item, from.x + (i + 1) * dx / pointCount, from.y + (i + 1) * dy / pointCount, speed);
1642- }
1643- mouseRelease(item, to.x, to.y);
1644- // empty event queues
1645- wait(500);
1646+
1647+ function init() {
1648+ waitForRendering(flickable, 1000);
1649+ }
1650+
1651+ function cleanup() {
1652+ flickable.contentY = 0;
1653+ moveSpy.clear();
1654+ inFlickable.focus = false;
1655+ inFlickable.cursorPosition = 0;
1656+ // empty event buffer caused by the flick() events
1657+ wait(400);
1658 }
1659
1660 function test_DoNotStealFlickEvents() {
1661- mouseClick(inFlickable, 10, 10);
1662- flick(inFlickable, Qt.point(50, 20), Qt.point(50, 0), 100);
1663- tryCompare(flickableSpy, "count", 2, 200);
1664+ inFlickable.focus = true;
1665+ flick(inFlickable, 50, 150, 0, -150);
1666+ moveSpy.wait();
1667+ }
1668+
1669+ function test_flicker_moves_when_inactive() {
1670+ flick(flickable, 50, 150, 0, -150);
1671+ moveSpy.wait();
1672+ }
1673+
1674+ function test_select_state_locks_outer_flickable() {
1675+ var handler = findChild(inFlickable, "input_handler");
1676+ inFlickable.focus = true;
1677+ // select text
1678+ flick(inFlickable, 50, 50, -50, -50, handler.selectionModeTimeout+ 50);
1679+ compare(moveSpy.count, 0, "The Flickable has moved while the TextArea was in selection mode");
1680+ verify(inFlickable.selectedText !== "", "No text selected");
1681+ }
1682+
1683+ function test_scrolling_input_with_selected_text() {
1684+ var handler = findChild(inFlickable, "input_handler");
1685+ inFlickable.focus = true;
1686+ // select text
1687+ flick(inFlickable, 50, 50, -50, -50, handler.selectionModeTimeout + 100);
1688+ compare(moveSpy.count, 0, "The Flickable has moved while the TextArea was in selection mode");
1689+ verify(inFlickable.selectedText !== "", "No text selected");
1690+
1691+ // scroll
1692+ moveSpy.clear();
1693+ flick(inFlickable, 50, 100, 0, -100);
1694+ // wait till the move ends
1695+ moveSpy.wait();
1696+ verify(inFlickable.selectedText !== "", "There is still text selected");
1697 }
1698 }
1699 }
1700
1701=== modified file 'tests/unit_x11/tst_components/tst_textfield.qml'
1702--- tests/unit_x11/tst_components/tst_textfield.qml 2014-04-20 19:25:12 +0000
1703+++ tests/unit_x11/tst_components/tst_textfield.qml 2014-04-25 05:29:38 +0000
1704@@ -16,12 +16,13 @@
1705
1706 import QtQuick 2.0
1707 import QtTest 1.0
1708+import Ubuntu.Test 1.0
1709 import Ubuntu.Components 1.0
1710 import Ubuntu.Unity.Action 1.1 as UnityActions
1711
1712 Item {
1713 id: textItem
1714- width: 200; height: 200
1715+ width: units.gu(50); height: units.gu(70)
1716
1717 property bool hasOSK: QuickUtils.inputMethodProvider !== ""
1718
1719@@ -32,52 +33,79 @@
1720 t2.focus = false;
1721 }
1722
1723- TextField {
1724- id: colorTest
1725- color: colorTest.text.length < 4 ? "#0000ff" : "#00ff00"
1726- }
1727-
1728- TextField {
1729- id: textField
1730- SignalSpy {
1731- id: signalSpy
1732- target: parent
1733- }
1734-
1735- property int keyPressData
1736- property int keyReleaseData
1737- Keys.onPressed: keyPressData = event.key
1738- Keys.onReleased: keyReleaseData = event.key
1739- action: Action {
1740- enabled: true
1741- name: 'spam'
1742- text: 'Spam'
1743- }
1744- }
1745-
1746- Item {
1747+ Column {
1748+ TextField {
1749+ id: colorTest
1750+ color: colorTest.text.length < 4 ? "#0000ff" : "#00ff00"
1751+ text: "colorTest"
1752+ }
1753+
1754+ TextField {
1755+ id: textField
1756+ SignalSpy {
1757+ id: signalSpy
1758+ target: parent
1759+ }
1760+
1761+ property int keyPressData
1762+ property int keyReleaseData
1763+ Keys.onPressed: keyPressData = event.key
1764+ Keys.onReleased: keyReleaseData = event.key
1765+ action: Action {
1766+ enabled: true
1767+ name: 'spam'
1768+ text: 'Spam'
1769+ }
1770+ }
1771+
1772 TextField {
1773 id: t1
1774+ text: "t1"
1775 }
1776 TextField {
1777 id: t2
1778- }
1779- }
1780-
1781- TextField {
1782- id: enabledTextField
1783- enabled: true
1784- }
1785-
1786- TextField {
1787- id: disabledTextField
1788- enabled: false
1789- }
1790-
1791- TestCase {
1792+ text: "t2"
1793+ }
1794+
1795+ TextField {
1796+ id: enabledTextField
1797+ enabled: true
1798+ text: "enabledTextField"
1799+ }
1800+
1801+ TextField {
1802+ id: disabledTextField
1803+ enabled: false
1804+ text: "disabledTextField"
1805+ }
1806+ TextField {
1807+ id: longText
1808+ text: "The orange (specifically, the sweet orange) is the fruit of the citrus species Citrus × ​sinensis in the family Rutaceae."
1809+ }
1810+ }
1811+
1812+ UbuntuTestCase {
1813 name: "TextFieldAPI"
1814 when: windowShown
1815
1816+ // initialize test objects
1817+ function init() {
1818+ longText.cursorPosition = 0;
1819+ }
1820+
1821+ // empty event buffer
1822+ function cleanup() {
1823+ colorTest.focus =
1824+ textField.focus =
1825+ t1.focus =
1826+ t2.focus =
1827+ enabledTextField.focus =
1828+ longText.focus = false;
1829+ var scroller = findChild(longText, "textfield_scroller");
1830+ scroller.contentX = 0;
1831+ wait(200);
1832+ }
1833+
1834 function initTestCase() {
1835 textField.forceActiveFocus();
1836 compare(textField.focus, true, "TextField is focused");
1837@@ -140,7 +168,9 @@
1838 }
1839
1840 function test_0_cursorVisible() {
1841- compare(textField.cursorVisible, true, "cursorVisible true by default")
1842+ compare(textField.cursorVisible, false, "cursorVisible false by default when inactive");
1843+ textField.focus = true;
1844+ compare(textField.cursorVisible, true, "cursorVisible true by default when active");
1845 }
1846
1847 function test_0_customSoftwareInputPanel() {
1848@@ -291,7 +321,7 @@
1849 compare(textField.keyReleaseData, Qt.Key_Control, "Key release filtered");
1850 }
1851
1852- function test_1_undo_redo() {
1853+ function test_undo_redo() {
1854 textField.readOnly = false;
1855 textField.text = "";
1856 textField.focus = true;
1857@@ -304,7 +334,7 @@
1858 compare(textField.text, "test", "redone");
1859 }
1860
1861- function test_1_getText() {
1862+ function test_getText() {
1863 textField.text = "this is a longer text";
1864 compare(textField.getText(0, 10), "this is a ", "getText(0, 10)");
1865 compare(textField.getText(10, 0), "this is a ", "getText(10, 0)");
1866@@ -312,7 +342,7 @@
1867 compare(textField.getText(4, 0), "this", "getText(4, 0)");
1868 }
1869
1870- function test_1_removeText() {
1871+ function test_removeText() {
1872 textField.text = "this is a longer text";
1873 textField.remove(0, 10);
1874 compare(textField.text, "longer text", "remove(0, 10)");
1875@@ -335,14 +365,14 @@
1876 compare(textField.text, "this is a longer text", "select(0, 4) && remove()");
1877 }
1878
1879- function test_1_moveCursorSelection() {
1880+ function test_moveCursorSelection() {
1881 textField.text = "this is a longer text";
1882 textField.cursorPosition = 5;
1883 textField.moveCursorSelection(9, TextInput.SelectCharacters);
1884 compare(textField.selectedText, "is a", "moveCursorSelection from 5 to 9, selecting the text");
1885 }
1886
1887- function test_1_isRightToLeft() {
1888+ function test_isRightToLeft() {
1889 textField.text = "this is a longer text";
1890 compare(textField.isRightToLeft(0), false, "isRightToLeft(0)");
1891 compare(textField.isRightToLeft(0, 0), false, "isRightToLeft(0, 0)");
1892@@ -393,7 +423,7 @@
1893 }
1894
1895 // need to make the very first test case, otherwise OSK detection fails on phablet
1896- function test_zz_OSK_ShownWhenNextTextFieldIsFocused() {
1897+ function test_OSK_ShownWhenNextTextFieldIsFocused() {
1898 if (!hasOSK)
1899 expectFail("", "OSK can be tested only when present");
1900 t1.focus = true;
1901@@ -402,7 +432,7 @@
1902 compare(Qt.inputMethod.visible, true, "OSK is shown for the second TextField");
1903 }
1904
1905- function test_zz_RemoveOSKWhenFocusLost() {
1906+ function test_RemoveOSKWhenFocusLost() {
1907 if (!hasOSK)
1908 expectFail("", "OSK can be tested only when present");
1909 t1.focus = true;
1910@@ -411,7 +441,7 @@
1911 compare(Qt.inputMethod.visible, false, "OSK is hidden when TextField looses focus");
1912 }
1913
1914- function test_zz_ReEnabledInput() {
1915+ function test_ReEnabledInput() {
1916 textField.forceActiveFocus();
1917 textField.enabled = false;
1918 compare(textField.enabled, false, "textField is disabled");
1919@@ -428,7 +458,7 @@
1920 compare(Qt.inputMethod.visible, true, "OSK shown");
1921 }
1922
1923- function test_zz_Trigger() {
1924+ function test_Trigger() {
1925 signalSpy.signalName = 'accepted'
1926 textField.enabled = true
1927 textField.text = 'eggs'
1928@@ -436,7 +466,7 @@
1929 signalSpy.wait()
1930 }
1931
1932- function test_zz_ActionInputMethodHints() {
1933+ function test_ActionInputMethodHints() {
1934 // Preset digit only for numbers
1935 textField.inputMethodHints = Qt.ImhNone
1936 textField.action.parameterType = UnityActions.Action.Integer
1937@@ -464,22 +494,243 @@
1938
1939 function test_click_enabled_textfield_must_give_focus() {
1940 textField.forceActiveFocus();
1941- compare(
1942- enabledTextField.focus, false,
1943- 'enabledTextField is not focused');
1944- mouseClick(
1945- enabledTextField, enabledTextField.width/2,
1946- enabledTextField.height/2)
1947- compare(
1948- enabledTextField.focus, true, 'enabledTextField is focused')
1949+ compare(enabledTextField.focus, false, 'enabledTextField is not focused');
1950+ mouseClick(enabledTextField, enabledTextField.width/2, enabledTextField.height/2);
1951+ compare(enabledTextField.focus, true, 'enabledTextField is focused');
1952 }
1953
1954 function test_click_disabled_textfield_must_not_give_focus() {
1955- mouseClick(
1956- disabledTextField, disabledTextField.width/2,
1957- disabledTextField.height/2)
1958- compare(
1959- textField.focus, false, 'disabledTextField is not focused');
1960+ mouseClick(disabledTextField, disabledTextField.width/2, disabledTextField.height/2);
1961+ compare(textField.focus, false, 'disabledTextField is not focused');
1962+ }
1963+
1964+
1965+ // text selection
1966+ SignalSpy {
1967+ id: flickSpy
1968+ signalName: "onMovementEnded"
1969+ }
1970+
1971+ function test_scroll_when_not_focused() {
1972+ var handler = findChild(longText, "input_handler");
1973+ verify(handler);
1974+
1975+ flickSpy.target = findChild(longText, "textfield_scroller");
1976+ flickSpy.clear();
1977+ // scroll when inactive
1978+ verify(longText.focus == false);
1979+ var x = longText.width / 2;
1980+ var y = longText.height / 2;
1981+ var dx = x;
1982+ flick(longText, x, y, -dx, 0);
1983+ verify(longText.focus);
1984+ compare(flickSpy.count, 0, "The input had scrolled while inactive");
1985+ }
1986+
1987+ function test_scroll_when_focused() {
1988+ longText.focus = true;
1989+ var handler = findChild(longText, "input_handler");
1990+ verify(handler);
1991+
1992+ flickSpy.target = findChild(longText, "textfield_scroller");
1993+ flickSpy.clear();
1994+ var x = longText.width / 2;
1995+ var y = longText.height / 2;
1996+
1997+ compare(handler.state, "", "The input is not in default state before selection");
1998+ flick(longText, x, y, - x, 0);
1999+ flickSpy.wait();
2000+ compare(handler.state, "", "The input has not returned to default state.");
2001+ }
2002+
2003+ function test_scroll_with_selected_text() {
2004+ longText.focus = true;
2005+ var handler = findChild(longText, "input_handler");
2006+ verify(handler);
2007+ var y = longText.height / 2;
2008+ flickSpy.target = findChild(longText, "textfield_scroller");
2009+ flickSpy.clear();
2010+
2011+ // select text
2012+ compare(handler.state, "", "The input is not in default state before selection");
2013+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
2014+ verify(longText.selectedText !== "");
2015+ compare(handler.state, "", "The input has not returned to default state.");
2016+
2017+ // flick
2018+ var dx = longText.width / 2;
2019+ flick(longText, dx, y, -dx, 0);
2020+ flickSpy.wait();
2021+ compare(handler.state, "", "The input has not returned to default state.");
2022+ verify(longText.selectedText !== "");
2023+ }
2024+
2025+ function test_select_by_pressAndDrag() {
2026+ longText.focus = true;
2027+ var handler = findChild(longText, "input_handler");
2028+ verify(handler);
2029+ var dx = longText.width / 4;
2030+ var x = units.gu(5);
2031+ var y = longText.height / 2;
2032+ compare(handler.state, "", "The input is not in default state before selection");
2033+ flick(longText, x, y, 2*dx, 0, handler.selectionModeTimeout + 50);
2034+ verify(longText.selectedText !== "");
2035+ compare(handler.state, "", "The input has not returned to default state.");
2036+ }
2037+
2038+ function test_select_text_doubletap() {
2039+ longText.focus = true;
2040+ var x = units.gu(2);
2041+ var y = longText.height / 4;
2042+ mouseDoubleClick(longText, x, y);
2043+ expectFail("", "mouseDoubleClick fails to trigger");
2044+ verify(longText.selectedText !== "");
2045+ }
2046+
2047+ SignalSpy {
2048+ id: popoverSpy
2049+ signalName: "onPressAndHold"
2050+ }
2051+
2052+ function test_press_and_hold_moves_cursor_position_and_opens_context_menu() {
2053+ longText.focus = true;
2054+ var handler = findChild(longText, "input_handler");
2055+ var y = longText.height / 2;
2056+ flickSpy.target = findChild(longText, "textfield_scroller");
2057+ flickSpy.clear();
2058+ popoverSpy.target = handler;
2059+ popoverSpy.clear();
2060+
2061+ // long press
2062+ compare(handler.state, "", "The input is not in default state before long press");
2063+ mouseLongPress(longText, units.gu(8), y);
2064+ waitForRendering(longText);
2065+ popoverSpy.wait();
2066+ verify(longText.cursorPosition != 0);
2067+ compare(handler.state, "inactive", "The input is not in inactive state while context menu is open");
2068+
2069+ // cleanup, release the mouse, that should bring the handler back to default state
2070+ mouseRelease(longText, units.gu(8), y);
2071+ compare(handler.state, "", "The input has not returned to default state.");
2072+ // dismiss popover
2073+ mouseClick(longText, 10, 10);
2074+ }
2075+
2076+ function test_press_and_hold_moves_cursor_position_and_opens_context_menu_when_not_focus() {
2077+ var handler = findChild(longText, "input_handler");
2078+ var y = longText.height / 2;
2079+ flickSpy.target = findChild(longText, "textfield_scroller");
2080+ flickSpy.clear();
2081+ popoverSpy.target = handler;
2082+ popoverSpy.clear();
2083+
2084+ // long press
2085+ compare(handler.state, "inactive", "The input is not in inactive state before long press");
2086+ mouseLongPress(longText, units.gu(8), y);
2087+ waitForRendering(longText);
2088+ popoverSpy.wait();
2089+ verify(longText.focus, "The input is not focused");
2090+ verify(longText.cursorPosition != 0, "The cursor wasn't moved");
2091+ compare(handler.state, "inactive", "The input is not in inactive state while the context menu is open");
2092+
2093+ // cleanup, release the mouse, that should bring the handler back to default state
2094+ mouseRelease(longText, units.gu(8), y);
2095+ compare(handler.state, "", "The input has not returned to default state.");
2096+ // dismiss popover
2097+ mouseClick(longText, 10, 10);
2098+ }
2099+
2100+ function test_press_and_hold_over_selected_text() {
2101+ longText.focus = true;
2102+ var handler = findChild(longText, "input_handler");
2103+ var y = longText.height / 2;
2104+ flickSpy.target = findChild(longText, "textfield_scroller");
2105+ flickSpy.clear();
2106+ popoverSpy.target = handler;
2107+ popoverSpy.clear();
2108+
2109+ // select text
2110+ compare(handler.state, "", "The input is not in default state before long press");
2111+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
2112+ waitForRendering(longText);
2113+ compare(handler.state, "", "The input has not returned to default state.");
2114+ verify(longText.selectedText !== "");
2115+
2116+ mouseLongPress(longText, units.gu(7), y);
2117+ // wait till popover is shown
2118+ waitForRendering(longText);
2119+ popoverSpy.wait();
2120+ // cleanup, release the mouse, that should bring the handler back to default state
2121+ mouseRelease(textItem, 0, 0);
2122+ compare(handler.state, "", "The input has not returned to default state.");
2123+ // dismiss popover
2124+ mouseClick(longText, 10, 10);
2125+ }
2126+
2127+ function test_move_mouse_while_context_menu_open_does_not_move_selection() {
2128+ longText.focus = true;
2129+ var handler = findChild(longText, "input_handler");
2130+ var y = longText.height / 2;
2131+ flickSpy.target = findChild(longText, "textfield_scroller");
2132+ flickSpy.clear();
2133+ popoverSpy.target = handler;
2134+ popoverSpy.clear();
2135+
2136+ // select text
2137+ compare(handler.state, "", "The input is not in default state before long press");
2138+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
2139+ verify(longText.selectedText !== "", "Selected text differs");
2140+ var selection = longText.selectedText;
2141+ compare(handler.state, "", "The input has not returned to default state.");
2142+
2143+ mouseLongPress(longText, units.gu(4), y);
2144+ // wait till popover is shown
2145+ waitForRendering(longText);
2146+ popoverSpy.wait();
2147+ // do some mouse moves and compare whether we have the same selection
2148+ mouseMoveSlowly(longText, units.gu(4), y, units.gu(4), 0, 3, 100);
2149+ compare(selection, longText.selectedText, "Selection differs");
2150+
2151+ // cleanup, release the mouse, that should bring the handler back to default state
2152+ mouseRelease(textItem, 0, 0);
2153+ compare(handler.state, "", "The input has not returned to default state.");
2154+ mouseClick(longText, 10, 10);
2155+ }
2156+
2157+ function test_clear_selection_by_click_on_selection() {
2158+ longText.focus = true;
2159+ var handler = findChild(longText, "input_handler");
2160+ var y = longText.height / 2;
2161+ flickSpy.target = findChild(longText, "textfield_scroller");
2162+ flickSpy.clear();
2163+
2164+ // select text
2165+ compare(handler.state, "", "The input is not in default state before long press");
2166+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
2167+ compare(handler.state, "", "The input has not returned to default state.");
2168+ verify(longText.selectedText !== "");
2169+
2170+ // click on selection
2171+ mouseClick(longText, units.gu(4), y);
2172+ verify(longText.selectedText === "");
2173+ }
2174+
2175+ function test_clear_selection_by_click_beside_selection() {
2176+ longText.focus = true;
2177+ var handler = findChild(longText, "input_handler");
2178+ var y = longText.height / 2;
2179+ flickSpy.target = findChild(longText, "textfield_scroller");
2180+ flickSpy.clear();
2181+
2182+ // select text
2183+ compare(handler.state, "", "The input is not in default state before long press");
2184+ flick(longText, 0, y, units.gu(8), 0, handler.selectionModeTimeout + 50);
2185+ compare(handler.state, "", "The input has not returned to default state.");
2186+ verify(longText.selectedText !== "");
2187+
2188+ // click on selection
2189+ mouseClick(longText, units.gu(10), y);
2190+ verify(longText.selectedText === "");
2191 }
2192 }
2193 }
2194
2195=== added file 'tests/unit_x11/tst_mousefilters/HoverEvent.qml.moved'
2196--- tests/unit_x11/tst_mousefilters/HoverEvent.qml.moved 1970-01-01 00:00:00 +0000
2197+++ tests/unit_x11/tst_mousefilters/HoverEvent.qml.moved 2014-04-25 05:29:38 +0000
2198@@ -0,0 +1,38 @@
2199+/*
2200+ * Copyright 2014 Canonical Ltd.
2201+ *
2202+ * This program is free software; you can redistribute it and/or modify
2203+ * it under the terms of the GNU Lesser General Public License as published by
2204+ * the Free Software Foundation; version 3.
2205+ *
2206+ * This program is distributed in the hope that it will be useful,
2207+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2208+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2209+ * GNU Lesser General Public License for more details.
2210+ *
2211+ * You should have received a copy of the GNU Lesser General Public License
2212+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2213+ */
2214+
2215+import QtQuick 2.0
2216+import Ubuntu.Components 0.1
2217+
2218+Item {
2219+ id: root
2220+ width: units.gu(40)
2221+ height: units.gu(71)
2222+
2223+ Rectangle {
2224+ width: units.gu(30)
2225+ height: units.gu(30)
2226+ anchors.centerIn: parent
2227+ color: "blue"
2228+
2229+ MouseArea {
2230+ objectName: "FilterOwner"
2231+ anchors.fill: parent
2232+ hoverEnabled: true
2233+ Mouse.enabled: true
2234+ }
2235+ }
2236+}

Subscribers

People subscribed via source and target branches