Unity 8
PanelMenuPage.qml
1 /*
2  * Copyright 2013-2016 Canonical Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 import QtQuick 2.4
18 import Ubuntu.Components 1.3
19 import Ubuntu.Components.ListItems 1.3 as ListItems
20 import Unity.Indicators 0.1 as Indicators
21 import "../Components"
22 import "Indicators"
23 
24 PageStack {
25  id: root
26 
27  property var submenuIndex: undefined
28  property QtObject menuModel: null
29  property Component factory
30 
31  Connections {
32  id: dynamicChanges
33  target: root.menuModel
34  property bool ready: false
35 
36  // fix async creation with signal from model before it's finished.
37  onRowsInserted: {
38  if (submenuIndex !== undefined && first <= submenuIndex) {
39  reset(true);
40  }
41  }
42  onRowsRemoved: {
43  if (submenuIndex !== undefined && first <= submenuIndex) {
44  reset(true);
45  }
46  }
47  onModelReset: {
48  if (root.submenuIndex !== undefined) {
49  reset(true);
50  }
51  }
52  }
53 
54  Component.onCompleted: {
55  reset(true);
56  dynamicChanges.ready = true;
57  }
58 
59  function reset(clearModel) {
60  if (clearModel) {
61  clear();
62  var model = root.submenuIndex == undefined ? menuModel : menuModel.submenu(root.submenuIndex)
63  if (model) {
64  push(pageComponent, { "menuModel": model });
65  }
66  } else if (root.currentPage) {
67  root.currentPage.reset();
68  }
69  }
70 
71  Component {
72  id: pageComponent
73  Page {
74  id: page
75 
76  property alias menuModel: listView.model
77  property alias title: backLabel.title
78  property bool isSubmenu: false
79 
80  function reset() {
81  listView.positionViewAtBeginning();
82  }
83 
84  property QtObject factory: root.factory.createObject(page, { menuModel: page.menuModel } )
85 
86  header: PageHeader {
87  id: backLabel
88  visible: page.isSubmenu
89  leadingActionBar.actions: [
90  Action {
91  iconName: "back"
92  text: i18n.tr("Back")
93  onTriggered: {
94  root.pop();
95  }
96  }
97  ]
98  }
99 
100  ListView {
101  id: listView
102  objectName: "listView"
103 
104  anchors {
105  top: page.isSubmenu ? backLabel.bottom : parent.top
106  left: parent.left
107  right: parent.right
108  bottom: parent.bottom
109  bottomMargin: Qt.inputMethod.visible ? (Qt.inputMethod.keyboardRectangle.height - root.anchors.bottomMargin) : 0
110 
111  Behavior on bottomMargin {
112  NumberAnimation {
113  duration: 175
114  easing.type: Easing.OutQuad
115  }
116  }
117  // TODO - does ever frame.
118  onBottomMarginChanged: {
119  listView.positionViewAtIndex(listView.currentIndex, ListView.End)
120  }
121  }
122 
123  // Don't load all the delegates (only max of 3 pages worth -1/0/+1)
124  cacheBuffer: Math.max(height * 3, units.gu(70))
125 
126  // Only allow flicking if the content doesn't fit on the page
127  interactive: contentHeight > height
128 
129  property int selectedIndex: -1
130  property bool blockCurrentIndexChange: false
131  // for count = 0
132  onCountChanged: {
133  if (count == 0 && selectedIndex != -1) {
134  selectedIndex = -1;
135  }
136  }
137  // for highlight following
138  onSelectedIndexChanged: {
139  if (currentIndex != selectedIndex) {
140  var blocked = blockCurrentIndexChange;
141  blockCurrentIndexChange = true;
142 
143  currentIndex = selectedIndex;
144 
145  blockCurrentIndexChange = blocked;
146  }
147  }
148  // for item addition/removal
149  onCurrentIndexChanged: {
150  if (!blockCurrentIndexChange) {
151  if (selectedIndex != -1 && selectedIndex != currentIndex) {
152  selectedIndex = currentIndex;
153  }
154  }
155  }
156 
157  Connections {
158  target: listView.model ? listView.model : null
159  onRowsAboutToBeRemoved: {
160  // track current item deletion.
161  if (listView.selectedIndex >= first && listView.selectedIndex <= last) {
162  listView.selectedIndex = -1;
163  }
164  }
165  }
166 
167  delegate: Loader {
168  id: loader
169  objectName: "menuItem" + index
170  width: ListView.view.width
171  visible: status == Loader.Ready
172 
173  property int modelIndex: index
174  sourceComponent: page.factory.load(model)
175 
176  onLoaded: {
177  if (item.hasOwnProperty("selected")) {
178  item.selected = listView.selectedIndex == index;
179  }
180  if (item.hasOwnProperty("menuSelected")) {
181  item.menuSelected.connect(function() { listView.selectedIndex = index; });
182  }
183  if (item.hasOwnProperty("menuDeselected")) {
184  item.menuDeselected.connect(function() { listView.selectedIndex = -1; });
185  }
186  if (item.hasOwnProperty("menuData")) {
187  item.menuData = Qt.binding(function() { return model; });
188  }
189  if (item.hasOwnProperty("menuIndex")) {
190  item.menuIndex = Qt.binding(function() { return modelIndex; });
191  }
192  if (item.hasOwnProperty("clicked")) {
193  item.clicked.connect(function() {
194  if (model.hasSubmenu) {
195  page.menuModel.aboutToShow(modelIndex);
196  root.push(pageComponent, {
197  "isSubmenu": true,
198  "title": model.label.replace(/_|&/, ""),
199  "menuModel": page.menuModel.submenu(modelIndex)
200  });
201  }
202  });
203  }
204  }
205 
206  Binding {
207  target: item ? item : null
208  property: "objectName"
209  value: model.action
210  }
211 
212  // TODO: Fixes lp#1243146
213  // This is a workaround for a Qt bug. https://bugreports.qt-project.org/browse/QTBUG-34351
214  Connections {
215  target: listView
216  onSelectedIndexChanged: {
217  if (loader.item && loader.item.hasOwnProperty("selected")) {
218  loader.item.selected = listView.selectedIndex == index;
219  }
220  }
221  }
222  }
223  }
224  }
225  }
226 }