Merge lp:~ahayzen/webbrowser-app/dnd-tabs-001 into lp:webbrowser-app
- dnd-tabs-001
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~ahayzen/webbrowser-app/dnd-tabs-001 |
Merge into: | lp:webbrowser-app |
Prerequisite: | lp:webbrowser-app/staging |
Diff against target: |
1612 lines (+1080/-47) 22 files modified
po/webbrowser-app.pot (+24/-20) src/app/webbrowser/Browser.qml (+129/-4) src/app/webbrowser/BrowserTab.qml (+20/-3) src/app/webbrowser/CMakeLists.txt (+2/-0) src/app/webbrowser/Chrome.qml (+6/-0) src/app/webbrowser/TabComponent.qml (+22/-10) src/app/webbrowser/TabsBar.qml (+84/-4) src/app/webbrowser/TabsList.qml (+2/-2) src/app/webbrowser/drag-helper.cpp (+100/-0) src/app/webbrowser/drag-helper.h (+61/-0) src/app/webbrowser/reparenter.cpp (+94/-0) src/app/webbrowser/reparenter.h (+45/-0) src/app/webbrowser/webbrowser-app.cpp (+4/-0) src/app/webbrowser/webbrowser-app.qml (+48/-2) tests/autopilot/webbrowser_app/tests/__init__.py (+28/-0) tests/autopilot/webbrowser_app/tests/test_multiple_windows.py (+203/-0) tests/unittests/qml/CMakeLists.txt (+2/-0) tests/unittests/qml/ReparenterFakeContainer.qml (+40/-0) tests/unittests/qml/ReparenterFakeTab.qml (+37/-0) tests/unittests/qml/tst_BrowserTab.qml (+5/-2) tests/unittests/qml/tst_QmlTests.cpp (+4/-0) tests/unittests/qml/tst_Reparenter.qml (+120/-0) |
To merge this branch: | bzr merge lp:~ahayzen/webbrowser-app/dnd-tabs-001 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
system-apps-ci-bot | continuous-integration | Needs Fixing | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+308505@code.launchpad.net |
This proposal supersedes a proposal from 2016-09-28.
This proposal has been superseded by a proposal from 2016-10-14.
Commit message
WIP Drag and drop support
Description of the change
WIP Drag and drop support, proposing so that we can get debs easily.
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1518
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1519
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1520
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1521
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1522
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1523
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Bill Filler (bfiller) wrote : Posted in a previous version of this proposal | # |
tested, works great!
One comment: if you create a private window and then some tabs, not sure if you should be able to drag that tab into a non-private window. Seems to be allowed in this implementation but not sure we want that behavior. Seems in Chromium you can only move private tabs between private windows and public tabs between public windows. Guessing we should follow same thing.
Bill Filler (bfiller) wrote : Posted in a previous version of this proposal | # |
previously was testing with unity7, and could drag a tab into it's own new window or drag into another existing browser window. this all functioned well.
On unity8, I encountered the following problems, guessing they are limitations with unity/mir
1) when trying to drag a tab into a new window it actually works but leaves an additional shadow window with pixelated display. so if you start with one window and drag out you are left with 3 windows.
2) cannot seem to drag out of one window and drop in another. I see an icon with circle with line through it on the drop target.
You should probably file qtmir bugs on these
Andrew Hayzen (ahayzen) wrote : Posted in a previous version of this proposal | # |
For unity7:
Agreed tabs should not be able to move between windows of different incognito modes, I'll get onto that.
For the issues under unity8:
1) I suspected this is due to handle on the mouse, which didn't work on unity8 when I tried and appeared as a separate window. This seems like the same issue that Gtk/Qt right click menus have where they are in another window. I mentioned this in the bug I reported below as well.
2) I reported a bug in mir while developing https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1524
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1525
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1526
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1527
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1528
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1529
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1529
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1531. By Andrew Hayzen
-
* Fix for progress bar appearing after tab is dragged out
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1530
https:/
Executed test runs:
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 1532. By Andrew Hayzen
-
* Be more declarative in DropArea
* Change use of DropActions to make more sense
* Change unit test to use default timeout
* Allow setting of expectedAction in DragHelper
* Update uses of null to Q_NULLPTR
* Disable dragging on mir clients - 1533. By Andrew Hayzen
-
* Limit y diff of visual tabitem when dragging
- 1534. By Andrew Hayzen
-
* Fix regression where new tabs have massive favicons
- 1535. By Andrew Hayzen
-
* Remove white shade on bottom part of browser to match design
- 1536. By Andrew Hayzen
-
* Revert change in use of DropAction as dropping outside of a DropArea is always IgnoreAction
* Change test_drag_tab_outside_ new_window to move the tab below the window
* Change property tabsBarHeight to be real rather than int - 1537. By Andrew Hayzen
-
* Revert .pot changes
- 1538. By Andrew Hayzen
-
* Revert changes to test as it was fine before :-)
- 1539. By Andrew Hayzen
-
* Remove referenes to browser, tabModel and windo in TabsBar.qml
* Remove reference to builder in Browser.qml
* Allowed passing of properties to Reparenter::createObject( )
* Made Reparenter a singleton
* Moved buildContextProperties( ) and createTabHelper() into internal to make them private
* Change dropArea.heightThreshold to be real not int
* Removed many console.debug() calls
* Changes includes to be fully-qualified in drag-helper and reparenter
* Renamed closeMethod to callback for newWindowFromTab() - 1540. By Andrew Hayzen
-
* Ensure that switchToTab is called after binding a new tab, otherwise the chrome's position can be broken
- 1541. By Andrew Hayzen
-
* Merge of lp:webbrowser-app/staging
- 1542. By Andrew Hayzen
-
* Update dropArea shade to match new design
* Fix for bad conflict resolution - 1543. By Andrew Hayzen
-
* Fix for typo
- 1544. By Andrew Hayzen
-
* Add .dragging property to DragHelper so that drop area shade knows when a drag event is occuring
* Change DragHelper to a singleton - 1545. By Andrew Hayzen
-
* Changed DragHelper to be a QObject and use member initialisation
* Changed Reparenter::createObject to use beginCreate and completeCreate
* Various fixes to reduce warnings under QML tests - 1546. By Andrew Hayzen
-
* Use forward declaration of QQuickItem
- 1547. By Andrew Hayzen
-
* Fix for missed case of createTabHelper -> createTab
- 1548. By Andrew Hayzen
-
* Ensure webviews are not shown when newTabView is active
- 1549. By Andrew Hayzen
-
* Change keyboard shortcuts to use contentsContainer visibility instead of tabsContainer
- 1550. By Andrew Hayzen
-
* Use enabled rather than visible on the tabContainer to prevent events being stolen, otherwise chrome disappears when opening new tabs as the locationBarCont
roller. offset doesn't get set - 1551. By Andrew Hayzen
-
* Before tab is closed, check if a new tab needs to be generated as the context will disappear
- 1552. By Andrew Hayzen
-
* Use deleteLater rather than delete item; otherwise on slower devices such as phones it crashes
* Don't delete the context if it was not removed from the reparenter context store
* Change qml test for reparenter to respect that the delete may take time to happen
Unmerged revisions
Preview Diff
1 | === modified file 'po/webbrowser-app.pot' |
2 | --- po/webbrowser-app.pot 2016-10-04 14:07:17 +0000 |
3 | +++ po/webbrowser-app.pot 2016-10-14 13:08:37 +0000 |
4 | @@ -8,7 +8,7 @@ |
5 | msgstr "" |
6 | "Project-Id-Version: webbrowser-app\n" |
7 | "Report-Msgid-Bugs-To: \n" |
8 | -"POT-Creation-Date: 2016-10-04 16:02+0200\n" |
9 | +"POT-Creation-Date: 2016-10-10 17:45+0100\n" |
10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
11 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
12 | "Language-Team: LANGUAGE <LL@li.org>\n" |
13 | @@ -378,7 +378,7 @@ |
14 | msgid "Erase" |
15 | msgstr "" |
16 | |
17 | -#: src/app/actions/FindInPage.qml:23 src/app/webbrowser/Browser.qml:569 |
18 | +#: src/app/actions/FindInPage.qml:23 src/app/webbrowser/Browser.qml:604 |
19 | msgid "Find in page" |
20 | msgstr "" |
21 | |
22 | @@ -408,8 +408,8 @@ |
23 | msgid "Address;URL;www" |
24 | msgstr "" |
25 | |
26 | -#: src/app/actions/NewTab.qml:23 src/app/webbrowser/Browser.qml:440 |
27 | -#: src/app/webbrowser/TabsBar.qml:91 |
28 | +#: src/app/actions/NewTab.qml:23 src/app/webbrowser/Browser.qml:475 |
29 | +#: src/app/webbrowser/TabsBar.qml:95 |
30 | msgid "New Tab" |
31 | msgstr "" |
32 | |
33 | @@ -456,7 +456,7 @@ |
34 | msgstr "" |
35 | |
36 | #: src/app/actions/Reload.qml:23 src/app/webbrowser/SadTab.qml:86 |
37 | -#: src/app/webbrowser/TabsBar.qml:96 src/app/webcontainer/SadPage.qml:51 |
38 | +#: src/app/webbrowser/TabsBar.qml:100 src/app/webcontainer/SadPage.qml:51 |
39 | msgid "Reload" |
40 | msgstr "" |
41 | |
42 | @@ -482,7 +482,7 @@ |
43 | msgid "Select all" |
44 | msgstr "" |
45 | |
46 | -#: src/app/actions/Share.qml:22 src/app/webbrowser/Browser.qml:549 |
47 | +#: src/app/actions/Share.qml:22 src/app/webbrowser/Browser.qml:584 |
48 | msgid "Share" |
49 | msgstr "" |
50 | |
51 | @@ -547,14 +547,14 @@ |
52 | |
53 | #: src/app/webbrowser/BookmarksView.qml:32 |
54 | #: src/app/webbrowser/BookmarksViewWide.qml:32 |
55 | -#: src/app/webbrowser/Browser.qml:557 src/app/webbrowser/NewTabView.qml:130 |
56 | +#: src/app/webbrowser/Browser.qml:592 src/app/webbrowser/NewTabView.qml:130 |
57 | #: src/app/webbrowser/NewTabViewWide.qml:139 |
58 | msgid "Bookmarks" |
59 | msgstr "" |
60 | |
61 | #: src/app/webbrowser/BookmarksView.qml:76 |
62 | #: src/app/webbrowser/BookmarksViewWide.qml:75 |
63 | -#: src/app/webbrowser/Browser.qml:426 src/app/webbrowser/HistoryView.qml:126 |
64 | +#: src/app/webbrowser/Browser.qml:461 src/app/webbrowser/HistoryView.qml:126 |
65 | #: src/app/webbrowser/HistoryViewWide.qml:407 |
66 | msgid "Done" |
67 | msgstr "" |
68 | @@ -563,42 +563,42 @@ |
69 | #: src/app/webbrowser/BookmarksViewWide.qml:89 |
70 | #: src/app/webbrowser/HistoryView.qml:140 |
71 | #: src/app/webbrowser/HistoryViewWide.qml:421 |
72 | -#: src/app/webbrowser/TabsBar.qml:153 src/app/webbrowser/TabsList.qml:99 |
73 | +#: src/app/webbrowser/TabsBar.qml:174 src/app/webbrowser/TabsList.qml:99 |
74 | msgid "New tab" |
75 | msgstr "" |
76 | |
77 | -#: src/app/webbrowser/Browser.qml:537 |
78 | +#: src/app/webbrowser/Browser.qml:572 |
79 | msgid "New window" |
80 | msgstr "" |
81 | |
82 | -#: src/app/webbrowser/Browser.qml:543 |
83 | +#: src/app/webbrowser/Browser.qml:578 |
84 | msgid "New private window" |
85 | msgstr "" |
86 | |
87 | -#: src/app/webbrowser/Browser.qml:563 src/app/webbrowser/HistoryView.qml:30 |
88 | +#: src/app/webbrowser/Browser.qml:598 src/app/webbrowser/HistoryView.qml:30 |
89 | #: src/app/webbrowser/HistoryViewWide.qml:35 |
90 | msgid "History" |
91 | msgstr "" |
92 | |
93 | -#: src/app/webbrowser/Browser.qml:576 src/app/webbrowser/DownloadsPage.qml:45 |
94 | +#: src/app/webbrowser/Browser.qml:611 src/app/webbrowser/DownloadsPage.qml:45 |
95 | msgid "Downloads" |
96 | msgstr "" |
97 | |
98 | -#: src/app/webbrowser/Browser.qml:583 src/app/webbrowser/SettingsPage.qml:41 |
99 | +#: src/app/webbrowser/Browser.qml:618 src/app/webbrowser/SettingsPage.qml:41 |
100 | msgid "Settings" |
101 | msgstr "" |
102 | |
103 | #. TRANSLATORS: %1 refers to the current number of tabs opened |
104 | -#: src/app/webbrowser/Browser.qml:756 src/app/webbrowser/Browser.qml:794 |
105 | +#: src/app/webbrowser/Browser.qml:791 src/app/webbrowser/Browser.qml:829 |
106 | #, qt-format |
107 | msgid "(%1)" |
108 | msgstr "" |
109 | |
110 | -#: src/app/webbrowser/Browser.qml:1337 |
111 | +#: src/app/webbrowser/Browser.qml:1386 |
112 | msgid "Swipe Up To Exit Full Screen" |
113 | msgstr "" |
114 | |
115 | -#: src/app/webbrowser/Browser.qml:1338 |
116 | +#: src/app/webbrowser/Browser.qml:1387 |
117 | msgid "Press ESC To Exit Full Screen" |
118 | msgstr "" |
119 | |
120 | @@ -823,7 +823,11 @@ |
121 | msgid "Tap to view" |
122 | msgstr "" |
123 | |
124 | -#: src/app/webbrowser/TabsBar.qml:102 |
125 | +#: src/app/webbrowser/TabsBar.qml:106 |
126 | +msgid "Move to New Window" |
127 | +msgstr "" |
128 | + |
129 | +#: src/app/webbrowser/TabsBar.qml:115 |
130 | msgid "Close Tab" |
131 | msgstr "" |
132 | |
133 | @@ -832,13 +836,13 @@ |
134 | msgstr "" |
135 | |
136 | #. TRANSLATORS: %1 refers to the current page’s title |
137 | -#: src/app/webbrowser/webbrowser-app.qml:108 |
138 | +#: src/app/webbrowser/webbrowser-app.qml:112 |
139 | #: src/app/webcontainer/webapp-container.qml:72 |
140 | #, qt-format |
141 | msgid "%1 - Ubuntu Web Browser" |
142 | msgstr "" |
143 | |
144 | -#: src/app/webbrowser/webbrowser-app.qml:110 |
145 | +#: src/app/webbrowser/webbrowser-app.qml:114 |
146 | #: src/app/webcontainer/webapp-container.qml:74 |
147 | msgid "Ubuntu Web Browser" |
148 | msgstr "" |
149 | |
150 | === modified file 'src/app/webbrowser/Browser.qml' |
151 | --- src/app/webbrowser/Browser.qml 2016-10-14 13:08:37 +0000 |
152 | +++ src/app/webbrowser/Browser.qml 2016-10-14 13:08:37 +0000 |
153 | @@ -64,12 +64,47 @@ |
154 | 'restoreType': Oxide.WebView.RestoreLastSessionExitedCleanly} |
155 | return createTab(properties) |
156 | } |
157 | - |
158 | + |
159 | + function buildContextProperties(properties) { |
160 | + if (properties === undefined) { |
161 | + properties = {}; |
162 | + } |
163 | + |
164 | + properties["bottomEdgeHandle"] = bottomEdgeHandle; |
165 | + properties["browser"] = browser; |
166 | + properties["chrome"] = chrome; |
167 | + properties["chromeController"] = chromeController; |
168 | + properties["contentHandlerLoader"] = contentHandlerLoader; |
169 | + properties["downloadDialogLoader"] = downloadDialogLoader; |
170 | + properties["downloadsViewLoader"] = downloadsViewLoader; |
171 | + properties["filePickerLoader"] = filePickerLoader; |
172 | + properties["internal"] = internal; |
173 | + properties["recentView"] = recentView; |
174 | + properties["tabsModel"] = tabsModel; |
175 | + |
176 | + return properties; |
177 | + } |
178 | + |
179 | + function createTabHelper(properties) { |
180 | + return builder(tabComponent, tabContainer, buildContextProperties(properties)); |
181 | + } |
182 | + |
183 | function createTab(properties) { |
184 | - return tabComponent.createObject(tabContainer, properties) |
185 | + return createTabHelper(properties) |
186 | + } |
187 | + |
188 | + function bindExistingTab(tab) { |
189 | + reparenter.reparent(tab, tabContainer); |
190 | + |
191 | + var properties = buildContextProperties(); |
192 | + |
193 | + for (var prop in properties) { |
194 | + tab[prop] = properties[prop]; |
195 | + } |
196 | } |
197 | |
198 | signal newWindowRequested(bool incognito) |
199 | + signal newWindowFromTab(var tab, var closeMethod) |
200 | signal openLinkInWindowRequested(url url, bool incognito) |
201 | |
202 | Connections { |
203 | @@ -977,7 +1012,7 @@ |
204 | |
205 | function openUrlInNewTab(url, setCurrent, load, index) { |
206 | load = typeof load !== 'undefined' ? load : true |
207 | - var tab = tabComponent.createObject(tabContainer, {"initialUrl": url}) |
208 | + var tab = createTabHelper({"initialUrl": url}) |
209 | addTab(tab, setCurrent, index) |
210 | if (load) { |
211 | tab.load() |
212 | @@ -998,6 +1033,7 @@ |
213 | |
214 | function closeTab(index) { |
215 | var tab = tabsModel.get(index) |
216 | + tabsModel.remove(index) |
217 | if (tab) { |
218 | if (!incognito && tab.url.toString().length > 0) { |
219 | closedTabHistory.push({ |
220 | @@ -1007,7 +1043,6 @@ |
221 | } |
222 | tab.close() |
223 | } |
224 | - tabsModel.remove(index) |
225 | if (tabsModel.currentTab) { |
226 | tabsModel.currentTab.load() |
227 | } |
228 | @@ -1400,4 +1435,94 @@ |
229 | source: "ContentPickerDialog.qml" |
230 | asynchronous: true |
231 | } |
232 | + |
233 | + DropArea { |
234 | + anchors { |
235 | + fill: parent |
236 | + } |
237 | + keys: ["webbrowser/tab-" + (incognito ? "incognito" : "public")] |
238 | + |
239 | + onPositionChanged: { |
240 | + if (drag.source.tabWindow === window && drag.y < chrome.height) { |
241 | + // tab drag is within same window and in chrome |
242 | + // so reorder tabs by setting tabDelegate x position |
243 | + drag.source.x = drag.x - (drag.source.width / 2); |
244 | + } |
245 | + |
246 | + dropChromeShade.opacity = drag.y <= chrome.height ? 0.7 : 0.4 |
247 | + } |
248 | + onEntered: { |
249 | + window.raise() |
250 | + dropShade.opacity = 1 |
251 | + } |
252 | + onExited: dropShade.opacity = 0 |
253 | + onDropped: { |
254 | + if (drag.y > chrome.height) { |
255 | + console.debug("Dropped in bottom area, creating new window"); |
256 | + drop.accept(Qt.IgnoreAction); |
257 | + } else if (drag.source.tabWindow === window) { |
258 | + console.debug("Dropped in same window"); |
259 | + drop.accept(Qt.CopyAction); |
260 | + } else { |
261 | + console.debug("Dropped in new window, moving tab"); |
262 | + |
263 | + window.addExistingTab(drag.source.tab); |
264 | + window.tabsModel.currentIndex = window.tabsModel.count - 1; |
265 | + window.show(); |
266 | + window.requestActivate(); |
267 | + |
268 | + window.tabsModel.currentTab.load(); |
269 | + |
270 | + drop.accept(Qt.MoveAction); |
271 | + } |
272 | + |
273 | + dropShade.opacity = 0 |
274 | + } |
275 | + |
276 | + Item { |
277 | + id: dropShade |
278 | + anchors { |
279 | + fill: parent |
280 | + } |
281 | + opacity: 0 |
282 | + |
283 | + Rectangle { |
284 | + id: dropChromeShade |
285 | + anchors { |
286 | + left: parent.left |
287 | + right: parent.right |
288 | + top: parent.top |
289 | + } |
290 | + border { |
291 | + color: UbuntuColors.orange |
292 | + width: units.gu(1) |
293 | + } |
294 | + color: "transparent" |
295 | + height: chrome.height |
296 | + opacity: 0.4 |
297 | + |
298 | + Behavior on opacity { |
299 | + NumberAnimation { |
300 | + |
301 | + } |
302 | + } |
303 | + } |
304 | + |
305 | + Rectangle { |
306 | + id: dropTabShade |
307 | + anchors { |
308 | + fill: parent |
309 | + topMargin: chrome.height |
310 | + } |
311 | + color: "#FFF" |
312 | + opacity: 0.7 |
313 | + } |
314 | + |
315 | + Behavior on opacity { |
316 | + NumberAnimation { |
317 | + |
318 | + } |
319 | + } |
320 | + } |
321 | + } |
322 | } |
323 | |
324 | === modified file 'src/app/webbrowser/BrowserTab.qml' |
325 | --- src/app/webbrowser/BrowserTab.qml 2016-10-14 13:08:37 +0000 |
326 | +++ src/app/webbrowser/BrowserTab.qml 2016-10-14 13:08:37 +0000 |
327 | @@ -97,7 +97,18 @@ |
328 | } |
329 | } |
330 | } |
331 | - |
332 | + |
333 | + function loadExisting(existingTab) { |
334 | + if (!webview && !internal.incubator) { |
335 | + // Reparent the webview and any other vars |
336 | + existingTab.webview.parent = webviewContainer; |
337 | + existingTab.webview.tab = tab; |
338 | + |
339 | + // Set the webview into this window |
340 | + webviewContainer.webview = existingTab.webview; |
341 | + } |
342 | + } |
343 | + |
344 | function unload() { |
345 | if (webview) { |
346 | initialUrl = webview.url |
347 | @@ -118,11 +129,17 @@ |
348 | } |
349 | } |
350 | |
351 | - function close() { |
352 | + function close(reparentDestroy) { |
353 | var _url = url |
354 | unload() |
355 | if (_url.toString()) PreviewManager.checkDelete(_url) |
356 | - destroy() |
357 | + |
358 | + if (reparentDestroy || reparentDestroy === undefined) { |
359 | + // Destroys context and object |
360 | + reparenter.destroyContextAndObject(tab); |
361 | + } else { |
362 | + destroy(); |
363 | + } |
364 | } |
365 | |
366 | QtObject { |
367 | |
368 | === modified file 'src/app/webbrowser/CMakeLists.txt' |
369 | --- src/app/webbrowser/CMakeLists.txt 2015-12-10 09:06:06 +0000 |
370 | +++ src/app/webbrowser/CMakeLists.txt 2016-10-14 13:08:37 +0000 |
371 | @@ -33,7 +33,9 @@ |
372 | |
373 | set(WEBBROWSER_APP_SRC |
374 | cache-deleter.cpp |
375 | + drag-helper.cpp |
376 | file-operations.cpp |
377 | + reparenter.cpp |
378 | searchengine.cpp |
379 | webbrowser-app.cpp |
380 | ) |
381 | |
382 | === modified file 'src/app/webbrowser/Chrome.qml' |
383 | --- src/app/webbrowser/Chrome.qml 2016-10-14 13:08:37 +0000 |
384 | +++ src/app/webbrowser/Chrome.qml 2016-10-14 13:08:37 +0000 |
385 | @@ -51,6 +51,12 @@ |
386 | |
387 | implicitHeight: tabsBar.height + navigationBar.height + content.anchors.topMargin |
388 | |
389 | + onWebviewChanged: { |
390 | + if (webview) { |
391 | + loading = webview.loading |
392 | + } |
393 | + } |
394 | + |
395 | function selectAll() { |
396 | navigationBar.selectAll() |
397 | } |
398 | |
399 | === modified file 'src/app/webbrowser/TabComponent.qml' |
400 | --- src/app/webbrowser/TabComponent.qml 2016-10-14 13:08:37 +0000 |
401 | +++ src/app/webbrowser/TabComponent.qml 2016-10-14 13:08:37 +0000 |
402 | @@ -34,10 +34,22 @@ |
403 | |
404 | BrowserTab { |
405 | anchors.fill: parent |
406 | - incognito: browser.incognito |
407 | - current: tabsModel && tabsModel.currentTab === this |
408 | + incognito: browser ? browser.incognito : false |
409 | + current: browser ? browser.tabsModel && browser.tabsModel.currentTab === this : false |
410 | focus: current |
411 | |
412 | + property var bottomEdgeHandle |
413 | + property var browser |
414 | + property var chrome |
415 | + property var chromeController |
416 | + property var contentHandlerLoader |
417 | + property var downloadDialogLoader |
418 | + property var downloadsViewLoader |
419 | + property var filePickerLoader |
420 | + property var internal |
421 | + property var recentView |
422 | + property var tabsModel |
423 | + |
424 | Item { |
425 | id: contextualMenuTarget |
426 | visible: false |
427 | @@ -49,18 +61,18 @@ |
428 | property BrowserTab tab |
429 | readonly property bool current: tab.current |
430 | |
431 | - currentWebview: browser.currentWebview |
432 | - filePicker: filePickerLoader.item |
433 | + currentWebview: browser ? browser.currentWebview : null |
434 | + filePicker: filePickerLoader ? filePickerLoader.item : null |
435 | |
436 | anchors.fill: parent |
437 | |
438 | focus: true |
439 | |
440 | - enabled: current && !bottomEdgeHandle.dragging && !recentView.visible && tabContainer.focus |
441 | + enabled: current && !bottomEdgeHandle.dragging && !recentView.visible && parent.focus |
442 | |
443 | locationBarController { |
444 | - height: chrome.height |
445 | - mode: chromeController.defaultMode |
446 | + height: chrome ? chrome.height : 0 |
447 | + mode: chromeController ? chromeController.defaultMode : null |
448 | } |
449 | |
450 | //experimental.preferences.developerExtrasEnabled: developerExtrasEnabled |
451 | @@ -115,7 +127,7 @@ |
452 | } |
453 | Actions.Share { |
454 | objectName: "ShareContextualAction" |
455 | - enabled: (contentHandlerLoader.status == Loader.Ready) && contextModel && |
456 | + enabled: (contentHandlerLoader && contentHandlerLoader.status == Loader.Ready) && contextModel && |
457 | (contextModel.linkUrl.toString() || contextModel.selectionText) |
458 | onTriggered: { |
459 | if (contextModel.linkUrl.toString()) { |
460 | @@ -237,10 +249,10 @@ |
461 | Component.onCompleted: webviewimpl.contextMenuOnCompleted(this) |
462 | } |
463 | } |
464 | - contextMenu: browser.wide ? contextMenuWideComponent : contextMenuNarrowComponent |
465 | + contextMenu: browser && browser.wide ? contextMenuWideComponent : contextMenuNarrowComponent |
466 | |
467 | onNewViewRequested: { |
468 | - var tab = tabComponent.createObject(tabContainer, {"request": request}) |
469 | + var tab = browser.createTabHelper({"request": request}) |
470 | var setCurrent = (request.disposition == Oxide.NewViewRequest.DispositionNewForegroundTab) |
471 | internal.addTab(tab, setCurrent) |
472 | if (setCurrent) tabContainer.forceActiveFocus() |
473 | |
474 | === modified file 'src/app/webbrowser/TabsBar.qml' |
475 | --- src/app/webbrowser/TabsBar.qml 2016-10-14 13:08:37 +0000 |
476 | +++ src/app/webbrowser/TabsBar.qml 2016-10-14 13:08:37 +0000 |
477 | @@ -19,6 +19,10 @@ |
478 | import QtQuick 2.4 |
479 | import Ubuntu.Components 1.3 |
480 | import Ubuntu.Components.Popups 1.3 |
481 | + |
482 | +import webbrowserapp.private 0.1 |
483 | + |
484 | +import "." |
485 | import ".." |
486 | |
487 | Item { |
488 | @@ -110,6 +114,15 @@ |
489 | onTriggered: menu.tab.reload() |
490 | } |
491 | Action { |
492 | + objectName: "tab_action_move_to_new_window" |
493 | + text: i18n.tr("Move to New Window") |
494 | + onTriggered: { |
495 | + // callback function only removes from model |
496 | + // and not destroy as webview is in new window |
497 | + browser.newWindowFromTab(menu.tab, function() { tabsModel.remove(menu.targetIndex); }) |
498 | + } |
499 | + } |
500 | + Action { |
501 | objectName: "tab_action_close_tab" |
502 | text: i18n.tr("Close Tab") |
503 | onTriggered: root.tabClosed(menu.targetIndex) |
504 | @@ -140,21 +153,29 @@ |
505 | objectName: "tabDelegate" |
506 | |
507 | readonly property int tabIndex: index |
508 | + readonly property BrowserTab tab: tabsModel.get(index) |
509 | + readonly property BrowserWindow tabWindow: window |
510 | |
511 | - anchors.top: tabsContainer.top |
512 | - |
513 | + property real rightMargin: units.dp(1) |
514 | width: getSize(index) |
515 | height: tabsContainer.height |
516 | + y: tabsContainer.y // don't use anchor otherwise drag doesn't work |
517 | |
518 | acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton |
519 | readonly property bool dragging: drag.active |
520 | drag { |
521 | target: (pressedButtons === Qt.LeftButton) ? tabDelegate : null |
522 | - axis: Drag.XAxis |
523 | + axis: Drag.XAndYAxis |
524 | minimumX: 0 |
525 | maximumX: root.width - tabDelegate.width |
526 | filterChildren: true |
527 | } |
528 | + |
529 | + DragHelper { |
530 | + id: dragHelper |
531 | + mimeType: "webbrowser/tab-" + (window.incognito ? "incognito" : "public") |
532 | + source: tabDelegate |
533 | + } |
534 | |
535 | TabItem { |
536 | anchors.fill: parent |
537 | @@ -188,7 +209,66 @@ |
538 | value: getLeftX(index) |
539 | } |
540 | |
541 | - Behavior on x { NumberAnimation { duration: 250 } } |
542 | + Behavior on x { |
543 | + NumberAnimation { |
544 | + duration: 250 |
545 | + } |
546 | + } |
547 | + |
548 | + NumberAnimation { |
549 | + id: resetVerticalAnimation |
550 | + target: tabDelegate |
551 | + duration: 250 |
552 | + property: "y" |
553 | + to: 0 |
554 | + } |
555 | + |
556 | + onPositionChanged: { |
557 | + if (Math.abs(y) > height) { |
558 | + // Reset visual position of tab delegate |
559 | + resetVerticalAnimation.start(); |
560 | + |
561 | + if (mouse.buttons === Qt.LeftButton) { |
562 | + // Generate tab preview for drag handle |
563 | + dragHelper.previewUrl = PreviewManager.previewPathFromUrl(tab.url); |
564 | + |
565 | + var dropAction = dragHelper.execDrag(tab.url); |
566 | + |
567 | + if (dropAction === Qt.MoveAction) { |
568 | + // Moved into another window |
569 | + console.debug("Moved to another window, closing tab"); |
570 | + |
571 | + // drag.active does not become false when |
572 | + // closing the tab so set reordering back |
573 | + repeater.reordering = false; |
574 | + |
575 | + // Just remove from model and do not destory |
576 | + // as webview is used in other window |
577 | + tabsModel.remove(index); |
578 | + } else if (dropAction === Qt.CopyAction) { |
579 | + // Moved into the same window |
580 | + |
581 | + // So no action |
582 | + console.debug("No action, dropped in same window"); |
583 | + } else if (dropAction === Qt.IgnoreAction) { |
584 | + // Moved outside of any window |
585 | + console.debug("Moved outside, generating new window"); |
586 | + |
587 | + // drag.active does not become false when |
588 | + // closing the tab so set reordering back |
589 | + repeater.reordering = false; |
590 | + |
591 | + // callback function only removes from model |
592 | + // and not destroy as webview is in new window |
593 | + browser.newWindowFromTab(tab, function() { tabsModel.remove(index); }) |
594 | + } else { |
595 | + // Unknown state |
596 | + console.debug("Unknown drop action:", dropAction); |
597 | + } |
598 | + } |
599 | + } |
600 | + } |
601 | + onReleased: resetVerticalAnimation.start(); |
602 | |
603 | function getLeftX(index) { |
604 | if (unevenTabWidth) { |
605 | |
606 | === modified file 'src/app/webbrowser/TabsList.qml' |
607 | --- src/app/webbrowser/TabsList.qml 2016-02-25 14:54:54 +0000 |
608 | +++ src/app/webbrowser/TabsList.qml 2016-10-14 13:08:37 +0000 |
609 | @@ -109,14 +109,14 @@ |
610 | incognito: tabslist.incognito |
611 | tab: model.tab |
612 | showContent: ((index > 0) && (delegate.y > flickable.contentY)) || |
613 | - !(tab.webview && tab.webview.visible) |
614 | + !(tab && tab.webview && tab.webview.visible) |
615 | |
616 | Binding { |
617 | // Change the height of the location bar controller |
618 | // for the first webview only, and only while the tabs |
619 | // list view is visible. |
620 | when: tabslist.visible && (index == 0) |
621 | - target: tab.webview ? tab.webview.locationBarController : null |
622 | + target: tab && tab.webview ? tab.webview.locationBarController : null |
623 | property: "height" |
624 | value: invisibleTabChrome.height |
625 | } |
626 | |
627 | === added file 'src/app/webbrowser/drag-helper.cpp' |
628 | --- src/app/webbrowser/drag-helper.cpp 1970-01-01 00:00:00 +0000 |
629 | +++ src/app/webbrowser/drag-helper.cpp 2016-10-14 13:08:37 +0000 |
630 | @@ -0,0 +1,100 @@ |
631 | +/* |
632 | + * Copyright 2016 Canonical Ltd. |
633 | + * |
634 | + * This file is part of webbrowser-app. |
635 | + * |
636 | + * webbrowser-app is free software; you can redistribute it and/or modify |
637 | + * it under the terms of the GNU General Public License as published by |
638 | + * the Free Software Foundation; version 3. |
639 | + * |
640 | + * webbrowser-app is distributed in the hope that it will be useful, |
641 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
642 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
643 | + * GNU General Public License for more details. |
644 | + * |
645 | + * You should have received a copy of the GNU General Public License |
646 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
647 | + */ |
648 | + |
649 | +#include "drag-helper.h" |
650 | + |
651 | +#include <QDrag> |
652 | +#include <QDropEvent> |
653 | +#include <QMimeData> |
654 | +#include <QPainter> |
655 | +#include <QPixmap> |
656 | +#include <QSize> |
657 | + |
658 | +DragHelper::DragHelper() |
659 | +{ |
660 | + m_active = false; |
661 | + m_mime_type = QStringLiteral("webbrowser/tab"); |
662 | + m_preview_url = ""; |
663 | + m_source = NULL; |
664 | +} |
665 | + |
666 | +Qt::DropAction DragHelper::execDrag(QString tabId) |
667 | +{ |
668 | + QDrag *drag = new QDrag(m_source); |
669 | + |
670 | + QMimeData *mimeData = new QMimeData; |
671 | + mimeData->setData(mimeType(), tabId.toLatin1()); |
672 | + |
673 | + QSize pixmapSize(200, 150); |
674 | + |
675 | + QPixmap pixmap(previewUrl()); |
676 | + |
677 | + if (pixmap.isNull()) { |
678 | + // If loading pixmap failed, draw a white rectangle |
679 | + pixmap = QPixmap(pixmapSize); |
680 | + QPainter painter(&pixmap); |
681 | + painter.eraseRect(0, 0, pixmapSize.width(), pixmapSize.height()); |
682 | + painter.fillRect(0, 0, pixmapSize.width(), pixmapSize.height(), QColor(255, 255, 255, 255)); |
683 | + } else { |
684 | + // Scale image to fit the expected size |
685 | + pixmap = pixmap.scaled(pixmapSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); |
686 | + } |
687 | + |
688 | + drag->setHotSpot(QPoint(0, 0)); |
689 | + drag->setMimeData(mimeData); |
690 | + drag->setPixmap(pixmap); |
691 | + |
692 | + return drag->exec(Qt::CopyAction | Qt::MoveAction | Qt::IgnoreAction); |
693 | +} |
694 | + |
695 | +void DragHelper::setActive(bool active) |
696 | +{ |
697 | + if (m_active != active) { |
698 | + m_active = active; |
699 | + |
700 | + Q_EMIT activeChanged(m_active); |
701 | + } |
702 | +} |
703 | + |
704 | +void DragHelper::setMimeType(QString mimeType) |
705 | +{ |
706 | + if (m_mime_type != mimeType) { |
707 | + m_mime_type = mimeType; |
708 | + |
709 | + Q_EMIT mimeTypeChanged(m_mime_type); |
710 | + } |
711 | +} |
712 | + |
713 | +void DragHelper::setPreviewUrl(QString previewUrl) |
714 | +{ |
715 | + if (m_preview_url != previewUrl) { |
716 | + m_preview_url = previewUrl; |
717 | + |
718 | + Q_EMIT previewUrlChanged(m_preview_url); |
719 | + } |
720 | +} |
721 | + |
722 | +void DragHelper::setSource(QQuickItem *source) |
723 | +{ |
724 | + if (m_source != source) { |
725 | + m_source = source; |
726 | + |
727 | + Q_EMIT sourceChanged(m_source); |
728 | + } |
729 | +} |
730 | + |
731 | |
732 | === added file 'src/app/webbrowser/drag-helper.h' |
733 | --- src/app/webbrowser/drag-helper.h 1970-01-01 00:00:00 +0000 |
734 | +++ src/app/webbrowser/drag-helper.h 2016-10-14 13:08:37 +0000 |
735 | @@ -0,0 +1,61 @@ |
736 | +/* |
737 | + * Copyright 2016 Canonical Ltd. |
738 | + * |
739 | + * This file is part of webbrowser-app. |
740 | + * |
741 | + * webbrowser-app is free software; you can redistribute it and/or modify |
742 | + * it under the terms of the GNU General Public License as published by |
743 | + * the Free Software Foundation; version 3. |
744 | + * |
745 | + * webbrowser-app is distributed in the hope that it will be useful, |
746 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
747 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
748 | + * GNU General Public License for more details. |
749 | + * |
750 | + * You should have received a copy of the GNU General Public License |
751 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
752 | + */ |
753 | + |
754 | +#ifndef __DRAGHELPER_H__ |
755 | +#define __DRAGHELPER_H__ |
756 | + |
757 | +#include <QQuickItem> |
758 | +#include <QMouseEvent> |
759 | +#include <QRect> |
760 | + |
761 | +class DragHelper : public QQuickItem |
762 | +{ |
763 | + Q_OBJECT |
764 | + |
765 | + // TODO: add expectedAction |
766 | + |
767 | + Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) |
768 | + Q_PROPERTY(QString mimeType READ mimeType WRITE setMimeType NOTIFY mimeTypeChanged) |
769 | + Q_PROPERTY(QString previewUrl READ previewUrl WRITE setPreviewUrl NOTIFY previewUrlChanged) |
770 | + Q_PROPERTY(QQuickItem* source READ source WRITE setSource NOTIFY sourceChanged) |
771 | +public: |
772 | + DragHelper(); |
773 | + bool active() { return m_active; } |
774 | + QString mimeType() { return m_mime_type; } |
775 | + QString previewUrl() { return m_preview_url; } |
776 | + QQuickItem *source() { return m_source; } |
777 | +signals: |
778 | + void activeChanged(bool active); |
779 | + void mimeTypeChanged(QString mimeType); |
780 | + void previewUrlChanged(QString previewUrl); |
781 | + void sourceChanged(QQuickItem *source); |
782 | +public slots: |
783 | + Q_INVOKABLE Qt::DropAction execDrag(QString tabId); |
784 | + void setActive(bool active); |
785 | + void setMimeType(QString mimeType); |
786 | + void setPreviewUrl(QString previewUrl); |
787 | + void setSource(QQuickItem *source); |
788 | +private: |
789 | + bool m_active; |
790 | + QString m_mime_type; |
791 | + QString m_preview_url; |
792 | + QQuickItem *m_source; |
793 | +}; |
794 | + |
795 | +#endif // __DRAGHELPER_H__ |
796 | + |
797 | |
798 | === added file 'src/app/webbrowser/reparenter.cpp' |
799 | --- src/app/webbrowser/reparenter.cpp 1970-01-01 00:00:00 +0000 |
800 | +++ src/app/webbrowser/reparenter.cpp 2016-10-14 13:08:37 +0000 |
801 | @@ -0,0 +1,94 @@ |
802 | +/* |
803 | + * Copyright 2016 Canonical Ltd. |
804 | + * |
805 | + * This file is part of webbrowser-app. |
806 | + * |
807 | + * webbrowser-app is free software; you can redistribute it and/or modify |
808 | + * it under the terms of the GNU General Public License as published by |
809 | + * the Free Software Foundation; version 3. |
810 | + * |
811 | + * webbrowser-app is distributed in the hope that it will be useful, |
812 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
813 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
814 | + * GNU General Public License for more details. |
815 | + * |
816 | + * You should have received a copy of the GNU General Public License |
817 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
818 | + */ |
819 | + |
820 | +#include "reparenter.h" |
821 | + |
822 | +#include <QPointer> |
823 | +#include <QQuickItem> |
824 | +#include <QQmlComponent> |
825 | +#include <QQmlContext> |
826 | +#include <QQmlEngine> |
827 | + |
828 | +Reparenter::Reparenter() |
829 | +{ |
830 | +} |
831 | + |
832 | +Reparenter::~Reparenter() |
833 | +{ |
834 | + QMap<QPointer<QQmlContext>, QPointer<QObject>>::iterator i; |
835 | + |
836 | + for (i = m_contexts.begin(); i != m_contexts.end(); ++i) { |
837 | + QPointer<QQmlContext> context = i.key(); |
838 | + QPointer<QObject> obj = i.value(); |
839 | + |
840 | + // If there is valid object then delete |
841 | + if (obj) { |
842 | + delete obj; |
843 | + } |
844 | + |
845 | + // If there is a valid contex then delete |
846 | + if (context) { |
847 | + delete context; |
848 | + } |
849 | + } |
850 | + |
851 | + m_contexts.clear(); // ensure contexts are removed |
852 | +} |
853 | + |
854 | +QObject *Reparenter::createObject(QQmlComponent *comp, QQuickItem *contextItem) |
855 | +{ |
856 | + if (contextItem == NULL) { |
857 | + contextItem = this; |
858 | + } |
859 | + |
860 | + // Make context |
861 | + QPointer<QQmlContext> context = new QQmlContext(QQmlEngine::contextForObject(contextItem)); |
862 | + context->setContextObject(contextItem); |
863 | + |
864 | + // Make component |
865 | + QPointer<QObject> obj = comp->create(context); |
866 | + |
867 | + // Add to store |
868 | + m_contexts.insert(context, obj); |
869 | + |
870 | + return obj; |
871 | +} |
872 | + |
873 | +void Reparenter::destroyContextAndObject(QQuickItem *item) |
874 | +{ |
875 | + // Get context for object |
876 | + QQmlContext *context = QQmlEngine::contextForObject(item)->parentContext(); |
877 | + |
878 | + // Remove from store |
879 | + m_contexts.remove(context); |
880 | + |
881 | + // Disconnect everything |
882 | + item->disconnect(); |
883 | + |
884 | + // Delete context and object |
885 | + delete context; |
886 | + delete item; |
887 | +} |
888 | + |
889 | +void Reparenter::reparent(QQuickItem *obj, QQuickItem *newParent) |
890 | +{ |
891 | + // Set object and visual parent |
892 | + obj->setParent(newParent); |
893 | + obj->setParentItem(newParent); |
894 | +} |
895 | + |
896 | |
897 | === added file 'src/app/webbrowser/reparenter.h' |
898 | --- src/app/webbrowser/reparenter.h 1970-01-01 00:00:00 +0000 |
899 | +++ src/app/webbrowser/reparenter.h 2016-10-14 13:08:37 +0000 |
900 | @@ -0,0 +1,45 @@ |
901 | +/* |
902 | + * Copyright 2016 Canonical Ltd. |
903 | + * |
904 | + * This file is part of webbrowser-app. |
905 | + * |
906 | + * webbrowser-app is free software; you can redistribute it and/or modify |
907 | + * it under the terms of the GNU General Public License as published by |
908 | + * the Free Software Foundation; version 3. |
909 | + * |
910 | + * webbrowser-app is distributed in the hope that it will be useful, |
911 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
912 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
913 | + * GNU General Public License for more details. |
914 | + * |
915 | + * You should have received a copy of the GNU General Public License |
916 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
917 | + */ |
918 | + |
919 | +#ifndef __REPARENTER_H__ |
920 | +#define __REPARENTER_H__ |
921 | + |
922 | +#include <QMap> |
923 | +#include <QObject> |
924 | +#include <QPointer> |
925 | +#include <QQmlComponent> |
926 | +#include <QQmlContext> |
927 | +#include <QQuickItem> |
928 | + |
929 | +class Reparenter : public QQuickItem |
930 | +{ |
931 | + Q_OBJECT |
932 | + |
933 | +public: |
934 | + Reparenter(); |
935 | + ~Reparenter(); |
936 | + |
937 | + Q_INVOKABLE QObject *createObject(QQmlComponent *comp, QQuickItem *contextItem=NULL); |
938 | + Q_INVOKABLE void destroyContextAndObject(QQuickItem *item); |
939 | + Q_INVOKABLE void reparent(QQuickItem *obj, QQuickItem *newParent); |
940 | +private: |
941 | + QMap<QPointer<QQmlContext>, QPointer<QObject>> m_contexts; |
942 | +}; |
943 | + |
944 | +#endif // __REPARENTER_H__ |
945 | + |
946 | |
947 | === modified file 'src/app/webbrowser/webbrowser-app.cpp' |
948 | --- src/app/webbrowser/webbrowser-app.cpp 2016-08-18 16:29:35 +0000 |
949 | +++ src/app/webbrowser/webbrowser-app.cpp 2016-10-14 13:08:37 +0000 |
950 | @@ -21,11 +21,13 @@ |
951 | #include "cache-deleter.h" |
952 | #include "config.h" |
953 | #include "downloads-model.h" |
954 | +#include "drag-helper.h" |
955 | #include "file-operations.h" |
956 | #include "history-domainlist-model.h" |
957 | #include "history-lastvisitdatelist-model.h" |
958 | #include "history-model.h" |
959 | #include "limit-proxy-model.h" |
960 | +#include "reparenter.h" |
961 | #include "searchengine.h" |
962 | #include "text-search-filter-model.h" |
963 | #include "tabs-model.h" |
964 | @@ -76,6 +78,8 @@ |
965 | qmlRegisterSingletonType<CacheDeleter>(uri, 0, 1, "CacheDeleter", CacheDeleter_singleton_factory); |
966 | qmlRegisterSingletonType<DownloadsModel>(uri, 0, 1, "DownloadsModel", DownloadsModel_singleton_factory); |
967 | qmlRegisterType<TextSearchFilterModel>(uri, 0, 1, "TextSearchFilterModel"); |
968 | + qmlRegisterType<DragHelper>(uri, 0, 1, "DragHelper"); |
969 | + qmlRegisterType<Reparenter>(uri, 0, 1, "Reparenter"); |
970 | |
971 | if (BrowserApplication::initialize("webbrowser/webbrowser-app.qml", QStringLiteral("webbrowser-app"))) { |
972 | QStringList searchEnginesSearchPaths; |
973 | |
974 | === modified file 'src/app/webbrowser/webbrowser-app.qml' |
975 | --- src/app/webbrowser/webbrowser-app.qml 2016-10-14 13:08:37 +0000 |
976 | +++ src/app/webbrowser/webbrowser-app.qml 2016-10-14 13:08:37 +0000 |
977 | @@ -83,7 +83,11 @@ |
978 | window.show() |
979 | window.requestActivate() |
980 | } |
981 | - |
982 | + |
983 | + property Reparenter reparenter: Reparenter { |
984 | + |
985 | + } |
986 | + |
987 | property var windowFactory: Component { |
988 | BrowserWindow { |
989 | id: window |
990 | @@ -119,6 +123,7 @@ |
991 | session.clear() |
992 | } |
993 | } |
994 | + |
995 | if (incognito && (allWindows.length > 1)) { |
996 | // If the last incognito window is being closed, |
997 | // prune incognito entries from the downloads model |
998 | @@ -133,6 +138,7 @@ |
999 | DownloadsModel.pruneIncognitoDownloads() |
1000 | } |
1001 | } |
1002 | + |
1003 | destroy() |
1004 | } |
1005 | |
1006 | @@ -182,6 +188,25 @@ |
1007 | id: browser |
1008 | anchors.fill: parent |
1009 | settings: webbrowserapp.settings |
1010 | + onNewWindowFromTab: { |
1011 | + var window = windowFactory.createObject( |
1012 | + null, |
1013 | + { |
1014 | + "incognito": tab.incognito, |
1015 | + "height": parent.height, |
1016 | + "width": parent.width, |
1017 | + } |
1018 | + ); |
1019 | + |
1020 | + window.addExistingTab(tab); |
1021 | + window.tabsModel.currentIndex = window.tabsModel.count - 1; |
1022 | + window.show(); |
1023 | + window.requestActivate(); |
1024 | + |
1025 | + window.tabsModel.currentTab.load(); |
1026 | + |
1027 | + closeMethod(); |
1028 | + } |
1029 | onNewWindowRequested: { |
1030 | var window = windowFactory.createObject( |
1031 | null, |
1032 | @@ -261,10 +286,18 @@ |
1033 | } |
1034 | |
1035 | function addTab(url) { |
1036 | - var tab = browser.createTab({"initialUrl": url}) |
1037 | + var tab = browser.createTab({"initialUrl": url || ""}) |
1038 | tabsModel.add(tab) |
1039 | return tab |
1040 | } |
1041 | + |
1042 | + function addExistingTab(tab) { |
1043 | + tabsModel.add(tab); |
1044 | + |
1045 | + browser.bindExistingTab(tab); |
1046 | + |
1047 | + return tab; |
1048 | + } |
1049 | } |
1050 | } |
1051 | |
1052 | @@ -465,4 +498,17 @@ |
1053 | PreviewManager.cleanUnusedPreviews(doNotCleanUrls) |
1054 | } |
1055 | } |
1056 | + |
1057 | + // Builder for components which have a context set to reparenter |
1058 | + // so that when a window closes the context is still valid |
1059 | + function builder(comp, parent, properties) { |
1060 | + var obj = reparenter.createObject(comp); |
1061 | + obj.parent = parent; |
1062 | + |
1063 | + for (var prop in properties) { |
1064 | + obj[prop] = properties[prop]; |
1065 | + } |
1066 | + |
1067 | + return obj; |
1068 | + } |
1069 | } |
1070 | |
1071 | === modified file 'tests/autopilot/webbrowser_app/tests/__init__.py' |
1072 | --- tests/autopilot/webbrowser_app/tests/__init__.py 2016-09-20 21:56:25 +0000 |
1073 | +++ tests/autopilot/webbrowser_app/tests/__init__.py 2016-10-14 13:08:37 +0000 |
1074 | @@ -256,6 +256,34 @@ |
1075 | os.kill(child.pid, signal) |
1076 | break |
1077 | |
1078 | + def switch_to_unfocused_window(self, target_window, |
1079 | + expected_number_unfocused_windows=1): |
1080 | + try: |
1081 | + windows = [ |
1082 | + window for window in self.process_manager.get_open_windows() |
1083 | + if window.application.desktop_file == |
1084 | + "webbrowser-app.desktop" and |
1085 | + not window.is_focused |
1086 | + ] |
1087 | + |
1088 | + # There should be 1 unfocused window |
1089 | + self.assertThat(len(windows), |
1090 | + Equals(expected_number_unfocused_windows)) |
1091 | + |
1092 | + # Cycle through possible windows until target gets focus |
1093 | + for window in windows: |
1094 | + window.set_focus() |
1095 | + self.assertThat(lambda: window.is_focused, |
1096 | + Eventually(Equals(True))) |
1097 | + |
1098 | + if target_window.activeFocus: |
1099 | + break |
1100 | + except (RuntimeError, ImportError): |
1101 | + # Fallback to clicking on the window |
1102 | + self.pointing_device.click_object(target_window) |
1103 | + |
1104 | + self.assertThat(target_window.activeFocus, Eventually(Equals(True))) |
1105 | + |
1106 | |
1107 | class StartOpenRemotePageTestCaseBase(BrowserTestCaseBase): |
1108 | |
1109 | |
1110 | === modified file 'tests/autopilot/webbrowser_app/tests/test_multiple_windows.py' |
1111 | --- tests/autopilot/webbrowser_app/tests/test_multiple_windows.py 2016-09-20 21:56:25 +0000 |
1112 | +++ tests/autopilot/webbrowser_app/tests/test_multiple_windows.py 2016-10-14 13:08:37 +0000 |
1113 | @@ -15,6 +15,8 @@ |
1114 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1115 | |
1116 | from testtools.matchers import Equals |
1117 | +from autopilot.matchers import Eventually |
1118 | +from time import sleep |
1119 | |
1120 | from webbrowser_app.tests import StartOpenRemotePageTestCaseBase |
1121 | |
1122 | @@ -38,3 +40,204 @@ |
1123 | self.assertThat(len(windows), Equals(2)) |
1124 | self.assertThat(len(self.app.get_windows(incognito=False)), Equals(1)) |
1125 | self.assertThat(len(self.app.get_windows(incognito=True)), Equals(1)) |
1126 | + |
1127 | + |
1128 | +class TestMultipleWindowsDrag(StartOpenRemotePageTestCaseBase): |
1129 | + def setUp(self): |
1130 | + super(TestMultipleWindowsDrag, self).setUp() |
1131 | + |
1132 | + if not self.main_window.wide: |
1133 | + self.skipTest("Only on wide form factors") |
1134 | + |
1135 | + def drag_tab(self, tab, x2, y2): |
1136 | + x1, y1 = self.get_object_center(tab) |
1137 | + |
1138 | + self.pointing_device.move(x1, y1) |
1139 | + self.pointing_device.press() |
1140 | + |
1141 | + # Drag tab downwards first to ensure we activate tab dragging |
1142 | + for i in range(100): |
1143 | + self.pointing_device.move(x1, y1 + i) |
1144 | + |
1145 | + # Move to destination and release |
1146 | + # pause at each point so we can what is happening |
1147 | + sleep(0.25) |
1148 | + self.pointing_device.move(x2, y2) |
1149 | + sleep(0.25) |
1150 | + self.pointing_device.release() |
1151 | + sleep(0.25) |
1152 | + |
1153 | + def get_object_center(self, obj): |
1154 | + x1, y1, width, height = obj.globalRect |
1155 | + x1 += width // 2 |
1156 | + y1 += height // 2 |
1157 | + |
1158 | + return x1, y1 |
1159 | + |
1160 | + def get_tab_delegate(self, window, tabIndex): |
1161 | + return window.chrome.get_tabs_bar().get_tab(tabIndex) |
1162 | + |
1163 | + def test_drag_tab_tabbar_nothing(self): |
1164 | + '''test that dragging a tab out and in of tabbar is same''' |
1165 | + window = self.app.get_windows()[0] |
1166 | + |
1167 | + # Drag tab down and then back into tab bar |
1168 | + tab = self.get_tab_delegate(window, 0) |
1169 | + x2, y2 = self.get_object_center(tab) |
1170 | + |
1171 | + self.drag_tab(tab, x2, y2) |
1172 | + |
1173 | + # Check we still have 1 window and 1 tab |
1174 | + windows = self.app.get_windows() |
1175 | + self.assertThat(len(windows), Equals(1)) |
1176 | + self.assertThat(lambda: len(windows[0].get_webviews()), |
1177 | + Eventually(Equals(1))) |
1178 | + |
1179 | + def test_drag_tab_bottom_new_window(self): |
1180 | + '''test with two tabs dragging one to the bottom opens a new window''' |
1181 | + window = self.app.get_windows()[0] |
1182 | + |
1183 | + # Open a new tab and check we have two |
1184 | + self.open_new_tab() |
1185 | + self.assertThat(lambda: len(window.get_webviews()), |
1186 | + Eventually(Equals(2))) |
1187 | + |
1188 | + # Drag new tab to bottom part of window |
1189 | + tab = self.get_tab_delegate(window, 1) |
1190 | + x2, y2 = self.get_object_center(window) |
1191 | + |
1192 | + self.drag_tab(tab, x2, y2) |
1193 | + |
1194 | + # Check that a new window has been opened |
1195 | + windows = self.app.get_windows() |
1196 | + self.assertThat(len(windows), Equals(2)) |
1197 | + self.assertThat(lambda: len(windows[0].get_webviews()), |
1198 | + Eventually(Equals(1))) |
1199 | + self.assertThat(lambda: len(windows[1].get_webviews()), |
1200 | + Eventually(Equals(1))) |
1201 | + |
1202 | + def test_drag_tab_outside_new_window(self): |
1203 | + '''test with two tabs dragging one to the bottom opens a new window''' |
1204 | + window = self.app.get_windows()[0] |
1205 | + |
1206 | + # Open a new tab and check we have two |
1207 | + self.open_new_tab() |
1208 | + self.assertThat(lambda: len(window.get_webviews()), |
1209 | + Eventually(Equals(2))) |
1210 | + |
1211 | + # Drag new tab outside of window |
1212 | + tab = self.get_tab_delegate(window, 1) |
1213 | + x2, y2, width, height = window.globalRect |
1214 | + |
1215 | + if x2 > 20: |
1216 | + x2 -= 20 |
1217 | + else: |
1218 | + x2 += width + 20 |
1219 | + |
1220 | + self.drag_tab(tab, x2, y2) |
1221 | + |
1222 | + # Check that a new window has been opened |
1223 | + windows = self.app.get_windows() |
1224 | + self.assertThat(len(windows), Equals(2)) |
1225 | + self.assertThat(lambda: len(windows[0].get_webviews()), |
1226 | + Eventually(Equals(1))) |
1227 | + self.assertThat(lambda: len(windows[1].get_webviews()), |
1228 | + Eventually(Equals(1))) |
1229 | + |
1230 | + def test_drag_tab_between_windows_move(self): |
1231 | + '''test that dragging a tab from one window to another''' |
1232 | + # Open a new tab and window |
1233 | + self.open_new_tab() |
1234 | + self.open_new_window() |
1235 | + |
1236 | + windows = self.app.get_windows() |
1237 | + self.assertThat(len(windows), Equals(2)) |
1238 | + self.assertThat(lambda: len(windows[0].get_webviews()), |
1239 | + Eventually(Equals(2))) |
1240 | + self.assertThat(lambda: len(windows[1].get_webviews()), |
1241 | + Eventually(Equals(1))) |
1242 | + |
1243 | + # Focus window 0 |
1244 | + self.switch_to_unfocused_window(windows[0]) |
1245 | + |
1246 | + # Move tab into window 1 |
1247 | + tab = self.get_tab_delegate(windows[0], 1) |
1248 | + x2, y2 = self.get_object_center(windows[1].chrome.get_tabs_bar()) |
1249 | + |
1250 | + self.drag_tab(tab, x2, y2) |
1251 | + |
1252 | + # Check there are two windows and two tabs open in the second window |
1253 | + windows = self.app.get_windows() |
1254 | + self.assertThat(len(windows), Equals(2)) |
1255 | + self.assertThat(lambda: len(windows[0].get_webviews()), |
1256 | + Eventually(Equals(1))) |
1257 | + self.assertThat(lambda: len(windows[1].get_webviews()), |
1258 | + Eventually(Equals(2))) |
1259 | + |
1260 | + def test_drag_tab_between_windows_move_and_close_window(self): |
1261 | + '''test that dragging tab from one window to another closes original''' |
1262 | + self.open_new_window() |
1263 | + windows = self.app.get_windows() |
1264 | + self.assertThat(len(windows), Equals(2)) |
1265 | + |
1266 | + # Focus window 0 |
1267 | + self.switch_to_unfocused_window(windows[0]) |
1268 | + |
1269 | + # Move tab into window 1 |
1270 | + tab = self.get_tab_delegate(windows[0], 0) |
1271 | + x2, y2 = self.get_object_center(windows[1].chrome.get_tabs_bar()) |
1272 | + |
1273 | + self.drag_tab(tab, x2, y2) |
1274 | + |
1275 | + # Check there are two tabs open in the single remaining window |
1276 | + windows = self.app.get_windows() |
1277 | + self.assertThat(len(windows), Equals(1)) |
1278 | + self.assertThat(lambda: len(windows[0].get_webviews()), |
1279 | + Eventually(Equals(2))) |
1280 | + |
1281 | + def test_drag_public_tab_into_private_window(self): |
1282 | + '''test that you cannot drag a public tab into private window''' |
1283 | + # Open private window, check there are two windows |
1284 | + self.open_new_private_window() |
1285 | + windows = self.app.get_windows() |
1286 | + self.assertThat(len(windows), Equals(2)) |
1287 | + |
1288 | + public_window = self.app.get_windows(incognito=False)[0] |
1289 | + private_window = self.app.get_windows(incognito=True)[0] |
1290 | + |
1291 | + # Focus public window |
1292 | + self.switch_to_unfocused_window(public_window) |
1293 | + |
1294 | + # Move tab into private window |
1295 | + tab = self.get_tab_delegate(public_window, 0) |
1296 | + x2, y2 = self.get_object_center(private_window) |
1297 | + |
1298 | + self.drag_tab(tab, x2, y2) |
1299 | + |
1300 | + # Check there are two windows, one of public and one private |
1301 | + windows = self.app.get_windows() |
1302 | + self.assertThat(len(windows), Equals(2)) |
1303 | + self.assertThat(len(self.app.get_windows(incognito=False)), Equals(1)) |
1304 | + self.assertThat(len(self.app.get_windows(incognito=True)), Equals(1)) |
1305 | + |
1306 | + def test_drag_private_tab_into_public_window(self): |
1307 | + '''test that you cannot drag a private tab into public window''' |
1308 | + # Open private window, check there are two windows |
1309 | + self.open_new_private_window() |
1310 | + windows = self.app.get_windows() |
1311 | + self.assertThat(len(windows), Equals(2)) |
1312 | + |
1313 | + public_window = self.app.get_windows(incognito=False)[0] |
1314 | + private_window = self.app.get_windows(incognito=True)[0] |
1315 | + |
1316 | + # Move tab into public window |
1317 | + tab = self.get_tab_delegate(private_window, 0) |
1318 | + x2, y2 = self.get_object_center(public_window) |
1319 | + |
1320 | + self.drag_tab(tab, x2, y2) |
1321 | + |
1322 | + # Check there are two windows, one of public and one private |
1323 | + windows = self.app.get_windows() |
1324 | + self.assertThat(len(windows), Equals(2)) |
1325 | + self.assertThat(len(self.app.get_windows(incognito=False)), Equals(1)) |
1326 | + self.assertThat(len(self.app.get_windows(incognito=True)), Equals(1)) |
1327 | |
1328 | === modified file 'tests/unittests/qml/CMakeLists.txt' |
1329 | --- tests/unittests/qml/CMakeLists.txt 2016-10-14 13:08:37 +0000 |
1330 | +++ tests/unittests/qml/CMakeLists.txt 2016-10-14 13:08:37 +0000 |
1331 | @@ -18,12 +18,14 @@ |
1332 | ${webbrowser-app_SOURCE_DIR}/bookmarks-model.cpp |
1333 | ${webbrowser-app_SOURCE_DIR}/bookmarks-folder-model.cpp |
1334 | ${webbrowser-app_SOURCE_DIR}/bookmarks-folderlist-model.cpp |
1335 | + ${webbrowser-app_SOURCE_DIR}/drag-helper.cpp |
1336 | ${webbrowser-app_SOURCE_DIR}/file-operations.cpp |
1337 | ${webbrowser-app_SOURCE_DIR}/history-domain-model.cpp |
1338 | ${webbrowser-app_SOURCE_DIR}/history-domainlist-model.cpp |
1339 | ${webbrowser-app_SOURCE_DIR}/history-model.cpp |
1340 | ${webbrowser-app_SOURCE_DIR}/history-lastvisitdatelist-model.cpp |
1341 | ${webbrowser-app_SOURCE_DIR}/limit-proxy-model.cpp |
1342 | + ${webbrowser-app_SOURCE_DIR}/reparenter.cpp |
1343 | ${webbrowser-app_SOURCE_DIR}/searchengine.cpp |
1344 | ${webbrowser-app_SOURCE_DIR}/tabs-model.cpp |
1345 | ${webbrowser-app_SOURCE_DIR}/text-search-filter-model.cpp |
1346 | |
1347 | === added file 'tests/unittests/qml/ReparenterFakeContainer.qml' |
1348 | --- tests/unittests/qml/ReparenterFakeContainer.qml 1970-01-01 00:00:00 +0000 |
1349 | +++ tests/unittests/qml/ReparenterFakeContainer.qml 2016-10-14 13:08:37 +0000 |
1350 | @@ -0,0 +1,40 @@ |
1351 | +/* |
1352 | + * Copyright 2016 Canonical Ltd. |
1353 | + * |
1354 | + * This file is part of webbrowser-app. |
1355 | + * |
1356 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1357 | + * it under the terms of the GNU General Public License as published by |
1358 | + * the Free Software Foundation; version 3. |
1359 | + * |
1360 | + * webbrowser-app is distributed in the hope that it will be useful, |
1361 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1362 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1363 | + * GNU General Public License for more details. |
1364 | + * |
1365 | + * You should have received a copy of the GNU General Public License |
1366 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1367 | + */ |
1368 | +import QtQuick 2.4 |
1369 | + |
1370 | +Item { |
1371 | + id: containerLeft |
1372 | + anchors { |
1373 | + bottom: parent.bottom |
1374 | + left: parent.left |
1375 | + right: parent.horizontalCenter |
1376 | + top: parent.top |
1377 | + } |
1378 | + |
1379 | + property var root |
1380 | + |
1381 | + function makeTab() { |
1382 | + var component = Qt.createComponent(Qt.resolvedUrl("ReparenterFakeTab.qml")) |
1383 | + return component.createObject(containerLeft, {}) |
1384 | + } |
1385 | + |
1386 | + function makeTabHelper() { |
1387 | + var component = Qt.createComponent(Qt.resolvedUrl("ReparenterFakeTab.qml")) |
1388 | + return root.builder(component, containerLeft) |
1389 | + } |
1390 | +} |
1391 | |
1392 | === added file 'tests/unittests/qml/ReparenterFakeTab.qml' |
1393 | --- tests/unittests/qml/ReparenterFakeTab.qml 1970-01-01 00:00:00 +0000 |
1394 | +++ tests/unittests/qml/ReparenterFakeTab.qml 2016-10-14 13:08:37 +0000 |
1395 | @@ -0,0 +1,37 @@ |
1396 | +/* |
1397 | + * Copyright 2016 Canonical Ltd. |
1398 | + * |
1399 | + * This file is part of webbrowser-app. |
1400 | + * |
1401 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1402 | + * it under the terms of the GNU General Public License as published by |
1403 | + * the Free Software Foundation; version 3. |
1404 | + * |
1405 | + * webbrowser-app is distributed in the hope that it will be useful, |
1406 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1407 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1408 | + * GNU General Public License for more details. |
1409 | + * |
1410 | + * You should have received a copy of the GNU General Public License |
1411 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1412 | + */ |
1413 | +import QtQuick 2.4 |
1414 | + |
1415 | +Item { |
1416 | + anchors { |
1417 | + fill: parent |
1418 | + } |
1419 | + |
1420 | + readonly property MouseArea mouseArea: mouseAreaObj |
1421 | + |
1422 | + MouseArea { |
1423 | + id: mouseAreaObj |
1424 | + anchors { |
1425 | + fill: parent |
1426 | + } |
1427 | + |
1428 | + property int clickCount: 0 |
1429 | + |
1430 | + onClicked: clickCount++ |
1431 | + } |
1432 | +} |
1433 | |
1434 | === modified file 'tests/unittests/qml/tst_BrowserTab.qml' |
1435 | --- tests/unittests/qml/tst_BrowserTab.qml 2016-08-18 17:10:32 +0000 |
1436 | +++ tests/unittests/qml/tst_BrowserTab.qml 2016-10-14 13:08:37 +0000 |
1437 | @@ -26,6 +26,10 @@ |
1438 | |
1439 | width: 200 |
1440 | height: 200 |
1441 | + |
1442 | + property Reparenter reparenter: Reparenter { |
1443 | + |
1444 | + } |
1445 | |
1446 | Component { |
1447 | id: tabComponent |
1448 | @@ -163,9 +167,8 @@ |
1449 | tab.current = false |
1450 | tryCompare(previewSavedSpy, "count", 1) |
1451 | verify(FileOperations.exists(path)) |
1452 | - tab.close() |
1453 | + tab.close(false) |
1454 | verify(!FileOperations.exists(path)) |
1455 | - |
1456 | tab.destroy() |
1457 | } |
1458 | } |
1459 | |
1460 | === modified file 'tests/unittests/qml/tst_QmlTests.cpp' |
1461 | --- tests/unittests/qml/tst_QmlTests.cpp 2016-02-11 08:26:47 +0000 |
1462 | +++ tests/unittests/qml/tst_QmlTests.cpp 2016-10-14 13:08:37 +0000 |
1463 | @@ -27,6 +27,7 @@ |
1464 | // local |
1465 | #include "bookmarks-model.h" |
1466 | #include "bookmarks-folderlist-model.h" |
1467 | +#include "drag-helper.h" |
1468 | #include "favicon-fetcher.h" |
1469 | #include "file-operations.h" |
1470 | #include "history-domain-model.h" |
1471 | @@ -34,6 +35,7 @@ |
1472 | #include "history-model.h" |
1473 | #include "history-lastvisitdatelist-model.h" |
1474 | #include "limit-proxy-model.h" |
1475 | +#include "reparenter.h" |
1476 | #include "searchengine.h" |
1477 | #include "tabs-model.h" |
1478 | #include "text-search-filter-model.h" |
1479 | @@ -183,6 +185,8 @@ |
1480 | qmlRegisterType<LimitProxyModel>(browserUri, 0, 1, "LimitProxyModel"); |
1481 | qmlRegisterType<TextSearchFilterModel>(browserUri, 0, 1, "TextSearchFilterModel"); |
1482 | qmlRegisterSingletonType<FileOperations>(browserUri, 0, 1, "FileOperations", FileOperations_singleton_factory); |
1483 | + qmlRegisterType<DragHelper>(browserUri, 0, 1, "DragHelper"); |
1484 | + qmlRegisterType<Reparenter>(browserUri, 0, 1, "Reparenter"); |
1485 | |
1486 | const char* testUri = "webbrowsertest.private"; |
1487 | qmlRegisterSingletonType<TestContext>(testUri, 0, 1, "TestContext", TestContext_singleton_factory); |
1488 | |
1489 | === added file 'tests/unittests/qml/tst_Reparenter.qml' |
1490 | --- tests/unittests/qml/tst_Reparenter.qml 1970-01-01 00:00:00 +0000 |
1491 | +++ tests/unittests/qml/tst_Reparenter.qml 2016-10-14 13:08:37 +0000 |
1492 | @@ -0,0 +1,120 @@ |
1493 | +/* |
1494 | + * Copyright 2016 Canonical Ltd. |
1495 | + * |
1496 | + * This file is part of webbrowser-app. |
1497 | + * |
1498 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1499 | + * it under the terms of the GNU General Public License as published by |
1500 | + * the Free Software Foundation; version 3. |
1501 | + * |
1502 | + * webbrowser-app is distributed in the hope that it will be useful, |
1503 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1504 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1505 | + * GNU General Public License for more details. |
1506 | + * |
1507 | + * You should have received a copy of the GNU General Public License |
1508 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1509 | + */ |
1510 | +import QtQuick 2.4 |
1511 | +import QtTest 1.0 |
1512 | +import webbrowserapp.private 0.1 |
1513 | + |
1514 | +Item { |
1515 | + id: root |
1516 | + height: 100 |
1517 | + width: 100 |
1518 | + |
1519 | + property Item containerLeft |
1520 | + |
1521 | + property Reparenter reparenter: Reparenter { |
1522 | + |
1523 | + } |
1524 | + |
1525 | + Item { |
1526 | + id: containerRight |
1527 | + anchors { |
1528 | + bottom: parent.bottom |
1529 | + left: parent.horizontalCenter |
1530 | + right: parent.right |
1531 | + top: parent.top |
1532 | + } |
1533 | + } |
1534 | + |
1535 | + function addExisting(container, tab) { |
1536 | + tab.parent = container |
1537 | + } |
1538 | + |
1539 | + function addExistingHelper(container, tab) { |
1540 | + reparenter.reparent(tab, container, {}); |
1541 | + } |
1542 | + |
1543 | + function builder(component, container) { |
1544 | + var tab = reparenter.createObject(component) |
1545 | + tab.parent = container |
1546 | + return tab |
1547 | + } |
1548 | + |
1549 | + TestCase { |
1550 | + name: "Reparenter" |
1551 | + when: windowShown |
1552 | + |
1553 | + function init() { |
1554 | + var component = Qt.createComponent(Qt.resolvedUrl("ReparenterFakeContainer.qml")) |
1555 | + containerLeft = component.createObject(root, {}) |
1556 | + containerLeft.root = root |
1557 | + } |
1558 | + |
1559 | + function cleanup() { |
1560 | + containerRight.children = null |
1561 | + } |
1562 | + |
1563 | + function test_reparenter_cpp() { |
1564 | + var tab = containerLeft.makeTabHelper() |
1565 | + |
1566 | + // Click on tab ensure it has been clicked |
1567 | + mouseClick(root, 25, 50, Qt.LeftButton) |
1568 | + compare(tab.mouseArea.clickCount, 1) |
1569 | + |
1570 | + // Move tab |
1571 | + addExistingHelper(containerRight, tab) |
1572 | + |
1573 | + // Click on tab ensure it has been clicked |
1574 | + mouseClick(root, 75, 50, Qt.LeftButton) |
1575 | + compare(tab.mouseArea.clickCount, 2) |
1576 | + |
1577 | + // Destroy context |
1578 | + containerLeft.destroy() |
1579 | + |
1580 | + // Click on tab ensure it has been clicked |
1581 | + mouseClick(root, 75, 50, Qt.LeftButton) |
1582 | + compare(tab.mouseArea.clickCount, 3) |
1583 | + |
1584 | + // Destroy object and check children have gone |
1585 | + reparenter.destroyContextAndObject(tab) |
1586 | + compare(tab.mouseArea, undefined) |
1587 | + } |
1588 | + |
1589 | + function test_reparenter_qml_expect_fail() { |
1590 | + var tab = containerLeft.makeTab() |
1591 | + |
1592 | + // Click on tab ensure it has been clicked |
1593 | + mouseClick(root, 25, 50, Qt.LeftButton) |
1594 | + compare(tab.mouseArea.clickCount, 1) |
1595 | + |
1596 | + // Move tab |
1597 | + addExisting(containerRight, tab) |
1598 | + |
1599 | + // Click on tab ensure it has been clicked |
1600 | + mouseClick(root, 75, 50, Qt.LeftButton) |
1601 | + compare(tab.mouseArea.clickCount, 2) |
1602 | + |
1603 | + // Destroy context |
1604 | + containerLeft.destroy() |
1605 | + |
1606 | + // Attempt to click on tab find that children of tab have been |
1607 | + // destroyed as the context has gone |
1608 | + mouseClick(root, 75, 50, Qt.LeftButton) |
1609 | + tryCompare(tab, "mouseArea", undefined, 1000) |
1610 | + } |
1611 | + } |
1612 | +} |
FAILED: Continuous integration, rev:1518 /jenkins. canonical. com/system- apps/job/ lp-webbrowser- app-ci/ 661/ /jenkins. canonical. com/system- apps/job/ build/1655/ console /jenkins. canonical. com/system- apps/job/ build-0- fetch/1655 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 1501/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 1501/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 1501/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 1501/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 1501/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 1501/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 1501/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 1501/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= yakkety/ 1501/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/system- apps/job/ lp-webbrowser- app-ci/ 661/rebuild
https:/