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 bool pinnedMode:
true
32 property alias overFlowWidth: indicatorRow.overFlowWidth
33 property alias showAll: indicatorRow.showAll
35 property string profile: indicatorProfile
37 readonly
property real hintValue: panelHeight + menuContent.headerHeight
38 readonly
property int lockThreshold: openedHeight / 2
39 property bool fullyOpened: height == openedHeight
40 property bool partiallyOpened: height > panelHeight && !fullyOpened
41 property real visualBottom: Math.max(y+height, y+indicatorRow.y+indicatorRow.height)
42 property bool contentEnabled:
true
43 property bool initalizeItem:
true
44 readonly
property alias content: menuContent
45 property real unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
49 showAnimation: StandardAnimation {
53 easing.type: Easing.OutCubic
57 if (indicators.shown) {
63 hideAnimation: StandardAnimation {
67 easing.type: Easing.OutCubic
71 onHeightChanged: updateRevealProgressState(indicators.height - panelHeight,
true)
73 function updateRevealProgressState(revealProgress, enableRelease) {
74 if (!showAnimation.running && !hideAnimation.running) {
75 if (revealProgress === 0) {
76 indicators.state =
"initial";
77 }
else if (revealProgress > 0 && revealProgress <= hintValue) {
78 indicators.state =
"hint";
79 }
else if (revealProgress > hintValue && revealProgress < lockThreshold) {
80 indicators.state =
"reveal";
81 }
else if (revealProgress >= lockThreshold && lockThreshold > 0) {
82 indicators.state =
"locked";
86 if (enableRelease && revealProgress === 0) {
87 menuContent.releaseContent();
91 function calculateCurrentItem(xValue, useBuffer) {
95 var distanceFromRightEdge;
96 var bufferExceeded =
false;
98 if (indicators.state ==
"commit" || indicators.state ==
"locked" || showAnimation.running || hideAnimation.running)
return;
105 var verticalProgress =
106 MathUtils.clamp((indicators.height - handle.height - hintValue) /
107 (lockThreshold - hintValue), 0, 1);
112 var verticalSpeed = Math.abs(yVelocityCalculator.calculate());
113 if (verticalSpeed >= 0.05 && !initalizeItem) {
122 var maxBufferThreshold = 0.5;
130 var effectiveBufferThreshold = maxBufferThreshold * verticalProgress;
132 rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
134 currentItem = indicatorRow.row.itemAt(rowCoordinates.x, 0);
136 itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
137 distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
138 if (currentItem != indicatorRow.currentItem) {
139 if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
140 bufferExceeded =
true;
142 if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
143 bufferExceeded =
true;
144 }
else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
145 bufferExceeded =
true;
148 if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItemIndex < 0 || indicatorRow.currentItem == null) {
149 indicatorRow.setCurrentItem(currentItem);
153 itemCoordinates = indicatorRow.row.mapToItem(indicatorRow.currentItem, rowCoordinates.x, 0);
154 distanceFromRightEdge = (indicatorRow.currentItem.width - itemCoordinates.x) / (indicatorRow.currentItem.width);
156 indicatorRow.currentItemOffset = 1 - (distanceFromRightEdge * 2);
157 }
else if (initalizeItem) {
158 indicatorRow.setDefaultItem();
159 indicatorRow.currentItemOffset = 0;
161 initalizeItem = indicatorRow.currentItem == null;
168 bottom: handle.bottom
174 VerticalThinDivider {
177 topMargin: panelHeight
178 bottom: handle.bottom
179 right: indicators.left
182 source:
"graphics/VerticalDivider.png"
186 id: visibleIndicators
191 objectName:
"menuContent"
196 top: indicatorRow.bottom
199 indicatorsModel: visibleIndicators.model
200 clip: !indicators.fullyOpened
201 activeHeader: indicators.state ==
"hint" || indicators.state ==
"reveal"
202 enabled: contentEnabled
209 bottom: parent.bottom
211 height: units.gu(0.5)
213 GradientStop { position: 0.0; color:
"transparent" }
214 GradientStop { position: 1.0; color:
"black" }
223 color: menuContent.backgroundColor
228 bottom: parent.bottom
230 height: Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0)
231 clip: height < handleImage.height
235 source:
"graphics/handle.sci"
240 bottom: parent.bottom
249 anchors.fill: indicatorRow
254 objectName:
"indicatorRow"
259 height: indicators.panelHeight
260 indicatorsModel: visibleIndicators.model
261 state: indicators.state
262 unitProgress: indicators.unitProgress
266 anchors.fill: indicatorRow
267 direction: Direction.Downwards
274 initalizeItem =
true;
275 updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue),
false);
276 indicators.calculateCurrentItem(touchX,
false);
278 indicators.state =
"commit";
283 indicators.calculateCurrentItem(touchX,
true);
285 onTouchSceneYChanged: {
286 updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue),
false);
287 yVelocityCalculator.trackedPosition = touchSceneY;
293 target: showAnimation
295 if (showAnimation.running) {
296 indicators.state =
"commit";
297 indicatorRow.currentItemOffset = 0;
303 target: hideAnimation
305 if (hideAnimation.running) {
306 indicators.state =
"initial";
307 initalizeItem =
true;
308 indicatorRow.currentItemOffset = 0;
315 property bool enableIndexChangeSignal:
true
316 property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
321 onCurrentMenuIndexChanged: {
322 var oldActive = d.enableIndexChangeSignal;
323 if (!oldActive)
return;
324 d.enableIndexChangeSignal =
false;
326 indicatorRow.setCurrentItemIndex(menuContent.currentMenuIndex);
328 d.enableIndexChangeSignal = oldActive;
334 onCurrentItemIndexChanged: {
335 var oldActive = d.enableIndexChangeSignal;
336 if (!oldActive)
return;
337 d.enableIndexChangeSignal =
false;
339 menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex, fullyOpened || partiallyOpened);
341 d.enableIndexChangeSignal = oldActive;
346 target: d.activeDragHandle
348 indicators.calculateCurrentItem(d.activeDragHandle.touchX,
true);
350 onTouchSceneYChanged: {
351 yVelocityCalculator.trackedPosition = d.activeDragHandle.touchSceneY;
357 anchors.bottom: parent.bottom
359 anchors.bottomMargin: pinnedMode ? 0 : -panelHeight
360 anchors.left: parent.left
361 anchors.right: parent.right
363 direction: Direction.Downwards
364 enabled: !indicators.shown && indicators.available
365 hintDisplacement: pinnedMode ? indicators.hintValue : 0
366 autoCompleteDragThreshold: maxTotalDragDistance / 2
368 maxTotalDragDistance: openedHeight - panelHeight
369 distanceThreshold: pinnedMode ? 0 : units.gu(3)
372 if (status === DirectionalDragArea.Recognized) {
373 menuContent.activateContent();
380 direction: Direction.Upwards
381 enabled: indicators.shown && indicators.available
382 hintDisplacement: indicators.hintValue
383 autoCompleteDragThreshold: maxTotalDragDistance / 6
385 maxTotalDragDistance: openedHeight - panelHeight
389 AxisVelocityCalculator {
390 id: yVelocityCalculator
400 target: indicatorRow;
401 y: pinnedMode ? 0 : panelHeight
405 if (d.activeDragHandle) {
406 calculateCurrentItem(d.activeDragHandle.touchX,
false);
428 NumberAnimation {targets: [indicatorRow, menuContent];
property:
"y"; duration: 300; easing.type: Easing.OutCubic}
432 Component.onCompleted: initialise();
433 function initialise() {
434 visibleIndicators.load(profile);