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