on xenial help sign in
Replaced indicator menu listView with tabs view.
bc86943Nick Dedekind
Panel/Indicators.qml+3−6
@@ -177,15 +177,12 @@ Showable {
177 }
177 }
178 indicatorsModel: indicatorsModel
178 indicatorsModel: indicatorsModel
179 clip: !indicators.fullyOpened
179 clip: !indicators.fullyOpened
180+ activeHeader: indicators.state == "hint" || indicators.state == "reveal"
180
181
181 Connections {
182 Connections {
182 target: indicatorRow
183 target: indicatorRow
183− onCurrentItemIndexChanged: menuContent.updateCurrentMenu();
184+ onCurrentItemIndexChanged: {
184− }
185−
186− function updateCurrentMenu() {
187− if (currentMenuIndex != indicatorRow.currentItemIndex) {
188− currentMenuIndex = indicatorRow.currentItemIndex;
185+ menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex);
189 }
186 }
190 }
187 }
191
188
Panel/Indicators/NetworkIndicatorPage.qml+15−11
@@ -23,23 +23,27 @@ import Unity.Indicators.Network 0.1 as ICNetwork
23import Ubuntu.Components 0.1
23import Ubuntu.Components 0.1
24
24
25PageStack {
25PageStack {
26− id: _network
26+ id: network
27+ // Stops the search for a parent tree node.
28+ // We don't want it going up to the indicator tabs.
29+ // FIXME: need a better way of doing this.
30+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
31+ __propagated: null
27
32
28 property alias title: _mainPage.title
33 property alias title: _mainPage.title
29− property alias emptyText: _pluginItem.emptyText
34+ property alias emptyText: pluginItem.emptyText
30− property alias busType: _pluginItem.busType
35+ property alias busType: pluginItem.busType
31− property alias busName: _pluginItem.busName
36+ property alias busName: pluginItem.busName
32− property alias actionsObjectPath : _pluginItem.actionsObjectPath
37+ property alias actionsObjectPath : pluginItem.actionsObjectPath
33− property alias menuObjectPaths : _pluginItem.menuObjectPaths
38+ property alias menuObjectPaths : pluginItem.menuObjectPaths
34
39
35− __showHeader: false
36 anchors.fill: parent
40 anchors.fill: parent
37
41
38 Page {
42 Page {
39 id: _mainPage
43 id: _mainPage
40
44
41 Indicators.IndicatorPage {
45 Indicators.IndicatorPage {
42− id: _pluginItem
46+ id: pluginItem
43 anchors.fill: parent
47 anchors.fill: parent
44 }
48 }
45 }
49 }
@@ -63,17 +67,17 @@ PageStack {
63 function start()
67 function start()
64 {
68 {
65 push(_mainPage);
69 push(_mainPage);
66− _pluginItem.start();
70+ pluginItem.start();
67 }
71 }
68
72
69 function stop()
73 function stop()
70 {
74 {
71 clear();
75 clear();
72− _pluginItem.stop();
76+ pluginItem.stop();
73 }
77 }
74
78
75 function reset()
79 function reset()
76 {
80 {
77− _pluginItem.reset();
81+ pluginItem.reset();
78 }
82 }
79}
83}
Panel/MenuContent.qml+78−97
@@ -19,20 +19,31 @@ import Ubuntu.Components 0.1
19import Unity.Indicators 0.1 as Indicators
19import Unity.Indicators 0.1 as Indicators
20import "../Components"
20import "../Components"
21
21
22−Rectangle {
22+// FIXME : We dont want to use MainView.
23+// Need a regular Item which can have tabs with local header.
24+// https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
25+MainView {
23 id: content
26 id: content
24
27
25 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
26 property bool __contentActive: false
29 property bool __contentActive: false
27− property alias currentMenuIndex : menus.currentIndex
30+ readonly property int currentMenuIndex : tabs.selectedTabIndex
28− property color backgroundColor: "#221e1c" // FIXME not in palette yet
31+ backgroundColor: "#221e1c" // FIXME not in palette yet
29 property int contentReleaseInterval: 20000
32 property int contentReleaseInterval: 20000
33+ property bool activeHeader: false
30
34
31 width: units.gu(40)
35 width: units.gu(40)
32 height: units.gu(42)
36 height: units.gu(42)
33− color: backgroundColor
34
37
35− signal menuSelected(int index)
38+ function setCurrentMenuIndex(index) {
39+ if (currentMenuIndex !== index) {
40+ if (currentMenuIndex == -1) {
41+ tabs.tabBar.animate = false;
42+ }
43+ tabs.selectedTabIndex = index;
44+ tabs.tabBar.animate = true;
45+ }
46+ }
36
47
37 function activateContent() {
48 function activateContent() {
38 contentReleaseTimer.stop();
49 contentReleaseTimer.stop();
@@ -40,95 +51,75 @@ Rectangle {
40 }
51 }
41
52
42 function releaseContent() {
53 function releaseContent() {
43− if (__contentActive)
54+ if (__contentActive) {
44 contentReleaseTimer.restart();
55 contentReleaseTimer.restart();
56+ }
45 }
57 }
46
58
47− ListView {
59+ onActiveHeaderChanged: {
48− id: menus
60+ tabs.tabBar.selectionMode = activeHeader;
49− objectName: "menus"
61+ tabs.tabBar.alwaysSelectionMode = activeHeader;
50−
51− anchors.bottom: parent.bottom
52− anchors.top: header.bottom
53− width: parent.width
54−
55− spacing: units.gu(0.5)
56− enabled: opacity != 0
57− orientation: ListView.Horizontal
58− model: indicatorsModel
59− interactive: false
60− //FIXME: This will make sure that all plugins still alive
61− //check bug https://bugs.launchpad.net/manhattan/+bug/1088945 for more details
62− cacheBuffer: 2147483647
63−
64− delegate: Loader {
65− clip: true
66− property bool contentActive: content.__contentActive && menuActivator.content[index].active
67−
68− onContentActiveChanged: {
69− if (contentActive && item) {
70− item.start();
71− } else if (!contentActive && item) {
72− item.stop();
73− }
74− }
62+ }
75
63
76− width: menus.width
64+ Tabs {
77− height: menus.height
65+ id: tabs
78− source: pageSource
66+ objectName: "tabs"
79− onVisibleChanged: {
67+ anchors.fill: parent
80− // Reset the indicator states
68+ selectedTabIndex: -1
81− if (!visible && item && item["reset"]) {
69+
82− item.reset();
70+ Repeater {
83− }
71+ id: repeater
72+ model: indicatorsModel
73+ objectName: "menus"
74+
75+ // FIXME: This is needed because tabs dont handle repeaters well.
76+ // Due to the child ordering happening after child insertion.
77+ // QTBUG-32438
78+ onItemAdded: {
79+ parent.childrenChanged();
84 }
80 }
85− asynchronous: true
86
81
82+ Tab {
83+ id: tab
84+ objectName: model.identifier
85+ title: model.title
86+
87+ page: Page {
88+ Loader {
89+ clip: true
90+ anchors.fill: parent
91+ source: pageSource
92+ asynchronous: true
93+
94+ property bool contentActive: content.__contentActive
95+
96+ onContentActiveChanged: {
97+ if (contentActive && item) {
98+ item.start()
99+ } else if (!contentActive && item) {
100+ item.stop()
101+ }
102+ }
103+
104+ onVisibleChanged: {
105+ // Reset the indicator states
106+ if (!visible && item && item["reset"]) {
107+ item.reset()
108+ }
109+ }
110+
87− onLoaded: {
111+ onLoaded: {
88− for(var pName in indicatorProperties) {
112+ for(var pName in indicatorProperties) {
89− if (item.hasOwnProperty(pName)) {
113+ if (item.hasOwnProperty(pName)) {
90− item[pName] = indicatorProperties[pName];
114+ item[pName] = indicatorProperties[pName]
115+ }
116+ }
117+ if (contentActive && menus.visible) {
118+ item.start()
119+ }
120+ }
91 }
121 }
92 }
122 }
93− if (contentActive && menus.visible) {
94− item.start();
95− }
96− }
97−
98− // Need to use a binding because the handle height changes.
99− // FIXME: Dont know why we're using handle height (introduces dep).. Check with design about bottom margin
100− Binding {
101− target: item
102− property: "anchors.bottomMargin"
103− value: handle.height
104− }
105− }
106− }
107−
108− Rectangle {
109− id: header
110− objectName: "header"
111−
112− property alias title: pageHeader.text
113−
114− color: backgroundColor
115− opacity: pageHeader.opacity
116− anchors {
117− left: parent.left
118− right: parent.right
119− }
120− height: childrenRect.height
121−
122− PageHeader {
123− id: pageHeader
124− anchors {
125− left: parent.left
126− right: parent.right
127− }
128− text: {
129− if (indicatorsModel && menus.currentIndex >= 0 && menus.currentIndex < indicatorsModel.count)
130− return indicatorsModel.data(menus.currentIndex, Indicators.IndicatorsModelRole.Title);
131− return "";
132 }
123 }
133 }
124 }
134 }
125 }
@@ -137,16 +128,6 @@ Rectangle {
137 id: contentReleaseTimer
128 id: contentReleaseTimer
138
129
139 interval: contentReleaseInterval
130 interval: contentReleaseInterval
140− onTriggered: {
141− __contentActive = false;
131+ onTriggered: content.__contentActive = false
142− menuActivator.clear();
143− }
144− }
145−
146− Indicators.MenuContentActivator {
147− id: menuActivator
148− running: content.__contentActive
149− baseIndex: content.currentMenuIndex
150− count: indicatorsModel.count
151 }
132 }
152}
133}
tests/mocks/Unity/Indicators/IndicatorsModel.qml+1−1
@@ -64,7 +64,7 @@ ListModel {
64 isValid: "";
64 isValid: "";
65 }
65 }
66 ListElement {
66 ListElement {
67− identifier: "indicator-fake4";
67+ identifier: "indicator-fake5";
68 priority: 4
68 priority: 4
69 title: "Menu5";
69 title: "Menu5";
70 description: "";
70 description: "";
tests/qmltests/Panel/tst_MenuContent.qml+32−47
@@ -85,29 +85,29 @@ Item {
85
85
86 function activate_content(index)
86 function activate_content(index)
87 {
87 {
88− menuContent.currentMenuIndex = index
88+ menuContent.setCurrentMenuIndex(index)
89 }
89 }
90
90
91 function get_test_menu_objecName(index) {
91 function get_test_menu_objecName(index) {
92 return "menu_page"+(index+1);
92 return "menu_page"+(index+1);
93 }
93 }
94
94
95− property string test_menu_objectName : ""
95+ property string testTabObjectName : ""
96− function current_menu_equals_test_menu() {
96+
97+ function selected_tab_equals_test_tab() {
97− var current_loader = menu_content_test.findChild(menuContent, "menus").currentItem
98+ var currentTab = menu_content_test.findChild(menuContent, "tabs").selectedTab
98− if (current_loader == undefined) {
99+ if (currentTab == undefined) {
99− console.log("current_loader 'menus' undefined");
100+ console.log("selected tab undefined");
100 return false;
101 return false;
101 }
102 }
102
103
103− var menu = menu_content_test.findChild(menuContent, test_menu_objectName);
104+ var testTab = menu_content_test.findChild(menuContent, testTabObjectName);
104− if (menu == undefined) {
105+ if (testTab == undefined) {
105− console.log("test_menu " + test_menu_objectName + " undefined");
106+ console.log("test_tab " + testTabObjectName + " undefined");
106 return false;
107 return false;
107 }
108 }
108
109
109− // The parent of the menu will be the loader
110+ return testTab == currentTab;
110− return menu.parent == current_loader;
111 }
111 }
112
112
113 UT.UnityTestCase {
113 UT.UnityTestCase {
@@ -123,81 +123,66 @@ Item {
123
123
124 // Check that the correct menus are displayed for the requested item.
124 // Check that the correct menus are displayed for the requested item.
125 function test_show_menu() {
125 function test_show_menu() {
126− var menu_count = indicatorsModel.count;
126+ var menuCount = indicatorsModel.count;
127− verify(menu_count > 0, "Menu count should be greater than zero");
127+ verify(menuCount > 0, "Menu count should be greater than zero");
128
128
129− var menus = menu_content_test.findChild(menuContent, "menus")
129+ var tabs = menu_content_test.findChild(menuContent, "tabs")
130
130
131 // Loop over twice to test jump between last and first.
131 // Loop over twice to test jump between last and first.
132− for (var i = 0; i < menu_count*2; i++) {
132+ for (var i = 0; i < menuCount*2; i++) {
133
133
134− var menu_index = i%menu_count;
134+ var menuIndex = i%menuCount;
135
135
136− activate_content(menu_index);
136+ activate_content(menuIndex);
137− test_menu_objectName = get_test_menu_objecName(menu_index);
137+ testTabObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
138− compare(menus.currentIndex, menu_index, "Current menu index does not match selected menu index");
138+ compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
139− tryCompareFunction(current_menu_equals_test_menu, true);
139+ tryCompareFunction(selected_tab_equals_test_tab, true);
140 }
140 }
141 }
141 }
142
142
143 // Calling activateContent should call start on all menus
143 // Calling activateContent should call start on all menus
144 function test_activate_content() {
144 function test_activate_content() {
145− var menu_count = indicatorsModel.count;
145+ var menuCount = indicatorsModel.count;
146− verify(menu_count > 0, "Menu count should be greater than zero");
146+ verify(menuCount > 0, "Menu count should be greater than zero");
147
147
148 // Ensure all the menus are stopped first
148 // Ensure all the menus are stopped first
149 menuContent.__contentActive = false;
149 menuContent.__contentActive = false;
150− for (var i = 0; i < menu_count; i++) {
150+ for (var i = 0; i < menuCount; i++) {
151 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
151 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
152 }
152 }
153
153
154 // activate content the content to call stop.
154 // activate content the content to call stop.
155 menuContent.activateContent();
155 menuContent.activateContent();
156− for (var i = 0; i < menu_count; i++) {
156+ for (var i = 0; i < menuCount; i++) {
157 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
157 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
158 }
158 }
159 }
159 }
160
160
161 // Calling activateContent should call stop on all menus.
161 // Calling activateContent should call stop on all menus.
162 function test_release_content() {
162 function test_release_content() {
163− var menu_count = indicatorsModel.count;
163+ var menuCount = indicatorsModel.count;
164− verify(menu_count > 0, "Menu count should be greater than zero");
164+ verify(menuCount > 0, "Menu count should be greater than zero");
165
165
166 // Ensure all the menus are started first
166 // Ensure all the menus are started first
167 menuContent.__contentActive = true;
167 menuContent.__contentActive = true;
168− for (var i = 0; i < menu_count; i++) {
168+ for (var i = 0; i < menuCount; i++) {
169 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
169 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
170 }
170 }
171 // release the content to call stop.
171 // release the content to call stop.
172 menuContent.releaseContent();
172 menuContent.releaseContent();
173− for (var i = 0; i < menu_count; i++) {
173+ for (var i = 0; i < menuCount; i++) {
174 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
174 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
175 }
175 }
176 }
176 }
177
177
178− // Header title should be the same as the item
179− function test_menu_header() {
180− var menu_count = indicatorsModel.count;
181− verify(menu_count > 0, "Menu count should be greater than zero");
182−
183− var header = findChild(menuContent, "header")
184−
185− for (var i = 0; i < menu_count; i++) {
186− activate_content(i);
187−
188− var menu_title = indicatorsModel.get(i).title;
189− compare(header.title, menu_title, "Header doesnt match menu title for menu " + i);
190− }
191− }
192−
193 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
178 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
194 function test_multi_activate() {
179 function test_multi_activate() {
195− var menu_count = indicatorsModel.count;
180+ var menuCount = indicatorsModel.count;
196− verify(menu_count > 0, "Menu count should be greater than zero");
181+ verify(menuCount > 0, "Menu count should be greater than zero");
197
182
198 for (var i = 0; i < 100; i++) {
183 for (var i = 0; i < 100; i++) {
199− activate_content(i % menu_count);
184+ activate_content(i % menuCount);
200− compare(menuContent.currentMenuIndex, i%menu_count);
185+ compare(menuContent.currentMenuIndex, i%menuCount);
201 }
186 }
202 wait(100);
187 wait(100);
203 }
188 }