Added TODO items to be resolved before a release
[harmattan/cameraplus] / qml / FocusReticle.qml
1 // -*- qml -*-
2
3 /*!
4  * This file is part of CameraPlus.
5  *
6  * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 import QtQuick 2.0
24 import QtCamera 1.0
25 import CameraPlus 1.0
26
27 // TODO: hide all controls when we are focusing
28 // TODO: hide all controls when we are dragging
29
30 MouseArea {
31     id: mouse
32     x: renderArea.x
33     y: renderArea.y
34     width: renderArea.width
35     height: renderArea.height
36     drag.minimumX: 0
37     drag.minimumY: 0
38     drag.maximumX: width - reticle.width
39     drag.maximumY: height - reticle.height
40
41     property variant videoResolution
42     property variant renderArea
43
44     property bool locked: false
45
46     property int cafStatus: AutoFocus.None
47     property int status: AutoFocus.None
48     property Camera cam
49     property bool touchMode
50
51     property variant touchPoint: Qt.point(mouse.width / 2, mouse.height / 2)
52
53     // A 100x100 central "rectangle"
54     property variant centerRect: Qt.rect((mouse.width / 2 - 50), (mouse.height / 2) - 50, 100, 100)
55
56     // ROI:
57     property variant primaryRoiRect: Qt.rect(0, 0, 0, 0)
58     property variant roiRects
59     property variant allRoiRects
60     property bool roiMode: allRoiRects != null && allRoiRects.length > 0 && !touchMode && !pressed
61
62     property variant __initialPos
63     onPressed: {
64
65         if (mouse.x >= reticle.x &&
66             mouse.y >= reticle.y &&
67             mouse.x <= reticle.x + reticle.width &&
68             mouse.y <= reticle.y + reticle.height) {
69             locked = true
70         }
71
72         __initialPos = touchPoint
73         calculateTouchPoint(mouse.x, mouse.y)
74     }
75
76     onReleased: {
77         calculateTouchPoint(mouse.x, mouse.y)
78         locked = false
79     }
80
81     onPositionChanged: calculateTouchPoint(mouse.x, mouse.y)
82     onCanceled: {
83         calculateTouchPoint(__initialPos.x, __initialPos.y)
84         locked = false
85     }
86
87     function resetReticle() {
88         calculateTouchPoint(centerRect.x, centerRect.y)
89     }
90
91     function setRegionOfInterest() {
92         if (!cam) {
93             // console.log("Cannot set ROI without camera object")
94             return
95         } else if (mouse.pressed) {
96             // console.log("Will not set ROI while pressed")
97             return
98         } else if (!touchMode && !roiMode) {
99             // console.log("resetting ROI")
100             if (cam.roi) {
101                 cam.roi.resetRegionOfInterest()
102             }
103
104             return
105         }
106
107         // TODO: rework this and move to unnormalized coordinates
108         // in terms of video resolution:
109         var rx = (videoResolution.width * reticle.x) / mouse.width
110         var rwidth = (videoResolution.width * reticle.width) / mouse.width
111         var ry = (videoResolution.height * reticle.y) / mouse.height
112         var rheight = (videoResolution.height * reticle.height) / mouse.height
113
114         // Translate to normalized coordinates (1x1 square) as expected by our C++ backend
115         rx = rx / videoResolution.width
116         rwidth = rwidth / videoResolution.width
117         ry = ry / videoResolution.height
118         rheight = rheight / videoResolution.height
119
120         // console.log("Setting ROI to: " + rx + "x" + ry)
121         cam.roi.setRegionOfInterest(Qt.rect(rx, ry, rwidth, rheight))
122     }
123
124     function calculateTouchPoint(x, y) {
125         if (x >= centerRect.x && y >= centerRect.y &&
126             x <= centerRect.x + centerRect.width &&
127             y <= centerRect.y + centerRect.height) {
128                 touchMode = false
129                 touchPoint = Qt.point(mouse.width / 2, mouse.height / 2)
130                 return
131         }
132
133         touchMode = true
134         touchPoint = Qt.point(x, y)
135     }
136
137     function predictColor(caf, status) {
138         if (status == AutoFocus.Success) {
139             return "steelblue"
140         } else if (status == AutoFocus.Fail) {
141             return "red"
142         } else if (status == AutoFocus.Running) {
143             return "white"
144         } else if (caf == AutoFocus.Success) {
145             return "steelblue"
146         } else {
147             return "white"
148         }
149     }
150
151     Repeater {
152         anchors.fill: parent
153         model: roiMode ? roiRects : 0
154
155         delegate: Rectangle {
156             x: modelData.x
157             y: modelData.y
158             width: modelData.width
159             height: modelData.height
160             color: "transparent"
161             border.color: "gray"
162             border.width: 2
163         }
164     }
165
166     FocusRectangle {
167         id: reticle
168         width: mouse.pressed ? 150 : mouse.touchMode ? 200 : roiMode ? primaryRoiRect.width : 250
169         height: mouse.pressed ? 90 : mouse.touchMode ? 120 : roiMode ? primaryRoiRect.height : 150
170         x: Math.min(Math.max(mouse.touchPoint.x - (width / 2), drag.minimumX), drag.maximumX)
171         y: Math.min(Math.max(mouse.touchPoint.y - (height / 2), drag.minimumY), drag.maximumY)
172         color: predictColor(cafStatus, status)
173
174         onXChanged: setRegionOfInterest()
175         onYChanged: setRegionOfInterest()
176         /*
177         Behavior on x {
178             PropertyAnimation { duration: 100 }
179             enabled: !mouse.pressed
180         }
181
182         Behavior on y {
183             PropertyAnimation { duration: 100 }
184             enabled: !mouse.pressed
185         }
186         */
187         Behavior on width {
188             PropertyAnimation { duration: 100 }
189         }
190
191         Behavior on height {
192             PropertyAnimation { duration: 100 }
193         }
194
195     }
196
197     Connections {
198         target: settings
199         // Changing mode (which implies changing pages) will not reset ROI
200         // thus we do it here
201         onModeChanged: resetReticle()
202     }
203
204     Connections {
205         target: cam
206         onRunningChanged: resetReticle()
207         onVideoResolutionChanged: resetReticle()
208     }
209
210     Connections {
211         target: cam.roi
212         onRegionsChanged: {
213             allRoiRects = regions
214             primaryRoiRect = primary
215             roiRects = rest
216
217             if (regions.length == 0) {
218                 resetReticle()
219                 return
220             }
221
222             touchPoint = Qt.point(primary.x + (reticle.width / 2),
223                 primary.y + (reticle.height / 2))
224         }
225     }
226
227     /*
228     // This is for debugging
229     Rectangle {
230         color: "blue"
231         opacity: 0.2
232         anchors.fill: parent
233     }
234
235     Rectangle {
236         color: "red"
237         opacity: 0.4
238         x: centerRect.x
239         y: centerRect.y
240         width: centerRect.width
241         height: centerRect.height
242     }
243     */
244     Timer {
245         interval: 500
246         running: status == AutoFocus.Running
247         triggeredOnStart: true
248         repeat: true
249         onTriggered: reticle.visible = !reticle.visible
250         onRunningChanged: {
251             if (!running) {
252                 reticle.visible = true
253             }
254         }
255     }
256 }