18 import Ubuntu.Components 0.1
19 import Ubuntu.Gestures 0.1
20 import Unity.Indicators 0.1 as Indicators
22 import "../Components"
23 import "../Components/ListItems"
29 property real openedHeight: units.gu(71)
30 property int panelHeight: units.gu(3)
31 property alias overFlowWidth: indicatorRow.overFlowWidth
32 property alias showAll: indicatorRow.showAll
34 property string profile: indicatorProfile
36 readonly
property real hintValue: panelHeight + menuContent.headerHeight
37 readonly
property int lockThreshold: openedHeight / 2
38 property bool fullyOpened: height == openedHeight
39 property bool partiallyOpened: height > panelHeight && !fullyOpened
40 property bool contentEnabled:
true
41 property bool initalizeItem:
true
42 readonly
property alias content: menuContent
43 property real unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
44 property bool enableHint:
true
45 property real showHintBottomMargin: 0
47 signal showTapped(point position)
51 showAnimation: StandardAnimation {
56 hideAnimation: StandardAnimation {
60 easing.type: Easing.OutCubic
63 onOpenedHeightChanged: {
64 if (showAnimation.running) {
65 showAnimation.restart();
66 }
else if (indicators.shown) {
67 height = openedHeight;
72 onHeightChanged: updateRevealProgressState(indicators.height - panelHeight - showHintBottomMargin,
true)
74 function updateRevealProgressState(revealProgress, enableRelease) {
75 if (!showAnimation.running && !hideAnimation.running) {
76 if (revealProgress === 0) {
77 indicators.state =
"initial";
78 }
else if (enableHint && revealProgress > 0 && revealProgress <= hintValue) {
79 indicators.state =
"hint";
80 }
else if ((!enableHint || revealProgress > hintValue) && revealProgress < lockThreshold) {
81 indicators.state =
"reveal";
82 }
else if (revealProgress >= lockThreshold && lockThreshold > 0) {
83 indicators.state =
"locked";
87 if (enableRelease && revealProgress === 0) {
88 menuContent.releaseContent();
92 function calculateCurrentItem(xValue, useBuffer) {
96 var distanceFromRightEdge;
97 var bufferExceeded =
false;
99 if (indicators.state ==
"commit" || indicators.state ==
"locked" || showAnimation.running || hideAnimation.running)
return;
106 var verticalProgress =
107 MathUtils.clamp((indicators.height - handle.height - hintValue) /
108 (lockThreshold - hintValue), 0, 1);
113 var verticalSpeed = Math.abs(yVelocityCalculator.calculate());
114 if (verticalSpeed >= 0.05 && !initalizeItem) {
123 var maxBufferThreshold = 0.5;
131 var effectiveBufferThreshold = maxBufferThreshold * verticalProgress;
133 rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
135 currentItem = indicatorRow.row.itemAt(rowCoordinates.x, 0);
137 itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
138 distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
139 if (currentItem != indicatorRow.currentItem) {
140 if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
141 bufferExceeded =
true;
143 if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
144 bufferExceeded =
true;
145 }
else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
146 bufferExceeded =
true;
149 if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItemIndex < 0 || indicatorRow.currentItem == null) {
150 indicatorRow.setCurrentItem(currentItem);
154 itemCoordinates = indicatorRow.row.mapToItem(indicatorRow.currentItem, rowCoordinates.x, 0);
155 distanceFromRightEdge = (indicatorRow.currentItem.width - itemCoordinates.x) / (indicatorRow.currentItem.width);
157 indicatorRow.currentItemOffset = 1 - (distanceFromRightEdge * 2);
158 }
else if (initalizeItem) {
159 indicatorRow.setDefaultItem();
160 indicatorRow.currentItemOffset = 0;
162 initalizeItem = indicatorRow.currentItem == null;
169 bottom: handle.bottom
176 id: visibleIndicators
181 objectName:
"menuContent"
186 top: indicatorRow.bottom
189 indicatorsModel: visibleIndicators.model
190 clip: !indicators.fullyOpened
191 activeHeader: indicators.state ==
"hint" || indicators.state ==
"reveal"
192 enabled: contentEnabled
199 bottom: parent.bottom
201 height: units.gu(0.5)
203 GradientStop { position: 0.0; color:
"transparent" }
204 GradientStop { position: 1.0; color:
"black" }
213 color: menuContent.backgroundColor
218 bottom: parent.bottom
220 height: Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0)
221 clip: height < handleImage.height
225 source:
"graphics/handle.sci"
230 bottom: parent.bottom
240 objectName:
"indicatorRow"
245 height: indicators.panelHeight
246 indicatorsModel: visibleIndicators.model
247 state: indicators.state
248 unitProgress: indicators.unitProgress
252 anchors.fill: indicatorRow
253 direction: Direction.Downwards
260 initalizeItem =
true;
261 updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue),
false);
262 indicators.calculateCurrentItem(touchX,
false);
264 indicators.state =
"commit";
265 indicatorRow.currentItemOffset = 0;
270 indicators.calculateCurrentItem(touchX,
true);
272 onTouchSceneYChanged: {
273 updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue),
false);
274 yVelocityCalculator.trackedPosition = touchSceneY;
280 target: showAnimation
282 if (showAnimation.running) {
283 indicators.state =
"commit";
284 indicatorRow.currentItemOffset = 0;
290 target: hideAnimation
292 if (hideAnimation.running) {
293 indicators.state =
"initial";
294 initalizeItem =
true;
295 indicatorRow.currentItemOffset = 0;
302 property bool enableIndexChangeSignal:
true
303 property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
308 onCurrentMenuIndexChanged: {
309 var oldActive = d.enableIndexChangeSignal;
310 if (!oldActive)
return;
311 d.enableIndexChangeSignal =
false;
313 indicatorRow.setCurrentItemIndex(menuContent.currentMenuIndex);
315 d.enableIndexChangeSignal = oldActive;
321 onCurrentItemIndexChanged: {
322 var oldActive = d.enableIndexChangeSignal;
323 if (!oldActive)
return;
324 d.enableIndexChangeSignal =
false;
326 menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex, fullyOpened || partiallyOpened);
328 d.enableIndexChangeSignal = oldActive;
333 target: d.activeDragHandle
335 indicators.calculateCurrentItem(d.activeDragHandle.touchX,
true);
337 onTouchSceneYChanged: {
338 yVelocityCalculator.trackedPosition = d.activeDragHandle.touchSceneY;
344 anchors.bottom: parent.bottom
346 anchors.bottomMargin: showHintBottomMargin
347 anchors.left: parent.left
348 anchors.right: parent.right
350 direction: Direction.Downwards
351 enabled: !indicators.shown && indicators.available
352 hintDisplacement: enableHint ? indicators.hintValue : 0
353 autoCompleteDragThreshold: maxTotalDragDistance / 2
355 maxTotalDragDistance: openedHeight - panelHeight
356 distanceThreshold: panelHeight
359 if (status === DirectionalDragArea.Recognized) {
360 menuContent.activateContent();
364 onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
370 direction: Direction.Upwards
371 enabled: indicators.shown && indicators.available
372 hintDisplacement: indicators.hintValue
373 autoCompleteDragThreshold: maxTotalDragDistance / 6
375 maxTotalDragDistance: openedHeight - panelHeight
379 AxisVelocityCalculator {
380 id: yVelocityCalculator
391 if (d.activeDragHandle) {
392 calculateCurrentItem(d.activeDragHandle.touchX,
false);
414 NumberAnimation {targets: [indicatorRow, menuContent];
property:
"y"; duration: 300; easing.type: Easing.OutCubic}
418 Component.onCompleted: initialise();
419 function initialise() {
420 visibleIndicators.load(profile);
421 indicatorRow.setDefaultItem();