some UI changes for usability
[makneto-zunavac1.git] / src / ui-mobile / declarative / LeftPanel.qml
blobd21c702b16950a0c07f742d04e4a995dc1a56cf8
1 /*
2  *   Copyright (C) 2011 Lukáš Karas <lukas.karas@centrum.cz>
3  *
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; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the
16  *   Free Software Foundation, Inc.,
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 import Qt 4.7
20 import QtQuick 1.0
21 import "./components/styles/default" as DefaultStyles
22 import "components"
23 import org.makneto 0.1 as Makneto
25 Rectangle{
26     id: leftPanel
27     SystemPalette{ id: syspal }
28     color: main.useSyspal ? syspal.mid : "#262626"
30     property variant expandedWidth : 200
31     property variant hidedWidth : 0
32     property variant animationLength: 200
34     property variant _model: ListModel {}
35     property variant _notifications: ListModel {}
37     function onSessionsModelChanged(model){
38         log("set sessions model");
39         leftPanel._model = model;
40     }
41     function onNotificationsModelChanged(model){
42         log("set notifications model");
43         leftPanel._notifications = model;
44     }
46     function log(msg){
47         console.log("II [LeftPanel.qml]: "+msg);
48     }
49     function error(msg){
50         console.log("EE [LeftPanel.qml]: "+msg);
51     }
52     function warn(msg){
53         console.log("WW [LeftPanel.qml]: "+msg);
54     }
56     states: [
57         State {
58             name: "expanded"
59             PropertyChanges { target: leftPanelShadow; opacity: 1}
60             PropertyChanges { target: leftPanel; width: expandedWidth}
61             PropertyChanges { target: clipImage; source: "img/clip-hide.png"}
62         },
63         State {
64             name: "hided"
65             PropertyChanges { target: leftPanelShadow; opacity: 0}
66             PropertyChanges { target: leftPanel; width: hidedWidth}
67             PropertyChanges { target: clipImage; source: "img/clip-show.png"}
68         },
69         State {
70             name: "hold"
71             PropertyChanges { target: leftPanelShadow; opacity: 1}
72         }
73     ]
74     transitions: [
75         Transition {
76             from: "hided"; to: "expanded"
77             NumberAnimation { properties: "width,height"; easing.type: Easing.InOutQuad; duration: animationLength }
78         },
79         Transition {
80             from: "expanded"; to: "hided"
81             NumberAnimation { properties: "width,height"; easing.type: Easing.InOutQuad; duration: animationLength }
82         },
83         Transition {
84             from: "hold"; to: "expanded"
85             NumberAnimation { properties: "width,height"; easing.type: Easing.InOutQuad; duration: animationLength /2 }
86         },
87         Transition {
88             from: "hold"; to: "hided"
89             NumberAnimation { properties: "width,height"; easing.type: Easing.InOutQuad; duration: animationLength /2 }
90         },
91         Transition {
92             NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad; duration: animationLength }
93         }
94     ]
96     state: "expanded"
97     width: expandedWidth
99     Component.onCompleted : {
100         main.sessionsModelChanged.connect(onSessionsModelChanged);
101         main.notificationsModelChanged.connect(onNotificationsModelChanged);
102     }
104     Image{
105         id: leftPanelShadow
106         anchors{top: parent.top; bottom: parent.bottom; left: parent.right}
107         width: 15
109         source: "./img/shadow-vertical.svg"
110         clip: true
111         z: -1
112     }
114     Rectangle{
115         id: clip
116         anchors.left: parent.right
117         anchors.top: parent.top
118         anchors.topMargin: leftPanel.height / 4
119         width: 30
120         height: 100
121         color: leftPanel.color
123         //property bool hold : false
124         property int dragStartX:0
125         property variant dragStartWidth:0
127         Image {
128             id: clipImage
129             source: "img/clip-hide.png"
130             width: clip.width
131             height: width*2
132             anchors.horizontalCenter: clip.horizontalCenter
133             anchors.verticalCenter: clip.verticalCenter
134         }
136         MouseArea{
137             id: clipMouseArea
138             anchors.fill: parent
140             onClicked: {
141                 var pos = clipMouseArea.mapToItem(main,mouseX, mouseY);
142                 var div = Math.abs(clip.dragStartX - pos.x);
143                 if (div > 10){
144                     //log("ignore clicked "+div);
145                     return;
146                 }
147                 //log("clicked "+div+" "+leftPanel.state);
149                 leftPanel.state = (leftPanel.state == "hided")? "expanded": "hided";
150             }
151             onReleased: {
152                 if (clip.dragStartWidth == leftPanel.hidedWidth){
153                     leftPanel.state = (leftPanel.width - leftPanel.hidedWidth) > ((leftPanel.expandedWidth - leftPanel.hidedWidth) * .25) ?
154                             "expanded": "hided";
155                 }else{
156                     leftPanel.state = (leftPanel.width - leftPanel.hidedWidth) < ((leftPanel.expandedWidth - leftPanel.hidedWidth) * .75) ?
157                             "hided":"expanded";
158                 }
160                 //log("onReleased "+leftPanel.state);
161             }
162             function startDrag(mouseX, mouseY){
163                 var pos = clipMouseArea.mapToItem(main,mouseX, mouseY);
164                 var currentWidth = leftPanel.width;
166                 leftPanel.state = "hold";
167                 leftPanel.width = currentWidth;
168                 clip.dragStartX = pos.x;
169                 clip.dragStartWidth = leftPanel.width;
171                 //log("start hold w: "+leftPanel.width+", x: "+pos.x);
172             }
174             onPressAndHold: {
175                 startDrag(mouseX, mouseY);
176             }
177             onPressed: {
178                 startDrag(mouseX, mouseY);
179             }
180             onMousePositionChanged: {
181                 if (leftPanel.state != "hold")
182                     return
184                 var pos = clipMouseArea.mapToItem(main,mouseX, mouseY);
185                 //log("move w: "+clip.dragStartWidth +" + x: "+ pos.x +" - s: "+ clip.dragStartX);
186                 var w = clip.dragStartWidth + (pos.x - clip.dragStartX);
187                 if (w < leftPanel.hidedWidth)
188                     w = leftPanel.hidedWidth;
189                 if (w > leftPanel.expandedWidth)
190                     w = leftPanel.expandedWidth;
191                 leftPanel.width = w;
192             }
193         }
194     }
196     Button{
197         id: contactListButton
198         anchors{margins: 6; top: parent.top; right: parent.right}
199         width: leftPanel.expandedWidth - (contactListButton.anchors.margins * 2)
200         text: "Show Contact List"
201         onClicked: {
202             main.getSessionController().showContactList();
203         }
204     }
206     Text{
207         id: sessionsLabel
208         anchors{ top: contactListButton.bottom; right: parent.right;  }
209         anchors{ topMargin: 12; leftMargin: 6; bottomMargin: 10; rightMargin: 6}
210         width: leftPanel.expandedWidth - (contactListButton.anchors.margins * 2)
211         color: main.useSyspal? syspal.windowText :"white"
212         text: "Opened sessions:"
213     }
215     ListView {
216         id: sessionsView
217         model: leftPanel._model;
218         anchors{margins: 6; topMargin: 20; top: sessionsLabel.bottom; right: parent.right; bottom: notificationsView.top}
219         width: leftPanel.expandedWidth - (contactListButton.anchors.margins * 2)
220         clip: true
222         Timer {
223             id: sessionCloseTimer
224             property variant sessionId:0
225             interval: 10; running: false; repeat: false
226             onTriggered: {
227                 /*  this method destroys this ListViewLine (id notificationLine).
228                     invoking this code directly from Button's method onClicked
229                     causes application SEGFAULT! This is workaround, we invoke
230                     it from timer...
231                  */
232                 main.getSessionController().closeSession(sessionId);
233             }
234         }
236         delegate: ListViewLine{
237             id: sessionLine
238             width: sessionsView.width
239             property variant sessionId : id
240             property variant titleText: ((type & Makneto.Session.SessionTypeAudio) || (type & Makneto.Session.SessionTypeVideo)?
241                                              "call":"chat")+" with "+sessionName
242             property variant statusText: lastMessage // sessionId
243             property variant iconSource: icon !== ""?
244                                              "file:"+ icon : "qrc:/declarative/img/default_avatar.jpg"
246             property variant titleTextBold: main.getSessionController().getTopScene() == sessionId;
247             property variant textElide: Text.ElideRight;
249             MouseArea{
250                 anchors.fill: parent
251                 onClicked: {
252                     main.getSessionController().activateSession(sessionId);
253                 }
254             }
255             Rectangle {
256                 Image {
257                     source: "img/cross.png"
258                     height: parent.height * 0.8
259                     anchors{ margins: parent.height*0.1; fill:parent}
260                 }
261                 color: leftPanel.color
262                 height: parent.height
263                 width: height
264                 opacity: .5
265                 anchors{ margins: parent.height*0.1; right: parent.right; top: parent.top}
266                 //BorderImage { source: "img/lineedit.sci"; anchors.fill: parent }
268                 MouseArea{
269                     anchors.fill: parent
270                     onClicked: {
271                         sessionCloseTimer.sessionId = sessionId;
272                         sessionCloseTimer.start();
273                     }
274                 }
275             }
276         }
277     }
279     Rectangle{
280         id: separatorLine
281         anchors{top: sessionsView.bottom; right: parent.right;}
282         width: sessionsView.width
283         anchors.margins: 4
284         height: 1
285         color: "transparent"
287         Image {
288             id: lineImage
289             source: "img/line-horizontal.svg"
290             anchors.fill: parent
291         }
292     }
294     ListView {
295         id: notificationsView
296         model: leftPanel._notifications;
297         anchors{margins: 6; topMargin: 20; right: parent.right; bottom: parent.bottom}
298         width: sessionsView.width
299         height: Math.max(leftPanel.height * .2, 100)
300         clip: true
302         Timer {
303             id: acceptTimer
304             property variant notificationId:0
305             interval: 10; running: false; repeat: false
306             onTriggered: {
307                 /*  this method destroys this ListViewLine (id notificationLine).
308                     invoking this code directly from Button's method onClicked
309                     causes application SEGFAULT! This is workaround, we invoke
310                     it from timer...
311                  */
312                 leftPanel._notifications.acceptNotification(notificationId);
313             }
314         }
315         Timer {
316             id: closeTimer
317             property variant notificationId:0
318             interval: 10; running: false; repeat: false
319             onTriggered: {
320                 leftPanel._notifications.declineNotification(notificationId);
321             }
322         }
325         delegate: AbstractListViewLine{
326             id: notificationLine
327             width: notificationsView.width
328             height: titleText.height + contactText.height + acceptButton.height + acceptButton.anchors.topMargin +
329                     (wrapper.anchors.topMargin + wrapper.anchors.bottomMargin ) + 2
331             property variant notificationId : id
332             property variant sessionId: sessionId
333             property variant name : sessionName
334             property variant notificationType : type
335             property variant notificationTitle: title
336             property variant notificationIcon: icon
337             property variant notificationMessage: message
339             Rectangle{
340                 id: wrapper
341                 anchors.topMargin: topMargin
342                 anchors.bottomMargin: bottomMargin
343                 anchors.leftMargin: leftMargin
344                 anchors.rightMargin: rightMargin
345                 anchors.fill: parent
346                 color: "transparent"
348                 Image {
349                     id: notificationIcon
350                     source: notificationLine.notificationIcon !== "" ?
351                                 "file:"+ notificationLine.notificationIcon : "qrc:/declarative/img/default_avatar.jpg"
352                     width: height
353                     anchors{top: titleText.top; bottom: contactText.bottom; left: parent.left}
354                 }
356                 Text {
357                     id: titleText
358                     text: notificationLine.notificationTitle
359                     anchors{left: notificationIcon.right; right: parent.right; top: parent.top}
360                     anchors.leftMargin: 8
361                     wrapMode: Text.NoWrap
362                     color: "grey"
363                     clip: true
364                 }
366                 Text {
367                     id: contactText
368                     text: notificationLine.name+ (notificationLine.notificationMessage === ""?"":": "+notificationLine.notificationMessage)
369                     anchors{left: titleText.left; right: parent.right; top: titleText.bottom}
370                     wrapMode: Text.NoWrap
371                     color: "white"
372                     clip: true
373                 }
374                 Button{
375                     id: acceptButton
376                     anchors{ right: parent.right; top: contactText.bottom; topMargin: 4}
377                     text: notificationLine.notificationType == Makneto.NotificationItem.CallErrorType? "Hide": "Accept"
378                     onClicked: {
379                         acceptTimer.notificationId = notificationLine.notificationId;
380                         acceptTimer.start();
381                     }
382                 }
383                 Button{
384                     id: closeButton
385                     opacity: notificationLine.notificationType == Makneto.NotificationItem.CallErrorType? 0: 1
386                     anchors{ right: acceptButton.left; top: acceptButton.top}
387                     text: "Close"
388                     onClicked: {
389                         closeTimer.notificationId = notificationLine.notificationId;
390                         closeTimer.start();
391                     }
392                 }
393             }
394         }
395     }