Merge lp:~phablet-team/messaging-app/side_panel into lp:messaging-app
- side_panel
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~phablet-team/messaging-app/side_panel |
Merge into: | lp:messaging-app |
Prerequisite: | lp:~phablet-team/messaging-app/add-apparmor-profile |
Diff against target: |
2824 lines (+1073/-1017) 29 files modified
CMakeLists.txt (+14/-18) TODO.convergence (+3/-0) config.h.in (+1/-0) src/messagingapplication.cpp (+1/-0) src/qml/AccountSectionDelegate.qml (+2/-1) src/qml/ComposeBar.qml (+4/-0) src/qml/EmptyState.qml (+38/-0) src/qml/InputInfo.qml (+24/-0) src/qml/LocalPageWithBottomEdge.qml (+0/-428) src/qml/MMS/Previewer.qml (+26/-15) src/qml/MMS/PreviewerImage.qml (+1/-0) src/qml/MMS/PreviewerMultipleContacts.qml (+7/-2) src/qml/MMS/PreviewerVideo.qml (+1/-0) src/qml/MMSDelegate.qml (+1/-1) src/qml/MainPage.qml (+136/-65) src/qml/Messages.qml (+452/-350) src/qml/MessagesHeader.qml (+7/-2) src/qml/MessagingBottomEdge.qml (+43/-0) src/qml/MessagingContactEditorPage.qml (+6/-2) src/qml/MessagingContactViewPage.qml (+11/-10) src/qml/MessagingPageLayout.qml (+64/-0) src/qml/MultiRecipientInput.qml (+1/-1) src/qml/NewRecipientPage.qml (+69/-37) src/qml/RegularMessageDelegate.qml (+1/-1) src/qml/SettingsPage.qml (+22/-1) src/qml/messaging-app.qml (+115/-30) tests/autopilot/messaging_app/emulators.py (+12/-48) tests/qml/CMakeLists.txt (+2/-1) tests/qml/tst_MessagesView.qml (+9/-4) |
To merge this branch: | bzr merge lp:~phablet-team/messaging-app/side_panel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+284299@code.launchpad.net |
This proposal supersedes a proposal from 2016-01-14.
This proposal has been superseded by a proposal from 2016-02-05.
Commit message
Implement convergent layout for messaging-app.
Description of the change
Implement convergent layout for messaging-app.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:451
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:452
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:454
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:455
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:456
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:457
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:458
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:460
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:461
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:462
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:463
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:464
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:465
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:466
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:468
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:469
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:470
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:471
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:472
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:474
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:475
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:476
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:477
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:478
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:479
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:480
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:481
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 484. By Gustavo Pichorim Boiko
-
Set the title correctly.
- 485. By Gustavo Pichorim Boiko
-
Make it possible to unselect all too in the main view.
- 486. By Gustavo Pichorim Boiko
-
Fix the header in the messages view.
- 487. By Gustavo Pichorim Boiko
-
Clear some warnings.
- 488. By Gustavo Pichorim Boiko
-
Make sure the page has a height when loading.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:483
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 489. By Tiago Salem Herrmann
-
Fix ListView anchors also on NewRecipientPage
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:488
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 490. By Gustavo Pichorim Boiko
-
Create pages manually before pushing to the AdaptivePageLayout as it is very
slow to create the pages by itself.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:489
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:490
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 491. By Gustavo Pichorim Boiko
-
Make sure pages get destroyed when hitting back
- 492. By Tiago Salem Herrmann
-
Implement back button to destroy dynamically created pages
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:491
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:492
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 493. By Tiago Salem Herrmann
-
Push all views synchronously
- 494. By Gustavo Pichorim Boiko
-
Make it possible to launch the new message view from settings.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:494
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 495. By Tiago Salem Herrmann
-
fix anchors in the settings page
- 496. By Tiago Salem Herrmann
-
Empty stack before adding SettingsPage to the stack
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:496
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 497. By Tiago Salem Herrmann
-
Only set properties once during object creation
implement removePage() that destroy instances when needed - 498. By Tiago Salem Herrmann
-
check before destroying instance
- 499. By Tiago Salem Herrmann
-
anchor previewer to header
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:497
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:499
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 500. By Gustavo Pichorim Boiko
-
Move the custom page layout functions to a separate QML file.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:500
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 501. By Gustavo Pichorim Boiko
-
Merge swipe to cancel fix.
- 502. By Gustavo Pichorim Boiko
-
Remove duplicated code.
- 503. By Gustavo Pichorim Boiko
-
Fix loading pages by source
- 504. By Gustavo Pichorim Boiko
-
Hide the bottom edge bar in the empty state screen.
- 505. By Gustavo Pichorim Boiko
-
Messaging-app doesn't belong to side stage anymore
- 506. By Gustavo Pichorim Boiko
-
Set a minimum window size.
- 507. By Gustavo Pichorim Boiko
-
Make it landscape by default on windowed environments
- 508. By Gustavo Pichorim Boiko
-
Hide the new message list item once the message is sent.
- 509. By Gustavo Pichorim Boiko
-
Show the group chat participants popup anchored to the right
- 510. By Gustavo Pichorim Boiko
-
Make the header fixed in two column mode
- 511. By Gustavo Pichorim Boiko
-
Add a scrollbar to the Messages view.
- 512. By Gustavo Pichorim Boiko
-
Merge trunk.
- 513. By Gustavo Pichorim Boiko
-
Make the readme more clear
- 514. By Gustavo Pichorim Boiko
-
In dual panel mode, always hide the empty state label.
Unmerged revisions
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-10-13 15:13:32 +0000 |
3 | +++ CMakeLists.txt 2016-02-01 23:18:57 +0000 |
4 | @@ -35,12 +35,7 @@ |
5 | # Instruct CMake to run moc automatically when needed. |
6 | set(CMAKE_AUTOMOC ON) |
7 | |
8 | -configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) |
9 | - |
10 | -#find_package(Qt5Contacts) |
11 | find_package(Qt5DBus) |
12 | -#find_package(Qt5Gui) |
13 | -#find_package(Qt5Multimedia) |
14 | find_package(Qt5Qml) |
15 | find_package(Qt5Quick) |
16 | find_package(Qt5Test) |
17 | @@ -49,23 +44,24 @@ |
18 | include(qt5) |
19 | |
20 | find_package(PkgConfig REQUIRED) |
21 | -#pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5) |
22 | -#pkg_check_modules(TPL_QT5 REQUIRED TelepathyLoggerQt5) |
23 | -#pkg_check_modules(QTGLIB REQUIRED QtGLib-2.0) |
24 | -#pkg_check_modules(GLIB REQUIRED glib-2.0) |
25 | pkg_check_modules(NOTIFY REQUIRED libnotify) |
26 | -#pkg_check_modules(MESSAGING_MENU REQUIRED messaging-menu) |
27 | - |
28 | -# Check if the messaging menu has the message header |
29 | -#set(CMAKE_REQUIRED_INCLUDES ${MESSAGING_MENU_INCLUDE_DIRS}) |
30 | -#check_include_file("messaging-menu-message.h" HAVE_MESSAGING_MENU_MESSAGE) |
31 | - |
32 | -if (HAVE_MESSAGING_MENU_MESSAGE) |
33 | - add_definitions(-DHAVE_MESSAGING_MENU_MESSAGE) |
34 | -endif (HAVE_MESSAGING_MENU_MESSAGE) |
35 | + |
36 | +#find unity8 qml libraries |
37 | +set(UNITY8_QML_PATH /usr/lib/${CMAKE_C_LIBRARY_ARCHITECTURE}/unity8/qml/) |
38 | +find_path(LIB_UNITY_QML_EXISTS NAMES libUnity-qml.so |
39 | + HINTS "${UNITY8_QML_PATH}" |
40 | + NO_CMAKE_PATH |
41 | + NO_CMAKE_ENVIRONMENT_PATH |
42 | + NO_SYSTEM_ENVIRONMENT_PATH |
43 | +) |
44 | +if(!LIB_UNITY_QML_EXISTS) |
45 | + MESSAGE(FATAL_ERROR "unity8 private package not-found") |
46 | +endif() |
47 | |
48 | add_definitions(-DQT_NO_KEYWORDS) |
49 | |
50 | +configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) |
51 | + |
52 | include_directories( |
53 | ${CMAKE_CURRENT_BINARY_DIR} |
54 | ${CMAKE_CURRENT_SOURCE_DIR} |
55 | |
56 | === added file 'TODO.convergence' |
57 | --- TODO.convergence 1970-01-01 00:00:00 +0000 |
58 | +++ TODO.convergence 2016-02-01 23:18:57 +0000 |
59 | @@ -0,0 +1,3 @@ |
60 | +- Right now if you call startChat() in the two panel layout, it won't match the |
61 | + thread on the left side, and if you resize it to one panel, the conversation |
62 | + is closed. |
63 | |
64 | === modified file 'config.h.in' |
65 | --- config.h.in 2014-05-28 20:28:55 +0000 |
66 | +++ config.h.in 2016-02-01 23:18:57 +0000 |
67 | @@ -27,6 +27,7 @@ |
68 | #include <QtDBus/QDBusReply> |
69 | |
70 | #define I18N_DIRECTORY "@CMAKE_INSTALL_PREFIX@/share/locale" |
71 | +#define UNITY8_QML_PATH "@UNITY8_QML_PATH@" |
72 | |
73 | inline bool isRunningInstalled() { |
74 | static bool installed = (QCoreApplication::applicationDirPath() == |
75 | |
76 | === modified file 'src/messagingapplication.cpp' |
77 | --- src/messagingapplication.cpp 2016-02-01 23:18:57 +0000 |
78 | +++ src/messagingapplication.cpp 2016-02-01 23:18:57 +0000 |
79 | @@ -165,6 +165,7 @@ |
80 | m_view->rootContext()->setContextProperty("application", this); |
81 | m_view->rootContext()->setContextProperty("i18nDirectory", I18N_DIRECTORY); |
82 | m_view->engine()->setBaseUrl(QUrl::fromLocalFile(messagingAppDirectory())); |
83 | + m_view->engine()->addImportPath(UNITY8_QML_PATH); |
84 | |
85 | // check if there is a contacts backend override |
86 | QString contactsBackend = qgetenv("QTCONTACTS_MANAGER_OVERRIDE"); |
87 | |
88 | === modified file 'src/qml/AccountSectionDelegate.qml' |
89 | --- src/qml/AccountSectionDelegate.qml 2015-09-14 13:51:27 +0000 |
90 | +++ src/qml/AccountSectionDelegate.qml 2016-02-01 23:18:57 +0000 |
91 | @@ -28,7 +28,8 @@ |
92 | property var messageData: null |
93 | property int index: -1 |
94 | property Item delegateItem |
95 | - property string accountLabel: telepathyHelper.accountForId(messageData.accountId).displayName |
96 | + property var account: telepathyHelper.accountForId(messageData.accountId) |
97 | + property string accountLabel: account ? account.displayName : "" |
98 | |
99 | // update the accountLabel when the list of accounts become available |
100 | Item { |
101 | |
102 | === modified file 'src/qml/ComposeBar.qml' |
103 | --- src/qml/ComposeBar.qml 2016-02-01 23:18:57 +0000 |
104 | +++ src/qml/ComposeBar.qml 2016-02-01 23:18:57 +0000 |
105 | @@ -64,6 +64,10 @@ |
106 | } |
107 | |
108 | function addAttachments(transfer) { |
109 | + if (!transfer || !transfer.items) { |
110 | + return |
111 | + } |
112 | + |
113 | for (var i = 0; i < transfer.items.length; i++) { |
114 | if (String(transfer.items[i].text).length > 0) { |
115 | composeBar.text = String(transfer.items[i].text) |
116 | |
117 | === added file 'src/qml/EmptyState.qml' |
118 | --- src/qml/EmptyState.qml 1970-01-01 00:00:00 +0000 |
119 | +++ src/qml/EmptyState.qml 2016-02-01 23:18:57 +0000 |
120 | @@ -0,0 +1,38 @@ |
121 | +import QtQuick 2.0 |
122 | +import Ubuntu.Components 1.3 |
123 | + |
124 | +Item { |
125 | + id: emptyStateScreen |
126 | + |
127 | + property alias labelVisible: emptyStateLabel.visible |
128 | + |
129 | + anchors { |
130 | + left: parent.left |
131 | + leftMargin: units.gu(6) |
132 | + right: parent.right |
133 | + rightMargin: units.gu(6) |
134 | + verticalCenter: parent.verticalCenter |
135 | + } |
136 | + height: childrenRect.height |
137 | + Icon { |
138 | + id: emptyStateIcon |
139 | + anchors.top: emptyStateScreen.top |
140 | + anchors.horizontalCenter: parent.horizontalCenter |
141 | + height: units.gu(5) |
142 | + width: height |
143 | + opacity: 0.3 |
144 | + name: "message" |
145 | + } |
146 | + Label { |
147 | + id: emptyStateLabel |
148 | + anchors.top: emptyStateIcon.bottom |
149 | + anchors.topMargin: units.gu(2) |
150 | + anchors.left: parent.left |
151 | + anchors.right: parent.right |
152 | + text: i18n.tr("Compose a new message by swiping up from the bottom of the screen.") |
153 | + color: "#5d5d5d" |
154 | + fontSize: "x-large" |
155 | + wrapMode: Text.WordWrap |
156 | + horizontalAlignment: Text.AlignHCenter |
157 | + } |
158 | +} |
159 | |
160 | === added file 'src/qml/InputInfo.qml' |
161 | --- src/qml/InputInfo.qml 1970-01-01 00:00:00 +0000 |
162 | +++ src/qml/InputInfo.qml 2016-02-01 23:18:57 +0000 |
163 | @@ -0,0 +1,24 @@ |
164 | +import QtQuick 2.0 |
165 | +//import Unity.InputInfo 0.1 |
166 | + |
167 | +Item { |
168 | + // FIXME: implement correctly without relying on unity private stuff |
169 | + property bool hasMouse: mainView.dualPanel //miceModel.count > 0 || touchPadModel.count > 0 |
170 | + property bool hasKeyboard: false //keyboardsModel.count > 0 |
171 | + |
172 | + /*InputDeviceModel { |
173 | + id: miceModel |
174 | + deviceFilter: InputInfo.Mouse |
175 | + } |
176 | + |
177 | + InputDeviceModel { |
178 | + id: touchPadModel |
179 | + deviceFilter: InputInfo.TouchPad |
180 | + } |
181 | + |
182 | + InputDeviceModel { |
183 | + id: keyboardsModel |
184 | + deviceFilter: InputInfo.Keyboard |
185 | + }*/ |
186 | +} |
187 | + |
188 | |
189 | === removed file 'src/qml/LocalPageWithBottomEdge.qml' |
190 | --- src/qml/LocalPageWithBottomEdge.qml 2015-09-14 13:51:27 +0000 |
191 | +++ src/qml/LocalPageWithBottomEdge.qml 1970-01-01 00:00:00 +0000 |
192 | @@ -1,428 +0,0 @@ |
193 | -/* |
194 | - * Copyright (C) 2014 Canonical, Ltd. |
195 | - * |
196 | - * This program is free software; you can redistribute it and/or modify |
197 | - * it under the terms of the GNU General Public License as published by |
198 | - * the Free Software Foundation; version 3. |
199 | - * |
200 | - * This program is distributed in the hope that it will be useful, |
201 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
202 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
203 | - * GNU General Public License for more details. |
204 | - * |
205 | - * You should have received a copy of the GNU General Public License |
206 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
207 | - */ |
208 | - |
209 | -/* |
210 | - Example: |
211 | - |
212 | - MainView { |
213 | - objectName: "mainView" |
214 | - |
215 | - applicationName: "com.ubuntu.developer.boiko.bottomedge" |
216 | - |
217 | - width: units.gu(100) |
218 | - height: units.gu(75) |
219 | - |
220 | - Component { |
221 | - id: pageComponent |
222 | - |
223 | - PageWithBottomEdge { |
224 | - id: mainPage |
225 | - title: i18n.tr("Main Page") |
226 | - |
227 | - Rectangle { |
228 | - anchors.fill: parent |
229 | - color: "white" |
230 | - } |
231 | - |
232 | - bottomEdgePageComponent: Page { |
233 | - title: "Contents" |
234 | - anchors.fill: parent |
235 | - //anchors.topMargin: contentsPage.flickable.contentY |
236 | - |
237 | - ListView { |
238 | - anchors.fill: parent |
239 | - model: 50 |
240 | - delegate: ListItems.Standard { |
241 | - text: "One Content Item: " + index |
242 | - } |
243 | - } |
244 | - } |
245 | - bottomEdgeTitle: i18n.tr("Bottom edge action") |
246 | - } |
247 | - } |
248 | - |
249 | - PageStack { |
250 | - id: stack |
251 | - Component.onCompleted: stack.push(pageComponent) |
252 | - } |
253 | - } |
254 | - |
255 | -*/ |
256 | - |
257 | -import QtQuick 2.2 |
258 | -import Ubuntu.Components 1.3 |
259 | - |
260 | -Page { |
261 | - id: page |
262 | - |
263 | - property alias bottomEdgePageComponent: edgeLoader.sourceComponent |
264 | - property alias bottomEdgePageSource: edgeLoader.source |
265 | - property alias bottomEdgeTitle: tipLabel.text |
266 | - property bool bottomEdgeEnabled: true |
267 | - property int bottomEdgeExpandThreshold: page.height * 0.2 |
268 | - property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded |
269 | - property bool reloadBottomEdgePage: true |
270 | - |
271 | - readonly property alias bottomEdgePage: edgeLoader.item |
272 | - readonly property bool isReady: ((bottomEdge.y === 0) && bottomEdgePageLoaded && edgeLoader.item.active) |
273 | - readonly property bool isCollapsed: (bottomEdge.y === page.height) |
274 | - readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready) |
275 | - property var temporaryProperties: null |
276 | - |
277 | - property bool _showEdgePageWhenReady: false |
278 | - property int _areaWhenExpanded: 0 |
279 | - |
280 | - signal bottomEdgeReleased() |
281 | - signal bottomEdgeDismissed() |
282 | - |
283 | - |
284 | - function showBottomEdgePage(source, properties) |
285 | - { |
286 | - edgeLoader.setSource(source, properties) |
287 | - temporaryProperties = properties |
288 | - _showEdgePageWhenReady = true |
289 | - } |
290 | - |
291 | - function setBottomEdgePage(source, properties) |
292 | - { |
293 | - edgeLoader.setSource(source, properties) |
294 | - } |
295 | - |
296 | - function _pushPage() |
297 | - { |
298 | - if (edgeLoader.status === Loader.Ready) { |
299 | - edgeLoader.item.active = true |
300 | - page.pageStack.push(edgeLoader.item) |
301 | - if (edgeLoader.item.flickable) { |
302 | - edgeLoader.item.flickable.contentY = -page.header.height |
303 | - edgeLoader.item.flickable.returnToBounds() |
304 | - } |
305 | - if (edgeLoader.item.ready) |
306 | - edgeLoader.item.ready() |
307 | - } |
308 | - } |
309 | - |
310 | - |
311 | - Component.onCompleted: { |
312 | - // avoid a binding on the expanded height value |
313 | - var expandedHeight = height; |
314 | - _areaWhenExpanded = expandedHeight; |
315 | - } |
316 | - |
317 | - onActiveChanged: { |
318 | - if (active) { |
319 | - bottomEdge.state = "collapsed" |
320 | - } |
321 | - } |
322 | - |
323 | - onBottomEdgePageLoadedChanged: { |
324 | - if (_showEdgePageWhenReady && bottomEdgePageLoaded) { |
325 | - bottomEdge.state = "expanded" |
326 | - _showEdgePageWhenReady = false |
327 | - } |
328 | - } |
329 | - |
330 | - InverseMouseArea { |
331 | - anchors.fill: edgeLoader.item |
332 | - sensingArea: mainView |
333 | - enabled: !tip.hidden |
334 | - onPressed: { |
335 | - mouse.accepted = false |
336 | - page.focus = false |
337 | - } |
338 | - z: 1 |
339 | - } |
340 | - |
341 | - Rectangle { |
342 | - id: bgVisual |
343 | - |
344 | - color: "black" |
345 | - anchors.fill: page |
346 | - opacity: 0.7 * ((page.height - bottomEdge.y) / page.height) |
347 | - z: 1 |
348 | - } |
349 | - |
350 | - UbuntuShape { |
351 | - id: tip |
352 | - objectName: "bottomEdgeTip" |
353 | - |
354 | - property bool hidden: (activeFocus === false) || ((bottomEdge.y - units.gu(1)) < tip.y) |
355 | - |
356 | - enabled: mouseArea.enabled |
357 | - anchors { |
358 | - bottom: parent.bottom |
359 | - horizontalCenter: bottomEdge.horizontalCenter |
360 | - bottomMargin: hidden ? - height + units.gu(1) : -units.gu(1) |
361 | - Behavior on bottomMargin { |
362 | - SequentialAnimation { |
363 | - // wait some msecs in case of the focus change again, to avoid flickering |
364 | - PauseAnimation { |
365 | - duration: 300 |
366 | - } |
367 | - UbuntuNumberAnimation { |
368 | - duration: UbuntuAnimation.SnapDuration |
369 | - } |
370 | - } |
371 | - } |
372 | - } |
373 | - |
374 | - z: 1 |
375 | - width: tipLabel.paintedWidth + units.gu(6) |
376 | - height: bottomEdge.tipHeight + units.gu(1) |
377 | - color: Theme.palette.normal.overlay |
378 | - Label { |
379 | - id: tipLabel |
380 | - |
381 | - anchors { |
382 | - top: parent.top |
383 | - left: parent.left |
384 | - right: parent.right |
385 | - } |
386 | - height: bottomEdge.tipHeight |
387 | - verticalAlignment: Text.AlignVCenter |
388 | - horizontalAlignment: Text.AlignHCenter |
389 | - opacity: tip.hidden ? 0.0 : 1.0 |
390 | - Behavior on opacity { |
391 | - UbuntuNumberAnimation { |
392 | - duration: UbuntuAnimation.SnapDuration |
393 | - } |
394 | - } |
395 | - } |
396 | - } |
397 | - |
398 | - Rectangle { |
399 | - id: shadow |
400 | - |
401 | - anchors { |
402 | - left: parent.left |
403 | - right: parent.right |
404 | - bottom: parent.bottom |
405 | - } |
406 | - height: units.gu(1) |
407 | - z: 1 |
408 | - opacity: 0.0 |
409 | - gradient: Gradient { |
410 | - GradientStop { position: 0.0; color: "transparent" } |
411 | - GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) } |
412 | - } |
413 | - } |
414 | - |
415 | - MouseArea { |
416 | - id: mouseArea |
417 | - |
418 | - property real previousY: -1 |
419 | - property string dragDirection: "None" |
420 | - |
421 | - preventStealing: true |
422 | - drag { |
423 | - axis: Drag.YAxis |
424 | - target: bottomEdge |
425 | - minimumY: bottomEdge.pageStartY |
426 | - maximumY: page.height |
427 | - } |
428 | - enabled: edgeLoader.status == Loader.Ready |
429 | - |
430 | - anchors { |
431 | - left: parent.left |
432 | - right: parent.right |
433 | - bottom: parent.bottom |
434 | - |
435 | - } |
436 | - height: bottomEdge.tipHeight |
437 | - z: 1 |
438 | - |
439 | - onReleased: { |
440 | - page.bottomEdgeReleased() |
441 | - if ((dragDirection === "BottomToTop") && |
442 | - bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) { |
443 | - bottomEdge.state = "expanded" |
444 | - } else { |
445 | - bottomEdge.state = "collapsed" |
446 | - } |
447 | - previousY = -1 |
448 | - dragDirection = "None" |
449 | - } |
450 | - |
451 | - onPressed: { |
452 | - previousY = mouse.y |
453 | - tip.forceActiveFocus() |
454 | - } |
455 | - |
456 | - onMouseYChanged: { |
457 | - var yOffset = previousY - mouseY |
458 | - // skip if was a small move |
459 | - if (Math.abs(yOffset) <= units.gu(2)) { |
460 | - return |
461 | - } |
462 | - previousY = mouseY |
463 | - dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom" |
464 | - } |
465 | - } |
466 | - |
467 | - Rectangle { |
468 | - id: bottomEdge |
469 | - objectName: "bottomEdge" |
470 | - |
471 | - readonly property int tipHeight: units.gu(3) |
472 | - readonly property int pageStartY: 0 |
473 | - |
474 | - z: 1 |
475 | - color: Theme.palette.normal.background |
476 | - clip: true |
477 | - anchors { |
478 | - left: parent.left |
479 | - right: parent.right |
480 | - } |
481 | - height: page.height |
482 | - y: height |
483 | - visible: page.bottomEdgeEnabled && !page.isCollapsed |
484 | - state: "collapsed" |
485 | - states: [ |
486 | - State { |
487 | - name: "collapsed" |
488 | - PropertyChanges { |
489 | - target: bottomEdge |
490 | - y: bottomEdge.height |
491 | - } |
492 | - }, |
493 | - State { |
494 | - name: "expanded" |
495 | - PropertyChanges { |
496 | - target: bottomEdge |
497 | - y: bottomEdge.pageStartY |
498 | - } |
499 | - }, |
500 | - State { |
501 | - name: "floating" |
502 | - when: mouseArea.drag.active |
503 | - PropertyChanges { |
504 | - target: shadow |
505 | - opacity: 1.0 |
506 | - } |
507 | - } |
508 | - ] |
509 | - |
510 | - transitions: [ |
511 | - Transition { |
512 | - to: "expanded" |
513 | - SequentialAnimation { |
514 | - alwaysRunToEnd: true |
515 | - |
516 | - SmoothedAnimation { |
517 | - target: bottomEdge |
518 | - property: "y" |
519 | - duration: UbuntuAnimation.FastDuration |
520 | - easing.type: Easing.Linear |
521 | - } |
522 | - SmoothedAnimation { |
523 | - target: edgeLoader |
524 | - property: "anchors.topMargin" |
525 | - to: - units.gu(4) |
526 | - duration: UbuntuAnimation.FastDuration |
527 | - easing.type: Easing.Linear |
528 | - } |
529 | - SmoothedAnimation { |
530 | - target: edgeLoader |
531 | - property: "anchors.topMargin" |
532 | - to: 0 |
533 | - duration: UbuntuAnimation.FastDuration |
534 | - easing: UbuntuAnimation.StandardEasing |
535 | - } |
536 | - ScriptAction { |
537 | - script: page._pushPage() |
538 | - } |
539 | - } |
540 | - }, |
541 | - Transition { |
542 | - from: "expanded" |
543 | - to: "collapsed" |
544 | - SequentialAnimation { |
545 | - alwaysRunToEnd: true |
546 | - |
547 | - ScriptAction { |
548 | - script: { |
549 | - Qt.inputMethod.hide() |
550 | - edgeLoader.item.parent = edgeLoader |
551 | - edgeLoader.item.anchors.fill = edgeLoader |
552 | - edgeLoader.item.active = false |
553 | - } |
554 | - } |
555 | - SmoothedAnimation { |
556 | - target: bottomEdge |
557 | - property: "y" |
558 | - duration: UbuntuAnimation.SlowDuration |
559 | - } |
560 | - ScriptAction { |
561 | - script: { |
562 | - // destroy current bottom page |
563 | - if (page.reloadBottomEdgePage) { |
564 | - edgeLoader.active = false |
565 | - // remove properties from old instance |
566 | - if (edgeLoader.source !== "") { |
567 | - var properties = {} |
568 | - if (temporaryProperties !== null) { |
569 | - properties = temporaryProperties |
570 | - temporaryProperties = null |
571 | - } |
572 | - |
573 | - edgeLoader.setSource(edgeLoader.source, properties) |
574 | - } |
575 | - // tip will receive focus on page active true |
576 | - } else { |
577 | - tip.forceActiveFocus() |
578 | - } |
579 | - |
580 | - // notify |
581 | - page.bottomEdgeDismissed() |
582 | - |
583 | - edgeLoader.active = true |
584 | - } |
585 | - } |
586 | - } |
587 | - }, |
588 | - Transition { |
589 | - from: "floating" |
590 | - to: "collapsed" |
591 | - SmoothedAnimation { |
592 | - target: bottomEdge |
593 | - property: "y" |
594 | - duration: UbuntuAnimation.FastDuration |
595 | - } |
596 | - } |
597 | - ] |
598 | - |
599 | - Loader { |
600 | - id: edgeLoader |
601 | - |
602 | - asynchronous: true |
603 | - anchors.fill: parent |
604 | - //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging |
605 | - Binding { |
606 | - target: edgeLoader.status === Loader.Ready ? edgeLoader : null |
607 | - property: "anchors.topMargin" |
608 | - value: edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0 |
609 | - when: !page.isReady |
610 | - } |
611 | - |
612 | - onLoaded: { |
613 | - tip.forceActiveFocus() |
614 | - if (page.isReady && edgeLoader.item.active !== true) { |
615 | - page._pushPage() |
616 | - } |
617 | - } |
618 | - } |
619 | - } |
620 | -} |
621 | |
622 | === modified file 'src/qml/MMS/Previewer.qml' |
623 | --- src/qml/MMS/Previewer.qml 2015-11-17 14:39:21 +0000 |
624 | +++ src/qml/MMS/Previewer.qml 2016-02-01 23:18:57 +0000 |
625 | @@ -31,7 +31,7 @@ |
626 | |
627 | function handleAttachment(filePath, handlerType) |
628 | { |
629 | - mainStack.push(picker, {"url": filePath, "handler": handlerType}); |
630 | + mainStack.addPageToCurrentColumn(previewerPage, picker, {"url": filePath, "handler": handlerType}); |
631 | actionTriggered() |
632 | } |
633 | |
634 | @@ -45,23 +45,29 @@ |
635 | previewerPage.handleAttachment(attachment.filePath, ContentHandler.Share) |
636 | } |
637 | |
638 | - function backAction() |
639 | - { |
640 | - mainStack.pop() |
641 | + header: PageHeader { |
642 | + id: pageHeader |
643 | + |
644 | + property alias leadingActions: leadingBar.actions |
645 | + property alias trailingActions: trailingBar.actions |
646 | + |
647 | + title: previewerPage.title |
648 | + leadingActionBar { |
649 | + id: leadingBar |
650 | + } |
651 | + |
652 | + trailingActionBar { |
653 | + id: trailingBar |
654 | + } |
655 | } |
656 | |
657 | - title: "" |
658 | state: "default" |
659 | states: [ |
660 | - PageHeadState { |
661 | + State { |
662 | + id: defaultState |
663 | name: "default" |
664 | - head: previewerPage.head |
665 | - backAction: Action { |
666 | - iconName: "back" |
667 | - text: i18n.tr("Back") |
668 | - onTriggered: previewerPage.backAction() |
669 | - } |
670 | - actions: [ |
671 | + |
672 | + property list<QtObject> trailingActions: [ |
673 | Action { |
674 | objectName: "saveButton" |
675 | text: i18n.tr("Save") |
676 | @@ -75,6 +81,11 @@ |
677 | onTriggered: previewerPage.shareAttchment() |
678 | } |
679 | ] |
680 | + PropertyChanges { |
681 | + target: pageHeader |
682 | + |
683 | + trailingActions: defaultState.trailingActions |
684 | + } |
685 | } |
686 | ] |
687 | |
688 | @@ -109,11 +120,11 @@ |
689 | |
690 | onPeerSelected: { |
691 | picker.curTransfer = peer.request(); |
692 | - mainStack.pop(); |
693 | + mainStack.removePage(picker); |
694 | if (picker.curTransfer.state === ContentTransfer.InProgress) |
695 | picker.__exportItems(picker.url); |
696 | } |
697 | - onCancelPressed: mainStack.pop(); |
698 | + onCancelPressed: mainStack.removePage(picker); |
699 | } |
700 | |
701 | Connections { |
702 | |
703 | === modified file 'src/qml/MMS/PreviewerImage.qml' |
704 | --- src/qml/MMS/PreviewerImage.qml 2016-02-01 23:18:57 +0000 |
705 | +++ src/qml/MMS/PreviewerImage.qml 2016-02-01 23:18:57 +0000 |
706 | @@ -25,6 +25,7 @@ |
707 | Previewer { |
708 | id: imagePreviewer |
709 | |
710 | + // FIXME: this won't work correctly in windowed mode |
711 | Component.onCompleted: application.fullscreen = true |
712 | Component.onDestruction: application.fullscreen = false |
713 | |
714 | |
715 | === modified file 'src/qml/MMS/PreviewerMultipleContacts.qml' |
716 | --- src/qml/MMS/PreviewerMultipleContacts.qml 2015-11-19 00:34:45 +0000 |
717 | +++ src/qml/MMS/PreviewerMultipleContacts.qml 2016-02-01 23:18:57 +0000 |
718 | @@ -42,7 +42,12 @@ |
719 | MultipleSelectionListView { |
720 | id: contactList |
721 | |
722 | - anchors.fill: parent |
723 | + anchors { |
724 | + top: root.header.bottom |
725 | + bottom: parent.bottom |
726 | + left: parent.left |
727 | + right: parent.right |
728 | + } |
729 | listModel: thumbnail.vcard.contacts |
730 | listDelegate: ContactDelegate { |
731 | id: contactDelegate |
732 | @@ -51,7 +56,7 @@ |
733 | property var contact: thumbnail.vcard.contacts[index] |
734 | |
735 | onClicked: { |
736 | - mainStack.push(sigleContatPreviewer, {'contact': contact}) |
737 | + mainStack.addComponentToCurrentColumnSync(root, sigleContatPreviewer, {'contact': contact}) |
738 | } |
739 | } |
740 | } |
741 | |
742 | === modified file 'src/qml/MMS/PreviewerVideo.qml' |
743 | --- src/qml/MMS/PreviewerVideo.qml 2016-02-01 23:18:57 +0000 |
744 | +++ src/qml/MMS/PreviewerVideo.qml 2016-02-01 23:18:57 +0000 |
745 | @@ -30,6 +30,7 @@ |
746 | title: i18n.tr("Video Preview") |
747 | clip: true |
748 | |
749 | + // FIXME: this won't work correctly in windowed mode |
750 | Component.onCompleted: { |
751 | application.fullscreen = true |
752 | // Load Video player after toggling fullscreen to reduce flickering |
753 | |
754 | === modified file 'src/qml/MMSDelegate.qml' |
755 | --- src/qml/MMSDelegate.qml 2016-02-01 23:18:57 +0000 |
756 | +++ src/qml/MMSDelegate.qml 2016-02-01 23:18:57 +0000 |
757 | @@ -45,7 +45,7 @@ |
758 | var properties = {} |
759 | properties["attachment"] = attachment.item.attachment |
760 | properties["thumbnail"] = attachment.item |
761 | - mainStack.push(Qt.resolvedUrl(attachment.item.previewer), properties) |
762 | + mainStack.addFileToCurrentColumnSync(messages.basePage, Qt.resolvedUrl(attachment.item.previewer), properties) |
763 | } |
764 | } |
765 | |
766 | |
767 | === modified file 'src/qml/MainPage.qml' |
768 | --- src/qml/MainPage.qml 2016-02-01 23:18:57 +0000 |
769 | +++ src/qml/MainPage.qml 2016-02-01 23:18:57 +0000 |
770 | @@ -23,29 +23,27 @@ |
771 | import Ubuntu.History 0.1 |
772 | import "dateUtils.js" as DateUtils |
773 | |
774 | -LocalPageWithBottomEdge { |
775 | +Page { |
776 | id: mainPage |
777 | property alias selectionMode: threadList.isInSelectionMode |
778 | property bool searching: false |
779 | + property bool isEmpty: threadCount == 0 && !threadModel.canFetchMore |
780 | property alias threadCount: threadList.count |
781 | + property alias displayedThreadIndex: threadList.currentIndex |
782 | + |
783 | + property var _messagesPage: null |
784 | |
785 | function startSelection() { |
786 | threadList.startSelection() |
787 | } |
788 | |
789 | - state: selectionMode ? "select" : searching ? "search" : "default" |
790 | - title: selectionMode ? " " : i18n.tr("Messages") |
791 | - flickable: null |
792 | - |
793 | - bottomEdgeEnabled: !selectionMode && !searching |
794 | - bottomEdgeTitle: i18n.tr("+") |
795 | - bottomEdgePageComponent: Messages { active: false } |
796 | - |
797 | TextField { |
798 | id: searchField |
799 | objectName: "searchField" |
800 | visible: mainPage.searching |
801 | anchors { |
802 | + top: parent.top |
803 | + topMargin: units.gu(1) |
804 | left: parent.left |
805 | right: parent.right |
806 | rightMargin: units.gu(2) |
807 | @@ -60,11 +58,30 @@ |
808 | } |
809 | } |
810 | |
811 | + header: PageHeader { |
812 | + id: pageHeader |
813 | + |
814 | + property alias leadingActions: leadingBar.actions |
815 | + property alias trailingActions: trailingBar.actions |
816 | + |
817 | + title: i18n.tr("Messages") |
818 | + flickable: threadList |
819 | + leadingActionBar { |
820 | + id: leadingBar |
821 | + } |
822 | + |
823 | + trailingActionBar { |
824 | + id: trailingBar |
825 | + } |
826 | + } |
827 | + |
828 | states: [ |
829 | - PageHeadState { |
830 | + State { |
831 | + id: defaultState |
832 | name: "default" |
833 | - head: mainPage.head |
834 | - actions: [ |
835 | + when: !searching && !selectionMode |
836 | + |
837 | + property list<QtObject> trailingActions: [ |
838 | Action { |
839 | objectName: "searchAction" |
840 | iconName: "search" |
841 | @@ -77,38 +94,76 @@ |
842 | objectName: "settingsAction" |
843 | text: i18n.tr("Settings") |
844 | iconName: "settings" |
845 | - onTriggered: pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) |
846 | + onTriggered: { |
847 | + emptyStack() |
848 | + pageStack.addFileToNextColumnSync(mainPage, Qt.resolvedUrl("SettingsPage.qml")) |
849 | + } |
850 | + }, |
851 | + Action { |
852 | + objectName: "newMessageAction" |
853 | + text: i18n.tr("New message") |
854 | + iconName: "add" |
855 | + visible: dualPanel |
856 | + onTriggered: mainView.bottomEdge.commit() |
857 | } |
858 | + |
859 | ] |
860 | + |
861 | + PropertyChanges { |
862 | + target: pageHeader |
863 | + trailingActions: defaultState.trailingActions |
864 | + leadingActions: [] |
865 | + } |
866 | }, |
867 | - PageHeadState { |
868 | + State { |
869 | + id: searchState |
870 | name: "search" |
871 | - head: mainPage.head |
872 | - backAction: Action { |
873 | - objectName: "cancelSearch" |
874 | - visible: mainPage.searching |
875 | - iconName: "back" |
876 | - text: i18n.tr("Cancel") |
877 | - onTriggered: { |
878 | - searchField.text = "" |
879 | - mainPage.searching = false |
880 | + when: searching |
881 | + |
882 | + property list<QtObject> leadingActions: [ |
883 | + Action { |
884 | + objectName: "cancelSearch" |
885 | + visible: mainPage.searching |
886 | + iconName: "back" |
887 | + text: i18n.tr("Cancel") |
888 | + onTriggered: { |
889 | + searchField.text = "" |
890 | + mainPage.searching = false |
891 | + } |
892 | } |
893 | + ] |
894 | + |
895 | + PropertyChanges { |
896 | + target: pageHeader |
897 | + contents: searchField |
898 | + leadingActions: searchState.leadingActions |
899 | + trailingActions: [] |
900 | } |
901 | - contents: searchField |
902 | }, |
903 | - PageHeadState { |
904 | - name: "select" |
905 | - head: mainPage.head |
906 | - backAction: Action { |
907 | - objectName: "selectionModeCancelAction" |
908 | - iconName: "back" |
909 | - onTriggered: threadList.cancelSelection() |
910 | - } |
911 | - actions: [ |
912 | + State { |
913 | + id: selectionState |
914 | + name: "selection" |
915 | + when: selectionMode |
916 | + |
917 | + property list<QtObject> leadingActions: [ |
918 | + Action { |
919 | + objectName: "selectionModeCancelAction" |
920 | + iconName: "back" |
921 | + onTriggered: threadList.cancelSelection() |
922 | + } |
923 | + ] |
924 | + |
925 | + property list<QtObject> trailingActions: [ |
926 | Action { |
927 | objectName: "selectionModeSelectAllAction" |
928 | iconName: "select" |
929 | - onTriggered: threadList.selectAll() |
930 | + onTriggered: { |
931 | + if (threadList.selectedItems.count === threadList.count) { |
932 | + threadList.clearSelection() |
933 | + } else { |
934 | + threadList.selectAll() |
935 | + } |
936 | + } |
937 | }, |
938 | Action { |
939 | objectName: "selectionModeDeleteAction" |
940 | @@ -117,39 +172,18 @@ |
941 | onTriggered: threadList.endSelection() |
942 | } |
943 | ] |
944 | + PropertyChanges { |
945 | + target: pageHeader |
946 | + title: " " |
947 | + leadingActions: selectionState.leadingActions |
948 | + trailingActions: selectionState.trailingActions |
949 | + } |
950 | } |
951 | ] |
952 | |
953 | - Item { |
954 | + EmptyState { |
955 | id: emptyStateScreen |
956 | - anchors.left: parent.left |
957 | - anchors.leftMargin: units.gu(6) |
958 | - anchors.right: parent.right |
959 | - anchors.rightMargin: units.gu(6) |
960 | - height: childrenRect.height |
961 | - anchors.verticalCenter: parent.verticalCenter |
962 | - visible: threadCount == 0 && !threadModel.canFetchMore |
963 | - Icon { |
964 | - id: emptyStateIcon |
965 | - anchors.top: emptyStateScreen.top |
966 | - anchors.horizontalCenter: parent.horizontalCenter |
967 | - height: units.gu(5) |
968 | - width: height |
969 | - opacity: 0.3 |
970 | - name: "message" |
971 | - } |
972 | - Label { |
973 | - id: emptyStateLabel |
974 | - anchors.top: emptyStateIcon.bottom |
975 | - anchors.topMargin: units.gu(2) |
976 | - anchors.left: parent.left |
977 | - anchors.right: parent.right |
978 | - text: i18n.tr("Compose a new message by swiping up from the bottom of the screen.") |
979 | - color: "#5d5d5d" |
980 | - fontSize: "x-large" |
981 | - wrapMode: Text.WordWrap |
982 | - horizontalAlignment: Text.AlignHCenter |
983 | - } |
984 | + visible: mainPage.isEmpty && !mainView.dualPanel |
985 | } |
986 | |
987 | Component { |
988 | @@ -188,8 +222,18 @@ |
989 | clip: true |
990 | cacheBuffer: threadList.height * 2 |
991 | section.property: "eventDate" |
992 | + currentIndex: -1 |
993 | //spacing: searchField.text === "" ? units.gu(-2) : 0 |
994 | section.delegate: searching && searchField.text !== "" ? null : sectionDelegate |
995 | + header: ListItem.Standard { |
996 | + id: newItem |
997 | + height: mainView.bottomEdge.status === BottomEdge.Committed ? units.gu(10) : 0 |
998 | + text: i18n.tr("New message") |
999 | + iconName: "message-new" |
1000 | + iconFrame: false |
1001 | + selected: true |
1002 | + } |
1003 | + |
1004 | listDelegate: ThreadDelegate { |
1005 | id: threadDelegate |
1006 | // FIXME: find a better unique name |
1007 | @@ -201,7 +245,15 @@ |
1008 | } |
1009 | height: units.gu(8) |
1010 | selectionMode: threadList.isInSelectionMode |
1011 | - selected: threadList.isSelected(threadDelegate) |
1012 | + selected: { |
1013 | + if (selectionMode) { |
1014 | + return threadList.isSelected(threadDelegate) |
1015 | + } else { |
1016 | + // FIXME: there might be a better way of doing this |
1017 | + return index === threadList.currentIndex && mainView.bottomEdge.status !== BottomEdge.Committed |
1018 | + } |
1019 | + } |
1020 | + |
1021 | searchTerm: mainPage.searching ? searchField.text : "" |
1022 | onItemClicked: { |
1023 | if (threadList.isInSelectionMode) { |
1024 | @@ -222,7 +274,11 @@ |
1025 | if (displayedEvent != null) { |
1026 | properties["scrollToEventId"] = displayedEvent.eventId |
1027 | } |
1028 | - mainStack.push(Qt.resolvedUrl("Messages.qml"), properties) |
1029 | + emptyStack() |
1030 | + mainStack.addComponentToNextColumnSync(mainPage, messagesWithBottomEdge, properties) |
1031 | + |
1032 | + // mark this item as current |
1033 | + threadList.currentIndex = index |
1034 | } |
1035 | } |
1036 | onItemPressAndHold: { |
1037 | @@ -242,6 +298,13 @@ |
1038 | mainView.removeThreads(threadsToRemove); |
1039 | } |
1040 | } |
1041 | + |
1042 | + Binding { |
1043 | + target: threadList |
1044 | + property: 'contentY' |
1045 | + value: -threadList.headerItem.height |
1046 | + when: mainView.composingNewMessage |
1047 | + } |
1048 | } |
1049 | |
1050 | KeyboardRectangle { |
1051 | @@ -252,4 +315,12 @@ |
1052 | flickableItem: threadList |
1053 | align: Qt.AlignTrailing |
1054 | } |
1055 | + |
1056 | + Loader { |
1057 | + id: bottomEdgeLoader |
1058 | + active: !selectionMode && !searching && !mainView.dualPanel |
1059 | + sourceComponent: MessagingBottomEdge { |
1060 | + parent: mainPage |
1061 | + } |
1062 | + } |
1063 | } |
1064 | |
1065 | === modified file 'src/qml/Messages.qml' |
1066 | --- src/qml/Messages.qml 2016-02-01 23:18:57 +0000 |
1067 | +++ src/qml/Messages.qml 2016-02-01 23:18:57 +0000 |
1068 | @@ -63,6 +63,19 @@ |
1069 | property QtObject presenceRequest: presenceItem |
1070 | property var accountsModel: getAccountsModel() |
1071 | property alias oskEnabled: keyboard.oskEnabled |
1072 | + property bool isReady: false |
1073 | + property string firstRecipientAlias: ((contactWatcher.isUnknown && |
1074 | + contactWatcher.isInteractive) || |
1075 | + contactWatcher.alias === "") ? contactWatcher.identifier : contactWatcher.alias |
1076 | + |
1077 | + |
1078 | + // When using this view from the bottom edge, we are not in the stack, so we need to push on top of the parent page |
1079 | + property var basePage: messages |
1080 | + |
1081 | + property bool startedFromBottomEdge: false |
1082 | + |
1083 | + signal ready |
1084 | + signal cancel |
1085 | |
1086 | function getAccountsModel() { |
1087 | var accounts = [] |
1088 | @@ -221,99 +234,6 @@ |
1089 | return thread |
1090 | } |
1091 | |
1092 | - Connections { |
1093 | - target: telepathyHelper |
1094 | - onSetupReady: { |
1095 | - // force reevaluation |
1096 | - messages.account = Qt.binding(getCurrentAccount) |
1097 | - messages.phoneAccount = Qt.binding(isPhoneAccount) |
1098 | - head.sections.model = Qt.binding(getSectionsModel) |
1099 | - head.sections.selectedIndex = Qt.binding(getSelectedIndex) |
1100 | - } |
1101 | - } |
1102 | - |
1103 | - |
1104 | - Connections { |
1105 | - target: chatManager |
1106 | - onChatEntryCreated: { |
1107 | - // TODO: track using chatId and not participants |
1108 | - if (accountId == account.accountId && |
1109 | - firstParticipant && participants[0] == firstParticipant.identifier) { |
1110 | - messages.chatEntry = chatEntry |
1111 | - } |
1112 | - } |
1113 | - onChatsChanged: { |
1114 | - for (var i in chatManager.chats) { |
1115 | - var chat = chatManager.chats[i] |
1116 | - // TODO: track using chatId and not participants |
1117 | - if (chat.account.accountId == account.accountId && |
1118 | - firstParticipant && chat.participants[0] == firstParticipant.identifier) { |
1119 | - messages.chatEntry = chat |
1120 | - return |
1121 | - } |
1122 | - } |
1123 | - messages.chatEntry = null |
1124 | - } |
1125 | - } |
1126 | - |
1127 | - Timer { |
1128 | - id: typingTimer |
1129 | - interval: 6000 |
1130 | - onTriggered: { |
1131 | - messages.userTyping = false; |
1132 | - } |
1133 | - } |
1134 | - |
1135 | - Repeater { |
1136 | - model: messages.chatEntry ? messages.chatEntry.chatStates : null |
1137 | - Item { |
1138 | - function processChatState() { |
1139 | - if (modelData.state == ChatEntry.ChannelChatStateComposing) { |
1140 | - messages.userTyping = true |
1141 | - typingTimer.start() |
1142 | - } else { |
1143 | - messages.userTyping = false |
1144 | - } |
1145 | - } |
1146 | - Component.onCompleted: processChatState() |
1147 | - Connections { |
1148 | - target: modelData |
1149 | - onStateChanged: processChatState() |
1150 | - } |
1151 | - } |
1152 | - } |
1153 | - |
1154 | - MessagesHeader { |
1155 | - id: header |
1156 | - width: parent ? parent.width - units.gu(2) : undefined |
1157 | - height: units.gu(5) |
1158 | - title: messages.title |
1159 | - subtitle: { |
1160 | - if (userTyping) { |
1161 | - return i18n.tr("Typing..") |
1162 | - } |
1163 | - switch (presenceRequest.type) { |
1164 | - case PresenceRequest.PresenceTypeAvailable: |
1165 | - return i18n.tr("Online") |
1166 | - case PresenceRequest.PresenceTypeOffline: |
1167 | - return i18n.tr("Offline") |
1168 | - case PresenceRequest.PresenceTypeAway: |
1169 | - return i18n.tr("Away") |
1170 | - case PresenceRequest.PresenceTypeBusy: |
1171 | - return i18n.tr("Busy") |
1172 | - default: |
1173 | - return "" |
1174 | - } |
1175 | - } |
1176 | - visible: true |
1177 | - } |
1178 | - |
1179 | - head { |
1180 | - id: head |
1181 | - sections.model: getSectionsModel() |
1182 | - sections.selectedIndex: getSelectedIndex() |
1183 | - } |
1184 | - |
1185 | function sendMessageNetworkCheck() { |
1186 | if (messages.account.simLocked) { |
1187 | Qt.inputMethod.hide() |
1188 | @@ -363,7 +283,7 @@ |
1189 | if (event.senderId == "self" && event.accountId != messages.account.accountId) { |
1190 | var tmpAccount = telepathyHelper.accountForId(event.accountId) |
1191 | if (!tmpAccount || (tmpAccount.type == AccountEntry.MultimediaAccount && messages.account.type == AccountEntry.PhoneAccount)) { |
1192 | - // we don't add the information event if the last outgoing message |
1193 | + // we don't add the information event if the last outgoing message |
1194 | // was a fallback to a multimedia service |
1195 | break; |
1196 | } |
1197 | @@ -425,7 +345,7 @@ |
1198 | var isSelfContactKnown = account.selfContactId != "" |
1199 | if (isMmsGroupChat && !isSelfContactKnown) { |
1200 | // TODO: inform the user to enter the phone number of the selected sim card manually |
1201 | - // and use it in the telepathy-ofono account as selfContactId. |
1202 | + // and use it in the telepathy-ofono account as selfContactId. |
1203 | return false |
1204 | } |
1205 | var fallbackAccountId = chatManager.sendMessage(messages.account.accountId, participantIds, text, attachments, properties) |
1206 | @@ -448,102 +368,6 @@ |
1207 | return true |
1208 | } |
1209 | |
1210 | - PresenceRequest { |
1211 | - id: presenceItem |
1212 | - accountId: { |
1213 | - // if this is a regular sms chat, try requesting the presence on |
1214 | - // a multimedia account |
1215 | - if (!account) { |
1216 | - return "" |
1217 | - } |
1218 | - if (account.type == AccountEntry.PhoneAccount) { |
1219 | - for (var i in telepathyHelper.accounts) { |
1220 | - var tmpAccount = telepathyHelper.accounts[i] |
1221 | - if (tmpAccount.type == AccountEntry.MultimediaAccount) { |
1222 | - return tmpAccount.accountId |
1223 | - } |
1224 | - } |
1225 | - return "" |
1226 | - } |
1227 | - return account.accountId |
1228 | - } |
1229 | - // we just request presence on 1-1 chats |
1230 | - identifier: participants.length == 1 ? participants[0].identifier : "" |
1231 | - } |
1232 | - |
1233 | - // this is necessary to automatically update the view when the |
1234 | - // default account changes in system settings |
1235 | - Connections { |
1236 | - target: mainView |
1237 | - onAccountChanged: { |
1238 | - if (!messages.phoneAccount) { |
1239 | - return |
1240 | - } |
1241 | - messages.account = mainView.account |
1242 | - } |
1243 | - } |
1244 | - |
1245 | - ActivityIndicator { |
1246 | - id: activityIndicator |
1247 | - anchors { |
1248 | - verticalCenter: parent.verticalCenter |
1249 | - horizontalCenter: parent.horizontalCenter |
1250 | - } |
1251 | - running: isSearching |
1252 | - visible: running |
1253 | - } |
1254 | - |
1255 | - flickable: null |
1256 | - |
1257 | - property bool isReady: false |
1258 | - signal ready |
1259 | - onReady: { |
1260 | - isReady = true |
1261 | - if (participants.length === 0 && keyboardFocus) |
1262 | - multiRecipient.forceFocus() |
1263 | - } |
1264 | - |
1265 | - property string firstRecipientAlias: ((contactWatcher.isUnknown && |
1266 | - contactWatcher.isInteractive) || |
1267 | - contactWatcher.alias === "") ? contactWatcher.identifier : contactWatcher.alias |
1268 | - title: { |
1269 | - if (selectionMode || participants.length == 0) { |
1270 | - return " " |
1271 | - } |
1272 | - |
1273 | - if (landscape) { |
1274 | - return "" |
1275 | - } |
1276 | - if (participants.length > 0) { |
1277 | - if (participants.length == 1) { |
1278 | - return firstRecipientAlias |
1279 | - } else { |
1280 | - // TRANSLATORS: %1 refers to the number of participants in a group chat |
1281 | - return i18n.tr("Group (%1)").arg(participants.length) |
1282 | - } |
1283 | - } |
1284 | - return i18n.tr("New Message") |
1285 | - } |
1286 | - |
1287 | - Component.onCompleted: { |
1288 | - if (messages.accountId !== "") { |
1289 | - var account = telepathyHelper.accountForId(messages.accountId) |
1290 | - if (account && account.type == AccountEntry.MultimediaAccount) { |
1291 | - // fallback the first available phone account |
1292 | - if (telepathyHelper.phoneAccounts.length > 0) { |
1293 | - messages.accountId = telepathyHelper.phoneAccounts[0].accountId |
1294 | - } |
1295 | - } |
1296 | - } |
1297 | - composeBar.addAttachments(sharedAttachmentsTransfer) |
1298 | - } |
1299 | - |
1300 | - onActiveChanged: { |
1301 | - if (active && (eventModel.count > 0)){ |
1302 | - swipeItemDemo.enable() |
1303 | - } |
1304 | - } |
1305 | - |
1306 | function updateFilters(accounts, participants, reload, threads) { |
1307 | if (participants.length == 0 || accounts.length == 0) { |
1308 | return null |
1309 | @@ -603,8 +427,356 @@ |
1310 | return eventModel.markEventAsRead(accountId, threadId, eventId, type); |
1311 | } |
1312 | |
1313 | + header: PageHeader { |
1314 | + id: pageHeader |
1315 | + |
1316 | + property alias leadingActions: leadingBar.actions |
1317 | + property alias trailingActions: trailingBar.actions |
1318 | + |
1319 | + property list<QtObject> bottomEdgeLeadingActions: [ |
1320 | + Action { |
1321 | + id: backAction |
1322 | + |
1323 | + objectName: "cancel" |
1324 | + name: "cancel" |
1325 | + text: i18n.tr("Cancel") |
1326 | + iconName: "down" |
1327 | + shortcut: "Esc" |
1328 | + onTriggered: { |
1329 | + messages.cancel() |
1330 | + } |
1331 | + } |
1332 | + ] |
1333 | + |
1334 | + property list<QtObject> singlePanelLeadingActions: [ |
1335 | + Action { |
1336 | + id: singlePanelBackAction |
1337 | + objectName: "back" |
1338 | + name: "cancel" |
1339 | + text: i18n.tr("Cancel") |
1340 | + iconName: "back" |
1341 | + shortcut: "Esc" |
1342 | + onTriggered: { |
1343 | + // emptyStack will make sure the page gets removed. |
1344 | + mainView.emptyStack() |
1345 | + } |
1346 | + } |
1347 | + ] |
1348 | + |
1349 | + title: { |
1350 | + if (landscape) { |
1351 | + return "" |
1352 | + } |
1353 | + |
1354 | + if (participants.length == 1) { |
1355 | + return firstRecipientAlias |
1356 | + } |
1357 | + |
1358 | + return i18n.tr("New Message") |
1359 | + } |
1360 | + flickable: null |
1361 | + |
1362 | + Sections { |
1363 | + id: sections |
1364 | + anchors { |
1365 | + left: parent.left |
1366 | + leftMargin: units.gu(2) |
1367 | + bottom: parent.bottom |
1368 | + } |
1369 | + model: getSectionsModel() |
1370 | + selectedIndex: getSelectedIndex() |
1371 | + onSelectedIndexChanged: { |
1372 | + if (selectedIndex >= 0) { |
1373 | + messages.account = messages.accountsModel[selectedIndex] |
1374 | + } |
1375 | + } |
1376 | + } |
1377 | + |
1378 | + extension: sections.model.length > 1 ? sections : null |
1379 | + |
1380 | + leadingActionBar { |
1381 | + id: leadingBar |
1382 | + |
1383 | + states: [ |
1384 | + State { |
1385 | + name: "bottomEdgeBack" |
1386 | + when: startedFromBottomEdge |
1387 | + PropertyChanges { |
1388 | + target: leadingBar |
1389 | + actions: pageHeader.bottomEdgeLeadingActions |
1390 | + } |
1391 | + }, |
1392 | + State { |
1393 | + name: "singlePanelBack" |
1394 | + when: !mainView.dualPanel && !startedFromBottomEdge |
1395 | + PropertyChanges { |
1396 | + target: leadingBar |
1397 | + actions: pageHeader.singlePanelLeadingActions |
1398 | + } |
1399 | + } |
1400 | + |
1401 | + ] |
1402 | + } |
1403 | + |
1404 | + trailingActionBar { |
1405 | + id: trailingBar |
1406 | + } |
1407 | + } |
1408 | + |
1409 | + states: [ |
1410 | + State { |
1411 | + id: selectionState |
1412 | + name: "selection" |
1413 | + when: selectionMode |
1414 | + |
1415 | + property list<QtObject> leadingActions: [ |
1416 | + Action { |
1417 | + objectName: "selectionModeCancelAction" |
1418 | + iconName: "back" |
1419 | + onTriggered: messageList.cancelSelection() |
1420 | + } |
1421 | + ] |
1422 | + |
1423 | + property list<QtObject> trailingActions: [ |
1424 | + Action { |
1425 | + objectName: "selectionModeSelectAllAction" |
1426 | + iconName: "select" |
1427 | + onTriggered: { |
1428 | + if (messageList.selectedItems.count === messageList.count) { |
1429 | + messageList.clearSelection() |
1430 | + } else { |
1431 | + messageList.selectAll() |
1432 | + } |
1433 | + } |
1434 | + }, |
1435 | + Action { |
1436 | + objectName: "selectionModeDeleteAction" |
1437 | + enabled: messageList.selectedItems.count > 0 |
1438 | + iconName: "delete" |
1439 | + onTriggered: messageList.endSelection() |
1440 | + } |
1441 | + ] |
1442 | + |
1443 | + PropertyChanges { |
1444 | + target: pageHeader |
1445 | + title: " " |
1446 | + leadingActions: selectionState.leadingActions |
1447 | + trailingActions: selectionState.trailingActions |
1448 | + } |
1449 | + }, |
1450 | + State { |
1451 | + id: groupChatState |
1452 | + name: "groupChat" |
1453 | + when: groupChat |
1454 | + |
1455 | + property list<QtObject> trailingActions: [ |
1456 | + Action { |
1457 | + objectName: "groupChatAction" |
1458 | + iconName: "contact-group" |
1459 | + onTriggered: PopupUtils.open(participantsPopover, pageHeader) |
1460 | + } |
1461 | + ] |
1462 | + |
1463 | + PropertyChanges { |
1464 | + target: pageHeader |
1465 | + // TRANSLATORS: %1 refers to the number of participants in a group chat |
1466 | + title: i18n.tr("Group (%1)").arg(participants.length) |
1467 | + contents: headerContents |
1468 | + trailingActions: groupChatState.trailingActions |
1469 | + } |
1470 | + }, |
1471 | + State { |
1472 | + id: unknownContactState |
1473 | + name: "unknownContact" |
1474 | + when: participants.length == 1 && contactWatcher.isUnknown |
1475 | + |
1476 | + property list<QtObject> trailingActions: [ |
1477 | + Action { |
1478 | + objectName: "contactCallAction" |
1479 | + visible: participants.length == 1 && contactWatcher.interactive |
1480 | + iconName: "call-start" |
1481 | + text: i18n.tr("Call") |
1482 | + onTriggered: { |
1483 | + Qt.inputMethod.hide() |
1484 | + // FIXME: support other things than just phone numbers |
1485 | + Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) |
1486 | + } |
1487 | + }, |
1488 | + Action { |
1489 | + objectName: "addContactAction" |
1490 | + visible: contactWatcher.isUnknown && participants.length == 1 && contactWatcher.interactive |
1491 | + iconName: "contact-new" |
1492 | + text: i18n.tr("Add") |
1493 | + onTriggered: { |
1494 | + Qt.inputMethod.hide() |
1495 | + // FIXME: support other things than just phone numbers |
1496 | + mainView.addPhoneToContact(messages, "", contactWatcher.identifier, null, null) |
1497 | + } |
1498 | + } |
1499 | + ] |
1500 | + PropertyChanges { |
1501 | + target: pageHeader |
1502 | + contents: headerContents |
1503 | + trailingActions: unknownContactState.trailingActions |
1504 | + } |
1505 | + }, |
1506 | + State { |
1507 | + id: newMessageState |
1508 | + name: "newMessage" |
1509 | + when: participants.length === 0 |
1510 | + |
1511 | + property list<QtObject> trailingActions: [ |
1512 | + Action { |
1513 | + objectName: "contactList" |
1514 | + iconName: "contact" |
1515 | + onTriggered: { |
1516 | + Qt.inputMethod.hide() |
1517 | + mainStack.addFileToCurrentColumnSync(messages.basePage, Qt.resolvedUrl("NewRecipientPage.qml"), {"multiRecipient": multiRecipient}) |
1518 | + } |
1519 | + } |
1520 | + |
1521 | + ] |
1522 | + |
1523 | + property Item contents: MultiRecipientInput { |
1524 | + id: multiRecipient |
1525 | + objectName: "multiRecipient" |
1526 | + enabled: visible |
1527 | + anchors { |
1528 | + left: parent ? parent.left : undefined |
1529 | + right: parent ? parent.right : undefined |
1530 | + rightMargin: units.gu(2) |
1531 | + top: parent ? parent.top: undefined |
1532 | + topMargin: units.gu(1) |
1533 | + } |
1534 | + focus: true |
1535 | + |
1536 | + Connections { |
1537 | + target: mainView.bottomEdge |
1538 | + onStatusChanged: { |
1539 | + if (mainView.bottomEdge.status === BottomEdge.Committed) { |
1540 | + multiRecipient.forceFocus() |
1541 | + } |
1542 | + } |
1543 | + } |
1544 | + } |
1545 | + |
1546 | + PropertyChanges { |
1547 | + target: pageHeader |
1548 | + title: " " |
1549 | + trailingActions: newMessageState.trailingActions |
1550 | + contents: newMessageState.contents |
1551 | + } |
1552 | + }, |
1553 | + State { |
1554 | + id: knownContactState |
1555 | + name: "knownContact" |
1556 | + when: participants.length == 1 && !contactWatcher.isUnknown |
1557 | + property list<QtObject> trailingActions: [ |
1558 | + Action { |
1559 | + objectName: "contactCallKnownAction" |
1560 | + visible: participants.length == 1 && messages.phoneAccount |
1561 | + iconName: "call-start" |
1562 | + text: i18n.tr("Call") |
1563 | + onTriggered: { |
1564 | + Qt.inputMethod.hide() |
1565 | + // FIXME: support other things than just phone numbers |
1566 | + Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) |
1567 | + } |
1568 | + }, |
1569 | + Action { |
1570 | + objectName: "contactProfileAction" |
1571 | + visible: !contactWatcher.isUnknown && participants.length == 1 && messages.phoneAccount |
1572 | + iconSource: "image://theme/contact" |
1573 | + text: i18n.tr("Contact") |
1574 | + onTriggered: { |
1575 | + mainView.showContactDetails(messages.basePage, contactWatcher.contactId, null, null) |
1576 | + } |
1577 | + } |
1578 | + ] |
1579 | + PropertyChanges { |
1580 | + target: pageHeader |
1581 | + contents: headerContents |
1582 | + trailingActions: knownContactState.trailingActions |
1583 | + } |
1584 | + } |
1585 | + ] |
1586 | + |
1587 | + Component.onCompleted: { |
1588 | + if (messages.accountId !== "") { |
1589 | + var account = telepathyHelper.accountForId(messages.accountId) |
1590 | + if (account && account.type == AccountEntry.MultimediaAccount) { |
1591 | + // fallback the first available phone account |
1592 | + if (telepathyHelper.phoneAccounts.length > 0) { |
1593 | + messages.accountId = telepathyHelper.phoneAccounts[0].accountId |
1594 | + } |
1595 | + } |
1596 | + } |
1597 | + composeBar.addAttachments(sharedAttachmentsTransfer) |
1598 | + } |
1599 | + |
1600 | + Component.onDestruction: { |
1601 | + if (!mainView.dualPanel && !startedFromBottomEdge) { |
1602 | + mainPage.displayedThreadIndex = -1 |
1603 | + } |
1604 | + } |
1605 | + |
1606 | + onReady: { |
1607 | + isReady = true |
1608 | + if (participants.length === 0 && keyboardFocus) |
1609 | + multiRecipient.forceFocus() |
1610 | + } |
1611 | + |
1612 | + onActiveChanged: { |
1613 | + if (active && (eventModel.count > 0)){ |
1614 | + swipeItemDemo.enable() |
1615 | + } |
1616 | + } |
1617 | + |
1618 | + Connections { |
1619 | + target: telepathyHelper |
1620 | + onSetupReady: { |
1621 | + // force reevaluation |
1622 | + messages.account = Qt.binding(getCurrentAccount) |
1623 | + messages.phoneAccount = Qt.binding(isPhoneAccount) |
1624 | + head.sections.model = Qt.binding(getSectionsModel) |
1625 | + head.sections.selectedIndex = Qt.binding(getSelectedIndex) |
1626 | + } |
1627 | + } |
1628 | + |
1629 | + Connections { |
1630 | + target: chatManager |
1631 | + onChatEntryCreated: { |
1632 | + // TODO: track using chatId and not participants |
1633 | + if (accountId == account.accountId && |
1634 | + firstParticipant && participants[0] == firstParticipant.identifier) { |
1635 | + messages.chatEntry = chatEntry |
1636 | + } |
1637 | + } |
1638 | + onChatsChanged: { |
1639 | + for (var i in chatManager.chats) { |
1640 | + var chat = chatManager.chats[i] |
1641 | + // TODO: track using chatId and not participants |
1642 | + if (chat.account.accountId == account.accountId && |
1643 | + firstParticipant && chat.participants[0] == firstParticipant.identifier) { |
1644 | + messages.chatEntry = chat |
1645 | + return |
1646 | + } |
1647 | + } |
1648 | + messages.chatEntry = null |
1649 | + } |
1650 | + } |
1651 | + |
1652 | + // this is necessary to automatically update the view when the |
1653 | + // default account changes in system settings |
1654 | Connections { |
1655 | target: mainView |
1656 | + onAccountChanged: { |
1657 | + if (!messages.phoneAccount) { |
1658 | + return |
1659 | + } |
1660 | + messages.account = mainView.account |
1661 | + } |
1662 | + |
1663 | onApplicationActiveChanged: { |
1664 | if (mainView.applicationActive) { |
1665 | for (var i in pendingEventsToMarkAsRead) { |
1666 | @@ -616,6 +788,91 @@ |
1667 | } |
1668 | } |
1669 | |
1670 | + Timer { |
1671 | + id: typingTimer |
1672 | + interval: 6000 |
1673 | + onTriggered: { |
1674 | + messages.userTyping = false; |
1675 | + } |
1676 | + } |
1677 | + |
1678 | + Repeater { |
1679 | + model: messages.chatEntry ? messages.chatEntry.chatStates : null |
1680 | + Item { |
1681 | + function processChatState() { |
1682 | + if (modelData.state == ChatEntry.ChannelChatStateComposing) { |
1683 | + messages.userTyping = true |
1684 | + typingTimer.start() |
1685 | + } else { |
1686 | + messages.userTyping = false |
1687 | + } |
1688 | + } |
1689 | + Component.onCompleted: processChatState() |
1690 | + Connections { |
1691 | + target: modelData |
1692 | + onStateChanged: processChatState() |
1693 | + } |
1694 | + } |
1695 | + } |
1696 | + |
1697 | + MessagesHeader { |
1698 | + id: headerContents |
1699 | + width: parent ? parent.width - units.gu(2) : undefined |
1700 | + height: units.gu(5) |
1701 | + title: pageHeader.title |
1702 | + subtitle: { |
1703 | + if (userTyping) { |
1704 | + return i18n.tr("Typing..") |
1705 | + } |
1706 | + switch (presenceRequest.type) { |
1707 | + case PresenceRequest.PresenceTypeAvailable: |
1708 | + return i18n.tr("Online") |
1709 | + case PresenceRequest.PresenceTypeOffline: |
1710 | + return i18n.tr("Offline") |
1711 | + case PresenceRequest.PresenceTypeAway: |
1712 | + return i18n.tr("Away") |
1713 | + case PresenceRequest.PresenceTypeBusy: |
1714 | + return i18n.tr("Busy") |
1715 | + default: |
1716 | + return "" |
1717 | + } |
1718 | + } |
1719 | + visible: true |
1720 | + } |
1721 | + |
1722 | + PresenceRequest { |
1723 | + id: presenceItem |
1724 | + accountId: { |
1725 | + // if this is a regular sms chat, try requesting the presence on |
1726 | + // a multimedia account |
1727 | + if (!account) { |
1728 | + return "" |
1729 | + } |
1730 | + if (account.type == AccountEntry.PhoneAccount) { |
1731 | + for (var i in telepathyHelper.accounts) { |
1732 | + var tmpAccount = telepathyHelper.accounts[i] |
1733 | + if (tmpAccount.type == AccountEntry.MultimediaAccount) { |
1734 | + return tmpAccount.accountId |
1735 | + } |
1736 | + } |
1737 | + return "" |
1738 | + } |
1739 | + return account.accountId |
1740 | + } |
1741 | + // we just request presence on 1-1 chats |
1742 | + identifier: participants.length == 1 ? participants[0].identifier : "" |
1743 | + } |
1744 | + |
1745 | + ActivityIndicator { |
1746 | + id: activityIndicator |
1747 | + anchors { |
1748 | + verticalCenter: parent.verticalCenter |
1749 | + horizontalCenter: parent.horizontalCenter |
1750 | + } |
1751 | + running: isSearching |
1752 | + visible: running |
1753 | + } |
1754 | + |
1755 | Component { |
1756 | id: participantsPopover |
1757 | |
1758 | @@ -678,13 +935,6 @@ |
1759 | } |
1760 | } |
1761 | |
1762 | - Connections { |
1763 | - target: messages.head.sections |
1764 | - onSelectedIndexChanged: { |
1765 | - messages.account = messages.accountsModel[head.sections.selectedIndex] |
1766 | - } |
1767 | - } |
1768 | - |
1769 | Loader { |
1770 | id: searchListLoader |
1771 | |
1772 | @@ -696,7 +946,7 @@ |
1773 | visible: source != "" |
1774 | anchors { |
1775 | top: parent.top |
1776 | - topMargin: units.gu(2) |
1777 | + topMargin: header.height + units.gu(2) |
1778 | left: parent.left |
1779 | right: parent.right |
1780 | bottom: composeBar.top |
1781 | @@ -756,156 +1006,6 @@ |
1782 | addressableFields: messages.account ? messages.account.addressableVCardFields : ["tel"] // just to have a fallback there |
1783 | } |
1784 | |
1785 | - Action { |
1786 | - id: backButton |
1787 | - objectName: "backButton" |
1788 | - iconName: "back" |
1789 | - onTriggered: { |
1790 | - if (typeof mainPage !== 'undefined') { |
1791 | - mainPage.temporaryProperties = null |
1792 | - } |
1793 | - mainStack.pop() |
1794 | - } |
1795 | - } |
1796 | - |
1797 | - states: [ |
1798 | - PageHeadState { |
1799 | - name: "selection" |
1800 | - head: messages.head |
1801 | - when: selectionMode |
1802 | - |
1803 | - backAction: Action { |
1804 | - objectName: "selectionModeCancelAction" |
1805 | - iconName: "back" |
1806 | - onTriggered: messageList.cancelSelection() |
1807 | - } |
1808 | - |
1809 | - actions: [ |
1810 | - Action { |
1811 | - objectName: "selectionModeSelectAllAction" |
1812 | - iconName: "select" |
1813 | - onTriggered: { |
1814 | - if (messageList.selectedItems.count === messageList.count) { |
1815 | - messageList.clearSelection() |
1816 | - } else { |
1817 | - messageList.selectAll() |
1818 | - } |
1819 | - } |
1820 | - }, |
1821 | - Action { |
1822 | - objectName: "selectionModeDeleteAction" |
1823 | - enabled: messageList.selectedItems.count > 0 |
1824 | - iconName: "delete" |
1825 | - onTriggered: messageList.endSelection() |
1826 | - } |
1827 | - ] |
1828 | - }, |
1829 | - PageHeadState { |
1830 | - name: "groupChat" |
1831 | - head: messages.head |
1832 | - when: groupChat |
1833 | - contents: header |
1834 | - backAction: backButton |
1835 | - |
1836 | - actions: [ |
1837 | - Action { |
1838 | - objectName: "groupChatAction" |
1839 | - iconName: "contact-group" |
1840 | - onTriggered: PopupUtils.open(participantsPopover, screenTop) |
1841 | - } |
1842 | - ] |
1843 | - }, |
1844 | - PageHeadState { |
1845 | - name: "unknownContact" |
1846 | - head: messages.head |
1847 | - when: participants.length == 1 && contactWatcher.isUnknown |
1848 | - backAction: backButton |
1849 | - contents: header |
1850 | - |
1851 | - actions: [ |
1852 | - Action { |
1853 | - objectName: "contactCallAction" |
1854 | - visible: participants.length == 1 && contactWatcher.interactive |
1855 | - iconName: "call-start" |
1856 | - text: i18n.tr("Call") |
1857 | - onTriggered: { |
1858 | - Qt.inputMethod.hide() |
1859 | - // FIXME: support other things than just phone numbers |
1860 | - Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) |
1861 | - } |
1862 | - }, |
1863 | - Action { |
1864 | - objectName: "addContactAction" |
1865 | - visible: contactWatcher.isUnknown && participants.length == 1 && contactWatcher.interactive |
1866 | - iconName: "contact-new" |
1867 | - text: i18n.tr("Add") |
1868 | - onTriggered: { |
1869 | - Qt.inputMethod.hide() |
1870 | - // FIXME: support other things than just phone numbers |
1871 | - mainView.addPhoneToContact("", contactWatcher.identifier, null, null) |
1872 | - } |
1873 | - } |
1874 | - ] |
1875 | - }, |
1876 | - PageHeadState { |
1877 | - name: "newMessage" |
1878 | - head: messages.head |
1879 | - when: participants.length === 0 && isReady |
1880 | - backAction: backButton |
1881 | - actions: [ |
1882 | - Action { |
1883 | - objectName: "contactList" |
1884 | - iconName: "contact" |
1885 | - onTriggered: { |
1886 | - Qt.inputMethod.hide() |
1887 | - mainStack.push(Qt.resolvedUrl("NewRecipientPage.qml"), {"multiRecipient": multiRecipient, "parentPage": messages}) |
1888 | - } |
1889 | - } |
1890 | - |
1891 | - ] |
1892 | - |
1893 | - contents: MultiRecipientInput { |
1894 | - id: multiRecipient |
1895 | - objectName: "multiRecipient" |
1896 | - enabled: visible |
1897 | - anchors { |
1898 | - left: parent ? parent.left : undefined |
1899 | - right: parent ? parent.right : undefined |
1900 | - rightMargin: units.gu(2) |
1901 | - } |
1902 | - } |
1903 | - }, |
1904 | - PageHeadState { |
1905 | - name: "knownContact" |
1906 | - head: messages.head |
1907 | - when: participants.length == 1 && !contactWatcher.isUnknown |
1908 | - backAction: backButton |
1909 | - contents: header |
1910 | - actions: [ |
1911 | - Action { |
1912 | - objectName: "contactCallKnownAction" |
1913 | - visible: participants.length == 1 && messages.phoneAccount |
1914 | - iconName: "call-start" |
1915 | - text: i18n.tr("Call") |
1916 | - onTriggered: { |
1917 | - Qt.inputMethod.hide() |
1918 | - // FIXME: support other things than just phone numbers |
1919 | - Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) |
1920 | - } |
1921 | - }, |
1922 | - Action { |
1923 | - objectName: "contactProfileAction" |
1924 | - visible: !contactWatcher.isUnknown && participants.length == 1 && messages.phoneAccount |
1925 | - iconSource: "image://theme/contact" |
1926 | - text: i18n.tr("Contact") |
1927 | - onTriggered: { |
1928 | - mainView.showContactDetails(contactWatcher.contactId, null, null) |
1929 | - } |
1930 | - } |
1931 | - ] |
1932 | - } |
1933 | - ] |
1934 | - |
1935 | HistoryEventModel { |
1936 | id: eventModel |
1937 | type: HistoryThreadModel.EventTypeText |
1938 | @@ -958,7 +1058,7 @@ |
1939 | Item { |
1940 | id: screenTop |
1941 | anchors { |
1942 | - top: parent.top |
1943 | + top: pageHeader.bottom |
1944 | left: parent.left |
1945 | right: parent.right |
1946 | } |
1947 | @@ -1115,4 +1215,6 @@ |
1948 | } |
1949 | } |
1950 | } |
1951 | + |
1952 | + // FIXME: we need a bottom edge here somehow |
1953 | } |
1954 | |
1955 | === modified file 'src/qml/MessagesHeader.qml' |
1956 | --- src/qml/MessagesHeader.qml 2016-02-01 23:18:57 +0000 |
1957 | +++ src/qml/MessagesHeader.qml 2016-02-01 23:18:57 +0000 |
1958 | @@ -26,7 +26,12 @@ |
1959 | property string title: "" |
1960 | property string subtitle: "" |
1961 | |
1962 | - height: units.gu(7) |
1963 | + height: units.gu(8) |
1964 | + |
1965 | + anchors { |
1966 | + top: parent.top |
1967 | + topMargin: units.gu(1) |
1968 | + } |
1969 | |
1970 | Behavior on height { |
1971 | UbuntuNumberAnimation {} |
1972 | @@ -50,7 +55,7 @@ |
1973 | } |
1974 | verticalAlignment: Text.AlignVCenter |
1975 | |
1976 | - font.pixelSize: FontUtils.sizeToPixels("x-large") |
1977 | + font.pixelSize: FontUtils.sizeToPixels("large") |
1978 | elide: Text.ElideRight |
1979 | text: title |
1980 | } |
1981 | |
1982 | === added file 'src/qml/MessagingBottomEdge.qml' |
1983 | --- src/qml/MessagingBottomEdge.qml 1970-01-01 00:00:00 +0000 |
1984 | +++ src/qml/MessagingBottomEdge.qml 2016-02-01 23:18:57 +0000 |
1985 | @@ -0,0 +1,43 @@ |
1986 | +import QtQuick 2.0 |
1987 | +import Ubuntu.Components 1.3 |
1988 | + |
1989 | +BottomEdge { |
1990 | + id: bottomEdge |
1991 | + |
1992 | + function commitWithProperties(properties) { |
1993 | + _realPage = messagesComponent.createObject(null, properties) |
1994 | + commit() |
1995 | + } |
1996 | + |
1997 | + property var _realPage: null |
1998 | + |
1999 | + height: parent ? parent.height : 0 |
2000 | + hint.text: i18n.tr("+") |
2001 | + contentComponent: Item { |
2002 | + id: pageContent |
2003 | + implicitWidth: bottomEdge.width |
2004 | + implicitHeight: bottomEdge.height |
2005 | + children: bottomEdge._realPage |
2006 | + } |
2007 | + |
2008 | + Component.onCompleted: { |
2009 | + mainView.bottomEdge = bottomEdge |
2010 | + _realPage = messagesComponent.createObject(null) |
2011 | + } |
2012 | + |
2013 | + onCollapseCompleted: { |
2014 | + _realPage = messagesComponent.createObject(null) |
2015 | + } |
2016 | + |
2017 | + Component { |
2018 | + id: messagesComponent |
2019 | + |
2020 | + Messages { |
2021 | + anchors.fill: parent |
2022 | + onCancel: bottomEdge.collapse() |
2023 | + basePage: bottomEdge.parent |
2024 | + startedFromBottomEdge: true |
2025 | + } |
2026 | + } |
2027 | + |
2028 | +} |
2029 | |
2030 | === modified file 'src/qml/MessagingContactEditorPage.qml' |
2031 | --- src/qml/MessagingContactEditorPage.qml 2016-01-06 18:29:47 +0000 |
2032 | +++ src/qml/MessagingContactEditorPage.qml 2016-02-01 23:18:57 +0000 |
2033 | @@ -52,8 +52,12 @@ |
2034 | |
2035 | onContactSaved: { |
2036 | if (root.contactListPage) { |
2037 | - root.contactListPage.moveListToContact(contact) |
2038 | - root.contactListPage.phoneToAdd = "" |
2039 | + if (root.contactListPage.phoneToAdd !== "") { |
2040 | + mainStack.removePage(root.contactListPage) |
2041 | + } else { |
2042 | + root.contactListPage.moveListToContact(contact) |
2043 | + root.contactListPage.phoneToAdd = "" |
2044 | + } |
2045 | } |
2046 | } |
2047 | } |
2048 | |
2049 | === modified file 'src/qml/MessagingContactViewPage.qml' |
2050 | --- src/qml/MessagingContactViewPage.qml 2016-01-06 18:29:47 +0000 |
2051 | +++ src/qml/MessagingContactViewPage.qml 2016-02-01 23:18:57 +0000 |
2052 | @@ -42,12 +42,13 @@ |
2053 | var newDetail = Qt.createQmlObject(detailSourceTemplate, contact) |
2054 | if (newDetail) { |
2055 | contact.addDetail(newDetail) |
2056 | - pageStack.push(root.contactEditorPageURL, |
2057 | - { model: root.model, |
2058 | - contact: contact, |
2059 | - initialFocusSection: "phones", |
2060 | - newDetails: [newDetail], |
2061 | - contactListPage: root.contactListPage }) |
2062 | + mainStack.addPageToCurrentColumn(root, |
2063 | + root.contactEditorPageURL, |
2064 | + { model: root.model, |
2065 | + contact: contact, |
2066 | + initialFocusSection: "phones", |
2067 | + newDetails: [newDetail], |
2068 | + contactListPage: root.contactListPage }) |
2069 | root.addPhoneToContact = "" |
2070 | } else { |
2071 | console.warn("Fail to create phone number detail") |
2072 | @@ -61,7 +62,7 @@ |
2073 | iconName: "share" |
2074 | visible: root.editable |
2075 | onTriggered: { |
2076 | - pageStack.push(contactShareComponent, |
2077 | + pageStack.addComponentToCurrentColumnSync(root, contactShareComponent, |
2078 | { contactModel: root.model, |
2079 | contacts: [root.contact] }) |
2080 | } |
2081 | @@ -72,7 +73,7 @@ |
2082 | iconName: "edit" |
2083 | visible: root.editable |
2084 | onTriggered: { |
2085 | - pageStack.push(contactEditorPageURL, |
2086 | + pageStack.addFileToCurrentColumnSync(root, contactEditorPageURL, |
2087 | { model: root.model, |
2088 | contact: root.contact, |
2089 | contactListPage: root.contactListPage }) |
2090 | @@ -122,9 +123,9 @@ |
2091 | } else { |
2092 | Qt.openUrlExternally(("%1:%2").arg(action).arg(detail.value(0))) |
2093 | } |
2094 | - pageStack.pop() |
2095 | + pageStack.removePage(root) |
2096 | } |
2097 | - onContactRemoved: pageStack.pop() |
2098 | + onContactRemoved: pageStack.removePage(root) |
2099 | onContactFetched: { |
2100 | root.contact = contact |
2101 | if (root.active && root.addPhoneToContact != "") { |
2102 | |
2103 | === added file 'src/qml/MessagingPageLayout.qml' |
2104 | --- src/qml/MessagingPageLayout.qml 1970-01-01 00:00:00 +0000 |
2105 | +++ src/qml/MessagingPageLayout.qml 2016-02-01 23:18:57 +0000 |
2106 | @@ -0,0 +1,64 @@ |
2107 | +import QtQuick 2.0 |
2108 | +import Ubuntu.Components 1.3 |
2109 | + |
2110 | +AdaptivePageLayout { |
2111 | + id: layout |
2112 | + property var _pagesToRemove: [] |
2113 | + |
2114 | + function deleteInstances() { |
2115 | + for (var i in _pagesToRemove) { |
2116 | + if (_pagesToRemove[i].destroy) { |
2117 | + _pagesToRemove[i].destroy() |
2118 | + } |
2119 | + } |
2120 | + _pagesToRemove = [] |
2121 | + } |
2122 | + |
2123 | + function removePage(page) { |
2124 | + // check if this page was allocated dynamically and then remove it |
2125 | + for (var i in _pagesToRemove) { |
2126 | + if (_pagesToRemove[i] == page) { |
2127 | + _pagesToRemove[i].destroy() |
2128 | + _pagesToRemove.splice(i, 1) |
2129 | + break |
2130 | + } |
2131 | + } |
2132 | + removePages(page) |
2133 | + } |
2134 | + |
2135 | + function addFileToNextColumnSync(parentObject, resolvedUrl, properties) { |
2136 | + if (typeof(properties) === 'undefined') { |
2137 | + properties = {} |
2138 | + } |
2139 | + var page = Qt.createComponent(resolvedUrl).createObject(parentObject, properties) |
2140 | + layout.addPageToNextColumn(parentObject, page) |
2141 | + _pagesToRemove.push(page) |
2142 | + } |
2143 | + |
2144 | + function addFileToCurrentColumnSync(parentObject, resolvedUrl, properties) { |
2145 | + if (typeof(properties) === 'undefined') { |
2146 | + properties = {} |
2147 | + } |
2148 | + var page = Qt.createComponent(resolvedUrl).createObject(parentObject, properties) |
2149 | + layout.addPageToCurrentColumn(parentObject, page) |
2150 | + _pagesToRemove.push(page) |
2151 | + } |
2152 | + |
2153 | + function addComponentToNextColumnSync(parentObject, component, properties) { |
2154 | + if (typeof(properties) === 'undefined') { |
2155 | + properties = {} |
2156 | + } |
2157 | + var page = component.createObject(parentObject, properties) |
2158 | + layout.addPageToNextColumn(parentObject, page) |
2159 | + _pagesToRemove.push(page) |
2160 | + } |
2161 | + |
2162 | + function addComponentToCurrentColumnSync(parentObject, component, properties) { |
2163 | + if (typeof(properties) === 'undefined') { |
2164 | + properties = {} |
2165 | + } |
2166 | + var page = component.createObject(parentObject, properties) |
2167 | + layout.addPageToCurrentColumn(parentObject, page) |
2168 | + _pagesToRemove.push(page) |
2169 | + } |
2170 | +} |
2171 | |
2172 | === modified file 'src/qml/MultiRecipientInput.qml' |
2173 | --- src/qml/MultiRecipientInput.qml 2015-09-14 13:51:27 +0000 |
2174 | +++ src/qml/MultiRecipientInput.qml 2016-02-01 23:18:57 +0000 |
2175 | @@ -29,7 +29,7 @@ |
2176 | property variant recipients: [] |
2177 | property string searchString: "" |
2178 | signal clearSearch() |
2179 | - style: Theme.createStyleComponent("TextFieldStyle.qml", multiRecipientWidget) |
2180 | + styleName: "TextFieldStyle" |
2181 | clip: true |
2182 | height: contactFlow.height |
2183 | focus: activeFocus |
2184 | |
2185 | === modified file 'src/qml/NewRecipientPage.qml' |
2186 | --- src/qml/NewRecipientPage.qml 2015-11-16 22:16:06 +0000 |
2187 | +++ src/qml/NewRecipientPage.qml 2016-02-01 23:18:57 +0000 |
2188 | @@ -26,7 +26,6 @@ |
2189 | objectName: "newRecipientPage" |
2190 | |
2191 | property Item multiRecipient: null |
2192 | - property Item parentPage: null |
2193 | property string phoneToAdd: "" |
2194 | property QtObject contactIndex: null |
2195 | |
2196 | @@ -44,11 +43,39 @@ |
2197 | { |
2198 | multiRecipient.addRecipient(phoneNumber) |
2199 | multiRecipient.forceActiveFocus() |
2200 | - mainStack.pop() |
2201 | - } |
2202 | - |
2203 | - title: i18n.tr("Add recipient") |
2204 | - |
2205 | + mainStack.removePage(newRecipientPage) |
2206 | + } |
2207 | + |
2208 | + header: PageHeader { |
2209 | + id: pageHeader |
2210 | + |
2211 | + property alias leadingActions: leadingBar.actions |
2212 | + property alias trailingActions: trailingBar.actions |
2213 | + |
2214 | + title: i18n.tr("Add recipient") |
2215 | + leadingActionBar { |
2216 | + id: leadingBar |
2217 | + } |
2218 | + |
2219 | + trailingActionBar { |
2220 | + id: trailingBar |
2221 | + actions: [ |
2222 | + Action { |
2223 | + text: i18n.tr("Back") |
2224 | + iconName: "back" |
2225 | + onTriggered: { |
2226 | + mainStack.removePage(newRecipientPage) |
2227 | + newRecipientPage.destroy() |
2228 | + } |
2229 | + } |
2230 | + ] |
2231 | + } |
2232 | + } |
2233 | + |
2234 | + Sections { |
2235 | + id: headerSections |
2236 | + model: [i18n.tr("All"), i18n.tr("Favorites")] |
2237 | + } |
2238 | TextField { |
2239 | id: searchField |
2240 | |
2241 | @@ -69,11 +96,10 @@ |
2242 | |
2243 | state: "default" |
2244 | states: [ |
2245 | - PageHeadState { |
2246 | + State { |
2247 | id: defaultState |
2248 | - |
2249 | name: "default" |
2250 | - actions: [ |
2251 | + property list<QtObject> trailingActions: [ |
2252 | Action { |
2253 | text: i18n.tr("Search") |
2254 | iconName: "search" |
2255 | @@ -84,10 +110,11 @@ |
2256 | } |
2257 | } |
2258 | ] |
2259 | + |
2260 | PropertyChanges { |
2261 | - target: newRecipientPage.head |
2262 | - actions: defaultState.actions |
2263 | - sections.model: [i18n.tr("All"), i18n.tr("Favorites")] |
2264 | + target: pageHeader |
2265 | + trailingActions: defaultState.trailingActions |
2266 | + extension: headerSections |
2267 | } |
2268 | PropertyChanges { |
2269 | target: searchField |
2270 | @@ -95,27 +122,34 @@ |
2271 | visible: false |
2272 | } |
2273 | }, |
2274 | - PageHeadState { |
2275 | + State { |
2276 | id: searchingState |
2277 | - |
2278 | name: "searching" |
2279 | - backAction: Action { |
2280 | - iconName: "back" |
2281 | - text: i18n.tr("Cancel") |
2282 | - onTriggered: { |
2283 | - newRecipientPage.forceActiveFocus() |
2284 | - newRecipientPage.state = "default" |
2285 | - newRecipientPage.head.sections.selectedIndex = 0 |
2286 | + property list<QtObject> leadingActions: [ |
2287 | + Action { |
2288 | + iconName: "back" |
2289 | + text: i18n.tr("Cancel") |
2290 | + onTriggered: { |
2291 | + newRecipientPage.forceActiveFocus() |
2292 | + newRecipientPage.state = "default" |
2293 | + headerSections.selectedIndex = 0 |
2294 | + } |
2295 | } |
2296 | - } |
2297 | + ] |
2298 | |
2299 | PropertyChanges { |
2300 | - target: newRecipientPage.head |
2301 | - backAction: searchingState.backAction |
2302 | + target: pageHeader |
2303 | + leadingActions: searchingState.leadingActions |
2304 | + trailingActions: [] |
2305 | contents: searchField |
2306 | } |
2307 | |
2308 | PropertyChanges { |
2309 | + target: headerSections |
2310 | + visible: false |
2311 | + } |
2312 | + |
2313 | + PropertyChanges { |
2314 | target: searchField |
2315 | text: "" |
2316 | visible: true |
2317 | @@ -127,7 +161,7 @@ |
2318 | id: contactList |
2319 | objectName: "newRecipientList" |
2320 | anchors { |
2321 | - top: parent.top |
2322 | + top: pageHeader.bottom |
2323 | left: parent.left |
2324 | right: parent.right |
2325 | bottom: keyboard.top |
2326 | @@ -139,12 +173,14 @@ |
2327 | filterTerm: searchField.text |
2328 | onContactClicked: { |
2329 | if (newRecipientPage.phoneToAdd != "") { |
2330 | - mainView.addPhoneToContact(contact, |
2331 | + mainView.addPhoneToContact(newRecipientPage, |
2332 | + contact, |
2333 | newRecipientPage.phoneToAdd, |
2334 | newRecipientPage, |
2335 | contactList.listModel) |
2336 | } else { |
2337 | - mainView.showContactDetails(contact, |
2338 | + mainView.showContactDetails(newRecipientPage, |
2339 | + contact, |
2340 | newRecipientPage, |
2341 | contactList.listModel) |
2342 | } |
2343 | @@ -152,24 +188,20 @@ |
2344 | |
2345 | onAddNewContactClicked: { |
2346 | var newContact = ContactsJS.createEmptyContact(newRecipientPage.phoneToAdd, newRecipientPage) |
2347 | - pageStack.push(Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2348 | - { model: contactList.listModel, |
2349 | - contact: newContact, |
2350 | - initialFocusSection: (newRecipientPage.phoneToAdd != "" ? "phones" : "name"), |
2351 | - contactListPage: newRecipientPage |
2352 | - }) |
2353 | + mainStack.addFileToCurrentColumnSync(newRecipientPage, |
2354 | + Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2355 | + { model: contactList.listModel, |
2356 | + contact: newContact, |
2357 | + initialFocusSection: (newRecipientPage.phoneToAdd != "" ? "phones" : "name"), |
2358 | + contactListPage: newRecipientPage }) |
2359 | } |
2360 | } |
2361 | |
2362 | - // WORKAROUND: This is necessary to make the header visible from a bottom edge page |
2363 | Component.onCompleted: { |
2364 | - parentPage.active = false |
2365 | if (QTCONTACTS_PRELOAD_VCARD !== "") { |
2366 | contactList.listModel.importContacts("file://" + QTCONTACTS_PRELOAD_VCARD) |
2367 | } |
2368 | } |
2369 | - Component.onDestruction: parentPage.active = true |
2370 | - |
2371 | onActiveChanged: { |
2372 | if (active && (state === "searching")) { |
2373 | searchField.forceActiveFocus() |
2374 | |
2375 | === modified file 'src/qml/RegularMessageDelegate.qml' |
2376 | --- src/qml/RegularMessageDelegate.qml 2015-08-05 19:31:15 +0000 |
2377 | +++ src/qml/RegularMessageDelegate.qml 2016-02-01 23:18:57 +0000 |
2378 | @@ -57,7 +57,7 @@ |
2379 | selectionMode: root.isInSelectionMode |
2380 | accountLabel: { |
2381 | var account = telepathyHelper.accountForId(accountId) |
2382 | - if (account.type == AccountEntry.PhoneAccount || account.type == AccountEntry.MultimediaAccount) { |
2383 | + if (account && (account.type == AccountEntry.PhoneAccount || account.type == AccountEntry.MultimediaAccount)) { |
2384 | if (multiplePhoneAccounts) { |
2385 | return account.displayName |
2386 | } |
2387 | |
2388 | === modified file 'src/qml/SettingsPage.qml' |
2389 | --- src/qml/SettingsPage.qml 2015-09-14 13:51:27 +0000 |
2390 | +++ src/qml/SettingsPage.qml 2016-02-01 23:18:57 +0000 |
2391 | @@ -35,6 +35,11 @@ |
2392 | schema.id: "com.ubuntu.phone" |
2393 | } |
2394 | |
2395 | + header: PageHeader { |
2396 | + id: pageHeader |
2397 | + title: settingsPage.title |
2398 | + } |
2399 | + |
2400 | Component { |
2401 | id: settingDelegate |
2402 | Item { |
2403 | @@ -66,9 +71,25 @@ |
2404 | } |
2405 | |
2406 | ListView { |
2407 | - anchors.fill: parent |
2408 | + anchors { |
2409 | + top: pageHeader.bottom |
2410 | + left: parent.left |
2411 | + right: parent.right |
2412 | + bottom: parent.bottom |
2413 | + } |
2414 | model: settingsModel |
2415 | delegate: settingDelegate |
2416 | } |
2417 | + |
2418 | + Loader { |
2419 | + id: messagesBottomEdgeLoader |
2420 | + active: mainView.dualPanel |
2421 | + sourceComponent: MessagingBottomEdge { |
2422 | + id: messagesBottomEdge |
2423 | + parent: settingsPage |
2424 | + hint.text: "" |
2425 | + hint.height: 0 |
2426 | + } |
2427 | + } |
2428 | } |
2429 | |
2430 | |
2431 | === modified file 'src/qml/messaging-app.qml' |
2432 | --- src/qml/messaging-app.qml 2016-02-01 23:18:57 +0000 |
2433 | +++ src/qml/messaging-app.qml 2016-02-01 23:18:57 +0000 |
2434 | @@ -39,6 +39,10 @@ |
2435 | } |
2436 | property QtObject account: defaultPhoneAccount() |
2437 | property bool applicationActive: Qt.application.active |
2438 | + property alias mainStack: layout |
2439 | + property bool dualPanel: mainStack.columns > 1 |
2440 | + property QtObject bottomEdge: null |
2441 | + property bool composingNewMessage: bottomEdge.status === BottomEdge.Committed |
2442 | |
2443 | activeFocusOnPress: false |
2444 | |
2445 | @@ -58,7 +62,7 @@ |
2446 | return null |
2447 | } |
2448 | |
2449 | - function showContactDetails(contact, contactListPage, contactsModel) { |
2450 | + function showContactDetails(currentPage, contact, contactListPage, contactsModel) { |
2451 | var initialProperties = { "contactListPage": contactListPage, |
2452 | "model": contactsModel} |
2453 | |
2454 | @@ -68,21 +72,24 @@ |
2455 | initialProperties['contact'] = contact |
2456 | } |
2457 | |
2458 | - mainStack.push(Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2459 | - initialProperties) |
2460 | - } |
2461 | - |
2462 | - function addNewContact(phoneNumber, contactListPage) { |
2463 | - mainStack.push(Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2464 | - { "contactId": contactId, |
2465 | - "addPhoneToContact": phoneNumber, |
2466 | - "contactListPage": contactListPage }) |
2467 | - } |
2468 | - |
2469 | - function addPhoneToContact(contact, phoneNumber, contactListPage, contactsModel) { |
2470 | + mainStack.addFileToCurrentColumnSync(currentPage, |
2471 | + Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2472 | + initialProperties) |
2473 | + } |
2474 | + |
2475 | + function addNewContact(currentPage, phoneNumber, contactListPage) { |
2476 | + mainStack.addFileToCurrentColumnSync(currentPage, |
2477 | + Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2478 | + { "contactId": contactId, |
2479 | + "addPhoneToContact": phoneNumber, |
2480 | + "contactListPage": contactListPage }) |
2481 | + } |
2482 | + |
2483 | + function addPhoneToContact(currentPage, contact, phoneNumber, contactListPage, contactsModel) { |
2484 | if (contact === "") { |
2485 | - mainStack.push(Qt.resolvedUrl("NewRecipientPage.qml"), |
2486 | - { "phoneToAdd": phoneNumber }) |
2487 | + mainStack.addFileToCurrentColumnSync(currentPage, |
2488 | + Qt.resolvedUrl("NewRecipientPage.qml"), |
2489 | + { "phoneToAdd": phoneNumber }) |
2490 | } else { |
2491 | var initialProperties = { "addPhoneToContact": phoneNumber, |
2492 | "contactListPage": contactListPage, |
2493 | @@ -92,8 +99,9 @@ |
2494 | } else { |
2495 | initialProperties['contact'] = contact |
2496 | } |
2497 | - mainStack.push(Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2498 | - initialProperties) |
2499 | + mainStack.addFileToCurrentColumnSync(currentPage, |
2500 | + Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2501 | + initialProperties) |
2502 | } |
2503 | } |
2504 | |
2505 | @@ -119,6 +127,10 @@ |
2506 | threadModel.removeThreads(threads); |
2507 | } |
2508 | |
2509 | + function showBottomEdgePage(properties) { |
2510 | + bottomEdge.commitWithProperties(properties) |
2511 | + } |
2512 | + |
2513 | Connections { |
2514 | target: telepathyHelper |
2515 | // restore default bindings if any system settings changed |
2516 | @@ -141,13 +153,14 @@ |
2517 | Component.onCompleted: { |
2518 | i18n.domain = "messaging-app" |
2519 | i18n.bindtextdomain("messaging-app", i18nDirectory) |
2520 | + emptyStack() |
2521 | } |
2522 | |
2523 | Connections { |
2524 | target: telepathyHelper |
2525 | onSetupReady: { |
2526 | if (multiplePhoneAccounts && !telepathyHelper.defaultMessagingAccount && |
2527 | - !settings.mainViewIgnoreFirstTimeDialog && mainStack.depth === 1) { |
2528 | + !settings.mainViewIgnoreFirstTimeDialog && mainPage.displayedThreadIndex < 0) { |
2529 | PopupUtils.open(Qt.createComponent("Dialogs/NoDefaultSIMCardDialog.qml").createObject(mainView)) |
2530 | } |
2531 | } |
2532 | @@ -187,7 +200,7 @@ |
2533 | var properties = {} |
2534 | emptyStack() |
2535 | properties["sharedAttachmentsTransfer"] = transfer |
2536 | - mainStack.currentPage.showBottomEdgePage(Qt.resolvedUrl("Messages.qml"), properties) |
2537 | + mainView.showBottomEdgePage(properties) |
2538 | } |
2539 | } |
2540 | |
2541 | @@ -211,15 +224,21 @@ |
2542 | } |
2543 | |
2544 | function emptyStack() { |
2545 | - while (mainStack.depth !== 1 && mainStack.depth !== 0) { |
2546 | - mainStack.pop() |
2547 | + mainStack.removePage(mainPage) |
2548 | + layout.deleteInstances() |
2549 | + showEmptyState() |
2550 | + } |
2551 | + |
2552 | + function showEmptyState() { |
2553 | + if (mainStack.columns > 1) { |
2554 | + layout.addComponentToNextColumnSync(mainStack.primaryPage, emptyStatePageComponent) |
2555 | } |
2556 | } |
2557 | |
2558 | function startNewMessage() { |
2559 | var properties = {} |
2560 | emptyStack() |
2561 | - mainStack.currentPage.showBottomEdgePage(Qt.resolvedUrl("Messages.qml")) |
2562 | + mainView.showBottomEdgePage(properties) |
2563 | } |
2564 | |
2565 | function startChat(identifiers, text, accountId) { |
2566 | @@ -261,9 +280,26 @@ |
2567 | if (typeof(accountId)!=='undefined') { |
2568 | properties["accountId"] = accountId |
2569 | } |
2570 | + |
2571 | emptyStack() |
2572 | - mainStack.push(Qt.resolvedUrl("Messages.qml"), properties) |
2573 | - } |
2574 | + // FIXME: AdaptivePageLayout takes a really long time to create pages, |
2575 | + // so we create manually and push that |
2576 | + mainStack.addComponentToNextColumnSync(mainPage, messagesWithBottomEdge, properties) |
2577 | + } |
2578 | + |
2579 | + InputInfo { |
2580 | + id: inputInfo |
2581 | + } |
2582 | + |
2583 | + // WORKAROUND: Due the missing feature on SDK, they can not detect if |
2584 | + // there is a mouse attached to device or not. And this will cause the |
2585 | + // bootom edge component to not work correct on desktop. |
2586 | + Binding { |
2587 | + target: QuickUtils |
2588 | + property: "mouseAttached" |
2589 | + value: inputInfo.hasMouse |
2590 | + } |
2591 | + |
2592 | |
2593 | Connections { |
2594 | target: UriHandler |
2595 | @@ -274,11 +310,60 @@ |
2596 | } |
2597 | } |
2598 | |
2599 | - |
2600 | - PageStack { |
2601 | - id: mainStack |
2602 | - |
2603 | - objectName: "mainStack" |
2604 | - Component.onCompleted: mainStack.push(Qt.resolvedUrl("MainPage.qml")) |
2605 | + Component { |
2606 | + id: messagesWithBottomEdge |
2607 | + |
2608 | + Messages { |
2609 | + id: messages |
2610 | + height: mainPage.height |
2611 | + |
2612 | + Component.onCompleted: mainPage._messagesPage = messages |
2613 | + Loader { |
2614 | + id: messagesBottomEdgeLoader |
2615 | + active: mainView.dualPanel |
2616 | + sourceComponent: MessagingBottomEdge { |
2617 | + id: messagesBottomEdge |
2618 | + parent: messages |
2619 | + hint.text: "" |
2620 | + hint.height: 0 |
2621 | + } |
2622 | + } |
2623 | + } |
2624 | + } |
2625 | + |
2626 | + Component { |
2627 | + id: emptyStatePageComponent |
2628 | + Page { |
2629 | + id: emptyStatePage |
2630 | + |
2631 | + EmptyState { |
2632 | + labelVisible: mainPage.isEmpty |
2633 | + } |
2634 | + |
2635 | + header: PageHeader { } |
2636 | + |
2637 | + Loader { |
2638 | + id: bottomEdgeLoader |
2639 | + sourceComponent: MessagingBottomEdge { |
2640 | + parent: emptyStatePage |
2641 | + } |
2642 | + } |
2643 | + } |
2644 | + } |
2645 | + |
2646 | + MessagingPageLayout { |
2647 | + id: layout |
2648 | + anchors.fill: parent |
2649 | + primaryPage: MainPage { |
2650 | + id: mainPage |
2651 | + } |
2652 | + |
2653 | + onColumnsChanged: { |
2654 | + // we only have things to do here in case no thread is selected |
2655 | + if (mainPage.displayedThreadIndex < 0) { |
2656 | + layout.removePage(mainPage) |
2657 | + showEmptyState() |
2658 | + } |
2659 | + } |
2660 | } |
2661 | } |
2662 | |
2663 | === modified file 'tests/autopilot/messaging_app/emulators.py' |
2664 | --- tests/autopilot/messaging_app/emulators.py 2015-11-04 19:57:45 +0000 |
2665 | +++ tests/autopilot/messaging_app/emulators.py 2016-02-01 23:18:57 +0000 |
2666 | @@ -78,14 +78,14 @@ |
2667 | |
2668 | def click_header_action(self, action): |
2669 | """Click the action 'action' on the header""" |
2670 | - self.get_header().click_action_button(action) |
2671 | + action = self.wait_select_single(objectName='%s_button' % action) |
2672 | + self.pointing_device.click_object(action) |
2673 | |
2674 | # messages page |
2675 | def get_messages_page(self): |
2676 | """Return messages with objectName messagesPage""" |
2677 | |
2678 | - return self.wait_select_single("Messages", objectName="messagesPage", |
2679 | - active=True) |
2680 | + return self.wait_select_single("Messages", objectName="messagesPage") |
2681 | |
2682 | def get_newmessage_textfield(self): |
2683 | """Return TextField with objectName newPhoneNumberField""" |
2684 | @@ -116,40 +116,12 @@ |
2685 | |
2686 | return self.get_messages_page().select_single(objectName='sendButton') |
2687 | |
2688 | - def get_toolbar_back_button(self): |
2689 | - """Return toolbar button with objectName back_toolbar_button""" |
2690 | - |
2691 | - return self.select_single( |
2692 | - objectName='back_toolbar_button' |
2693 | - ) |
2694 | - |
2695 | - def get_toolbar_select_messages_button(self): |
2696 | - """Return toolbar button with objectName selectMessagesButton""" |
2697 | - |
2698 | - return self.select_single( |
2699 | - objectName='selectMessagesButton' |
2700 | - ) |
2701 | - |
2702 | def get_contact_list_view(self): |
2703 | """Returns the ContactListView object""" |
2704 | return self.select_single( |
2705 | objectName='newRecipientList' |
2706 | ) |
2707 | |
2708 | - def get_toolbar_contact_profile_button(self): |
2709 | - """Return toolbar button with objectName contactProfileButton""" |
2710 | - |
2711 | - return self.select_single( |
2712 | - objectName='contactProfileButton' |
2713 | - ) |
2714 | - |
2715 | - def get_toolbar_contact_call_button(self): |
2716 | - """Return toolbar button with objectName contactCallButton""" |
2717 | - |
2718 | - return self.select_single( |
2719 | - objectName='contactCallButton' |
2720 | - ) |
2721 | - |
2722 | def get_dialog_buttons(self, visible=True): |
2723 | """Return DialogButtons |
2724 | |
2725 | @@ -213,7 +185,7 @@ |
2726 | |
2727 | def start_new_message(self): |
2728 | """Reveal the bottom edge page to start composing a new message""" |
2729 | - self.get_main_page().reveal_bottom_edge_page() |
2730 | + self.reveal_bottom_edge_page() |
2731 | |
2732 | def enable_messages_selection_mode(self): |
2733 | """Enable the selection mode on the messages page by pressing and |
2734 | @@ -338,38 +310,30 @@ |
2735 | |
2736 | def open_settings_page(self): |
2737 | self.click_threads_header_settings() |
2738 | - return self.wait_select_single(SettingsPage) |
2739 | + settings_page = self.wait_select_single(SettingsPage) |
2740 | + settings_page.active.waitFor(True) |
2741 | + return settings_page |
2742 | |
2743 | def get_swipe_item_demo(self): |
2744 | return self.wait_select_single( |
2745 | 'SwipeItemDemo', objectName='swipeItemDemo', parentActive=True) |
2746 | |
2747 | - |
2748 | -class PageWithBottomEdge(MainView): |
2749 | - """An emulator class that makes it easy to interact with the bottom edge |
2750 | - swipe page""" |
2751 | - def __init__(self, *args): |
2752 | - super(PageWithBottomEdge, self).__init__(*args) |
2753 | - |
2754 | def reveal_bottom_edge_page(self): |
2755 | """Bring the bottom edge page to the screen""" |
2756 | - self.bottomEdgePageLoaded.wait_for(True) |
2757 | try: |
2758 | - action_item = self.wait_select_single(objectName='bottomEdgeTip') |
2759 | - start_x = (action_item.globalRect.x + |
2760 | - (action_item.globalRect.width * 0.5)) |
2761 | - start_y = (action_item.globalRect.y + |
2762 | - (action_item.height * 0.5)) |
2763 | + start_x = (self.globalRect.x + |
2764 | + (self.globalRect.width * 0.5)) |
2765 | + start_y = (self.globalRect.y + self.height) |
2766 | stop_y = start_y - (self.height * 0.7) |
2767 | self.pointing_device.drag(start_x, start_y, |
2768 | start_x, stop_y, rate=2) |
2769 | - self.isReady.wait_for(True) |
2770 | + self.composingNewMessage.wait_for(True) |
2771 | except StateNotFoundError: |
2772 | logger.error('BottomEdge element not found.') |
2773 | raise |
2774 | |
2775 | |
2776 | -class MainPage(PageWithBottomEdge): |
2777 | +class MainPage(MainView): |
2778 | """Autopilot helper for the Main Page.""" |
2779 | |
2780 | def get_thread_count(self): |
2781 | |
2782 | === modified file 'tests/qml/CMakeLists.txt' |
2783 | --- tests/qml/CMakeLists.txt 2016-02-01 23:18:57 +0000 |
2784 | +++ tests/qml/CMakeLists.txt 2016-02-01 23:18:57 +0000 |
2785 | @@ -36,7 +36,8 @@ |
2786 | |
2787 | add_test(${TEST} ${XVFB_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} |
2788 | -input ${CMAKE_CURRENT_SOURCE_DIR} |
2789 | - -import ${CMAKE_BINARY_DIR}/src) |
2790 | + -import ${CMAKE_BINARY_DIR}/src |
2791 | + -import ${UNITY8_QML_PATH}) |
2792 | |
2793 | # make qml files visible in QtCreator |
2794 | file(GLOB_RECURSE NON_COMPILED_FILES *.qml) |
2795 | |
2796 | === modified file 'tests/qml/tst_MessagesView.qml' |
2797 | --- tests/qml/tst_MessagesView.qml 2015-11-19 00:34:45 +0000 |
2798 | +++ tests/qml/tst_MessagesView.qml 2016-02-01 23:18:57 +0000 |
2799 | @@ -129,16 +129,21 @@ |
2800 | var senderId = "1234567" |
2801 | var stack = findChild(mainViewLoader, "mainStack") |
2802 | tryCompare(mainViewLoader.item, 'applicationActive', true) |
2803 | - tryCompare(stack, 'depth', 1) |
2804 | // if messaging-app has no account set, it will not try to get the thread from history |
2805 | // and instead will generate the list of participants, take advantage of that |
2806 | var account = mainViewLoader.item.account |
2807 | mainViewLoader.item.account = null |
2808 | mainViewLoader.item.startChat(senderId, "") |
2809 | mainViewLoader.item.account = account |
2810 | - tryCompare(stack, 'depth', 2) |
2811 | - mainViewLoader.item.applicationActive = false |
2812 | - var messageList = findChild(mainViewLoader, "messageList") |
2813 | + var messageList |
2814 | + while (true) { |
2815 | + messageList = findChild(mainViewLoader, "messageList") |
2816 | + if (messageList) { |
2817 | + break |
2818 | + } |
2819 | + wait(200) |
2820 | + } |
2821 | + |
2822 | messageList.listModel = messagesModel |
2823 | tryCompare(messageList, 'count', 2) |
2824 | compare(messageAcknowledgeSpy.count, 0) |
FAILED: Continuous integration, rev:449 jenkins. qa.ubuntu. com/job/ messaging- app-ci/ 685/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 4994/console jenkins. qa.ubuntu. com/job/ messaging- app-vivid- i386-ci/ 198/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 4991/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/messaging- app-ci/ 685/rebuild
http://