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/>.
18
import Ubuntu.Components 0.1
19
import Ubuntu.ChewieUI 0.1 as ChewieUI
21
import "../Components"
22
import "../Components/Math.js" as MathLocal
27
property int progress: panelHeight
28
property int 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
32
property int hintValue
33
readonly property int lockThreshold: shell.height/2
34
property bool fullyOpened: revealer ? progress == revealer.openedValue : false
35
property bool partiallyOpened: revealer ? progress > revealer.closedValue && progress < revealer.openedValue : false
37
property Revealer revealer: null
39
height: menuContent.height + handle.height + menuContent.y
42
// need to use handle.get_height(). As the handle height depends on progress changes (but this is called first!)
43
var contentProgress = progress - handle.get_height()
44
if (!showAnimation.running && !hideAnimation.running && !revealer.hintingAnimation.running) {
45
if (contentProgress <= hintValue && indicators.state == "reveal") {
46
indicators.state = "hint"
48
} else if (contentProgress > hintValue && contentProgress < lockThreshold) {
49
menuContent.showMenu()
50
indicators.state = "reveal"
51
} else if (contentProgress >= lockThreshold && lockThreshold > 0) {
52
// If we've shown the overview and are closing the view with progress changes (revealer handle),
53
// we dont want to switch to a indicator menu until we hit reveal state.
54
if (menuContent.overviewActive) {
55
menuContent.showOverview()
58
menuContent.showMenu()
60
indicators.state = "locked"
64
if (contentProgress == 0) {
65
menuContent.releaseContent()
69
function handlePress() {
71
menuContent.activateContent()
72
indicators.state = "hint"
75
function openOverview() {
76
indicatorRow.currentItem = null
77
menuContent.showOverview()
80
function calculateCurrentItem(xValue, useBuffer) {
84
var distanceFromRightEdge
85
var bufferExceeded = false
87
if (indicators.state == "commit" || indicators.state == "locked" || showAnimation.running || hideAnimation.running) return
90
If user drags the indicator handle bar down a distance hintValue or less, this is 0.
91
If bar is dragged down a distance greater than or equal to lockThreshold, this is 1.
92
Otherwise it contains the bar's location as a fraction of the distance between hintValue (is 0) and lockThreshold (is 1).
94
var verticalProgress = MathLocal.clamp((indicators.progress - handle.height - hintValue) / (lockThreshold - hintValue), 0, 1)
97
Percentage of an indicator icon's width the user's press can stray horizontally from the
98
focused icon before we change focus to another icon. E.g. a value of 0.5 means you must
99
go right a distance of half an icon's width before focus moves to the icon on the right
101
var maxBufferThreshold = 0.5
104
To help users find the indicator of their choice while opening the indicators, we add logic to add a
105
left/right buffer to each icon so it is harder for the focus to be moved accidentally to another icon,
106
as the user moves their finger down, but yet allows them to switch indicator if they want.
107
This buffer is wider the further the user's finger is from the top of the screen.
109
var effectiveBufferThreshold = maxBufferThreshold * verticalProgress;
111
rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
112
// get the current delegate
113
currentItem = indicatorRow.row.childAt(rowCoordinates.x, 0);
114
if (currentItem && currentItem != indicatorRow.currentItem ) {
115
itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
116
distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width)
117
if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
118
bufferExceeded = true
120
if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
121
bufferExceeded = true
122
} else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
123
bufferExceeded = true
126
if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItem < 0 || indicatorRow.currentItem == null) {
127
indicatorRow.currentItem = currentItem;
136
bottom: handle.bottom
144
objectName: "menuContent"
149
top: indicatorRow.bottom
152
indicatorsModel: indicatorsModel
154
clip: indicators.partiallyOpened
157
indicatorRow.setItem(index)
160
//small shadow gradient at bottom of menu
165
bottom: parent.bottom
167
height: units.gu(0.5)
169
GradientStop { position: 0.0; color: "transparent" }
170
GradientStop { position: 1.0; color: "black" }
179
color: menuContent.backgroundColor
184
top: menuContent.bottom
187
clip: height < handleImage.height
189
function get_height() {
190
return Math.max(Math.min(handleImage.height, progress - handleImage.height), 0)
195
source: "graphics/handle.sci"
200
bottom: parent.bottom
203
MouseArea { //prevent clicks passing through
209
anchors.fill: indicatorRow
214
objectName: "indicatorRow"
219
height: indicators.panelHeight
220
indicatorsModel: indicatorsModel
221
state: indicators.state
222
overviewActive: menuContent.overviewActive
224
onCurrentItemIndexChanged: menuContent.currentIndex = currentItemIndex
228
ChewieUI.PluginModel {
230
Component.onCompleted: load()
234
target: hideAnimation
236
if (hideAnimation.running) {
237
indicators.state = "initial"
238
menuContent.hideAll()
240
if (state == "initial") indicatorRow.setDefaultItem()
245
target: showAnimation
247
if (showAnimation.running) {
248
if (indicators.state == "initial") {
252
indicators.calculateCurrentItem(revealer.lateralPosition, false)
253
menuContent.showMenu()
255
indicators.state = "commit"
262
onLateralPositionChanged: {
263
var buffer = revealer.dragging ? true : false
264
indicators.calculateCurrentItem(revealer.lateralPosition, buffer)
268
// We start with pinned states. (default pinnedMode: true)
269
states: pinnedModeStates
270
// because of dynamic assignment of states, we need to assign state after completion.
271
Component.onCompleted: state = "initial"
273
// changing states will reset state to "".
274
onPinnedModeChanged: {
275
var last_state = state;
276
states = (pinnedMode) ? pinnedModeStates : offScreenModeStates;
280
property list<State> offScreenModeStates: [
286
PropertyChanges { target: indicatorRow; y: panelHeight }
291
PropertyChanges { target: menuContent; animate: true }
292
StateChangeScript { script: calculateCurrentItem(revealer.lateralPosition, false); }
304
property list<State> pinnedModeStates: [
313
PropertyChanges { target: menuContent; animate: true }
314
StateChangeScript { script: calculateCurrentItem(revealer.lateralPosition, false); }
326
NumberAnimation {targets: [indicatorRow, menuContent]; property: "y"; duration: 300; easing.type: Easing.OutCubic}