176 lines
5.4 KiB
QML
176 lines
5.4 KiB
QML
import QtQuick 2.0
|
|
import Sailfish.Silica 1.0
|
|
|
|
// TODO: Investigate using one quarter of the image mirrored in two directions
|
|
// TODO: Investigate using a scaled down version of the image
|
|
|
|
Item {
|
|
id: valuePicker
|
|
|
|
property int value
|
|
property int max
|
|
property int min
|
|
|
|
property real _scaleRatio: valueCircle.width / 408
|
|
|
|
width: valueCircle.width
|
|
height: valueCircle.height
|
|
|
|
onValueChanged: {
|
|
value = (value < 1 ? 1 : (value > max ? max : value))
|
|
|
|
if (mouse.changingProperty == 0) {
|
|
var delta = (value - valueIndicator.value)
|
|
valueIndicator.value += (delta % (max - 1))
|
|
}
|
|
}
|
|
|
|
function _xTranslation(value, bound) {
|
|
// Use sine to map range of 0-bound to the X translation of a circular locus (-1 to 1)
|
|
return Math.sin((value % bound) / bound * Math.PI * 2)
|
|
}
|
|
|
|
function _yTranslation(value, bound) {
|
|
// Use cosine to map range of 0-bound to the Y translation of a circular locus (-1 to 1)
|
|
return Math.cos((value % bound) / bound * Math.PI * 2)
|
|
}
|
|
|
|
Image {
|
|
id: valueCircle
|
|
width: page.width * 0.75
|
|
height: width
|
|
source: "../images/timepicker.png"
|
|
opacity: 0.2
|
|
}
|
|
|
|
GlassItem {
|
|
id: valueIndicator
|
|
falloffRadius: 0.22
|
|
radius: 0.25
|
|
anchors.centerIn: valueCircle
|
|
color: mouse.changingProperty == 2 ? Theme.highlightColor : Theme.primaryColor
|
|
|
|
property real value
|
|
|
|
transform: Translate {
|
|
// The ss band is 72px wide, ending at 204px from the center
|
|
x: _scaleRatio*168 * _xTranslation(valueIndicator.value, max)
|
|
y: -_scaleRatio*168 * _yTranslation(valueIndicator.value, max)
|
|
}
|
|
|
|
Behavior on value {
|
|
id: valueAnimation
|
|
SmoothedAnimation { velocity: 80 }
|
|
enabled: !mouse.isMoving || mouse.isLagging
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: mouse
|
|
|
|
property int changingProperty
|
|
property bool isMoving
|
|
property bool isLagging
|
|
|
|
anchors.fill: parent
|
|
preventStealing: true
|
|
|
|
function radiusForCoord(x, y) {
|
|
// Return the distance from the mouse position to the center
|
|
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
|
|
}
|
|
|
|
function angleForCoord(x, y) {
|
|
// Return the angular position in degrees, rising anticlockwise from the positive X-axis
|
|
var result = Math.atan(y / x) / (Math.PI * 2) * 360
|
|
|
|
// Adjust for various quadrants
|
|
if (x < 0) {
|
|
result += 180
|
|
} else if (y < 0) {
|
|
result += 360
|
|
}
|
|
return result
|
|
}
|
|
|
|
function remapAngle(value, bound) {
|
|
// Return the angle in degrees mapped to the adjusted range 0 - (bound-1) and
|
|
// translated to the clockwise from positive Y-axis orientation
|
|
return Math.round(bound - (((value - 90) / 360) * bound)) % bound
|
|
}
|
|
|
|
function remapMouse(mouseX, mouseY) {
|
|
// Return the mouse coordinates in cartesian coords relative to the circle center
|
|
return { x: mouseX - (width / 2), y: 0 - (mouseY - (height / 2)) }
|
|
}
|
|
|
|
function propertyForRadius(radius) {
|
|
// Return the property associated with clicking at radius distance from the center
|
|
if (radius < mouse.width / 2) {
|
|
return 2 // Minutes
|
|
}
|
|
return 0
|
|
}
|
|
|
|
function updateForAngle(angle) {
|
|
// Update the selected property for the specified angular position
|
|
// Minutes
|
|
// Map angular position to 0-59
|
|
var m = remapAngle(angle, max)
|
|
|
|
// Round single touch to the nearest 5 min mark
|
|
if (!isMoving) m = (Math.round(m/5) * 5) % (max - 1)
|
|
|
|
var delta = (m - valueIndicator.value) % (max - 1)
|
|
|
|
// It is not possible to make jumps of more than 30 minutes - reverse the direction
|
|
if (delta > 60) {
|
|
delta -= max
|
|
} else if (delta < -60) {
|
|
delta += max
|
|
}
|
|
if (isMoving && isLagging) {
|
|
if (Math.abs(delta) < 2) {
|
|
isLagging = false
|
|
}
|
|
}
|
|
|
|
valueIndicator.value += delta
|
|
|
|
valuePicker.value = m
|
|
}
|
|
|
|
onPressed: {
|
|
console.log("PRESSED")
|
|
var coords = remapMouse(mouseX, mouseY)
|
|
var radius = radiusForCoord(coords.x, coords.y)
|
|
|
|
changingProperty = propertyForRadius(radius)
|
|
if (changingProperty != 0) {
|
|
preventStealing = true
|
|
var angle = angleForCoord(coords.x, coords.y)
|
|
|
|
isLagging = true
|
|
updateForAngle(angle)
|
|
} else {
|
|
// Outside the minutes band - allow pass through to underlying component
|
|
preventStealing = false
|
|
}
|
|
}
|
|
onPositionChanged: {
|
|
if (changingProperty > 0) {
|
|
var coords = remapMouse(mouseX, mouseY)
|
|
var angle = angleForCoord(coords.x, coords.y)
|
|
|
|
isMoving = true
|
|
updateForAngle(angle)
|
|
}
|
|
}
|
|
onReleased: {
|
|
changingProperty = 0
|
|
isMoving = false
|
|
isLagging = false
|
|
}
|
|
}
|
|
}
|