Some changes for better touch support and scripts
[opentodolist:opentodolist.git] / OpenTodoList / qml / OpenTodoList / TodoDetailsView.qml
1 /*
2  *  OpenTodoListDesktopQml - Desktop QML frontend for OpenTodoList
3  *  Copyright (C) 2013  Martin Höher <martin@rpdev.net>
4  * 
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  * 
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  * 
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 import QtQuick 2.0
20 import net.rpdev.OpenTodoList 1.0
21 import "Utils.js" as Utils
22
23 View {
24     id: todoDetailsView
25     hidden: todo ? false : true
26     
27     property QtObject todo: null
28     property TodoSortFilterModel model: TodoSortFilterModel {
29         sourceModel: todo ? todo.subTodos : null
30         searchString: filterText.text
31     }
32
33     onTodoChanged: {
34         filterText.text = "";
35         if ( todo ) {
36             todoDueDateEdit.date = todo.dueDate;
37         } else {
38             todoDueDateEdit.date = new Date( NaN, NaN, NaN );
39         }
40         priorityIndicator.focus = false
41         // Ensure changes in description are saved
42         saveDescriptionTimer.save();
43         // Update the bread crump
44         updateBreadCrump();
45     }
46
47     function updateBreadCrump() {
48         var parents = [];
49         if ( todoDetailsView.todo ) {
50             var parent = todoDetailsView.todo.parentTodo;
51             while ( parent ) {
52                 parents.unshift( parent );
53                 parent = parent.parentTodo;
54             }
55         }
56         breadCrump.model = parents
57     }
58     
59     toolButtons: [
60         ToolButton {
61             font.family: symbolFont.name
62             label: "\uf060"
63
64             onClicked: {
65                 todoDetailsView.todo = todoDetailsView.todo.parentTodo
66             }
67         }
68     ]
69
70     Flickable {
71         anchors.fill: parent
72         contentWidth: detailsContents.width
73         contentHeight: detailsContents.height
74
75         Item {
76             id: detailsContents
77             width: parent.width
78             height: childrenRect.height
79
80             Column {
81                 width: todoDetailsView.clientWidth
82                 spacing: 10
83
84                 Item {
85                     width: parent.width
86                     height: childrenRect.height
87                     Column {
88                         width: parent.width
89                         Repeater {
90                             id: breadCrump
91                             delegate: Item {
92                                 id: parentLink
93                                 width: todoDetailsView.clientWidth
94                                 height: childrenRect.height
95                                 Item {
96                                     id: bcSpace
97                                     height: 1
98                                     width: index * 20
99                                 }
100                                 Text {
101                                     id: bcSymbol
102                                     anchors { left: bcSpace.right; leftMargin: 10 }
103                                     font.family: symbolFont.name
104                                     font.pointSize: fonts.h2
105                                     text: "\uf101"
106                                 }
107                                 Text {
108                                     anchors { left: bcSymbol.right; leftMargin: 10; right: parent.left }
109                                     font.pointSize: fonts.h2
110                                     text: modelData.title
111                                 }
112                                 MouseArea {
113                                     anchors.fill: parentLink
114                                     onClicked: todoDetailsView.todo = modelData
115                                 }
116                             }
117                         }
118                     }
119                     Behavior on height { NumberAnimation { duration: 300 } }
120                 }
121
122                 Item {
123                     width: parent.width
124                     height: childrenRect.height
125
126                     TextInput {
127                         id: todoDetailsViewTitleEdit
128                         x: breadCrump.model ? ( breadCrump.model.length + 1 ) * 20 : 0
129                         text: todoDetailsView.todo ? todoDetailsView.todo.title : ""
130                         font.bold: true
131                         font.pointSize: fonts.h1
132                         width: parent.width - saveTitleButton.width
133                         wrapMode: Text.Wrap
134
135                         function saveTitle() {
136                             todoDetailsView.todo.title = todoDetailsViewTitleEdit.text;
137                             todoDetailsViewTitleEdit.focus = false;
138                         }
139
140                         Keys.onEnterPressed: saveTitle()
141                         Keys.onReturnPressed: saveTitle()
142
143                         Behavior on x { NumberAnimation { duration: 300 } }
144                     }
145
146                     Button {
147                         id: saveTitleButton
148                         label: "Save"
149                         onClicked: todoDetailsViewTitleEdit.saveTitle()
150                         opacity: todoDetailsViewTitleEdit.focus ? 1 : 0
151                         visible: opacity !== 0
152                         anchors.right: parent.right
153                         Behavior on opacity { NumberAnimation { duration: 300 } }
154                     }
155                 }
156
157                 Flow {
158                     spacing: 10
159                     width: parent.width
160                     anchors.horizontalCenter: parent.horizontalCenter
161                     ProgressIndicator {
162                         id: progressIndicator
163                         percentage: todoDetailsView.todo ? todoDetailsView.todo.progress : 0
164
165                         onDecreasePressed: todoDetailsView.todo.progress = stepDown()
166                         onIncreasePressed: todoDetailsView.todo.progress = stepUp()
167                     }
168                     PriorityIndicator {
169                         id: priorityIndicator
170                         width: progressIndicator.width
171                         height: progressIndicator.height
172                         priority: todoDetailsView.todo ? todoDetailsView.todo.priority : -1
173
174                         onSelectedPriority: todoDetailsView.todo.priority = priority
175                     }
176                     DatePicker {
177                         id: todoDueDateEdit
178                         baseWidth: progressIndicator.width
179                         baseHeight: progressIndicator.height
180                         date: todoDetailsView.todo ? todoDetailsView.todo.dueDate : Utils.getNullDate()
181
182                         onDateChanged: {
183                             if ( todoDetailsView.todo ) {
184                                 todoDetailsView.todo.dueDate = date
185                             }
186                         }
187                     }
188                 }
189
190                 Row {
191                     width: parent.width
192
193                     SimpleTextInput {
194                         id: newSubTodoTitle
195                         text: ""
196                         placeholderText: "Add new subtodo"
197                         width: parent.width - addNewSubTodoButton.width
198
199                         onApply: addNewSubTodoButton.createNewSubTodo()
200                     }
201
202                     Button {
203                         id: addNewSubTodoButton
204                         label: "\uf067"
205                         font.family: symbolFont.name
206                         onClicked: createNewSubTodo()
207
208                         function createNewSubTodo() {
209                             var todo = todoDetailsView.todo.todoList.addTodo();
210                             todo.title = newSubTodoTitle.text;
211                             todo.parentTodo = todoDetailsView.todo;
212                             newSubTodoTitle.text = "";
213                         }
214                     }
215                 }
216
217                 SimpleTextInput {
218                     id: filterText
219                     placeholderText: "Filter subtodos"
220                     anchors.left: parent.left
221                     anchors.right: parent.right
222                 }
223
224                 TodoView {
225                     autoSize: true
226                     width: parent.width
227                     model: todoDetailsView.model
228                     onTodoSelected: todoDetailsView.todo = todo
229                 }
230
231                 Item {
232                     width: parent.width
233                     height: childrenRect.height
234
235                     BorderImage {
236                         width: todoDescriptionEditContains.width
237                         height: todoDescriptionEditContains.height
238                         source: "description.sci"
239                         cache: !settings.debug
240                     }
241
242                     MouseArea {
243                         width: todoDescriptionEditContains.width
244                         height: todoDescriptionEditContains.height
245                         onClicked: todoDescriptionEdit.focus = true;
246                     }
247
248                     Item {
249                         id: todoDescriptionEditContains
250                         width: parent.width
251                         height: childrenRect.height + 40
252
253                         TextEdit {
254                             id: todoDescriptionEdit
255
256                             text: todoDetailsView.todo ? todoDetailsView.todo.description : ""
257                             textFormat: TextEdit.RichText
258                             font.pointSize: fonts.p
259                             wrapMode: Text.Wrap
260                             selectByMouse: true
261                             width: parent.width - 40
262                             x: 20
263                             y: 20
264
265                             onTextChanged: if ( todoDetailsView.todo )
266                                                saveDescriptionTimer.delayedSave( todoDetailsView.todo, text );
267                         }
268                         Timer {
269                             id: saveDescriptionTimer
270                             interval: 10000
271
272                             property QtObject todo: todo
273                             property string text: text
274
275                             function delayedSave( todo, text ) {
276                                 if ( saveDescriptionTimer.todo ) {
277                                     if ( saveDescriptionTimer.todo !== todo ) {
278                                         // Other todo --> save immediately
279                                         save();
280                                     }
281                                 }
282                                 saveDescriptionTimer.todo = todo;
283                                 saveDescriptionTimer.text = text;
284                                 restart();
285                             }
286
287                             function save() {
288                                 if ( todo ) {
289                                     todo.description = text;
290                                     todo = null;
291                                     text = "";
292                                 }
293                             }
294
295                             onTriggered: save();
296                             Component.onDestruction: save();
297                         }
298                     }
299                 }
300             }
301         }
302     }
303 }