Merge lp:~ahayzen/webbrowser-app/migrate-to-tabs-component into lp:webbrowser-app/staging
- migrate-to-tabs-component
- Merge into staging
Status: | Merged |
---|---|
Approved by: | Olivier Tilloy |
Approved revision: | 1576 |
Merged at revision: | 1610 |
Proposed branch: | lp:~ahayzen/webbrowser-app/migrate-to-tabs-component |
Merge into: | lp:webbrowser-app/staging |
Diff against target: |
1522 lines (+201/-817) 16 files modified
debian/control (+1/-0) src/app/webbrowser/Browser.qml (+65/-68) src/app/webbrowser/BrowserTab.qml (+9/-1) src/app/webbrowser/CMakeLists.txt (+0/-1) src/app/webbrowser/Chrome.qml (+22/-21) src/app/webbrowser/TabsBar.qml (+0/-345) src/app/webbrowser/drag-helper.cpp (+0/-203) src/app/webbrowser/drag-helper.h (+0/-91) src/app/webbrowser/tabs-model.cpp (+19/-3) src/app/webbrowser/webbrowser-app.cpp (+1/-4) src/app/webbrowser/webbrowser-app.qml (+3/-28) tests/autopilot/webbrowser_app/emulators/browser.py (+3/-4) tests/unittests/qml/CMakeLists.txt (+0/-1) tests/unittests/qml/tst_QmlTests.cpp (+1/-4) tests/unittests/qml/tst_TabsBar.qml (+56/-38) tests/unittests/tabs-model/tst_TabsModelTests.cpp (+21/-5) |
To merge this branch: | bzr merge lp:~ahayzen/webbrowser-app/migrate-to-tabs-component |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Olivier Tilloy | Approve | ||
Review via email: mp+312340@code.launchpad.net |
Commit message
Description of the change
# UI Extras
bzr branch lp:~phablet-team/ubuntu-ui-extras/tabs
cd tabs
cmake .
make
# Running webbrowser
cmake .
make
QML2_IMPORT_
# Snapcraft
The snapcraft should build and pull in the ui-extras component
snapcraft
snap install --devmode webbrowser*.snap
snap run webbrowser-app
# Testing
QML2_IMPORT_
QML2_IMPORT_
# My results
## ctest
100% tests passed, 0 tests failed out of 26
Total Test time (real) = 98.53 sec
## Autopilot
Ran 204 tests in 1498.067s
OK
Olivier Tilloy (osomon) wrote : | # |
The functionality seems to work well. Visually the tabs look very short though, a few pixels taller would make them easier targets for a mouse cursor. That’s stressed by the vertical space between the window title bar and the tabs themselves, which looks too tall to my eye. And when hovering over a non active tab just next to the currently active one, the highlight itself is taller than the current tab, is that intended? (see http://
I guess my question really is: has that been validated by visual design?
- 1564. By Andrew Hayzen
-
* Remove top margin from tabs bar - spec says height should be 3GU
* Change ui-extras to be a stage package - 1565. By Andrew Hayzen
-
* Merge of staging
- 1566. By Andrew Hayzen
-
* Add extras to prime
- 1567. By Andrew Hayzen
-
* Add libexiv2 to prime
Andrew Hayzen (ahayzen) wrote : | # |
Fixed the issues with the snapcraft, at the moment ui-extras isn't within u-a-p (maybe we can request it if we think it is worth it?)
Removed the top margin, and set the height of the tabs bar to be 3GU - which is the same as the ubuntu-terminal-app and what the design spec says.
Please rereview :-)
- 1568. By Andrew Hayzen
-
* Add ui-extras to deb build
- 1569. By Andrew Hayzen
-
* Set the color of the background of the tabs bar to #D9D9D9 as per design
Olivier Tilloy (osomon) wrote : | # |
ubuntu-ui-extras is being added to the platform snap (see https:/
Can you please update the copyright year of all changed files to 2017 ?
When I right-click on a tab and choose "Move to New Window", a new window is indeed opened, but the tab remains (and the webview is blanked) in the original window. I’m seeing the following in the console:
file://
(a new autopilot test for that context menu option would be useful)
When I right-click on a tab and choose "New Tab", the new tab is not getting focus. This doesn't appear to be a regression, but would you mind fixing this while you're at it?
Please see additional comments/questions inline.
- 1570. By Andrew Hayzen
-
* Fix right-click "Move to Wew Window" using removeMovingTab instead of removeTabWithou
tDestroying
* Fix right-click "New Tab" not making the new tab the current
* Remove qml-module-qtqml-models2 depends
* Revert changes to .pot
* Reverted changes to snapcraft to add ui-extras as it is in ubuntu-app-platform now
* Removed extra blank lines
* Made selectedIndex a readonly property
* Move extras import to be grouped with other Ubuntu.Components imports
* Change faviconFactory to be a nested Component
* Change faviconFactory.incubateObject parent to be the tabsBar
* Added whitespace around equals sign
* Remove debug in tst_TabsBar for moveTab and remove whitespace
* Remove overriding onContentMenu in tst_TabsBar as the one in TabsBar.qml can be used
* Fix whitespace in for loop around equals sign and ++ operator
Andrew Hayzen (ahayzen) wrote : | # |
1) ubuntu-ui-extras is being added to the platform snap (see https:/
Removed those changes
2) Can you please update the copyright year of all changed files to 2017 ?
TODO
3) file://
FIXED
4) When I right-click on a tab and choose "New Tab", the new tab is not getting focus. This doesn't appear to be a regression, but would you mind fixing this while you're at it?
FIXED
5) What is this module needed for? I’m not seeing any explicit import of QtQml.Models in the diff.
Removed, and adding to ui-extras https:/
6) Please revert changes to the pot file.
REVERTED
7) ubuntu-ui-extras is being added to the platform snap (see https:/
Removed those changes
8) Please remove the extra blank line.
Removed
9) Please make it a readonly property.
Made it readonly
10) Can you please remove the extra blank line?
Removed
11) Cosmetics: can this import be grouped together with the other Ubuntu.Components imports?
Moved
12) Does faviconFactory really need to be a property? Can't it simply be a nested Component?
Yup, changed to a nested Component
13) I wonder how this actually works. The incubator may instantiate the component asynchronously, so by the time the function returns its status might not be Component.Ready, and the icon source will be empty.
Also, why make the FaviconFetcher a child of tabsBar.parent, and not of tabsBar itself?
Change the parent to be the tabsBar.
This seems to work like a binding, although it should be tested on low powered machines to ensure it is actually working and not always working as the incubator happens to load in time.
14) Cosmetics: can you please add whitespaces around the equal sign?
Added
15) So this is like the changes in https:/
I found that the old branch was actually wrong, and in the case where you move in a positive direction it would only allow you to move one position. Now with the changed logic it works correctly.
Also see the Qt documentation http://
It should be beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild)
I now have beginMoveRows(
16) Is this really needed? There's already a 'tabsModel' property (line below).
This is ...
- 1571. By Andrew Hayzen
-
* Really revert the pot this time :-)
- 1572. By Andrew Hayzen
-
* Fix Kate auto adding new line
Andrew Hayzen (ahayzen) wrote : | # |
Note to self, forgot todo this one (even wrote TODO :') ).
2) Can you please update the copyright year of all changed files to 2017 ?
TODO
- 1573. By Andrew Hayzen
-
* Bump copyright year to 2017
Olivier Tilloy (osomon) wrote : | # |
Mostly good to go, see two comments inline.
- 1574. By Andrew Hayzen
-
* Add FaviconFetcher to the BrowserTab so iconSourceFromM
odelItem can return the value easily
* Remove unused imports from tests
Andrew Hayzen (ahayzen) wrote : | # |
Resolved further comments:
1) See documentation: https:/
There is no guarantee that the incubator will be ready by the time of the return statement. In fact if it works here it’s by accident. It seems to me the only clean solution here is to use faviconFactory.
One side question: is iconSourceFromM
So what I've done is add a FaviconFetcher to the BrowserTab, then the iconSourceFromM
You can prove this by going to a website that loads the icon slowly and add the following traceline:
console.
I then get this output:
qml: http://
...
qml: http://
2) Those two imports don't appear to be needed.
Removed
- 1575. By Andrew Hayzen
-
* Add localIcon readonly property to BrowserTab that points to a FaviconFetcher.
localUrl
Olivier Tilloy (osomon) wrote : | # |
I’m getting the following two warnings in the logs at application startup:
QQmlContext: Cannot set context object on invalid context.
QQmlComponent: Cannot create a component in an invalid context
I verified that I’m not getting them with the staging branch, so this is new. I wonder what’s causing them, and if they are harmful at all?
Olivier Tilloy (osomon) wrote : | # |
When closing a tab, I’m seeing the following warning:
TabsBar.qml:57: TypeError: Cannot read property 'localIcon' of undefined
Olivier Tilloy (osomon) wrote : | # |
> QQmlContext: Cannot set context object on invalid context.
> QQmlComponent: Cannot create a component in an invalid context
Not seeing those any longer after closing all tabs and starting again the app.
- 1576. By Andrew Hayzen
-
* Check the tab is valid in iconSourceFromM
odelItem
Olivier Tilloy (osomon) wrote : | # |
And seeing them again after re-opening a good number of tabs (11), closing the app and starting it again.
Andrew Hayzen (ahayzen) wrote : | # |
1) When closing a tab, I’m seeing the following warning:
TabsBar.qml:57: TypeError: Cannot read property 'localIcon' of undefined
FIXED
2) QQmlContext errors when there are enough tabs open to cause some to be hidden
I've fixed this in the ubuntu-ui-extras, what was happening was at application startup, the loader is set to start loading a component but while it is doing this in async the tab is destroyed (as it is out of view in the listview) and then when the component is complete there is no context for it to go to. So now when the tab is destroyed it unsets the loader source to prevent it from performing the load. Please test with the following branch: https:/
Olivier Tilloy (osomon) wrote : | # |
Everything looks fine now. Thanks!
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2017-01-05 10:31:30 +0000 |
3 | +++ debian/control 2017-02-15 13:53:24 +0000 |
4 | @@ -55,6 +55,7 @@ |
5 | qml-module-ubuntu-web (= ${binary:Version}), |
6 | qtdeclarative5-ubuntu-content1, |
7 | qtdeclarative5-ubuntu-download-manager0.1, |
8 | + qtdeclarative5-ubuntu-ui-extras0.2, |
9 | qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3), |
10 | qtdeclarative5-unity-action-plugin, |
11 | Replaces: webbrowser-app-assets, |
12 | |
13 | === modified file 'src/app/webbrowser/Browser.qml' |
14 | --- src/app/webbrowser/Browser.qml 2017-02-06 22:17:36 +0000 |
15 | +++ src/app/webbrowser/Browser.qml 2017-02-15 13:53:24 +0000 |
16 | @@ -1,5 +1,5 @@ |
17 | /* |
18 | - * Copyright 2013-2016 Canonical Ltd. |
19 | + * Copyright 2013-2017 Canonical Ltd. |
20 | * |
21 | * This file is part of webbrowser-app. |
22 | * |
23 | @@ -44,9 +44,45 @@ |
24 | |
25 | property bool incognito: false |
26 | |
27 | - property var tabsModel: TabsModel {} |
28 | + property var tabsModel: TabsModel { |
29 | + // These methods are required by the TabsBar component |
30 | + readonly property int selectedIndex: currentIndex |
31 | + |
32 | + function addTab() { |
33 | + internal.openUrlInNewTab("", true, true, count) |
34 | + } |
35 | + |
36 | + function addExistingTab(tab) { |
37 | + add(tab); |
38 | + |
39 | + browser.bindExistingTab(tab); |
40 | + } |
41 | + |
42 | + function moveTab(from, to) { |
43 | + if (from == to |
44 | + || from < 0 || from >= count |
45 | + || to < 0 || to >= count) { |
46 | + return; |
47 | + } |
48 | + |
49 | + move(from, to); |
50 | + } |
51 | + |
52 | + function removeTab(index) { |
53 | + internal.closeTab(index, false); |
54 | + } |
55 | + |
56 | + function removeTabWithoutDestroying(index) { |
57 | + internal.closeTab(index, true); |
58 | + } |
59 | + |
60 | + function selectTab(index) { |
61 | + internal.switchToTab(index, true); |
62 | + } |
63 | + } |
64 | |
65 | property BrowserWindow thisWindow |
66 | + property Component windowFactory |
67 | |
68 | function serializeTabState(tab) { |
69 | var state = {} |
70 | @@ -487,11 +523,14 @@ |
71 | showFaviconInAddressBar: !browser.wide |
72 | |
73 | thisWindow: browser.thisWindow |
74 | + windowFactory: browser.windowFactory |
75 | |
76 | availableHeight: tabContainer.height - height - y |
77 | |
78 | touchEnabled: internal.hasTouchScreen |
79 | |
80 | + tabsBarDimmed: dropAreaTopCover.containsDrag || dropAreaBottomCover.containsDrag |
81 | + |
82 | property bool hidden: false |
83 | y: hidden ? -height : webview ? webview.locationBarController.offset : 0 |
84 | Behavior on y { |
85 | @@ -521,7 +560,6 @@ |
86 | |
87 | onSwitchToTab: internal.switchToTab(index, true) |
88 | onRequestNewTab: internal.openUrlInNewTab("", makeCurrent, true, index) |
89 | - onRequestNewWindowFromTab: browser.newWindowFromTab(tab, callback) |
90 | onTabClosed: internal.closeTab(index, moving) |
91 | |
92 | onFindInPageModeChanged: { |
93 | @@ -1118,6 +1156,7 @@ |
94 | maybeFocusAddressBar() |
95 | } else { |
96 | tabContainer.forceActiveFocus() |
97 | + tab.load(); |
98 | } |
99 | } |
100 | } |
101 | @@ -1456,77 +1495,35 @@ |
102 | asynchronous: true |
103 | } |
104 | |
105 | - DropArea { |
106 | - id: dropArea |
107 | + // Cover the webview (gaps around tabsbar) with DropArea so that webview doesn't steal events |
108 | + DropArea { |
109 | + id: dropAreaTopCover |
110 | + anchors { |
111 | + left: parent.left |
112 | + right: parent.right |
113 | + top: parent.top |
114 | + } |
115 | + height: units.gu(1) |
116 | + keys: ["webbrowser/tab-" + (incognito ? "incognito" : "public")] |
117 | + visible: chrome.showTabsBar |
118 | + |
119 | + onEntered: { |
120 | + window.raise() |
121 | + window.requestActivate() |
122 | + } |
123 | + } |
124 | + |
125 | + DropArea { |
126 | + id: dropAreaBottomCover |
127 | anchors { |
128 | fill: parent |
129 | + topMargin: chrome.tabsBarHeight |
130 | } |
131 | keys: ["webbrowser/tab-" + (incognito ? "incognito" : "public")] |
132 | |
133 | - readonly property real heightThreshold: chrome.tabsBarHeight |
134 | - |
135 | - onDropped: { |
136 | - // IgnoreAction - no DropArea accepted so New Window |
137 | - // MoveAction - DropArea accept but different window |
138 | - // CopyAction - DropArea accept but same window |
139 | - |
140 | - if (drag.y > heightThreshold) { |
141 | - // Dropped in bottom area, creating new window |
142 | - drop.accept(Qt.IgnoreAction); |
143 | - } else if (drag.source.tabWindow === thisWindow) { |
144 | - // Dropped in same window |
145 | - drop.accept(Qt.CopyAction); |
146 | - } else { |
147 | - // Dropped in new window, moving tab |
148 | - |
149 | - thisWindow.addExistingTab(drag.source.tab); |
150 | - thisWindow.tabsModel.currentIndex = window.tabsModel.count - 1; |
151 | - thisWindow.show(); |
152 | - thisWindow.requestActivate(); |
153 | - |
154 | - thisWindow.tabsModel.currentTab.load(); |
155 | - |
156 | - drop.accept(Qt.MoveAction); |
157 | - } |
158 | - } |
159 | onEntered: { |
160 | - thisWindow.raise() |
161 | - thisWindow.requestActivate(); |
162 | - } |
163 | - onPositionChanged: { |
164 | - if (drag.source.tabWindow === thisWindow && drag.y < heightThreshold) { |
165 | - // tab drag is within same window and in chrome |
166 | - // so reorder tabs by setting tabDelegate x position |
167 | - drag.source.x = drag.x - (drag.source.width / 2); |
168 | - } |
169 | - } |
170 | - |
171 | - Rectangle { |
172 | - anchors { |
173 | - left: parent.left |
174 | - right: parent.right |
175 | - top: parent.top |
176 | - } |
177 | - color: "#FFF" |
178 | - height: dropArea.heightThreshold |
179 | - opacity: { |
180 | - // Only hide the white shade when this is the active window |
181 | - // and there is no drag being performed or the drag event is |
182 | - // over the tabs bar |
183 | - if (thisWindow.active && !DragHelper.dragging) { |
184 | - 0 |
185 | - } else if (dropArea.containsDrag && dropArea.drag.y <= dropArea.heightThreshold) { |
186 | - 0 |
187 | - } else { |
188 | - 0.6 |
189 | - } |
190 | - } |
191 | - |
192 | - Behavior on opacity { |
193 | - NumberAnimation { |
194 | - |
195 | - } |
196 | - } |
197 | + window.raise() |
198 | + window.requestActivate() |
199 | } |
200 | } |
201 | } |
202 | |
203 | === modified file 'src/app/webbrowser/BrowserTab.qml' |
204 | --- src/app/webbrowser/BrowserTab.qml 2016-11-08 14:19:52 +0000 |
205 | +++ src/app/webbrowser/BrowserTab.qml 2017-02-15 13:53:24 +0000 |
206 | @@ -1,5 +1,5 @@ |
207 | /* |
208 | - * Copyright 2014-2016 Canonical Ltd. |
209 | + * Copyright 2014-2017 Canonical Ltd. |
210 | * |
211 | * This file is part of webbrowser-app. |
212 | * |
213 | @@ -21,6 +21,7 @@ |
214 | import Ubuntu.Web 0.2 |
215 | import com.canonical.Oxide 1.4 as Oxide |
216 | import webbrowserapp.private 0.1 |
217 | +import webbrowsercommon.private 0.1 |
218 | import "." |
219 | |
220 | FocusScope { |
221 | @@ -38,6 +39,7 @@ |
222 | readonly property url url: webview ? webview.url : initialUrl |
223 | readonly property string title: webview ? webview.title : initialTitle |
224 | readonly property url icon: webview ? webview.icon : initialIcon |
225 | + readonly property url localIcon: faviconFetcher.localUrl |
226 | property url preview |
227 | property bool current: false |
228 | readonly property real lastCurrent: internal.lastCurrent |
229 | @@ -61,6 +63,12 @@ |
230 | } |
231 | } |
232 | |
233 | + FaviconFetcher { |
234 | + id: faviconFetcher |
235 | + shouldCache: !tab.incognito |
236 | + url: tab.icon |
237 | + } |
238 | + |
239 | FocusScope { |
240 | id: webviewContainer |
241 | anchors.fill: parent |
242 | |
243 | === modified file 'src/app/webbrowser/CMakeLists.txt' |
244 | --- src/app/webbrowser/CMakeLists.txt 2016-09-28 12:35:43 +0000 |
245 | +++ src/app/webbrowser/CMakeLists.txt 2017-02-15 13:53:24 +0000 |
246 | @@ -33,7 +33,6 @@ |
247 | |
248 | set(WEBBROWSER_APP_SRC |
249 | cache-deleter.cpp |
250 | - drag-helper.cpp |
251 | file-operations.cpp |
252 | reparenter.cpp |
253 | searchengine.cpp |
254 | |
255 | === modified file 'src/app/webbrowser/Chrome.qml' |
256 | --- src/app/webbrowser/Chrome.qml 2016-11-08 16:11:45 +0000 |
257 | +++ src/app/webbrowser/Chrome.qml 2017-02-15 13:53:24 +0000 |
258 | @@ -1,5 +1,5 @@ |
259 | /* |
260 | - * Copyright 2013-2016 Canonical Ltd. |
261 | + * Copyright 2013-2017 Canonical Ltd. |
262 | * |
263 | * This file is part of webbrowser-app. |
264 | * |
265 | @@ -42,12 +42,13 @@ |
266 | property alias availableHeight: navigationBar.availableHeight |
267 | readonly property alias bookmarkTogglePlaceHolder: navigationBar.bookmarkTogglePlaceHolder |
268 | property bool touchEnabled: true |
269 | - readonly property real tabsBarHeight: tabsBar.height + content.anchors.topMargin |
270 | + readonly property real tabsBarHeight: tabsBar.height + tabsBar.anchors.topMargin + content.anchors.topMargin |
271 | property BrowserWindow thisWindow |
272 | + property Component windowFactory |
273 | + property bool tabsBarDimmed: false |
274 | |
275 | signal switchToTab(int index) |
276 | signal requestNewTab(int index, bool makeCurrent) |
277 | - signal requestNewWindowFromTab(var tab, var callback) |
278 | signal tabClosed(int index, bool moving) |
279 | |
280 | backgroundColor: incognito ? UbuntuColors.darkGrey : "#ffffff" |
281 | @@ -61,7 +62,6 @@ |
282 | FocusScope { |
283 | id: content |
284 | anchors.fill: parent |
285 | - anchors.topMargin: showTabsBar ? units.gu(1) : 0 |
286 | |
287 | focus: true |
288 | |
289 | @@ -72,33 +72,34 @@ |
290 | |
291 | Loader { |
292 | id: tabsBar |
293 | + anchors { |
294 | + left: parent.left |
295 | + right: parent.right |
296 | + top: parent.top |
297 | + } |
298 | + asynchronous: true |
299 | + height: active ? units.gu(3) : 0 |
300 | |
301 | Component.onCompleted: { |
302 | - tabsBar.setSource("TabsBar.qml", { |
303 | - "model": Qt.binding(function () {return chrome.tabsModel}), |
304 | - "incognito": Qt.binding(function () {return chrome.incognito}), |
305 | - "fgColor": Qt.binding(function () {return navigationBar.fgColor}), |
306 | - "thisWindow": Qt.binding(function () {return chrome.thisWindow}), |
307 | - "touchEnabled": Qt.binding(function () {return chrome.touchEnabled}) |
308 | - }) |
309 | + setSource( |
310 | + Qt.resolvedUrl("TabsBar.qml"), |
311 | + { |
312 | + "dimmed": Qt.binding(function() { return chrome.tabsBarDimmed; }), |
313 | + "model": Qt.binding(function() { return chrome.tabsModel; }), |
314 | + "incognito": Qt.binding(function() { return chrome.incognito; }), |
315 | + "dragAndDrop.previewTopCrop": Qt.binding(function() { return chrome.height; }), |
316 | + "dragAndDrop.thisWindow": Qt.binding(function() { return chrome.thisWindow; }), |
317 | + "windowFactory": Qt.binding(function() { return chrome.windowFactory; }), |
318 | + } |
319 | + ) |
320 | } |
321 | |
322 | Connections { |
323 | target: tabsBar.item |
324 | |
325 | - onSwitchToTab: chrome.switchToTab(index) |
326 | onRequestNewTab: chrome.requestNewTab(index, makeCurrent) |
327 | - onRequestNewWindowFromTab: chrome.requestNewWindowFromTab(tab, callback) |
328 | onTabClosed: chrome.tabClosed(index, moving) |
329 | } |
330 | - asynchronous: true |
331 | - |
332 | - anchors { |
333 | - top: parent.top |
334 | - left: parent.left |
335 | - right: parent.right |
336 | - } |
337 | - height: active ? (touchEnabled ? units.gu(4) : units.gu(3)) : 0 |
338 | } |
339 | |
340 | NavigationBar { |
341 | |
342 | === added file 'src/app/webbrowser/TabsBar.qml' |
343 | --- src/app/webbrowser/TabsBar.qml 1970-01-01 00:00:00 +0000 |
344 | +++ src/app/webbrowser/TabsBar.qml 2017-02-15 13:53:24 +0000 |
345 | @@ -0,0 +1,119 @@ |
346 | +/* |
347 | + * Copyright 2013-2017 Canonical Ltd. |
348 | + * |
349 | + * This file is part of webbrowser-app. |
350 | + * |
351 | + * webbrowser-app is free software; you can redistribute it and/or modify |
352 | + * it under the terms of the GNU General Public License as published by |
353 | + * the Free Software Foundation; version 3. |
354 | + * |
355 | + * webbrowser-app is distributed in the hope that it will be useful, |
356 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
357 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
358 | + * GNU General Public License for more details. |
359 | + * |
360 | + * You should have received a copy of the GNU General Public License |
361 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
362 | + */ |
363 | + |
364 | +import QtQuick 2.4 |
365 | +import Ubuntu.Components 1.3 |
366 | +import Ubuntu.Components.Extras 0.3 as Extras |
367 | +import Ubuntu.Components.Popups 1.3 |
368 | +import "." |
369 | +import ".." |
370 | + |
371 | +Extras.TabsBar { |
372 | + id: tabsBar |
373 | + color: "#D9D9D9" // FIXME: not in palette hardcode for now |
374 | + dragAndDrop { |
375 | + enabled: __platformName != "ubuntumirclient" |
376 | + maxYDiff: height / 12 |
377 | + mimeType: "webbrowser/tab-" + (incognito ? "incognito" : "public") |
378 | + previewUrlFromIndex: function(index) { |
379 | + if (tabsBar.model.get(index)) { |
380 | + return PreviewManager.previewPathFromUrl(tabsBar.model.get(index).url) |
381 | + } else { |
382 | + return ""; |
383 | + } |
384 | + } |
385 | + } |
386 | + fallbackIcon: "stock_website" |
387 | + windowFactoryProperties: { |
388 | + "incognito": tabsBar.incognito, |
389 | + "height": window.height, |
390 | + "width": window.width, |
391 | + } |
392 | + |
393 | + property bool incognito |
394 | + |
395 | + signal requestNewTab(int index, bool makeCurrent) |
396 | + signal tabClosed(int index, bool moving) |
397 | + |
398 | + onContextMenu: PopupUtils.open(contextualOptionsComponent, tabDelegate, {"targetIndex": index}) |
399 | + |
400 | + // Note: This works as a binding, when the returned value changes, QML recalls the function |
401 | + function iconSourceFromModelItem(modelData, index) { |
402 | + return modelData.tab ? modelData.tab.localIcon : ""; |
403 | + } |
404 | + |
405 | + function titleFromModelItem(modelItem) { |
406 | + return modelItem.title ? modelItem.title : (modelItem.url.toString() ? modelItem.url : i18n.tr("New tab")) |
407 | + } |
408 | + |
409 | + actions: [ |
410 | + Action { |
411 | + // FIXME: icon from theme is fuzzy at many GUs |
412 | +// iconSource: Qt.resolvedUrl("Tabs/tab_add.png") |
413 | + iconName: "add" |
414 | + objectName: "newTabButton" |
415 | + onTriggered: tabsBar.model.addTab() |
416 | + } |
417 | + ] |
418 | + |
419 | + Component { |
420 | + id: contextualOptionsComponent |
421 | + ActionSelectionPopover { |
422 | + id: menu |
423 | + objectName: "tabContextualActions" |
424 | + property int targetIndex |
425 | + readonly property var tab: tabsBar.model.get(targetIndex) |
426 | + |
427 | + actions: ActionList { |
428 | + Action { |
429 | + objectName: "tab_action_new_tab" |
430 | + text: i18n.tr("New Tab") |
431 | + onTriggered: tabsBar.requestNewTab(menu.targetIndex + 1, true) |
432 | + } |
433 | + Action { |
434 | + objectName: "tab_action_reload" |
435 | + text: i18n.tr("Reload") |
436 | + enabled: menu.tab.url.toString().length > 0 |
437 | + onTriggered: menu.tab.reload() |
438 | + } |
439 | + Action { |
440 | + objectName: "tab_action_move_to_new_window" |
441 | + text: i18n.tr("Move to New Window") |
442 | + onTriggered: { |
443 | + // callback function only removes from model |
444 | + // and not destroy as webview is in new window |
445 | + // Create new window and add existing tab |
446 | + var window = tabsBar.windowFactory.createObject(null, windowFactoryProperties); |
447 | + window.model.addExistingTab(menu.tab); |
448 | + window.model.selectTab(window.model.count - 1); |
449 | + window.show(); |
450 | + |
451 | + // Just remove from model and do not destroy |
452 | + // as webview is used in other window |
453 | + tabsBar.model.removeTabWithoutDestroying(menu.targetIndex); |
454 | + } |
455 | + } |
456 | + Action { |
457 | + objectName: "tab_action_close_tab" |
458 | + text: i18n.tr("Close Tab") |
459 | + onTriggered: tabsBar.tabClosed(menu.targetIndex, false) |
460 | + } |
461 | + } |
462 | + } |
463 | + } |
464 | +} |
465 | |
466 | === removed file 'src/app/webbrowser/TabsBar.qml' |
467 | --- src/app/webbrowser/TabsBar.qml 2016-11-29 15:56:48 +0000 |
468 | +++ src/app/webbrowser/TabsBar.qml 1970-01-01 00:00:00 +0000 |
469 | @@ -1,345 +0,0 @@ |
470 | -/* |
471 | - * Copyright 2015-2016 Canonical Ltd. |
472 | - * |
473 | - * This file is part of webbrowser-app. |
474 | - * |
475 | - * webbrowser-app is free software; you can redistribute it and/or modify |
476 | - * it under the terms of the GNU General Public License as published by |
477 | - * the Free Software Foundation; version 3. |
478 | - * |
479 | - * webbrowser-app is distributed in the hope that it will be useful, |
480 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
481 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
482 | - * GNU General Public License for more details. |
483 | - * |
484 | - * You should have received a copy of the GNU General Public License |
485 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
486 | - */ |
487 | - |
488 | -import QtQuick 2.4 |
489 | -import Ubuntu.Components 1.3 |
490 | -import Ubuntu.Components.Popups 1.3 |
491 | - |
492 | -import webbrowserapp.private 0.1 |
493 | - |
494 | -import "." |
495 | -import ".." |
496 | - |
497 | -Item { |
498 | - id: root |
499 | - |
500 | - property alias model: repeater.model |
501 | - |
502 | - property BrowserWindow thisWindow |
503 | - |
504 | - property real minTabWidth: 0 //units.gu(6) |
505 | - property real maxTabWidth: units.gu(20) |
506 | - property real tabWidth: model ? Math.max(Math.min(tabsContainer.maxWidth / model.count, maxTabWidth), minTabWidth) : 0 |
507 | - |
508 | - // Minimum size of the larger tab |
509 | - readonly property real minActiveTabWidth: units.gu(10) |
510 | - |
511 | - // When there is a larger tab, calc the smaller tab size |
512 | - readonly property real nonActiveTabWidth: (tabsContainer.maxWidth - minActiveTabWidth) / Math.max(model.count - 1, 1) |
513 | - |
514 | - // The size of the right margin of the tab |
515 | - readonly property real rightMargin: units.dp(1) |
516 | - |
517 | - // Whether there will be one larger tab or not |
518 | - readonly property bool unevenTabWidth: tabWidth + rightMargin < minActiveTabWidth |
519 | - |
520 | - property bool incognito: false |
521 | - |
522 | - property color fgColor: Theme.palette.normal.baseText |
523 | - |
524 | - property bool touchEnabled: true |
525 | - |
526 | - signal switchToTab(int index) |
527 | - signal requestNewTab(int index, bool makeCurrent) |
528 | - signal requestNewWindowFromTab(var tab, var callback) |
529 | - signal tabClosed(int index, bool moving) |
530 | - |
531 | - MouseArea { |
532 | - anchors.fill: parent |
533 | - onWheel: { |
534 | - var angle = (wheel.angleDelta.x != 0) ? wheel.angleDelta.x : wheel.angleDelta.y |
535 | - if ((angle < 0) && (root.model.currentIndex < (root.model.count - 1))) { |
536 | - switchToTab(root.model.currentIndex + 1) |
537 | - } else if ((angle > 0) && (root.model.currentIndex > 0)) { |
538 | - switchToTab(root.model.currentIndex - 1) |
539 | - } |
540 | - } |
541 | - } |
542 | - |
543 | - MouseArea { |
544 | - id: newTabButton |
545 | - objectName: "newTabButton" |
546 | - |
547 | - anchors { |
548 | - left: tabsContainer.right |
549 | - leftMargin: units.gu(1) |
550 | - top: parent.top |
551 | - bottom: parent.bottom |
552 | - } |
553 | - width: height |
554 | - |
555 | - visible: !repeater.reordering |
556 | - |
557 | - Icon { |
558 | - width: units.gu(2) |
559 | - height: units.gu(2) |
560 | - anchors.centerIn: parent |
561 | - name: "add" |
562 | - color: incognito ? "white" : root.fgColor |
563 | - } |
564 | - |
565 | - onClicked: root.requestNewTab(root.model.count, true) |
566 | - } |
567 | - |
568 | - Component { |
569 | - id: contextualOptionsComponent |
570 | - ActionSelectionPopover { |
571 | - id: menu |
572 | - objectName: "tabContextualActions" |
573 | - property int targetIndex |
574 | - readonly property var tab: root.model.get(targetIndex) |
575 | - |
576 | - actions: ActionList { |
577 | - Action { |
578 | - objectName: "tab_action_new_tab" |
579 | - text: i18n.tr("New Tab") |
580 | - onTriggered: root.requestNewTab(menu.targetIndex + 1, false) |
581 | - } |
582 | - Action { |
583 | - objectName: "tab_action_reload" |
584 | - text: i18n.tr("Reload") |
585 | - enabled: menu.tab.url.toString().length > 0 |
586 | - onTriggered: menu.tab.reload() |
587 | - } |
588 | - Action { |
589 | - objectName: "tab_action_move_to_new_window" |
590 | - text: i18n.tr("Move to New Window") |
591 | - onTriggered: { |
592 | - // callback function only removes from model |
593 | - // and not destroy as webview is in new window |
594 | - root.requestNewWindowFromTab(menu.tab, function() { root.tabClosed(menu.targetIndex, true); }); |
595 | - } |
596 | - } |
597 | - Action { |
598 | - objectName: "tab_action_close_tab" |
599 | - text: i18n.tr("Close Tab") |
600 | - onTriggered: root.tabClosed(menu.targetIndex, false) |
601 | - } |
602 | - } |
603 | - } |
604 | - } |
605 | - |
606 | - Item { |
607 | - id: tabsContainer |
608 | - objectName: "tabsContainer" |
609 | - |
610 | - anchors { |
611 | - top: parent.top |
612 | - bottom: parent.bottom |
613 | - left: parent.left |
614 | - } |
615 | - width: tabWidth * root.model.count |
616 | - readonly property real maxWidth: root.width - newTabButton.width - units.gu(2) |
617 | - |
618 | - readonly property int maxYDiff: height / 4 |
619 | - |
620 | - function sign(number) { return number / Math.abs(number); } |
621 | - |
622 | - Repeater { |
623 | - id: repeater |
624 | - |
625 | - property bool reordering: false |
626 | - |
627 | - delegate: MouseArea { |
628 | - id: tabDelegate |
629 | - objectName: "tabDelegate" |
630 | - |
631 | - readonly property int tabIndex: index |
632 | - readonly property var tab: root.model.get(index) |
633 | - readonly property BrowserWindow tabWindow: root.thisWindow |
634 | - |
635 | - property real rightMargin: units.dp(1) |
636 | - width: getSize(index) |
637 | - height: tabsContainer.height |
638 | - y: tabsContainer.y // don't use anchor otherwise drag doesn't work |
639 | - |
640 | - acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton |
641 | - readonly property bool dragging: drag.active |
642 | - drag { |
643 | - target: (pressedButtons === Qt.LeftButton) ? tabDelegate : null |
644 | - // FIXME: disable drag and drop on mir pad.lv/1627013 |
645 | - axis: __platformName != "ubuntumirclient" ? Drag.XAndYAxis : Drag.XAxis |
646 | - minimumX: 0 |
647 | - maximumX: root.width - tabDelegate.width |
648 | - filterChildren: true |
649 | - } |
650 | - |
651 | - TabItem { |
652 | - active: tabIndex === root.model.currentIndex |
653 | - anchors { |
654 | - left: parent.left |
655 | - right: parent.right |
656 | - } |
657 | - height: tabsContainer.height |
658 | - hoverable: true |
659 | - incognito: root.incognito |
660 | - title: model.title ? model.title : (model.url.toString() ? model.url : i18n.tr("New tab")) |
661 | - icon: model.icon |
662 | - fgColor: root.fgColor |
663 | - |
664 | - touchEnabled: root.touchEnabled |
665 | - |
666 | - rightMargin: root.rightMargin |
667 | - |
668 | - // Keep the visual tabitem within maxYDiff of starting point when |
669 | - // dragging vertically so that it doesn't cover other elements |
670 | - y: Math.abs(parent.y) > tabsContainer.maxYDiff ? (tabsContainer.sign(parent.y) * tabsContainer.maxYDiff) - parent.y : 0 |
671 | - |
672 | - onClosed: root.tabClosed(index, false) |
673 | - onSelected: root.switchToTab(index) |
674 | - onContextMenu: PopupUtils.open(contextualOptionsComponent, tabDelegate, {"targetIndex": index}) |
675 | - } |
676 | - |
677 | - Binding { |
678 | - target: repeater |
679 | - property: "reordering" |
680 | - value: dragging |
681 | - } |
682 | - |
683 | - Behavior on width { NumberAnimation { duration: 250 } } |
684 | - |
685 | - Binding on x { |
686 | - when: !dragging && !DragHelper.dragging |
687 | - value: getLeftX(index) |
688 | - } |
689 | - |
690 | - Behavior on x { |
691 | - NumberAnimation { |
692 | - duration: 250 |
693 | - } |
694 | - } |
695 | - |
696 | - NumberAnimation { |
697 | - id: resetVerticalAnimation |
698 | - target: tabDelegate |
699 | - duration: 250 |
700 | - property: "y" |
701 | - to: 0 |
702 | - } |
703 | - |
704 | - onPositionChanged: { |
705 | - // FIXME: disable drag and drop on mir pad.lv/1627013 |
706 | - if (Math.abs(y) > height && __platformName != "ubuntumirclient") { |
707 | - // Reset visual position of tab delegate |
708 | - resetVerticalAnimation.start(); |
709 | - |
710 | - if (mouse.buttons === Qt.LeftButton) { |
711 | - // Generate tab preview for drag handle |
712 | - DragHelper.expectedAction = Qt.IgnoreAction | Qt.CopyAction | Qt.MoveAction |
713 | - DragHelper.mimeType = "webbrowser/tab-" + (root.incognito ? "incognito" : "public") |
714 | - DragHelper.previewBorderWidth = units.gu(1) |
715 | - DragHelper.previewSize = Qt.size(units.gu(35), units.gu(22.5)) |
716 | - DragHelper.previewTopCrop = chrome.height |
717 | - DragHelper.previewUrl = PreviewManager.previewPathFromUrl(tab.url) |
718 | - DragHelper.source = tabDelegate |
719 | - |
720 | - var dropAction = DragHelper.execDrag(tab.url); |
721 | - |
722 | - // IgnoreAction - no DropArea accepted so New Window |
723 | - // MoveAction - DropArea accept but different window |
724 | - // CopyAction - DropArea accept but same window |
725 | - |
726 | - if (dropAction === Qt.MoveAction) { |
727 | - // Moved into another window |
728 | - |
729 | - // drag.active does not become false when |
730 | - // closing the tab so set reordering back |
731 | - repeater.reordering = false; |
732 | - |
733 | - // Just remove from model and do not destory |
734 | - // as webview is used in other window |
735 | - root.tabClosed(index, true); |
736 | - } else if (dropAction === Qt.CopyAction) { |
737 | - // Moved into the same window |
738 | - |
739 | - // So no action |
740 | - } else if (dropAction === Qt.IgnoreAction) { |
741 | - // Moved outside of any window |
742 | - |
743 | - // drag.active does not become false when |
744 | - // closing the tab so set reordering back |
745 | - repeater.reordering = false; |
746 | - |
747 | - // callback function only removes from model |
748 | - // and not destroy as webview is in new window |
749 | - root.requestNewWindowFromTab(tab, function() { root.tabClosed(index, true); }) |
750 | - } else { |
751 | - // Unknown state |
752 | - console.debug("Unknown drop action:", dropAction); |
753 | - } |
754 | - } |
755 | - } |
756 | - } |
757 | - onReleased: resetVerticalAnimation.start(); |
758 | - |
759 | - function getLeftX(index) { |
760 | - if (unevenTabWidth) { |
761 | - if (index > root.model.currentIndex) { |
762 | - return minActiveTabWidth + (nonActiveTabWidth * (index - 1)) |
763 | - } else { |
764 | - return nonActiveTabWidth * index |
765 | - } |
766 | - } else { |
767 | - // Do not depend on width otherwise X updates after |
768 | - // Width causing the animation to be two stage |
769 | - // instead perform same calculation (tabWidth + rightMargin) |
770 | - return index * (tabWidth + rightMargin) |
771 | - } |
772 | - } |
773 | - |
774 | - function getSize(index) { |
775 | - if (unevenTabWidth) { |
776 | - // Uneven tabs so use large or small depending which index |
777 | - if (index === root.model.currentIndex) { |
778 | - return minActiveTabWidth |
779 | - } else { |
780 | - return nonActiveTabWidth |
781 | - } |
782 | - } else { |
783 | - return tabWidth + rightMargin |
784 | - } |
785 | - } |
786 | - |
787 | - onXChanged: { |
788 | - if (!dragging) return |
789 | - |
790 | - var leftX = getLeftX(index) |
791 | - |
792 | - if (x < (leftX - getSize(index - 1) / 2) && index > 0) { |
793 | - root.model.move(index, index - 1) |
794 | - } else if ((x > (leftX + getSize(index + 1) / 2)) && (index < (root.model.count - 1))) { |
795 | - root.model.move(index + 1, index) |
796 | - } |
797 | - } |
798 | - |
799 | - z: (root.model.currentIndex == index) ? 3 : 1 - index / root.model.count |
800 | - } |
801 | - } |
802 | - |
803 | - Rectangle { |
804 | - anchors { |
805 | - left: parent.left |
806 | - bottom: parent.bottom |
807 | - } |
808 | - width: root.width |
809 | - height: units.dp(1) |
810 | - color: "#cacaca" |
811 | - z: 2 |
812 | - } |
813 | - } |
814 | -} |
815 | |
816 | === removed file 'src/app/webbrowser/drag-helper.cpp' |
817 | --- src/app/webbrowser/drag-helper.cpp 2016-11-29 15:55:31 +0000 |
818 | +++ src/app/webbrowser/drag-helper.cpp 1970-01-01 00:00:00 +0000 |
819 | @@ -1,203 +0,0 @@ |
820 | -/* |
821 | - * Copyright 2016 Canonical Ltd. |
822 | - * |
823 | - * This file is part of webbrowser-app. |
824 | - * |
825 | - * webbrowser-app is free software; you can redistribute it and/or modify |
826 | - * it under the terms of the GNU General Public License as published by |
827 | - * the Free Software Foundation; version 3. |
828 | - * |
829 | - * webbrowser-app is distributed in the hope that it will be useful, |
830 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
831 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
832 | - * GNU General Public License for more details. |
833 | - * |
834 | - * You should have received a copy of the GNU General Public License |
835 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
836 | - */ |
837 | - |
838 | -#include "drag-helper.h" |
839 | - |
840 | -#include <QtCore/QMimeData> |
841 | -#include <QtCore/QPoint> |
842 | -#include <QtCore/QSize> |
843 | -#include <QtCore/QString> |
844 | -#include <QtGui/QDrag> |
845 | -#include <QtGui/QDropEvent> |
846 | -#include <QtGui/QPainter> |
847 | -#include <QtGui/QPen> |
848 | -#include <QtGui/QPixmap> |
849 | -#include <QtQuick/QQuickItem> |
850 | - |
851 | -DragHelper::DragHelper() |
852 | - : QObject(), |
853 | - m_active(false), |
854 | - m_dragging(false), |
855 | - m_expected_action(Qt::IgnoreAction), |
856 | - m_mime_type(QStringLiteral("webbrowser/tab")), |
857 | - m_preview_border_width(8), |
858 | - m_preview_size(QSizeF(200, 150)), |
859 | - m_preview_top_crop(0), |
860 | - m_preview_url(""), |
861 | - m_source(Q_NULLPTR) |
862 | -{ |
863 | - |
864 | -} |
865 | - |
866 | -QPixmap DragHelper::drawPixmapWithBorder(QPixmap pixmap, int borderWidth, QColor color) |
867 | -{ |
868 | - // Create a transparent pixmap to draw to |
869 | - QPixmap output(pixmap.width() + borderWidth * 2, pixmap.height() + borderWidth * 2); |
870 | - output.fill(QColor(0, 0, 0, 0)); |
871 | - |
872 | - // Draw the pixmap with space around the edge for a border |
873 | - QPainter borderPainter(&output); |
874 | - borderPainter.setRenderHint(QPainter::Antialiasing); |
875 | - borderPainter.drawPixmap(borderWidth, borderWidth, pixmap); |
876 | - |
877 | - // Define a pen to use for the border |
878 | - QPen borderPen; |
879 | - borderPen.setColor(color); |
880 | - borderPen.setJoinStyle(Qt::MiterJoin); |
881 | - borderPen.setStyle(Qt::SolidLine); |
882 | - borderPen.setWidth(borderWidth); |
883 | - |
884 | - // Set the pen and draw the border |
885 | - borderPainter.setPen(borderPen); |
886 | - borderPainter.drawRect(borderWidth / 2, borderWidth / 2, |
887 | - output.width() - borderWidth, output.height() - borderWidth); |
888 | - |
889 | - return output; |
890 | -} |
891 | - |
892 | -Qt::DropAction DragHelper::execDrag(QString tabId) |
893 | -{ |
894 | - QDrag *drag = new QDrag(m_source); |
895 | - |
896 | - // Create a mimedata object to use for the drag |
897 | - QMimeData *mimeData = new QMimeData; |
898 | - mimeData->setData(mimeType(), tabId.toLatin1()); |
899 | - |
900 | - // Get a bordered pixmap of the previewUrl |
901 | - QSize size = previewSize().toSize(); |
902 | - |
903 | - QPixmap pixmap = drawPixmapWithBorder(getPreviewUrlAsPixmap(size.width(), size.height()), |
904 | - previewBorderWidth(), QColor(205, 205, 205, 255 * 0.6)); // #cdcdcd |
905 | - |
906 | - // Setup the drag and then execute it |
907 | - drag->setHotSpot(QPoint(size.width() * 0.1, size.height() * 0.1)); |
908 | - drag->setMimeData(mimeData); |
909 | - drag->setPixmap(pixmap); |
910 | - |
911 | - setDragging(true); |
912 | - |
913 | - Qt::DropAction action = drag->exec(expectedAction()); |
914 | - |
915 | - setDragging(false); |
916 | - |
917 | - return action; |
918 | -} |
919 | - |
920 | -QPixmap DragHelper::getPreviewUrlAsPixmap(int width, int height) |
921 | -{ |
922 | - QSize pixmapSize(width, height); |
923 | - QPixmap pixmap(previewUrl()); |
924 | - |
925 | - if (pixmap.isNull()) { |
926 | - // If loading pixmap failed, draw a white rectangle |
927 | - pixmap = QPixmap(pixmapSize); |
928 | - QPainter painter(&pixmap); |
929 | - painter.eraseRect(0, 0, pixmapSize.width(), pixmapSize.height()); |
930 | - painter.fillRect(0, 0, pixmapSize.width(), pixmapSize.height(), QColor(255, 255, 255, 255)); |
931 | - } else { |
932 | - // Crop transparent part off the top of the image |
933 | - pixmap = pixmap.copy(0, previewTopCrop(), pixmap.width(), pixmap.height() - previewTopCrop()); |
934 | - |
935 | - // Scale image to fit the expected size |
936 | - pixmap = pixmap.scaled(pixmapSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); |
937 | - } |
938 | - |
939 | - return pixmap; |
940 | -} |
941 | - |
942 | -void DragHelper::setActive(bool active) |
943 | -{ |
944 | - if (m_active != active) { |
945 | - m_active = active; |
946 | - |
947 | - Q_EMIT activeChanged(); |
948 | - } |
949 | -} |
950 | - |
951 | -void DragHelper::setDragging(bool dragging) |
952 | -{ |
953 | - if (m_dragging != dragging) { |
954 | - m_dragging = dragging; |
955 | - |
956 | - Q_EMIT draggingChanged(); |
957 | - } |
958 | -} |
959 | - |
960 | -void DragHelper::setExpectedAction(Qt::DropAction expectedAction) |
961 | -{ |
962 | - if (m_expected_action != expectedAction) { |
963 | - m_expected_action = expectedAction; |
964 | - |
965 | - Q_EMIT expectedActionChanged(); |
966 | - } |
967 | -} |
968 | - |
969 | -void DragHelper::setMimeType(QString mimeType) |
970 | -{ |
971 | - if (m_mime_type != mimeType) { |
972 | - m_mime_type = mimeType; |
973 | - |
974 | - Q_EMIT mimeTypeChanged(); |
975 | - } |
976 | -} |
977 | - |
978 | -void DragHelper::setPreviewBorderWidth(int previewBorderWidth) |
979 | -{ |
980 | - if (m_preview_border_width != previewBorderWidth) { |
981 | - m_preview_border_width = previewBorderWidth; |
982 | - |
983 | - Q_EMIT previewBorderWidthChanged(); |
984 | - } |
985 | -} |
986 | - |
987 | -void DragHelper::setPreviewSize(QSizeF previewSize) |
988 | -{ |
989 | - if (m_preview_size != previewSize) { |
990 | - m_preview_size = previewSize; |
991 | - |
992 | - Q_EMIT previewSizeChanged(); |
993 | - } |
994 | -} |
995 | - |
996 | -void DragHelper::setPreviewTopCrop(int previewTopCrop) |
997 | -{ |
998 | - if (m_preview_top_crop != previewTopCrop) { |
999 | - m_preview_top_crop = previewTopCrop; |
1000 | - |
1001 | - Q_EMIT previewTopCropChanged(); |
1002 | - } |
1003 | -} |
1004 | - |
1005 | -void DragHelper::setPreviewUrl(QString previewUrl) |
1006 | -{ |
1007 | - if (m_preview_url != previewUrl) { |
1008 | - m_preview_url = previewUrl; |
1009 | - |
1010 | - Q_EMIT previewUrlChanged(); |
1011 | - } |
1012 | -} |
1013 | - |
1014 | -void DragHelper::setSource(QQuickItem *source) |
1015 | -{ |
1016 | - if (m_source != source) { |
1017 | - m_source = source; |
1018 | - |
1019 | - Q_EMIT sourceChanged(); |
1020 | - } |
1021 | -} |
1022 | - |
1023 | |
1024 | === removed file 'src/app/webbrowser/drag-helper.h' |
1025 | --- src/app/webbrowser/drag-helper.h 2016-11-21 11:59:03 +0000 |
1026 | +++ src/app/webbrowser/drag-helper.h 1970-01-01 00:00:00 +0000 |
1027 | @@ -1,91 +0,0 @@ |
1028 | -/* |
1029 | - * Copyright 2016 Canonical Ltd. |
1030 | - * |
1031 | - * This file is part of webbrowser-app. |
1032 | - * |
1033 | - * webbrowser-app is free software; you can redistribute it and/or modify |
1034 | - * it under the terms of the GNU General Public License as published by |
1035 | - * the Free Software Foundation; version 3. |
1036 | - * |
1037 | - * webbrowser-app is distributed in the hope that it will be useful, |
1038 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1039 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1040 | - * GNU General Public License for more details. |
1041 | - * |
1042 | - * You should have received a copy of the GNU General Public License |
1043 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1044 | - */ |
1045 | - |
1046 | -#ifndef __DRAGHELPER_H__ |
1047 | -#define __DRAGHELPER_H__ |
1048 | - |
1049 | -#include <QtCore/QSizeF> |
1050 | -#include <QtCore/QObject> |
1051 | -#include <QtCore/QString> |
1052 | -#include <QtGui/QColor> |
1053 | -#include <QtGui/QMouseEvent> |
1054 | - |
1055 | -class QQuickItem; |
1056 | - |
1057 | -class DragHelper : public QObject |
1058 | -{ |
1059 | - Q_OBJECT |
1060 | - |
1061 | - Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) |
1062 | - Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) |
1063 | - Q_PROPERTY(Qt::DropAction expectedAction READ expectedAction WRITE setExpectedAction NOTIFY expectedActionChanged) |
1064 | - Q_PROPERTY(QString mimeType READ mimeType WRITE setMimeType NOTIFY mimeTypeChanged) |
1065 | - Q_PROPERTY(int previewBorderWidth READ previewBorderWidth WRITE setPreviewBorderWidth NOTIFY previewBorderWidthChanged) |
1066 | - Q_PROPERTY(QSizeF previewSize READ previewSize WRITE setPreviewSize NOTIFY previewSizeChanged) |
1067 | - Q_PROPERTY(int previewTopCrop READ previewTopCrop WRITE setPreviewTopCrop NOTIFY previewTopCropChanged) |
1068 | - Q_PROPERTY(QString previewUrl READ previewUrl WRITE setPreviewUrl NOTIFY previewUrlChanged) |
1069 | - Q_PROPERTY(QQuickItem* source READ source WRITE setSource NOTIFY sourceChanged) |
1070 | -public: |
1071 | - DragHelper(); |
1072 | - bool active() { return m_active; } |
1073 | - bool dragging() { return m_dragging; } |
1074 | - Qt::DropAction expectedAction() { return m_expected_action; } |
1075 | - QString mimeType() { return m_mime_type; } |
1076 | - int previewBorderWidth() { return m_preview_border_width; } |
1077 | - QSizeF previewSize() { return m_preview_size; } |
1078 | - int previewTopCrop() { return m_preview_top_crop; } |
1079 | - QString previewUrl() { return m_preview_url; } |
1080 | - QQuickItem *source() { return m_source; } |
1081 | -signals: |
1082 | - void activeChanged(); |
1083 | - void draggingChanged(); |
1084 | - void expectedActionChanged(); |
1085 | - void mimeTypeChanged(); |
1086 | - void previewBorderWidthChanged(); |
1087 | - void previewSizeChanged(); |
1088 | - void previewTopCropChanged(); |
1089 | - void previewUrlChanged(); |
1090 | - void sourceChanged(); |
1091 | -public slots: |
1092 | - Q_INVOKABLE Qt::DropAction execDrag(QString tabId); |
1093 | - void setActive(bool active); |
1094 | - void setExpectedAction(Qt::DropAction expectedAction); |
1095 | - void setMimeType(QString mimeType); |
1096 | - void setPreviewBorderWidth(int previewBorderWidth); |
1097 | - void setPreviewSize(QSizeF previewSize); |
1098 | - void setPreviewTopCrop(int previewTopCrop); |
1099 | - void setPreviewUrl(QString previewUrl); |
1100 | - void setSource(QQuickItem *source); |
1101 | -private: |
1102 | - QPixmap drawPixmapWithBorder(QPixmap pixmap, int borderWidth, QColor color); |
1103 | - QPixmap getPreviewUrlAsPixmap(int width, int height); |
1104 | - void setDragging(bool dragging); |
1105 | - |
1106 | - bool m_active; |
1107 | - bool m_dragging; |
1108 | - Qt::DropAction m_expected_action; |
1109 | - QString m_mime_type; |
1110 | - int m_preview_border_width; |
1111 | - QSizeF m_preview_size; |
1112 | - int m_preview_top_crop; |
1113 | - QString m_preview_url; |
1114 | - QQuickItem *m_source; |
1115 | -}; |
1116 | - |
1117 | -#endif // __DRAGHELPER_H__ |
1118 | - |
1119 | |
1120 | === modified file 'src/app/webbrowser/tabs-model.cpp' |
1121 | --- src/app/webbrowser/tabs-model.cpp 2017-02-15 11:22:05 +0000 |
1122 | +++ src/app/webbrowser/tabs-model.cpp 2017-02-15 13:53:24 +0000 |
1123 | @@ -217,9 +217,25 @@ |
1124 | if ((from == to) || !checkValidTabIndex(from) || !checkValidTabIndex(to)) { |
1125 | return; |
1126 | } |
1127 | - beginMoveRows(QModelIndex(), from, from, QModelIndex(), to); |
1128 | - m_tabs.move(from, to); |
1129 | - endMoveRows(); |
1130 | + |
1131 | + int diff = to - from; |
1132 | + int i = from; |
1133 | + |
1134 | + // Shuffle index along until destination |
1135 | + while (i != to) { |
1136 | + if (diff > 0) { |
1137 | + beginMoveRows(QModelIndex(), i, i, QModelIndex(), i + 2); |
1138 | + m_tabs.move(i + 1, i); |
1139 | + i += 1; |
1140 | + } else { |
1141 | + beginMoveRows(QModelIndex(), i, i, QModelIndex(), i - 1); |
1142 | + m_tabs.move(i, i - 1); |
1143 | + i -= 1; |
1144 | + } |
1145 | + |
1146 | + endMoveRows(); |
1147 | + } |
1148 | + |
1149 | if (m_currentIndex == from) { |
1150 | m_currentIndex = to; |
1151 | Q_EMIT currentIndexChanged(); |
1152 | |
1153 | === modified file 'src/app/webbrowser/webbrowser-app.cpp' |
1154 | --- src/app/webbrowser/webbrowser-app.cpp 2016-11-10 11:52:26 +0000 |
1155 | +++ src/app/webbrowser/webbrowser-app.cpp 2017-02-15 13:53:24 +0000 |
1156 | @@ -1,5 +1,5 @@ |
1157 | /* |
1158 | - * Copyright 2013-2016 Canonical Ltd. |
1159 | + * Copyright 2013-2017 Canonical Ltd. |
1160 | * |
1161 | * This file is part of webbrowser-app. |
1162 | * |
1163 | @@ -21,7 +21,6 @@ |
1164 | #include "cache-deleter.h" |
1165 | #include "config.h" |
1166 | #include "downloads-model.h" |
1167 | -#include "drag-helper.h" |
1168 | #include "file-operations.h" |
1169 | #include "history-domainlist-model.h" |
1170 | #include "history-lastvisitdatelist-model.h" |
1171 | @@ -63,7 +62,6 @@ |
1172 | MAKE_SINGLETON_FACTORY(HistoryModel) |
1173 | MAKE_SINGLETON_FACTORY(DownloadsModel) |
1174 | MAKE_SINGLETON_FACTORY(Reparenter) |
1175 | -MAKE_SINGLETON_FACTORY(DragHelper) |
1176 | |
1177 | bool WebbrowserApp::initialize() |
1178 | { |
1179 | @@ -80,7 +78,6 @@ |
1180 | qmlRegisterSingletonType<CacheDeleter>(uri, 0, 1, "CacheDeleter", CacheDeleter_singleton_factory); |
1181 | qmlRegisterSingletonType<DownloadsModel>(uri, 0, 1, "DownloadsModel", DownloadsModel_singleton_factory); |
1182 | qmlRegisterType<TextSearchFilterModel>(uri, 0, 1, "TextSearchFilterModel"); |
1183 | - qmlRegisterSingletonType<DragHelper>(uri, 0, 1, "DragHelper", DragHelper_singleton_factory); |
1184 | qmlRegisterSingletonType<Reparenter>(uri, 0, 1, "Reparenter", Reparenter_singleton_factory); |
1185 | |
1186 | if (BrowserApplication::initialize("webbrowser/webbrowser-app.qml", QStringLiteral("webbrowser-app"))) { |
1187 | |
1188 | === modified file 'src/app/webbrowser/webbrowser-app.qml' |
1189 | --- src/app/webbrowser/webbrowser-app.qml 2016-11-08 14:19:52 +0000 |
1190 | +++ src/app/webbrowser/webbrowser-app.qml 2017-02-15 13:53:24 +0000 |
1191 | @@ -1,5 +1,5 @@ |
1192 | /* |
1193 | - * Copyright 2013-2016 Canonical Ltd. |
1194 | + * Copyright 2013-2017 Canonical Ltd. |
1195 | * |
1196 | * This file is part of webbrowser-app. |
1197 | * |
1198 | @@ -89,6 +89,7 @@ |
1199 | id: window |
1200 | |
1201 | property alias incognito: browser.incognito |
1202 | + readonly property alias model: browser.tabsModel |
1203 | readonly property var tabsModel: browser.tabsModel |
1204 | |
1205 | currentWebview: browser.currentWebview |
1206 | @@ -185,25 +186,7 @@ |
1207 | anchors.fill: parent |
1208 | thisWindow: window |
1209 | settings: webbrowserapp.settings |
1210 | - onNewWindowFromTab: { |
1211 | - var window = windowFactory.createObject( |
1212 | - null, |
1213 | - { |
1214 | - "incognito": tab.incognito, |
1215 | - "height": parent.height, |
1216 | - "width": parent.width, |
1217 | - } |
1218 | - ); |
1219 | - |
1220 | - window.addExistingTab(tab); |
1221 | - window.tabsModel.currentIndex = window.tabsModel.count - 1; |
1222 | - window.show(); |
1223 | - window.requestActivate(); |
1224 | - |
1225 | - window.tabsModel.currentTab.load(); |
1226 | - |
1227 | - callback(); |
1228 | - } |
1229 | + windowFactory: webbrowserapp.windowFactory |
1230 | onNewWindowRequested: { |
1231 | var window = windowFactory.createObject( |
1232 | null, |
1233 | @@ -287,14 +270,6 @@ |
1234 | tabsModel.add(tab) |
1235 | return tab |
1236 | } |
1237 | - |
1238 | - function addExistingTab(tab) { |
1239 | - tabsModel.add(tab); |
1240 | - |
1241 | - browser.bindExistingTab(tab); |
1242 | - |
1243 | - return tab; |
1244 | - } |
1245 | } |
1246 | } |
1247 | |
1248 | |
1249 | === modified file 'tests/autopilot/webbrowser_app/emulators/browser.py' |
1250 | --- tests/autopilot/webbrowser_app/emulators/browser.py 2016-11-03 14:25:02 +0000 |
1251 | +++ tests/autopilot/webbrowser_app/emulators/browser.py 2017-02-15 13:53:24 +0000 |
1252 | @@ -1,6 +1,6 @@ |
1253 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
1254 | # |
1255 | -# Copyright 2013-2016 Canonical |
1256 | +# Copyright 2013-2017 Canonical |
1257 | # |
1258 | # This program is free software: you can redistribute it and/or modify it |
1259 | # under the terms of the GNU General Public License version 3, as published |
1260 | @@ -365,8 +365,7 @@ |
1261 | |
1262 | @autopilot.logging.log_action(logger.info) |
1263 | def click_new_tab_button(self): |
1264 | - button = self.select_single("QQuickMouseArea", |
1265 | - objectName="newTabButton") |
1266 | + button = self.select_single(objectName="newTabButton") |
1267 | self.pointing_device.click_object(button) |
1268 | |
1269 | def get_tabs(self): |
1270 | @@ -383,7 +382,7 @@ |
1271 | @autopilot.logging.log_action(logger.info) |
1272 | def close_tab(self, index): |
1273 | tab = self.get_tab(index) |
1274 | - close_button = tab.select_single(objectName="closeButton") |
1275 | + close_button = tab.select_single(objectName="tabCloseButton") |
1276 | self.pointing_device.click_object(close_button) |
1277 | |
1278 | |
1279 | |
1280 | === modified file 'tests/unittests/qml/CMakeLists.txt' |
1281 | --- tests/unittests/qml/CMakeLists.txt 2017-01-05 10:26:58 +0000 |
1282 | +++ tests/unittests/qml/CMakeLists.txt 2017-02-15 13:53:24 +0000 |
1283 | @@ -18,7 +18,6 @@ |
1284 | ${webbrowser-app_SOURCE_DIR}/bookmarks-model.cpp |
1285 | ${webbrowser-app_SOURCE_DIR}/bookmarks-folder-model.cpp |
1286 | ${webbrowser-app_SOURCE_DIR}/bookmarks-folderlist-model.cpp |
1287 | - ${webbrowser-app_SOURCE_DIR}/drag-helper.cpp |
1288 | ${webbrowser-app_SOURCE_DIR}/file-operations.cpp |
1289 | ${webbrowser-app_SOURCE_DIR}/history-domain-model.cpp |
1290 | ${webbrowser-app_SOURCE_DIR}/history-domainlist-model.cpp |
1291 | |
1292 | === modified file 'tests/unittests/qml/tst_QmlTests.cpp' |
1293 | --- tests/unittests/qml/tst_QmlTests.cpp 2017-01-05 10:26:58 +0000 |
1294 | +++ tests/unittests/qml/tst_QmlTests.cpp 2017-02-15 13:53:24 +0000 |
1295 | @@ -1,5 +1,5 @@ |
1296 | /* |
1297 | - * Copyright 2013-2016 Canonical Ltd. |
1298 | + * Copyright 2013-2017 Canonical Ltd. |
1299 | * |
1300 | * This file is part of webbrowser-app. |
1301 | * |
1302 | @@ -28,7 +28,6 @@ |
1303 | // local |
1304 | #include "bookmarks-model.h" |
1305 | #include "bookmarks-folderlist-model.h" |
1306 | -#include "drag-helper.h" |
1307 | #include "favicon-fetcher.h" |
1308 | #include "file-operations.h" |
1309 | #include "history-domain-model.h" |
1310 | @@ -168,7 +167,6 @@ |
1311 | MAKE_SINGLETON_FACTORY(HistoryModelMock) |
1312 | MAKE_SINGLETON_FACTORY(TestContext) |
1313 | MAKE_SINGLETON_FACTORY(Reparenter) |
1314 | -MAKE_SINGLETON_FACTORY(DragHelper) |
1315 | |
1316 | int main(int argc, char** argv) |
1317 | { |
1318 | @@ -187,7 +185,6 @@ |
1319 | qmlRegisterType<LimitProxyModel>(browserUri, 0, 1, "LimitProxyModel"); |
1320 | qmlRegisterType<TextSearchFilterModel>(browserUri, 0, 1, "TextSearchFilterModel"); |
1321 | qmlRegisterSingletonType<FileOperations>(browserUri, 0, 1, "FileOperations", FileOperations_singleton_factory); |
1322 | - qmlRegisterSingletonType<DragHelper>(browserUri, 0, 1, "DragHelper", DragHelper_singleton_factory); |
1323 | qmlRegisterSingletonType<Reparenter>(browserUri, 0, 1, "Reparenter", Reparenter_singleton_factory); |
1324 | |
1325 | const char* testUri = "webbrowsertest.private"; |
1326 | |
1327 | === modified file 'tests/unittests/qml/tst_TabsBar.qml' |
1328 | --- tests/unittests/qml/tst_TabsBar.qml 2016-11-18 15:10:47 +0000 |
1329 | +++ tests/unittests/qml/tst_TabsBar.qml 2017-02-15 13:53:24 +0000 |
1330 | @@ -1,5 +1,5 @@ |
1331 | /* |
1332 | - * Copyright 2015 Canonical Ltd. |
1333 | + * Copyright 2015-2017 Canonical Ltd. |
1334 | * |
1335 | * This file is part of webbrowser-app. |
1336 | * |
1337 | @@ -32,6 +32,35 @@ |
1338 | |
1339 | TabsModel { |
1340 | id: tabsModel |
1341 | + |
1342 | + // These methods are required by the TabsBar component |
1343 | + property int selectedIndex: currentIndex |
1344 | + |
1345 | + function addTab() { |
1346 | + tabs.requestNewTab(count, true); |
1347 | + } |
1348 | + |
1349 | + function moveTab(from, to) { |
1350 | + if (from == to |
1351 | + || from < 0 || from >= count |
1352 | + || to < 0 || to >= count) { |
1353 | + return; |
1354 | + } |
1355 | + |
1356 | + move(from, to); |
1357 | + } |
1358 | + |
1359 | + // Overload removeTab and add moving property so we can tell when |
1360 | + // the tab is closing due to moving to a new window |
1361 | + // This is required because we need to avoid destroying the content |
1362 | + // of that tab that is moved |
1363 | + function removeTab(index, moving) { |
1364 | + tabs.tabClosed(index, false); |
1365 | + } |
1366 | + |
1367 | + function selectTab(index) { |
1368 | + currentIndex = index; |
1369 | + } |
1370 | } |
1371 | |
1372 | Component { |
1373 | @@ -58,8 +87,9 @@ |
1374 | height: 50 |
1375 | |
1376 | model: tabsModel |
1377 | - onSwitchToTab: model.currentIndex = index |
1378 | + |
1379 | onRequestNewTab: insertTab("", "", "", index) |
1380 | + |
1381 | function appendTab(url, title, icon) { |
1382 | insertTab(url, title, icon, model.count) |
1383 | model.currentIndex = model.count - 1 |
1384 | @@ -97,16 +127,16 @@ |
1385 | } |
1386 | |
1387 | function getTabDelegate(index) { |
1388 | - var container = findChild(tabs, "tabsContainer") |
1389 | - for (var i = 0; i < container.children.length; ++i) { |
1390 | - var child = container.children[i] |
1391 | - if ((child.objectName == "tabDelegate") && (child.tabIndex == index)) { |
1392 | - return child |
1393 | - } |
1394 | + var listview = findChild(tabs, "tabListView") |
1395 | + var items = getListItems(listview, "tabDelegate") |
1396 | + |
1397 | + if (index < items.length) { |
1398 | + return items[index] |
1399 | + } else { |
1400 | + return null |
1401 | } |
1402 | - return null |
1403 | } |
1404 | - |
1405 | + |
1406 | function getTabItem(index) { |
1407 | return findChild(getTabDelegate(index), "tabItem") |
1408 | } |
1409 | @@ -208,7 +238,7 @@ |
1410 | var count = populateTabs() |
1411 | for (var i = 0; i < count; i++) { |
1412 | var tab = getTabDelegate(count - (i + 1)) |
1413 | - var closeButton = findChild(tab, "closeButton") |
1414 | + var closeButton = findChild(tab, "tabCloseButton") |
1415 | clickItem(closeButton, data.button) |
1416 | compare(tabClosedSpy.count, i + 1) |
1417 | compare(tabClosedSpy.signalArguments[i][0], count - (i + 1)) |
1418 | @@ -220,15 +250,25 @@ |
1419 | |
1420 | function dragTab(tab, dx, index) { |
1421 | var c = centerOf(tab) |
1422 | - mouseDrag(tab, c.x, c.y, dx, 0) |
1423 | - compare(getTabDelegate(index), tab) |
1424 | + |
1425 | + mousePress(tab, c.x, c.y); |
1426 | + |
1427 | + // Move tab slowly otherwise it can skip the DropArea |
1428 | + for (var j = 0; j < dx; j++) { |
1429 | + mouseMove(tabs, j, c.y) |
1430 | + wait(1) |
1431 | + } |
1432 | + |
1433 | + mouseRelease(tab, c.x + dx, c.y); |
1434 | + |
1435 | + compare(tabsModel.get(index).title, tab.title) |
1436 | compare(tabsModel.currentIndex, index) |
1437 | wait(500) |
1438 | } |
1439 | |
1440 | // Move the first tab to the right |
1441 | - var tab = getTabDelegate(0) |
1442 | - dragTab(tab, tab.width * 0.8, 1) |
1443 | + var tab = getTabItem(0) |
1444 | + dragTab(tab, tab.width, 1) |
1445 | |
1446 | // Start a move to the right and release too early |
1447 | dragTab(tab, tab.width * 0.3, 1) |
1448 | @@ -240,7 +280,7 @@ |
1449 | dragTab(tab, tab.width * 3, 2) |
1450 | |
1451 | // Move another tab all the way to the left and overshoot |
1452 | - tab = getTabDelegate(1) |
1453 | + tab = getTabItem(1) |
1454 | dragTab(tab, -tab.width * 2, 0) |
1455 | } |
1456 | |
1457 | @@ -300,27 +340,5 @@ |
1458 | compare(tabsModel.get(1).url, "") |
1459 | compare(tabsModel.get(2).url, baseUrl + "2") |
1460 | } |
1461 | - |
1462 | - function test_close_icon_invisible() { |
1463 | - var count = 20 |
1464 | - |
1465 | - // Add 2 tabs and check both have showCloseIcon |
1466 | - tabs.appendTab("", "tab " + 0, "") |
1467 | - tabs.appendTab("", "tab " + 0, "") |
1468 | - |
1469 | - compare(getTabItem(0).showCloseIcon, true) |
1470 | - compare(getTabItem(1).showCloseIcon, true) |
1471 | - |
1472 | - // Add new tabs and check that both icons are shown |
1473 | - for (var i = 2; i < count; ++i) { |
1474 | - tabs.appendTab("", "tab " + i, "") |
1475 | - compare(tabsModel.currentIndex, i) |
1476 | - |
1477 | - tryCompare(getTabItem(i), "showCloseIcon", true, 1000) |
1478 | - } |
1479 | - |
1480 | - // Check that middle non-selected tab icons are not shown |
1481 | - compare(getTabItem(count - 10).showCloseIcon, false) |
1482 | - } |
1483 | } |
1484 | } |
1485 | |
1486 | === modified file 'tests/unittests/tabs-model/tst_TabsModelTests.cpp' |
1487 | --- tests/unittests/tabs-model/tst_TabsModelTests.cpp 2017-02-15 11:22:05 +0000 |
1488 | +++ tests/unittests/tabs-model/tst_TabsModelTests.cpp 2017-02-15 13:53:24 +0000 |
1489 | @@ -507,12 +507,28 @@ |
1490 | QSignalSpy spyTab(model, SIGNAL(currentTabChanged())); |
1491 | |
1492 | model->move(from, to); |
1493 | - QCOMPARE(spyMoved.count(), moved ? 1 : 0); |
1494 | + QCOMPARE(spyMoved.count(), moved ? abs(from - to) : 0); |
1495 | if (moved) { |
1496 | - QList<QVariant> args = spyMoved.takeFirst(); |
1497 | - QCOMPARE(args.at(1).toInt(), from); |
1498 | - QCOMPARE(args.at(2).toInt(), from); |
1499 | - QCOMPARE(args.at(4).toInt(), to); |
1500 | + QList<QVariant> argsFirst = spyMoved.first(); |
1501 | + QList<QVariant> argsLast = spyMoved.last(); |
1502 | + |
1503 | + if (to > from) { |
1504 | + QCOMPARE(argsFirst.at(1).toInt(), from); |
1505 | + QCOMPARE(argsFirst.at(2).toInt(), from); |
1506 | + QCOMPARE(argsFirst.at(4).toInt(), from + 2); |
1507 | + |
1508 | + QCOMPARE(argsLast.at(1).toInt(), to - 1); |
1509 | + QCOMPARE(argsLast.at(2).toInt(), to - 1); |
1510 | + QCOMPARE(argsLast.at(4).toInt(), to + 1); |
1511 | + } else { |
1512 | + QCOMPARE(argsFirst.at(1).toInt(), from); |
1513 | + QCOMPARE(argsFirst.at(2).toInt(), from); |
1514 | + QCOMPARE(argsFirst.at(4).toInt(), from - 1); |
1515 | + |
1516 | + QCOMPARE(argsLast.at(1).toInt(), to + 1); |
1517 | + QCOMPARE(argsLast.at(2).toInt(), to + 1); |
1518 | + QCOMPARE(argsLast.at(4).toInt(), to); |
1519 | + } |
1520 | } |
1521 | QCOMPARE(spyIndex.count(), indexChanged ? 1 : 0); |
1522 | QCOMPARE(model->currentIndex(), newIndex); |
qtdeclarative5- ubuntu- ui-extras0. 2 needs to be added as a runtime dependency of webbrowser-app in debian/control.
When building the snap locally, the resulting snap file is 20MB, up from 2MB previously. And sure enough it ships lots of Qt libraries in usr/lib/$ARCH/. What’s the reason for building ui-extras from source instead of adding it the stage packages? Or even taking it one step further, shouldn’t we add qtdeclarative5- ubuntu- ui-extras0. 2 to the stage packages in the ubuntu-app-platform snap?