on xenial help sign in
Initial implementation of scopes overview
0aef820Albert Astals
qml/Components/PageHeader.qml+1
@@ -32,6 +32,7 @@ Item {
32 property ListModel searchHistory: SearchHistoryModel
32 property ListModel searchHistory: SearchHistoryModel
33 property alias searchQuery: searchTextField.text
33 property alias searchQuery: searchTextField.text
34 property bool searchInProgress: false
34 property bool searchInProgress: false
35+ property alias showSignatureLine: bottomBorder.visible
35
36
36 property alias bottomItem: bottomContainer.children
37 property alias bottomItem: bottomContainer.children
37
38
qml/Dash/Dash.qml+2
@@ -29,6 +29,8 @@ Showable {
29 property string showScopeOnLoaded: "clickscope"
29 property string showScopeOnLoaded: "clickscope"
30 property real contentScale: 1.0
30 property real contentScale: 1.0
31
31
32+ property alias overviewHandleHeight: dashContent.overviewHandleHeight
33+
32 function setCurrentScope(scopeId, animate, reset) {
34 function setCurrentScope(scopeId, animate, reset) {
33 var scopeIndex = filteredScopes.findFirst(Scopes.RoleId, scopeId)
35 var scopeIndex = filteredScopes.findFirst(Scopes.RoleId, scopeId)
34
36
qml/Dash/DashContent.qml+72−4
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Ubuntu.Gestures 0.1
19import Unity 0.2
20import Unity 0.2
20import Utils 0.1
21import Utils 0.1
21import "../Components"
22import "../Components"
@@ -26,6 +27,7 @@ Item {
26 property var model: null
27 property var model: null
27 property var scopes: null
28 property var scopes: null
28 readonly property alias currentIndex: dashContentList.currentIndex
29 readonly property alias currentIndex: dashContentList.currentIndex
30+ property alias overviewHandleHeight: overviewDragHandle.height
29
31
30 signal scopeLoaded(string scopeId)
32 signal scopeLoaded(string scopeId)
31 signal gotoScope(string scopeId)
33 signal gotoScope(string scopeId)
@@ -38,9 +40,12 @@ Item {
38 Connections {
40 Connections {
39 target: scopes
41 target: scopes
40 onLoadedChanged: {
42 onLoadedChanged: {
43+ if (scopes.loaded) {
41− if (scopes.loaded && set_current_index != undefined) {
44+ if (set_current_index != undefined) {
42− setCurrentScopeAtIndex(set_current_index[0], set_current_index[1], set_current_index[2]);
45+ setCurrentScopeAtIndex(set_current_index[0], set_current_index[1], set_current_index[2]);
43− set_current_index = undefined;
46+ set_current_index = undefined;
47+ }
48+ scopesOverview.scope = scopes.getScope("scopesOverview");
44 }
49 }
45 }
50 }
46 }
51 }
@@ -80,11 +85,41 @@ Item {
80 dashContentList.currentItem.theScope.closeScope(scope)
85 dashContentList.currentItem.theScope.closeScope(scope)
81 }
86 }
82
87
88+ QtObject {
89+ id: overviewController
90+
91+ property bool accepted: false
92+ property bool enableAnimation: false
93+ property real progress: 0
94+ }
95+
96+ ScopesOverview {
97+ id: scopesOverview
98+ anchors.fill: parent
99+ scope: scopes ? scopes.getScope("scopesOverview") : null
100+ progress: overviewController.progress
101+ onDone: {
102+ overviewController.enableAnimation = true;
103+ overviewController.progress = 0;
104+ overviewController.accepted = false;
105+ }
106+ }
107+
83 Item {
108 Item {
84 id: dashContentListHolder
109 id: dashContentListHolder
85
110
86 anchors.fill: parent
111 anchors.fill: parent
87
112
113+ enabled: scale == 1
114+
115+ Image {
116+ anchors.fill: parent
117+ source: parent.width > parent.height ? "graphics/paper_landscape.png" : "graphics/paper_portrait.png"
118+ fillMode: Image.PreserveAspectCrop
119+ horizontalAlignment: Image.AlignRight
120+ verticalAlignment: Image.AlignTop
121+ }
122+
88 ListView {
123 ListView {
89 id: dashContentList
124 id: dashContentList
90 objectName: "dashContentList"
125 objectName: "dashContentList"
@@ -103,7 +138,7 @@ Item {
103 // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
138 // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
104 cacheBuffer: 1073741823
139 cacheBuffer: 1073741823
105 onMovementStarted: currentItem.item.showHeader();
140 onMovementStarted: currentItem.item.showHeader();
106− clip: parent.x != 0
141+ clip: parent.x != 0 || parent.scale != 1
107
142
108 // If the number of items is less than the current index, then need to reset to another item.
143 // If the number of items is less than the current index, then need to reset to another item.
109 onCountChanged: {
144 onCountChanged: {
@@ -161,5 +196,38 @@ Item {
161 Component.onDestruction: active = false
196 Component.onDestruction: active = false
162 }
197 }
163 }
198 }
199+
200+ scale: 1 - overviewController.progress * 0.6
201+ Behavior on scale {
202+ id: dashContentScaleAnimation
203+ enabled: overviewController.enableAnimation
204+ UbuntuNumberAnimation { }
205+ }
206+ }
207+
208+ EdgeDragArea {
209+ id: overviewDragHandle
210+ direction: Direction.Upwards
211+ distanceThreshold: units.gu(20)
212+ enabled: !overviewController.accepted || dragging
213+
214+ anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
215+ height: units.gu(2)
216+
217+ onSceneDistanceChanged: {
218+ overviewController.enableAnimation = false;
219+ overviewController.progress = Math.min(1, sceneDistance / distanceThreshold);
220+ }
221+
222+ onStatusChanged: {
223+ if (status == DirectionalDragArea.Recognized) {
224+ overviewController.accepted = true;
225+ }
226+ }
227+
228+ onDraggingChanged: {
229+ overviewController.enableAnimation = true;
230+ overviewController.progress = overviewController.accepted ? 1 : 0;
231+ }
164 }
232 }
165}
233}
qml/Dash/ScopesOverview.qmlnew file+186
@@ -0,0 +1,186 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import "../Components"
20+
21+Item {
22+ id: root
23+
24+ property real progress: 0
25+ property var scope: null
26+
27+ signal done
28+
29+ Image {
30+ anchors.fill: parent
31+ source: parent.width > parent.height ? "graphics/paper_landscape.png" : "graphics/paper_portrait.png"
32+ fillMode: Image.PreserveAspectCrop
33+ horizontalAlignment: Image.AlignRight
34+ verticalAlignment: Image.AlignTop
35+ }
36+
37+ Rectangle {
38+ color: "black"
39+ anchors.fill: parent
40+ opacity: 0.6
41+ }
42+
43+ Item {
44+ id: topBar
45+ width: parent.width
46+ height: childrenRect.height
47+
48+ y: {
49+ if (root.progress < 0.5) {
50+ return -height;
51+ } else {
52+ return -height + (root.progress - 0.5) * height * 2;
53+ }
54+ }
55+
56+ PageHeader {
57+ id: pageHeader
58+ width: parent.width
59+ clip: true
60+ title: i18n.tr("Manage Dash")
61+ scopeStyle: QtObject {
62+ property string headerLogo: ""
63+ property color foreground: "white"
64+ }
65+ showSignatureLine: false
66+ searchEntryEnabled: true
67+// searchInProgress: scope ? scope.searchInProgress : false
68+ }
69+
70+ Item {
71+ id: tabBarHolder
72+
73+ property int currentTab: 0
74+
75+ anchors {
76+ left: parent.left
77+ right: parent.right
78+ top: pageHeader.bottom
79+ margins: units.gu(2)
80+ }
81+ height: units.gu(4)
82+
83+ AbstractButton {
84+ id: tab1
85+ height: parent.height
86+ width: parent.width / 2
87+ Rectangle {
88+ anchors.fill: parent
89+ color: tabBarHolder.currentTab == 0 ? "white" : "transparent"
90+ radius: units.dp(10)
91+ }
92+ Label {
93+ anchors.centerIn: parent
94+ text: i18n.tr("Favourites")
95+ color: tabBarHolder.currentTab == 0 ? "black" : "white"
96+ }
97+ onClicked: tabBarHolder.currentTab = 0
98+ }
99+ AbstractButton {
100+ id: tab2
101+ x: width
102+ height: parent.height
103+ width: parent.width / 2
104+ Rectangle {
105+ anchors.fill: parent
106+ color: tabBarHolder.currentTab == 1 ? "white" : "transparent"
107+ radius: units.dp(10)
108+ }
109+ Label {
110+ anchors.centerIn: parent
111+ text: i18n.tr("All")
112+ color: tabBarHolder.currentTab == 1 ? "black" : "white"
113+ }
114+ onClicked: tabBarHolder.currentTab = 1
115+ }
116+ Rectangle {
117+ id: centerPiece
118+ width: units.dp(10)
119+ height: parent.height
120+ color: "white"
121+ x: tabBarHolder.currentTab == 1 ? tab2.x : tab2.x - width
122+ }
123+ Rectangle {
124+ id: border
125+ anchors.fill: parent
126+ radius: units.dp(10)
127+ color: "transparent"
128+ border.color: centerPiece.color
129+ border.width: units.dp(1)
130+ }
131+ }
132+ }
133+
134+ Repeater {
135+ id: middleItems
136+ model: scope ? scope.categories : null
137+ delegate: Loader {
138+ source: {
139+ if (index == 0) return "ScopesOverviewFavourites.qml";
140+ else if (index == 1) return "ScopesOverviewAll.qml";
141+ else {
142+ console.log("WARNING: ScopesOverview scope is not supposed to have more than 2 categories");
143+ return "";
144+ }
145+ }
146+ }
147+ }
148+
149+ Rectangle {
150+ id: bottomBar
151+ color: "black"
152+ height: units.gu(6)
153+ width: parent.width
154+ y: {
155+ if (root.progress < 0.5) {
156+ return parent.height;
157+ } else {
158+ return parent.height - (root.progress - 0.5) * height * 2;
159+ }
160+ }
161+
162+ AbstractButton {
163+ width: Math.max(label.width + units.gu(2), units.gu(10))
164+ height: units.gu(4)
165+ anchors {
166+ left: parent.left
167+ leftMargin: units.gu(2)
168+ verticalCenter: parent.verticalCenter
169+ }
170+ Rectangle {
171+ anchors.fill: parent
172+ border.color: "white"
173+ border.width: units.dp(1)
174+ radius: units.dp(10)
175+ color: parent.pressed ? "gray" : "transparent"
176+ }
177+ Label {
178+ id: label
179+ anchors.centerIn: parent
180+ text: i18n.tr("Done")
181+ color: parent.pressed ? "black" : "white"
182+ }
183+ onClicked: root.done()
184+ }
185+ }
186+}
qml/Shell.qml+2−8
@@ -124,14 +124,6 @@ FocusScope {
124 opacity: dash.disappearingAnimationProgress
124 opacity: dash.disappearingAnimationProgress
125 }
125 }
126
126
127− Image {
128− anchors.fill: dash
129− source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
130− fillMode: Image.PreserveAspectCrop
131− horizontalAlignment: Image.AlignRight
132− verticalAlignment: Image.AlignTop
133− }
134−
135 Dash {
127 Dash {
136 id: dash
128 id: dash
137 objectName: "dash"
129 objectName: "dash"
@@ -141,6 +133,8 @@ FocusScope {
141 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
133 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
142 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
134 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
143
135
136+ overviewHandleHeight: shell.edgeSize
137+
144 anchors {
138 anchors {
145 fill: parent
139 fill: parent
146 topMargin: panel.panelHeight
140 topMargin: panel.panelHeight
tests/mocks/Unity/fake_scope.cpp+2−2
@@ -24,7 +24,7 @@ Scope::Scope(QObject* parent) : Scope(QString(), QString(), false, parent)
24{
24{
25}
25}
26
26
27−Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent)
27+Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent, int nCategories)
28 : unity::shell::scopes::ScopeInterface(parent)
28 : unity::shell::scopes::ScopeInterface(parent)
29 , m_id(id)
29 , m_id(id)
30 , m_name(name)
30 , m_name(name)
@@ -33,7 +33,7 @@ Scope::Scope(QString const& id, QString const& name, bool visible, QObject* pare
33 , m_isActive(false)
33 , m_isActive(false)
34 , m_currentDeparment("root")
34 , m_currentDeparment("root")
35 , m_previewRendererName("preview-generic")
35 , m_previewRendererName("preview-generic")
36− , m_categories(new Categories(20, this))
36+ , m_categories(new Categories(nCategories, this))
37{
37{
38}
38}
39
39
tests/mocks/Unity/fake_scope.h+1−1
@@ -30,7 +30,7 @@ class Scope : public unity::shell::scopes::ScopeInterface
30
30
31public:
31public:
32 Scope(QObject* parent = 0);
32 Scope(QObject* parent = 0);
33− Scope(QString const& id, QString const& name, bool visible, QObject* parent = 0);
33+ Scope(QString const& id, QString const& name, bool visible, QObject* parent = 0, int nCategories = 20);
34
34
35 /* getters */
35 /* getters */
36 QString id() const override;
36 QString id() const override;
tests/mocks/Unity/fake_scopes.cpp+6−1
@@ -46,6 +46,7 @@ void Scopes::updateScopes()
46 addScope(new Scope("MockScope2", "Music", false, this));
46 addScope(new Scope("MockScope2", "Music", false, this));
47 addScope(new Scope("clickscope", "Apps", true, this));
47 addScope(new Scope("clickscope", "Apps", true, this));
48 addScope(new Scope("MockScope5", "Videos", true, this));
48 addScope(new Scope("MockScope5", "Videos", true, this));
49+ addScope(new Scope("scopesOverview", "Scopes Overview", false, this, 2));
49
50
50 if (!m_loaded) {
51 if (!m_loaded) {
51 m_loaded = true;
52 m_loaded = true;
@@ -109,8 +110,12 @@ unity::shell::scopes::ScopeInterface* Scopes::getScope(int row) const
109 return m_scopes[row];
110 return m_scopes[row];
110}
111}
111
112
112−unity::shell::scopes::ScopeInterface* Scopes::getScope(QString const&) const
113+unity::shell::scopes::ScopeInterface* Scopes::getScope(QString const &scope_id) const
113{
114{
115+ for (Scope *scope : m_scopes) {
116+ if (scope->id() == scope_id)
117+ return scope;
118+ }
114 return nullptr;
119 return nullptr;
115}
120}
116
121