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