on xenial help sign in
added support for count emblems and progress overlays on the launcher.
78832edMichael Zanetti
Launcher/LauncherDelegate.qml+73−7
@@ -21,7 +21,8 @@ Item {
21 id: root
21 id: root
22
22
23 property string iconName
23 property string iconName
24−
24+ property int count: -1
25+ property int progress: -1
25 property bool highlighted: false
26 property bool highlighted: false
26 property real maxAngle: 0
27 property real maxAngle: 0
27 property bool inverted: false
28 property bool inverted: false
@@ -38,6 +39,11 @@ Item {
38 property real itemOpacity: 1
39 property real itemOpacity: 1
39 property real brightness: 0
40 property real brightness: 0
40
41
42+ onIconNameChanged: shaderEffectSource.scheduleUpdate();
43+ onCountChanged: shaderEffectSource.scheduleUpdate();
44+ onProgressChanged: shaderEffectSource.scheduleUpdate();
45+ onHighlightedChanged: shaderEffectSource.scheduleUpdate();
46+
41 Item {
47 Item {
42 id: iconItem
48 id: iconItem
43 width: parent.itemWidth
49 width: parent.itemWidth
@@ -54,9 +60,7 @@ Item {
54 id: iconImage
60 id: iconImage
55 sourceSize.width: iconShape.width
61 sourceSize.width: iconShape.width
56 sourceSize.height: iconShape.height
62 sourceSize.height: iconShape.height
57− source: "../graphics/applicationIcons/" + iconName + ".png"
63+ source: "../graphics/applicationIcons/" + root.iconName + ".png"
58− property string iconName: root.iconName
59− onIconNameChanged: shaderEffectSource.scheduleUpdate();
60 }
64 }
61 }
65 }
62
66
@@ -64,11 +68,73 @@ Item {
64 id: overlayHighlight
68 id: overlayHighlight
65 anchors.centerIn: iconItem
69 anchors.centerIn: iconItem
66 rotation: inverted ? 180 : 0
70 rotation: inverted ? 180 : 0
67− source: isSelected ? "graphics/selected.sci" : "graphics/non-selected.sci"
71+ source: root.highlighted ? "graphics/selected.sci" : "graphics/non-selected.sci"
68 width: root.itemWidth + units.gu(0.5)
72 width: root.itemWidth + units.gu(0.5)
69 height: root.itemHeight + units.gu(0.5)
73 height: root.itemHeight + units.gu(0.5)
70− property bool isSelected: root.highlighted
74+ }
71− onIsSelectedChanged: shaderEffectSource.scheduleUpdate();
75+
76+ BorderImage {
77+ objectName: "countEmblem"
78+ anchors {
79+ right: parent.right
80+ top: parent.top
81+ margins: -units.dp(1)
82+ }
83+ width: Math.min(units.gu(7.5), Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
84+ height: units.gu(3)
85+ source: "graphics/notification.sci"
86+ visible: root.count > 0
87+
88+ Label {
89+ id: countLabel
90+ text: root.count
91+ anchors.centerIn: parent
92+ width: units.gu(6)
93+ horizontalAlignment: Text.AlignHCenter
94+ elide: Text.ElideRight
95+ color: "white"
96+ fontSize: "small"
97+ font.bold: true
98+ }
99+ }
100+
101+ BorderImage {
102+ id: progressOverlay
103+ objectName: "progressOverlay"
104+ anchors {
105+ left: iconItem.left
106+ right: iconItem.right
107+ bottom: iconItem.bottom
108+ leftMargin: units.gu(0.5)
109+ rightMargin: units.gu(0.5)
110+ bottomMargin: units.gu(0.5)
111+ }
112+ height: units.gu(1.5)
113+ visible: root.progress > -1
114+ source: "graphics/progressbar-trough.sci"
115+
116+ // For fill calculation we need to remove the 2 units of border defined in .sci file
117+ property int adjustedWidth: width - units.gu(2)
118+
119+ Item {
120+ anchors {
121+ left: parent.left
122+ top: parent.top
123+ bottom: parent.bottom
124+ }
125+ width: Math.min(100, root.progress) / 100 * parent.adjustedWidth + units.gu(1)
126+ clip: true
127+
128+ BorderImage {
129+ anchors {
130+ left: parent.left
131+ top: parent.top
132+ bottom: parent.bottom
133+ }
134+ width: progressOverlay.width
135+ source: "graphics/progressbar-fill.sci"
136+ }
137+ }
72 }
138 }
73 }
139 }
74
140
Launcher/LauncherPanel.qml+4
@@ -133,6 +133,8 @@ Item {
133 width: itemWidth
133 width: itemWidth
134 height: itemHeight
134 height: itemHeight
135 iconName: model.icon
135 iconName: model.icon
136+ count: model.count
137+ progress: model.progress
136 inverted: root.inverted
138 inverted: root.inverted
137 highlighted: dragging && index === root.highlightIndex
139 highlighted: dragging && index === root.highlightIndex
138 z: -Math.abs(offset)
140 z: -Math.abs(offset)
@@ -322,6 +324,8 @@ Item {
322 fakeDragItem.y = mouseY - yOffset + launcherListView.anchors.topMargin + launcherListView.topMargin
324 fakeDragItem.y = mouseY - yOffset + launcherListView.anchors.topMargin + launcherListView.topMargin
323 fakeDragItem.angle = selectedItem.angle * (root.inverted ? -1 : 1)
325 fakeDragItem.angle = selectedItem.angle * (root.inverted ? -1 : 1)
324 fakeDragItem.offset = selectedItem.offset * (root.inverted ? -1 : 1)
326 fakeDragItem.offset = selectedItem.offset * (root.inverted ? -1 : 1)
327+ fakeDragItem.count = LauncherModel.get(draggedIndex).count
328+ fakeDragItem.progress = LauncherModel.get(draggedIndex).progress
325 fakeDragItem.flatten()
329 fakeDragItem.flatten()
326 drag.target = fakeDragItem
330 drag.target = fakeDragItem
327
331
Launcher/graphics/notification@30.pngnew file

Binary file not shown.

Launcher/graphics/notification@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 36
3+border.bottom: 36
4+border.right: 36
5+source: notification@30.png
Launcher/graphics/progressbar-fill@30.pngnew file

Binary file not shown.

Launcher/graphics/progressbar-fill@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 16
3+border.bottom: 28
4+border.right: 36
5+source: progressbar-fill@30.png
Launcher/graphics/progressbar-trough@30.pngnew file

Binary file not shown.

Launcher/graphics/progressbar-trough@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 16
3+border.bottom: 26
4+border.right: 36
5+source: progressbar-trough@30.png
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+2−2
@@ -31,8 +31,8 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
31 m_pinned(false),
31 m_pinned(false),
32 m_running(false),
32 m_running(false),
33 m_recent(false),
33 m_recent(false),
34− m_progress(8),
34+ m_progress(-1),
35− m_count(8),
35+ m_count(0),
36 m_quickList(new MockQuickListModel(this))
36 m_quickList(new MockQuickListModel(this))
37{
37{
38
38
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+31−1
@@ -23,22 +23,32 @@
23MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
23MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
24{
24{
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
26+ item->setProgress(0);
26 m_list.append(item);
27 m_list.append(item);
27 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
28 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29+ item->setProgress(10);
28 m_list.append(item);
30 m_list.append(item);
29 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
31 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
32+ item->setProgress(50);
30 m_list.append(item);
33 m_list.append(item);
31 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
34 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
35+ item->setProgress(150);
32 m_list.append(item);
36 m_list.append(item);
33 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
37 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
38+ item->setCount(1);
34 m_list.append(item);
39 m_list.append(item);
35 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
40 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
41+ item->setCount(12);
36 m_list.append(item);
42 m_list.append(item);
37 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
43 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
44+ item->setCount(123);
38 m_list.append(item);
45 m_list.append(item);
39 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
46 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
47+ item->setCount(1234567890);
40 m_list.append(item);
48 m_list.append(item);
41 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
49 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50+ item->setProgress(50);
51+ item->setCount(5);
42 m_list.append(item);
52 m_list.append(item);
43 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
53 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
44 m_list.append(item);
54 m_list.append(item);
@@ -91,9 +101,29 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
91
101
92void MockLauncherModel::move(int oldIndex, int newIndex)
102void MockLauncherModel::move(int oldIndex, int newIndex)
93{
103{
104+ // Make sure its not moved outside the lists
105+ if (newIndex < 0) {
106+ newIndex = 0;
107+ }
108+ if (newIndex >= m_list.count()) {
109+ newIndex = m_list.count()-1;
110+ }
111+
112+ // Nothing to do?
113+ if (oldIndex == newIndex) {
114+ return;
115+ }
116+
117+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
118+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
119+ // adjust the model's index by +1 in case we're moving upwards
120+ int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
121+
94− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
122+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
95 m_list.move(oldIndex, newIndex);
123 m_list.move(oldIndex, newIndex);
96 endMoveRows();
124 endMoveRows();
125+
126+ pin(m_list.at(newIndex)->appId());
97}
127}
98
128
99void MockLauncherModel::pin(const QString &appId, int index)
129void MockLauncherModel::pin(const QString &appId, int index)
tests/qmltests/Launcher/tst_Launcher.qml+16
@@ -182,6 +182,22 @@ Item {
182 revealer.waitUntilLauncherDisappears();
182 revealer.waitUntilLauncherDisappears();
183 launcher.available = true;
183 launcher.available = true;
184 }
184 }
185+
186+ function test_countEmblems() {
187+ var launcherListView = findChild(launcher, "launcherListView");
188+ for (var i = 0; i < launcherListView.count; ++i) {
189+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
190+ compare(findChild(delegate, "countEmblem").visible, LauncherModel.get(i).count > 0)
191+ }
192+ }
193+
194+ function test_progressOverlays() {
195+ var launcherListView = findChild(launcher, "launcherListView");
196+ for (var i = 0; i < launcherListView.count; ++i) {
197+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
198+ compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
199+ }
200+ }
185 }
201 }
186
202
187 UT.UnityTestCase {
203 UT.UnityTestCase {