Unity 8
 All Classes Functions
DashNavigationButton.qml
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.2
18 import Ubuntu.Components 1.1
19 
20 AbstractButton {
21  id: root
22  objectName: "dashNavigation"
23 
24  // Set by parent
25  property var scope: null
26  property var scopeStyle: null
27  property color foregroundColor: Theme.palette.normal.baseText
28  property bool isAltNavigation: false
29  property bool showDivider: false
30 
31  // Used by parent
32  readonly property var currentNavigation: scope && scope[hasNavigation] ? getNavigation(scope[currentNavigationId]) : null
33  readonly property alias listView: navigationListView
34  property bool showList: false
35 
36  // Internal
37  // Are we drilling down the tree or up?
38  property bool isGoingBack: false
39  readonly property string hasNavigation: isAltNavigation ? "hasAltNavigation" : "hasNavigation"
40  readonly property string currentNavigationId: isAltNavigation ? "currentAltNavigationId" : "currentNavigationId"
41  function getNavigation(navId) {
42  if (isAltNavigation) {
43  return scope.getAltNavigation(navId);
44  } else {
45  return scope.getNavigation(navId);
46  }
47  }
48 
49  visible: root.currentNavigation != null
50 
51  onClicked: {
52  navigationListView.updateMaxHeight();
53  root.showList = !root.showList;
54  }
55 
56  Label {
57  anchors.fill: parent
58  anchors.margins: units.gu(2)
59  anchors.rightMargin: units.gu(5)
60  verticalAlignment: Text.AlignVCenter
61  text: root.currentNavigation ? root.currentNavigation.label : ""
62  color: root.foregroundColor
63  elide: Text.ElideRight
64  maximumLineCount: 1
65  }
66 
67  Icon {
68  anchors.verticalCenter: parent.verticalCenter
69  anchors.right: parent.right
70  anchors.rightMargin: units.gu(2)
71  name: showList ? "up" : "down"
72  height: units.gu(2)
73  width: height
74  color: root.foregroundColor
75  }
76 
77  // navigationListView is outside root
78  ListView {
79  id: navigationListView
80  objectName: "navigationListView"
81  visible: height > 0
82  orientation: ListView.Horizontal
83  interactive: false
84  clip: root.width != windowWidth
85  model: ListModel {
86  id: navigationModel
87  // We have two roles
88  // navigationId: the navigation id of the navigation the list represents
89  // nullifyNavigation: overrides navigationId to be null
90  // This is used to "clear" the delegate when going "right" on the tree
91  }
92  anchors.top: root.bottom
93  property int maxHeight: -1
94  Component.onCompleted: updateMaxHeight();
95  function updateMaxHeight()
96  {
97  maxHeight = (windowHeight - mapToItem(null, 0, 0).y) - units.gu(8);
98  }
99  property int prevHeight: maxHeight
100  height: currentItem ? currentItem.height : maxHeight
101  onHeightChanged: {
102  if (root.showList) {
103  prevHeight = currentItem.desiredHeight;
104  }
105  }
106  highlightMoveDuration: UbuntuAnimation.FastDuration
107  delegate: DashNavigationList {
108  objectName: "navigation" + index
109  visible: height > 0
110  width: navigationListView.width
111  scopeStyle: root.scopeStyle
112  foregroundColor: root.foregroundColor
113  property real desiredHeight: {
114  if (root.showList) {
115  if (navigation && navigation.loaded && x == navigationListView.contentX)
116  {
117  navigationListView.updateMaxHeight();
118  return Math.min(implicitHeight, navigationListView.maxHeight);
119  } else {
120  return navigationListView.prevHeight;
121  }
122  } else {
123  return 0;
124  }
125  }
126  height: desiredHeight
127  navigation: (nullifyNavigation || !scope) ? null : getNavigation(navigationId)
128  currentNavigation: root.currentNavigation
129  onEnterNavigation: {
130  scope.setNavigationState(newNavigationId, isAltNavigation);
131  // We only need to add a new item to the model
132  // if we have children, otherwise just load it
133  if (hasChildren) {
134  isGoingBack = false;
135  navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
136  navigationListView.currentIndex++;
137  } else {
138  showList = false;
139  }
140  }
141  onGoBackToParentClicked: {
142  scope.setNavigationState(navigation.parentNavigationId, isAltNavigation);
143  isGoingBack = true;
144  navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", false);
145  navigationListView.currentIndex--;
146  }
147  onAllNavigationClicked: {
148  showList = false;
149  if (root.currentNavigation.parentNavigationId == navigation.navigationId) {
150  // For leaves we have to go to the parent too
151  scope.setNavigationState(root.currentNavigation.parentNavigationId, isAltNavigation);
152  }
153  }
154  }
155  onContentXChanged: {
156  if (contentX == width * navigationListView.currentIndex) {
157  if (isGoingBack) {
158  navigationModel.remove(navigationListView.currentIndex + 1);
159  } else {
160  navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
161  }
162  }
163  }
164  }
165 
166  Image {
167  anchors {
168  top: navigationListView.bottom
169  left: navigationListView.left
170  right: navigationListView.right
171  }
172  fillMode: Image.Stretch
173  source: "graphics/navigation_shadow.png"
174  visible: root.showList
175  }
176 
177  onScopeChanged: {
178  navigationModel.clear();
179  if (scope && scope[hasNavigation]) {
180  navigationModel.append({"navigationId": scope[currentNavigationId], "nullifyNavigation": false});
181  }
182  }
183 
184  Connections {
185  target: scope
186  // This is duplicated since we can't have something based on the dynamic hasNavigation string property
187  onHasNavigationChanged: {
188  if (!root.isAltNavigation) {
189  if (scope.hasNavigation) {
190  navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
191  } else {
192  navigationModel.clear();
193  }
194  }
195  }
196  onHasAltNavigationChanged: {
197  if (root.isAltNavigation) {
198  if (scope.hasAltNavigation) {
199  navigationModel.append({"navigationId": scope.currentAltNavigationId, "nullifyNavigation": false});
200  } else {
201  navigationModel.clear();
202  }
203  }
204  }
205  }
206 
207  InverseMouseArea {
208  anchors.fill: navigationListView
209  enabled: root.showList
210  onPressed: root.showList = false
211  }
212 
213  Rectangle {
214  visible: root.showDivider
215  anchors {
216  top: parent.top
217  topMargin: units.dp(1)
218  bottom: parent.bottom
219  left: parent.right
220  leftMargin: -units.dp(0.5)
221  }
222  width: units.dp(1)
223  color: root.foregroundColor
224  opacity: 0.2
225  }
226 }