2 * Copyright (C) 2013 Canonical, Ltd.
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.
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.
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/>.
18import Ubuntu.Components 0.1
19import Ubuntu.Gestures 0.1
20import Unity.Indicators 0.1 as Indicators
23import "../Components/ListItems"
28 property real openedHeight: units.gu(71)
29 property int panelHeight: units.gu(3)
30 property bool pinnedMode: true //should be set true if indicators menu can cover whole screen
31 property alias overFlowWidth: indicatorRow.overFlowWidth
32 property alias showAll: indicatorRow.showAll
34 property int hintValue
35 readonly property int lockThreshold: openedHeight / 2
36 property bool fullyOpened: height == openedHeight
37 property bool partiallyOpened: height > panelHeight && !fullyOpened
38 property real visualBottom: Math.max(y+height, y+indicatorRow.y+indicatorRow.height)
39 property bool contentEnabled: true
40 property bool initalizeItem: true
41 readonly property alias content: menuContent
42 property real unitProgress: 0.0
44 // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
45 // use its own values. Need to ask design about this.
46 showAnimation: StandardAnimation {
50 easing.type: Easing.OutCubic
52 // Re-size if we've changed the openHeight while shown.
54 if (indicators.shown) {
60 hideAnimation: StandardAnimation {
64 easing.type: Easing.OutCubic
68 onHeightChanged: updateRevealProgressState(indicators.height - panelHeight, true)
70 function updateRevealProgressState(revealProgress, enableRelease) {
71 if (!showAnimation.running && !hideAnimation.running) {
72 if (revealProgress === 0) {
73 indicators.state = "initial";
74 } else if (revealProgress > 0 && revealProgress <= hintValue) {
75 indicators.state = "hint";
76 } else if (revealProgress > hintValue && revealProgress < lockThreshold) {
77 indicators.state = "reveal";
78 } else if (revealProgress >= lockThreshold && lockThreshold > 0) {
79 indicators.state = "locked";
83 if (enableRelease && revealProgress === 0) {
84 menuContent.releaseContent();
88 function calculateCurrentItem(xValue, useBuffer) {
92 var distanceFromRightEdge;
93 var bufferExceeded = false;
95 if (indicators.state == "commit" || indicators.state == "locked" || showAnimation.running || hideAnimation.running) return;
98 If user drags the indicator handle bar down a distance hintValue or less, this is 0.
99 If bar is dragged down a distance greater than or equal to lockThreshold, this is 1.
100 Otherwise it contains the bar's location as a fraction of the distance between hintValue (is 0) and lockThreshold (is 1).
102 var verticalProgress =
103 MathUtils.clamp((indicators.height - handle.height - hintValue) /
104 (lockThreshold - hintValue), 0, 1);
107 Vertical velocity check. Don't change the indicator if we're moving too quickly.
109 var verticalSpeed = Math.abs(yVelocityCalculator.calculate());
110 if (verticalSpeed >= 0.05 && !initalizeItem) {
115 Percentage of an indicator icon's width the user's press can stray horizontally from the
116 focused icon before we change focus to another icon. E.g. a value of 0.5 means you must
117 go right a distance of half an icon's width before focus moves to the icon on the right
119 var maxBufferThreshold = 0.5;
122 To help users find the indicator of their choice while opening the indicators, we add logic to add a
123 left/right buffer to each icon so it is harder for the focus to be moved accidentally to another icon,
124 as the user moves their finger down, but yet allows them to switch indicator if they want.
125 This buffer is wider the further the user's finger is from the top of the screen.
127 var effectiveBufferThreshold = maxBufferThreshold * verticalProgress;
129 rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
130 // get the current delegate
131 currentItem = indicatorRow.row.childAt(rowCoordinates.x, 0);
133 itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
134 distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
135 if (currentItem != indicatorRow.currentItem) {
136 if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
137 bufferExceeded = true;
139 if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
140 bufferExceeded = true;
141 } else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
142 bufferExceeded = true;
145 if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItemIndex < 0 || indicatorRow.currentItem == null) {
146 indicatorRow.currentItem = currentItem;
149 // need to re-init the distanceFromRightEdge for offset calculation
150 itemCoordinates = indicatorRow.row.mapToItem(indicatorRow.currentItem, rowCoordinates.x, 0);
151 distanceFromRightEdge = (indicatorRow.currentItem.width - itemCoordinates.x) / (indicatorRow.currentItem.width);
153 indicatorRow.currentItemOffset = 1 - (distanceFromRightEdge * 2);
155 indicatorRow.setDefaultItem();
156 indicatorRow.currentItemOffset = 0;
158 initalizeItem = indicatorRow.currentItem == null;
165 bottom: handle.bottom
171 VerticalThinDivider {
174 topMargin: panelHeight
175 bottom: handle.bottom
176 right: indicators.left
179 source: "graphics/VerticalDivider.png"
184 objectName: "menuContent"
189 top: indicatorRow.bottom
192 indicatorsModel: indicatorsModel
193 clip: !indicators.fullyOpened
194 activeHeader: indicators.state == "hint" || indicators.state == "reveal"
195 enabled: contentEnabled
197 //small shadow gradient at bottom of menu
202 bottom: parent.bottom
204 height: units.gu(0.5)
206 GradientStop { position: 0.0; color: "transparent" }
207 GradientStop { position: 1.0; color: "black" }
216 color: menuContent.backgroundColor
221 bottom: parent.bottom
224 clip: height < handleImage.height
226 function get_height() {
227 return Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0);
232 source: "graphics/handle.sci"
237 bottom: parent.bottom
240 MouseArea { //prevent clicks passing through
246 anchors.fill: indicatorRow
249 Indicators.IndicatorsModel {
255 objectName: "indicatorRow"
260 height: indicators.panelHeight
261 indicatorsModel: indicatorsModel
262 state: indicators.state
263 unitProgress: indicators.unitProgress
265 onVisibleIndicatorsChanged: {
266 // need to do it here so we can control sequence
267 if (visibleIndicators !== undefined) {
268 menuContent.visibleIndicators = visibleIndicators;
269 menuContent.setCurrentMenuIndex(currentItemIndex);
275 anchors.fill: indicatorRow
276 direction: Direction.Downwards
283 initalizeItem = true;
284 updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue), false);
285 indicators.calculateCurrentItem(touchX, false);
287 indicators.state = "commit";
292 indicators.calculateCurrentItem(touchX, true);
294 onTouchSceneYChanged: {
295 updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue), false);
296 yVelocityCalculator.trackedPosition = touchSceneY;
302 target: showAnimation
304 if (showAnimation.running) {
305 indicators.state = "commit";
311 target: hideAnimation
313 if (hideAnimation.running) {
314 indicators.state = "initial";
315 initalizeItem = true;
316 menuContent.animateNextMenuChange = false;
323 property bool enableIndexChangeSignal: true
324 property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
329 onCurrentMenuIndexChanged: {
330 var oldActive = d.enableIndexChangeSignal;
331 if (!oldActive) return;
332 d.enableIndexChangeSignal = false;
334 indicatorRow.setCurrentItem(menuContent.currentMenuIndex);
336 d.enableIndexChangeSignal = oldActive;
342 onCurrentItemIndexChanged: {
343 var oldActive = d.enableIndexChangeSignal;
344 if (!oldActive) return;
345 d.enableIndexChangeSignal = false;
347 menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex);
349 d.enableIndexChangeSignal = oldActive;
352 // connections to the active drag handle
354 target: d.activeDragHandle
356 indicators.calculateCurrentItem(d.activeDragHandle.touchX, true);
358 onTouchSceneYChanged: {
359 yVelocityCalculator.trackedPosition = d.activeDragHandle.touchSceneY;
365 anchors.bottom: parent.bottom
366 // go beyond parent so that it stays reachable, at the top of the screen.
367 anchors.bottomMargin: pinnedMode ? 0 : -panelHeight
368 anchors.left: parent.left
369 anchors.right: parent.right
371 direction: Direction.Downwards
372 enabled: !indicators.shown && indicators.available
373 hintDisplacement: pinnedMode ? indicators.hintValue : 0
374 autoCompleteDragThreshold: maxTotalDragDistance / 2
376 maxTotalDragDistance: openedHeight - panelHeight
377 distanceThreshold: pinnedMode ? 0 : units.gu(3)
380 if (status === DirectionalDragArea.Recognized) {
381 menuContent.activateContent();
388 direction: Direction.Upwards
389 enabled: indicators.shown && indicators.available
390 hintDisplacement: indicators.hintValue
391 autoCompleteDragThreshold: maxTotalDragDistance / 6
393 maxTotalDragDistance: openedHeight - panelHeight
397 AxisVelocityCalculator {
398 id: yVelocityCalculator
408 target: indicatorRow;
409 y: pinnedMode ? 0 : panelHeight
413 if (d.activeDragHandle) {
414 calculateCurrentItem(d.activeDragHandle.touchX, false);
436 NumberAnimation {targets: [indicatorRow, menuContent]; property: "y"; duration: 300; easing.type: Easing.OutCubic}
440 Component.onCompleted: initialise();
441 function initialise() {
442 indicatorsModel.load();
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list