Merge lp:~osomon/webbrowser-app/contextual-selection into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 587
Merged at revision: 590
Proposed branch: lp:~osomon/webbrowser-app/contextual-selection
Merge into: lp:webbrowser-app
Diff against target: 680 lines (+340/-61)
14 files modified
.bzrignore (+1/-1)
debian/control (+10/-11)
debian/qtdeclarative5-ubuntu-web-plugin-assets.install (+1/-1)
debian/rules (+3/-1)
src/Ubuntu/Components/Extras/Browser/CMakeLists.txt (+2/-7)
src/Ubuntu/Web/CMakeLists.txt (+3/-0)
src/Ubuntu/Web/Selection.qml (+10/-2)
src/Ubuntu/Web/SelectionHandle.qml (+1/-3)
src/Ubuntu/Web/UbuntuWebView02.qml (+152/-30)
src/Ubuntu/Web/selection02.js (+40/-1)
src/app/WebViewImpl.qml (+4/-4)
tests/autopilot/webbrowser_app/emulators/browser.py (+16/-0)
tests/autopilot/webbrowser_app/tests/http_server.py (+6/-0)
tests/autopilot/webbrowser_app/tests/test_selection.py (+91/-0)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/contextual-selection
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Didier Roche-Tolomelli Approve
Review via email: mp+223760@code.launchpad.net

Commit message

Re-enable contextual selection that had been disabled when switching to oxide.

Packaging change: renamed the qtdeclarative5-ubuntu-ui-extras-browser-plugin-assets package to qtdeclarative5-ubuntu-web-plugin-assets.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
579. By Olivier Tilloy

Fix dismissing the current selection when the screen is rotated.

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

FAILED: Continuous integration, rev:579
http://jenkins.qa.ubuntu.com/job/webbrowser-app-ci/883/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/1057/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/953/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-amd64-ci/82
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-armhf-ci/82
        deb: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-armhf-ci/82/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-i386-ci/82
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/1428/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/1856
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/1856/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/8660
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/821/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/1096
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/1096/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/webbrowser-app-ci/883/rebuild

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

FAILED: Continuous integration, rev:579
http://jenkins.qa.ubuntu.com/job/webbrowser-app-ci/884/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/1060/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/955/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-amd64-ci/83
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-armhf-ci/83
        deb: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-armhf-ci/83/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-i386-ci/83
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/1430/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/1859
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/1859/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/8662
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/822/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/1098
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/1098/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/webbrowser-app-ci/884/rebuild

review: Needs Fixing (continuous-integration)
580. By Olivier Tilloy

Merge the latest changes from trunk and resolve a conflict.

581. By Olivier Tilloy

Autopilot tests for the selection functionality.

582. By Olivier Tilloy

Add a custom Selection emulator.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
583. By Olivier Tilloy

Correctly grab the handles.

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

(Try to) ensure that the selection actions won’t get in the way of resizing by covering the handles.

Since the actions popover is automatically positioned, we don’t have full control over where it will end up, and there are tons of tricky corner cases, but this should at least make the autopilot test reliable at various screen sizes, including N4, N7 and desktop.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

See the inline comment, once that's fixed: +1

review: Needs Fixing
585. By Olivier Tilloy

Remove useless Replaces stanza, thanks didrocks for the review.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

+1 on the packaging change

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

Cleaner way of simulating a long press, and increase the long press duration to trigger the selection on devices.

587. By Olivier Tilloy

Revert the last change as it makes autopilot tests fail on device, but keep the longer duration for the long press that triggers the selection.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2014-05-30 16:16:56 +0000
3+++ .bzrignore 2014-06-27 11:51:58 +0000
4@@ -30,8 +30,8 @@
5 debian/files
6 debian/tmp/
7 debian/qtdeclarative5-ubuntu-ui-extras-browser-plugin/
8-debian/qtdeclarative5-ubuntu-ui-extras-browser-plugin-assets/
9 debian/qtdeclarative5-ubuntu-web-plugin/
10+debian/qtdeclarative5-ubuntu-web-plugin-assets/
11 debian/webapp-container/
12 debian/webapp-container-autopilot/
13 debian/webbrowser-app/
14
15=== modified file 'debian/control'
16--- debian/control 2014-06-16 08:34:26 +0000
17+++ debian/control 2014-06-27 11:51:58 +0000
18@@ -78,7 +78,6 @@
19 ${shlibs:Depends},
20 libqt5webkit5-qmlwebkitplugin,
21 qtdeclarative5-qtquick2-plugin,
22- qtdeclarative5-ubuntu-ui-extras-browser-plugin-assets (>= ${source:Version}),
23 qtdeclarative5-ubuntu-ui-toolkit-plugin,
24 qtdeclarative5-ubuntu-web-plugin (= ${binary:Version}),
25 qtdeclarative5-window-plugin,
26@@ -89,16 +88,6 @@
27 (versions 0.1 based on WebKit and 0.2 based on Oxide), in the
28 Ubuntu.Components.Extras.Browser module.
29
30-Package: qtdeclarative5-ubuntu-ui-extras-browser-plugin-assets
31-Architecture: all
32-Multi-Arch: foreign
33-Depends: ${misc:Depends},
34-Description: Ubuntu web QML plugin assets
35- A standalone QML plugin that contains the UbuntuWebView component
36- (versions 0.1 based on WebKit and 0.2 based on Oxide), in the
37- Ubuntu.Components.Extras.Browser module. This package contains the
38- PNGs used as UI elements by the plugin.
39-
40 Package: qtdeclarative5-ubuntu-web-plugin
41 Architecture: any
42 Multi-Arch: same
43@@ -108,11 +97,21 @@
44 liboxideqt-qmlplugin,
45 qtdeclarative5-qtquick2-plugin,
46 qtdeclarative5-ubuntu-ui-toolkit-plugin,
47+ qtdeclarative5-ubuntu-web-plugin-assets (>= ${source:Version}),
48 qtdeclarative5-window-plugin,
49 Description: Ubuntu web QML plugin
50 A standalone QML plugin that contains the WebView component,
51 in the Ubuntu.Web module.
52
53+Package: qtdeclarative5-ubuntu-web-plugin-assets
54+Architecture: all
55+Multi-Arch: foreign
56+Depends: ${misc:Depends},
57+Description: Ubuntu web QML plugin assets
58+ A standalone QML plugin that contains the WebView component,
59+ in the Ubuntu.Web module. This package contains the PNGs used
60+ as UI elements by the plugin.
61+
62 Package: webbrowser-app-autopilot
63 Architecture: all
64 Multi-Arch: foreign
65
66=== renamed file 'debian/qtdeclarative5-ubuntu-ui-extras-browser-plugin-assets.install' => 'debian/qtdeclarative5-ubuntu-web-plugin-assets.install'
67--- debian/qtdeclarative5-ubuntu-ui-extras-browser-plugin-assets.install 2013-07-18 14:15:53 +0000
68+++ debian/qtdeclarative5-ubuntu-web-plugin-assets.install 2014-06-27 11:51:58 +0000
69@@ -1,1 +1,1 @@
70-usr/share/qtdeclarative5-ubuntu-ui-extras-browser-plugin/
71+usr/share/qtdeclarative5-ubuntu-web-plugin/
72
73=== modified file 'debian/rules'
74--- debian/rules 2014-03-04 09:32:47 +0000
75+++ debian/rules 2014-06-27 11:51:58 +0000
76@@ -11,7 +11,9 @@
77 dh $@ --parallel --with translations
78
79 override_dh_install:
80- ln -sf /usr/share/qtdeclarative5-ubuntu-ui-extras-browser-plugin/assets \
81+ ln -sf /usr/share/qtdeclarative5-ubuntu-web-plugin/assets \
82+ $(CURDIR)/debian/tmp/usr/lib/*/qt5/qml/Ubuntu/Web
83+ ln -sf /usr/share/qtdeclarative5-ubuntu-web-plugin/assets \
84 $(CURDIR)/debian/tmp/usr/lib/*/qt5/qml/Ubuntu/Components/Extras/Browser
85 dh_install --fail-missing
86
87
88=== modified file 'src/Ubuntu/Components/Extras/Browser/CMakeLists.txt'
89--- src/Ubuntu/Components/Extras/Browser/CMakeLists.txt 2014-05-29 17:03:44 +0000
90+++ src/Ubuntu/Components/Extras/Browser/CMakeLists.txt 2014-06-27 11:51:58 +0000
91@@ -13,19 +13,14 @@
92 file(GLOB QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml qmldir *.js)
93 install(TARGETS ${PLUGIN} DESTINATION ${WEBBROWSER_IMPORTS_DIR})
94 install(FILES ${QML_FILES} DESTINATION ${WEBBROWSER_IMPORTS_DIR})
95-install(DIRECTORY assets
96- DESTINATION ${CMAKE_INSTALL_DATADIR}/qtdeclarative5-ubuntu-ui-extras-browser-plugin
97- FILES_MATCHING PATTERN *.png)
98
99 if(NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
100 # copy qml files and assets over to build dir to be able to import them uninstalled
101- file(GLOB ASSETS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} assets/*)
102- set(copied ${QML_FILES} ${ASSETS})
103- foreach(_file ${copied})
104+ foreach(_file ${QML_FILES})
105 add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_file}
106 DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_file}
107 COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${_file} ${CMAKE_CURRENT_BINARY_DIR}/${_file})
108 endforeach(_file)
109- add_custom_target(copy_files_to_build_dir_legacy DEPENDS ${copied})
110+ add_custom_target(copy_files_to_build_dir_legacy DEPENDS ${QML_FILES})
111 add_dependencies(${PLUGIN} copy_files_to_build_dir_legacy)
112 endif()
113
114=== added symlink 'src/Ubuntu/Components/Extras/Browser/Selection.qml'
115=== target is u'../../../Web/Selection.qml'
116=== added symlink 'src/Ubuntu/Components/Extras/Browser/SelectionHandle.qml'
117=== target is u'../../../Web/SelectionHandle.qml'
118=== added symlink 'src/Ubuntu/Components/Extras/Browser/assets'
119=== target is u'../../../Web/assets/'
120=== modified file 'src/Ubuntu/Web/CMakeLists.txt'
121--- src/Ubuntu/Web/CMakeLists.txt 2014-05-29 17:03:44 +0000
122+++ src/Ubuntu/Web/CMakeLists.txt 2014-06-27 11:51:58 +0000
123@@ -13,6 +13,9 @@
124 file(GLOB QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml qmldir *.js)
125 install(TARGETS ${PLUGIN} DESTINATION ${UBUNTU_WEB_IMPORTS_DIR})
126 install(FILES ${QML_FILES} DESTINATION ${UBUNTU_WEB_IMPORTS_DIR})
127+install(DIRECTORY assets
128+ DESTINATION ${CMAKE_INSTALL_DATADIR}/qtdeclarative5-ubuntu-web-plugin
129+ FILES_MATCHING PATTERN *.png)
130
131 if(NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
132 # copy qml files over to build dir to be able to import them uninstalled
133
134=== renamed file 'src/Ubuntu/Components/Extras/Browser/Selection.qml' => 'src/Ubuntu/Web/Selection.qml'
135--- src/Ubuntu/Components/Extras/Browser/Selection.qml 2013-03-08 10:53:16 +0000
136+++ src/Ubuntu/Web/Selection.qml 2014-06-27 11:51:58 +0000
137@@ -1,5 +1,5 @@
138 /*
139- * Copyright 2013 Canonical Ltd.
140+ * Copyright 2013-2014 Canonical Ltd.
141 *
142 * This file is part of webbrowser-app.
143 *
144@@ -27,16 +27,20 @@
145 property real __minimumWidth: units.gu(5)
146 property real __minimumHeight: units.gu(5)
147
148+ readonly property bool resizing: __leftHandle.dragging || __topHandle.dragging || __rightHandle.dragging || __bottomHandle.dragging
149+
150 signal resized()
151+ signal dismissed()
152
153 MouseArea {
154 anchors.fill: parent
155 // dismiss the selection when tapping anywhere except for the handles
156- onClicked: __container.visible = false
157+ onClicked: __container.dismissed()
158 }
159
160 Item {
161 id: __rect
162+ objectName: "rectangle"
163 }
164
165 Rectangle {
166@@ -91,6 +95,7 @@
167
168 SelectionHandle {
169 id: __leftHandle
170+ objectName: "leftHandle"
171 axis: Drag.XAxis
172 x: __rect.x - width / 2
173 y: (__topHandle.y + __bottomHandle.y) / 2
174@@ -107,6 +112,7 @@
175
176 SelectionHandle {
177 id: __topHandle
178+ objectName: "topHandle"
179 axis: Drag.YAxis
180 x: (__leftHandle.x + __rightHandle.x) / 2
181 y: __rect.y - height / 2
182@@ -123,6 +129,7 @@
183
184 SelectionHandle {
185 id: __rightHandle
186+ objectName: "rightHandle"
187 axis: Drag.XAxis
188 x: __rect.x + __rect.width - width / 2
189 y: (__topHandle.y + __bottomHandle.y) / 2
190@@ -138,6 +145,7 @@
191
192 SelectionHandle {
193 id: __bottomHandle
194+ objectName: "bottomHandle"
195 axis: Drag.YAxis
196 x: (__leftHandle.x + __rightHandle.x) / 2
197 y: __rect.y + __rect.height - height / 2
198
199=== renamed file 'src/Ubuntu/Components/Extras/Browser/SelectionHandle.qml' => 'src/Ubuntu/Web/SelectionHandle.qml'
200--- src/Ubuntu/Components/Extras/Browser/SelectionHandle.qml 2013-03-08 10:53:16 +0000
201+++ src/Ubuntu/Web/SelectionHandle.qml 2014-06-27 11:51:58 +0000
202@@ -1,5 +1,5 @@
203 /*
204- * Copyright 2013 Canonical Ltd.
205+ * Copyright 2013-2014 Canonical Ltd.
206 *
207 * This file is part of webbrowser-app.
208 *
209@@ -23,8 +23,6 @@
210 property int axis
211 property real minimum
212 property real maximum
213- // Known issue: when dragging outside the window, the drag is canceled,
214- // but dragging remains true. See QTBUG-29146.
215 property bool dragging: __mousearea.drag.active
216
217 width: units.gu(3)
218
219=== modified file 'src/Ubuntu/Web/UbuntuWebView02.qml'
220--- src/Ubuntu/Web/UbuntuWebView02.qml 2014-06-11 09:31:38 +0000
221+++ src/Ubuntu/Web/UbuntuWebView02.qml 2014-06-27 11:51:58 +0000
222@@ -1,5 +1,5 @@
223 /*
224- * Copyright 2013 Canonical Ltd.
225+ * Copyright 2013-2014 Canonical Ltd.
226 *
227 * This file is part of webbrowser-app.
228 *
229@@ -62,29 +62,36 @@
230 msgId: "contextmenu"
231 contexts: ["oxide://selection/"]
232 callback: function(msg, frame) {
233- if (('img' in msg.args) || ('href' in msg.args)) {
234- if (internal.currentContextualMenu != null) {
235- PopupUtils.close(internal.currentContextualMenu)
236- }
237- contextualData.clear()
238- if ('img' in msg.args) {
239- contextualData.img = msg.args.img
240- }
241- if ('href' in msg.args) {
242- contextualData.href = msg.args.href
243- contextualData.title = msg.args.title
244- }
245- if (contextualActions != null) {
246- for (var i = 0; i < contextualActions.actions.length; ++i) {
247- if (contextualActions.actions[i].enabled) {
248- contextualRectangle.position(msg.args)
249- internal.currentContextualMenu = PopupUtils.open(contextualPopover, contextualRectangle)
250- break
251- }
252- }
253- }
254- } else if (internal.currentContextualMenu != null) {
255- PopupUtils.close(internal.currentContextualMenu)
256+ internal.dismissCurrentContextualMenu()
257+ internal.dismissCurrentSelection()
258+ internal.fillContextualData(msg.args)
259+ if (contextualActions != null) {
260+ for (var i = 0; i < contextualActions.actions.length; ++i) {
261+ if (contextualActions.actions[i].enabled) {
262+ contextualRectangle.position(msg.args)
263+ internal.currentContextualMenu = PopupUtils.open(contextualPopover, contextualRectangle)
264+ break
265+ }
266+ }
267+ }
268+ }
269+ },
270+ Oxide.ScriptMessageHandler {
271+ msgId: "selection"
272+ contexts: ["oxide://selection/"]
273+ callback: function(msg, frame) {
274+ internal.dismissCurrentSelection()
275+ internal.dismissCurrentContextualMenu()
276+ if (selectionActions != null) {
277+ for (var i = 0; i < selectionActions.actions.length; ++i) {
278+ if (selectionActions.actions[i].enabled) {
279+ var mimedata = internal.buildMimedata(msg.args)
280+ var bounds = internal.computeBounds(msg.args)
281+ internal.currentSelection = selection.createObject(_webview, {mimedata: mimedata, bounds: bounds})
282+ internal.currentSelection.showActions()
283+ break
284+ }
285+ }
286 }
287 }
288 },
289@@ -92,9 +99,8 @@
290 msgId: "scroll"
291 contexts: ["oxide://selection/"]
292 callback: function(msg, frame) {
293- if (internal.currentContextualMenu != null) {
294- PopupUtils.close(internal.currentContextualMenu)
295- }
296+ internal.dismissCurrentContextualMenu()
297+ internal.dismissCurrentSelection()
298 }
299 }
300 ]
301@@ -140,10 +146,127 @@
302 }
303 }
304
305+ property ActionList selectionActions
306+ onSelectionActionsChanged: {
307+ for (var i in selectionActions.actions) {
308+ selectionActions.actions[i].onTriggered.connect(function () {
309+ internal.dismissCurrentSelection()
310+ })
311+ }
312+ }
313+ Component {
314+ id: selection
315+ Selection {
316+ anchors.fill: parent
317+ property var mimedata: null
318+ property rect bounds
319+ onBoundsChanged: {
320+ rect.x = bounds.x
321+ rect.y = bounds.y
322+ rect.width = bounds.width
323+ rect.height = bounds.height
324+ }
325+ property Item actions: null
326+ Component {
327+ id: selectionPopover
328+ ActionSelectionPopover {
329+ objectName: "selectionActions"
330+ autoClose: false
331+ actions: selectionActions
332+ }
333+ }
334+ function showActions() {
335+ if (actions != null) {
336+ actions.destroy()
337+ }
338+ actions = PopupUtils.open(selectionPopover, rect)
339+ }
340+ onResizingChanged: {
341+ if (resizing) {
342+ if (actions != null) {
343+ actions.destroy()
344+ }
345+ }
346+ }
347+ onResized: {
348+ var args = {x: rect.x, y: rect.y, width: rect.width, height: rect.height}
349+ var msg = _webview.rootFrame.sendMessage("oxide://selection/", "adjustselection", args)
350+ msg.onreply = function(response) {
351+ internal.currentSelection.mimedata = internal.buildMimedata(response)
352+ // Ensure that the bounds are updated
353+ internal.currentSelection.bounds = Qt.rect(0, 0, 0, 0)
354+ internal.currentSelection.bounds = internal.computeBounds(response)
355+ internal.currentSelection.showActions()
356+ }
357+ msg.onerror = function(error) {
358+ internal.dismissCurrentSelection()
359+ }
360+ }
361+ onDismissed: internal.dismissCurrentSelection()
362+ }
363+ }
364+ function copy() {
365+ if (internal.currentSelection != null) {
366+ Clipboard.push(internal.currentSelection.mimedata)
367+ } else {
368+ console.warn("No current selection")
369+ }
370+ }
371+
372 QtObject {
373 id: internal
374 property int lastLoadRequestStatus: -1
375 property Item currentContextualMenu: null
376+ property Item currentSelection: null
377+
378+ function fillContextualData(data) {
379+ contextualData.clear()
380+ if ('img' in data) {
381+ contextualData.img = data.img
382+ }
383+ if ('href' in data) {
384+ contextualData.href = data.href
385+ contextualData.title = data.title
386+ }
387+ }
388+
389+ function buildMimedata(data) {
390+ var mimedata = Clipboard.newData()
391+ if ('html' in data) {
392+ mimedata.html = data.html
393+ }
394+ // FIXME: push the text and image data in the order
395+ // they appear in the selected block.
396+ if ('text' in data) {
397+ mimedata.text = data.text
398+ }
399+ if ('images' in data) {
400+ // TODO: download and cache the images locally
401+ // (grab them from the webview’s cache, if possible),
402+ // and forward local URLs.
403+ mimedata.urls = data.images
404+ }
405+ return mimedata
406+ }
407+
408+ function computeBounds(data) {
409+ return Qt.rect(data.left * data.scaleX, data.top * data.scaleY,
410+ data.width * data.scaleX, data.height * data.scaleY)
411+ }
412+
413+ function dismissCurrentContextualMenu() {
414+ if (currentContextualMenu != null) {
415+ PopupUtils.close(currentContextualMenu)
416+ }
417+ }
418+
419+ function dismissCurrentSelection() {
420+ if (currentSelection != null) {
421+ // For some reason a 0 delay fails to destroy the selection
422+ // when it was requested upon a screen orientation change…
423+ currentSelection.destroy(1)
424+ }
425+ }
426 }
427
428 readonly property bool lastLoadSucceeded: internal.lastLoadRequestStatus === Oxide.LoadEvent.TypeSucceeded
429@@ -157,9 +280,8 @@
430
431 readonly property int screenOrientation: Screen.orientation
432 onScreenOrientationChanged: {
433- if (internal.currentContextualMenu != null) {
434- PopupUtils.close(internal.currentContextualMenu)
435- }
436+ internal.dismissCurrentContextualMenu()
437+ internal.dismissCurrentSelection()
438 }
439
440 onFullscreenRequested: _webview.fullscreen = fullscreen
441
442=== renamed directory 'src/Ubuntu/Components/Extras/Browser/assets' => 'src/Ubuntu/Web/assets'
443=== modified file 'src/Ubuntu/Web/selection02.js'
444--- src/Ubuntu/Web/selection02.js 2014-05-29 16:45:21 +0000
445+++ src/Ubuntu/Web/selection02.js 2014-06-27 11:51:58 +0000
446@@ -16,6 +16,12 @@
447 * along with this program. If not, see <http://www.gnu.org/licenses/>.
448 */
449
450+function elementContainedInBox(element, box) {
451+ var rect = element.getBoundingClientRect();
452+ return ((box.left <= rect.left) && (box.right >= rect.right) &&
453+ (box.top <= rect.top) && (box.bottom >= rect.bottom));
454+}
455+
456 function getImgFullUri(uri) {
457 if ((uri.slice(0, 7) === 'http://') ||
458 (uri.slice(0, 8) === 'https://') ||
459@@ -98,15 +104,48 @@
460 return data;
461 }
462
463+function adjustSelection(selection) {
464+ // FIXME: allow selecting two consecutive blocks, instead of
465+ // interpolating to the containing block.
466+ var centerX = (selection.left + selection.right) / 2;
467+ var centerY = (selection.top + selection.bottom) / 2;
468+ var element = document.elementFromPoint(centerX, centerY);
469+ var parent = element;
470+ while (elementContainedInBox(parent, selection)) {
471+ parent = parent.parentNode;
472+ }
473+ element = parent;
474+ return getSelectedData(element);
475+}
476+
477 document.documentElement.addEventListener('contextmenu', function(event) {
478 var element = document.elementFromPoint(event.clientX, event.clientY);
479 var data = getSelectedData(element);
480 var w = document.defaultView;
481 data['scaleX'] = w.outerWidth / w.innerWidth * w.devicePixelRatio;
482 data['scaleY'] = w.outerHeight / w.innerHeight * w.devicePixelRatio;
483- oxide.sendMessage('contextmenu', data);
484+ if (('img' in data) || ('href' in data)) {
485+ oxide.sendMessage('contextmenu', data);
486+ } else {
487+ oxide.sendMessage('selection', data);
488+ }
489 });
490
491 document.defaultView.addEventListener('scroll', function(event) {
492 oxide.sendMessage('scroll', {});
493 });
494+
495+oxide.addMessageHandler("adjustselection", function (msg) {
496+ var w = document.defaultView;
497+ var scaleX = w.outerWidth / w.innerWidth * w.devicePixelRatio;
498+ var scaleY = w.outerHeight / w.innerHeight * w.devicePixelRatio;
499+ var selection = new Object;
500+ selection.left = msg.args.x / scaleX;
501+ selection.right = selection.left + msg.args.width / scaleX;
502+ selection.top = msg.args.y / scaleY;
503+ selection.bottom = selection.top + msg.args.height / scaleY;
504+ var adjusted = adjustSelection(selection);
505+ adjusted['scaleX'] = scaleX;
506+ adjusted['scaleY'] = scaleY;
507+ msg.reply(adjusted);
508+});
509
510=== modified file 'src/app/WebViewImpl.qml'
511--- src/app/WebViewImpl.qml 2014-06-18 05:55:37 +0000
512+++ src/app/WebViewImpl.qml 2014-06-27 11:51:58 +0000
513@@ -20,7 +20,7 @@
514 import Ubuntu.Components 0.1
515 import Ubuntu.Components.Popups 0.1
516 import Ubuntu.Web 0.2
517-//import "actions" as Actions
518+import "actions" as Actions
519
520 WebView {
521 id: webview
522@@ -42,11 +42,11 @@
523 source: formFactor == "desktop" ? "FilePickerDialog.qml" : "ContentPickerDialog.qml"
524 }
525
526- /*selectionActions: ActionList {
527+ selectionActions: ActionList {
528 Actions.Copy {
529- onTriggered: selection.copy()
530+ onTriggered: copy()
531 }
532- }*/
533+ }
534
535 onGeolocationPermissionRequested: {
536 if (webview.toolbar) {
537
538=== modified file 'tests/autopilot/webbrowser_app/emulators/browser.py'
539--- tests/autopilot/webbrowser_app/emulators/browser.py 2014-06-18 13:57:07 +0000
540+++ tests/autopilot/webbrowser_app/emulators/browser.py 2014-06-27 11:51:58 +0000
541@@ -21,6 +21,15 @@
542 pass
543
544
545+class Selection(uitk.UbuntuUIToolkitEmulatorBase):
546+
547+ def get_rectangle(self):
548+ return self.select_single("QQuickItem", objectName="rectangle")
549+
550+ def get_handle(self, name):
551+ return self.select_single("SelectionHandle", objectName=name)
552+
553+
554 class Browser(uitk.MainView):
555
556 """
557@@ -106,3 +115,10 @@
558
559 def get_geolocation_dialog(self):
560 return self.wait_select_single("GeolocationPermissionRequest")
561+
562+ def get_selection(self):
563+ return self.wait_select_single(Selection)
564+
565+ def get_selection_actions(self):
566+ return self.wait_select_single("ActionSelectionPopover",
567+ objectName="selectionActions")
568
569=== modified file 'tests/autopilot/webbrowser_app/tests/http_server.py'
570--- tests/autopilot/webbrowser_app/tests/http_server.py 2014-06-18 13:57:07 +0000
571+++ tests/autopilot/webbrowser_app/tests/http_server.py 2014-06-27 11:51:58 +0000
572@@ -121,6 +121,12 @@
573 html += 'navigator.geolocation.getCurrentPosition('
574 html += 'function r(p) {});</script></body></html>'
575 self.send_html(html)
576+ elif self.path == "/selection":
577+ self.send_response(200)
578+ html = '<html><body style="margin: 10%">'
579+ html += '<div style="position: absolute; width: 50%; height: 50%; '
580+ html += 'top: 25%; left: 25%"></div></body></html>'
581+ self.send_html(html)
582 else:
583 self.send_error(404)
584
585
586=== added file 'tests/autopilot/webbrowser_app/tests/test_selection.py'
587--- tests/autopilot/webbrowser_app/tests/test_selection.py 1970-01-01 00:00:00 +0000
588+++ tests/autopilot/webbrowser_app/tests/test_selection.py 2014-06-27 11:51:58 +0000
589@@ -0,0 +1,91 @@
590+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
591+#
592+# Copyright 2014 Canonical
593+#
594+# This program is free software: you can redistribute it and/or modify it
595+# under the terms of the GNU General Public License version 3, as published
596+# by the Free Software Foundation.
597+#
598+# This program is distributed in the hope that it will be useful,
599+# but WITHOUT ANY WARRANTY; without even the implied warranty of
600+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
601+# GNU General Public License for more details.
602+#
603+# You should have received a copy of the GNU General Public License
604+# along with this program. If not, see <http://www.gnu.org/licenses/>.
605+
606+import time
607+from autopilot.platform import model
608+from autopilot.matchers import Eventually
609+from testtools.matchers import Equals, GreaterThan, LessThan
610+
611+from webbrowser_app.tests import StartOpenRemotePageTestCaseBase
612+
613+
614+class TestSelection(StartOpenRemotePageTestCaseBase):
615+
616+ def setUp(self):
617+ super(TestSelection, self).setUp()
618+ url = self.base_url + "/selection"
619+ self.go_to_url(url)
620+ self.assert_page_eventually_loaded(url)
621+ webview = self.main_window.get_current_webview()
622+ self.pointing_device.move_to_object(webview)
623+ if model() == 'Desktop':
624+ self.pointing_device.click(button=3)
625+ else:
626+ self.pointing_device.press()
627+ time.sleep(1.5)
628+ self.pointing_device.release()
629+ self.selection = self.main_window.get_selection()
630+ self.rectangle = self.selection.get_rectangle()
631+ self.assertThat(self.rectangle.width, LessThan(webview.width))
632+ self.assertThat(self.rectangle.height, LessThan(webview.height))
633+ self.actions = self.main_window.get_selection_actions()
634+ self.assertThat(len(self.actions.select_many("Empty")), Equals(1))
635+
636+ def assert_selection_eventually_dismissed(self):
637+ self.actions.wait_until_destroyed()
638+ self.selection.wait_until_destroyed()
639+
640+ def test_copy_selection(self):
641+ copy_action = self.actions.select_single("Empty")
642+ self.pointing_device.click_object(copy_action)
643+ self.assert_selection_eventually_dismissed()
644+
645+ def test_cancel_selection(self):
646+ webview = self.main_window.get_current_webview()
647+ x = int((webview.globalRect.x + self.rectangle.globalRect.x) / 2)
648+ y = int(webview.globalRect.y + webview.globalRect.height / 2)
649+ self.pointing_device.move(x, y)
650+ self.pointing_device.click()
651+ self.assert_selection_eventually_dismissed()
652+
653+ def test_resize_selection(self):
654+ webview = self.main_window.get_current_webview()
655+ rect = self.rectangle.globalRect
656+
657+ # Grow selection to the right
658+ handle = self.selection.get_handle("rightHandle")
659+ x0 = handle.globalRect.x + int(handle.globalRect.width / 2)
660+ y0 = handle.globalRect.y + int(handle.globalRect.height / 2)
661+ x1 = int((x0 + webview.globalRect.x + webview.globalRect.width) / 2)
662+ y1 = y0
663+ self.pointing_device.drag(x0, y0, x1, y1)
664+ self.assertThat(self.rectangle.width,
665+ Eventually(GreaterThan(rect.width)))
666+ self.assertThat(self.rectangle.height,
667+ Eventually(GreaterThan(rect.height)))
668+ self.actions.wait_until_destroyed()
669+ self.actions = self.main_window.get_selection_actions()
670+
671+ # Shrink selection from the bottom
672+ handle = self.selection.get_handle("bottomHandle")
673+ x0 = handle.globalRect.x + int(handle.globalRect.width / 2)
674+ y0 = handle.globalRect.y + int(handle.globalRect.height / 2)
675+ x1 = x0
676+ y1 = webview.globalRect.y + int(webview.globalRect.height * 0.6)
677+ self.pointing_device.drag(x0, y0, x1, y1)
678+ self.assertThat(self.rectangle.globalRect, Eventually(Equals(rect)))
679+ self.actions.wait_until_destroyed()
680+ self.actions = self.main_window.get_selection_actions()

Subscribers

People subscribed via source and target branches

to status/vote changes: