Improve Contacts: sorting, searching and unread

ChatHandler::handleUnreadMsgs(...) deprecate 'id' field
  in favour of 'chat_id'
IdentityHandler::handleWildcard(...) reduce sleep time from 500 to 50 ms
IdentityHandler::handleWildcard(...) expose 'is_contact' to JSON API
Split contacts list delegate into GxsIdentityDelegate.qml
Update to QtQuick.Controls 2.0 because 1.4 is not supported anymore and
  2.1 is not available yet in Qt 5.7.1
ChatView.qml mark messages as read
Set refreshTimer.triggeredOnStart=true to improve reponsiveness of views
Contacts.qml use a Popup that is available in Controls 2.0 instead of a
  castrated Dialog to display full fingerprint
TrustedNodesView.qml check if locations[*].is_online is an array before
  attempting calling reduce
main.qml added menuentry to shutdown de core
main.qml added menuantry to search contact
This commit is contained in:
Gioacchino Mazzurco 2017-03-24 12:02:13 +01:00
parent 34dc1fac37
commit abe84a4f81
17 changed files with 409 additions and 141 deletions

View file

@ -924,7 +924,7 @@ ResponseTask* ChatHandler::handleLobbyParticipants(Request &req, Response &resp)
void ChatHandler::handleMessages(Request &req, Response &resp) void ChatHandler::handleMessages(Request &req, Response &resp)
{ {
/* G10h4ck: Whithout this the request processing won't happen, copied from /* G10h4ck: Whithout this the request processing won't happen, copied from
* ChatHandler::handleLobbies, is this a work around or is the right whay of * ChatHandler::handleLobbies, is this a work around or is the right way of
* doing it? */ * doing it? */
tick(); tick();
@ -1118,7 +1118,8 @@ void ChatHandler::handleUnreadMsgs(Request &/*req*/, Response &resp)
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/ RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
resp.mDataStream.getStreamToMember(); resp.mDataStream.getStreamToMember();
for(std::map<ChatId, std::list<Msg> >::const_iterator mit = mMsgs.begin(); mit != mMsgs.end(); ++mit) for( std::map<ChatId, std::list<Msg> >::const_iterator mit = mMsgs.begin();
mit != mMsgs.end(); ++mit )
{ {
uint32_t count = 0; uint32_t count = 0;
for(std::list<Msg>::const_iterator lit = mit->second.begin(); lit != mit->second.end(); ++lit) for(std::list<Msg>::const_iterator lit = mit->second.begin(); lit != mit->second.end(); ++lit)
@ -1130,7 +1131,10 @@ void ChatHandler::handleUnreadMsgs(Request &/*req*/, Response &resp)
if(count && (mit2 != mChatInfo.end())) if(count && (mit2 != mChatInfo.end()))
{ {
resp.mDataStream.getStreamToMember() resp.mDataStream.getStreamToMember()
<< makeKeyValue("id", mit->first.toStdString()) #warning @deprecated using "id" as key can cause problems in some JS based \
languages like Qml @see chat_id instead
<< makeKeyValue("id", mit->first.toStdString())
<< makeKeyValue("chat_id", mit->first.toStdString())
<< makeKeyValueReference("unread_count", count) << makeKeyValueReference("unread_count", count)
<< mit2->second; << mit2->second;
} }
@ -1162,7 +1166,8 @@ void ChatHandler::handleInitiateDistantChatConnexion(Request& req, Response& res
DistantChatPeerId distant_chat_id; DistantChatPeerId distant_chat_id;
uint32_t error_code; uint32_t error_code;
if(mRsMsgs->initiateDistantChatConnexion(receiver_id, sender_id, distant_chat_id, error_code)) if(mRsMsgs->initiateDistantChatConnexion(receiver_id, sender_id,
distant_chat_id, error_code))
resp.setOk(); resp.setOk();
else resp.setFail("Failed to initiate distant chat"); else resp.setFail("Failed to initiate distant chat");

View file

@ -61,7 +61,9 @@ protected:
{ {
case BEGIN:{ case BEGIN:{
RsIdentityParameters params; RsIdentityParameters params;
req.mStream << makeKeyValueReference("name", params.nickname) << makeKeyValueReference("pgp_linked", params.isPgpLinked); req.mStream
<< makeKeyValueReference("name", params.nickname)
<< makeKeyValueReference("pgp_linked", params.isPgpLinked);
if(params.nickname == "") if(params.nickname == "")
{ {
@ -141,9 +143,9 @@ void IdentityHandler::handleWildcard(Request & /*req*/, Response &resp)
) )
{ {
#ifdef WINDOWS_SYS #ifdef WINDOWS_SYS
Sleep(500); Sleep(50);
#else #else
usleep(500*1000); usleep(50*1000);
#endif #endif
} }
@ -156,14 +158,18 @@ void IdentityHandler::handleWildcard(Request & /*req*/, Response &resp)
RsGxsIdGroup& grp = *vit; RsGxsIdGroup& grp = *vit;
//electron: not very happy about this, i think the flags should stay hidden in rsidentities //electron: not very happy about this, i think the flags should stay hidden in rsidentities
bool own = (grp.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN); bool own = (grp.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN);
bool pgp_linked = (grp.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility ) ; bool pgp_linked = (grp.mMeta.mGroupFlags &
resp.mDataStream.getStreamToMember() RSGXSID_GROUPFLAG_REALID_kept_for_compatibility);
<< makeKeyValueReference("id", grp.mMeta.mGroupId) /// @deprecated using "id" as key can cause problems in some JS based languages like Qml @see gxs_id instead resp.mDataStream.getStreamToMember()
#warning @deprecated using "id" as key can cause problems in some JS based \
languages like Qml @see gxs_id instead
<< makeKeyValueReference("id", grp.mMeta.mGroupId)
<< makeKeyValueReference("gxs_id", grp.mMeta.mGroupId) << makeKeyValueReference("gxs_id", grp.mMeta.mGroupId)
<< makeKeyValueReference("pgp_id",grp.mPgpId ) << makeKeyValueReference("pgp_id",grp.mPgpId )
<< makeKeyValueReference("name", grp.mMeta.mGroupName) << makeKeyValueReference("name", grp.mMeta.mGroupName)
<< makeKeyValueReference("own", own) << makeKeyValueReference("own", own)
<< makeKeyValueReference("pgp_linked", pgp_linked); << makeKeyValueReference("pgp_linked", pgp_linked)
<< makeKeyValueReference("is_contact", grp.mIsAContact);
} }
} }
else ok = false; else ok = false;

View file

@ -91,11 +91,11 @@ class GxsReputation
}; };
class RsGxsIdGroup struct RsGxsIdGroup
{ {
public: RsGxsIdGroup() :
RsGxsIdGroup(): mLastUsageTS(0), mPgpKnown(false),mIsAContact(false) { return; } mLastUsageTS(0), mPgpKnown(false), mIsAContact(false) {}
~RsGxsIdGroup() { return; } ~RsGxsIdGroup() {}
RsGroupMetaData mMeta; RsGroupMetaData mMeta;

View file

@ -13,5 +13,10 @@
<file>qml/icons/remove-link.png</file> <file>qml/icons/remove-link.png</file>
<file>qml/icons/state-offline.png</file> <file>qml/icons/state-offline.png</file>
<file>qml/icons/state-ok.png</file> <file>qml/icons/state-ok.png</file>
<file>qml/GxsIdentityDelegate.qml</file>
<file>qml/icons/edit-find.png</file>
<file>qml/icons/edit-image-face-detect.png</file>
<file>qml/icons/document-share.png</file>
<file>qml/icons/application-menu.png</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -1,5 +1,5 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 1.4 import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import org.retroshare.qml_components.LibresapiLocalClient 1.0 import org.retroshare.qml_components.LibresapiLocalClient 1.0

View file

@ -17,7 +17,7 @@
*/ */
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 1.4 import QtQuick.Controls 2.0
import org.retroshare.qml_components.LibresapiLocalClient 1.0 import org.retroshare.qml_components.LibresapiLocalClient 1.0
Item Item
@ -27,8 +27,12 @@ Item
function refreshData() function refreshData()
{ {
rsApi.request( "/chat/messages/"+ chatId, "", rsApi.request( "/chat/messages/"+chatId, "", function(par)
function(par) { chatModel.json = par.response } ) {
chatModel.json = par.response
if(visible) rsApi.request("/chat/mark_chat_as_read/"+chatId, "",
null)
} )
} }
onFocusChanged: focus && refreshData() onFocusChanged: focus && refreshData()
@ -89,6 +93,7 @@ Item
id: refreshTimer id: refreshTimer
interval: 800 interval: 800
repeat: true repeat: true
triggeredOnStart: true
onTriggered: if(chatView.visible) chatView.refreshData() onTriggered: if(chatView.visible) chatView.refreshData()
Component.onCompleted: start() Component.onCompleted: start()
} }

View file

@ -17,8 +17,9 @@
*/ */
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 1.4 import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2 import QtQuick.Dialogs 1.2
import QtQml.Models 2.2
import org.retroshare.qml_components.LibresapiLocalClient 1.0 import org.retroshare.qml_components.LibresapiLocalClient 1.0
Item Item
@ -26,6 +27,8 @@ Item
id: contactsView id: contactsView
property string own_gxs_id: "" property string own_gxs_id: ""
property string own_nick: "" property string own_nick: ""
property bool searching: false
property var unreadMessages: ({})
Component.onCompleted: refreshOwn() Component.onCompleted: refreshOwn()
@ -33,7 +36,7 @@ Item
{ {
function refreshCallback(par) function refreshCallback(par)
{ {
locationsModel.json = par.response gxsIdsModel.json = par.response
if(contactsView.own_gxs_id == "") refreshOwn() if(contactsView.own_gxs_id == "") refreshOwn()
} }
rsApi.request("/identity/*/", "", refreshCallback) rsApi.request("/identity/*/", "", refreshCallback)
@ -54,113 +57,174 @@ Item
function startChatCallback(par) function startChatCallback(par)
{ {
var chId = JSON.parse(par.response).data.chat_id var chId = JSON.parse(par.response).data.chat_id
stackView.push({ stackView.push("qrc:/qml/ChatView.qml", {'chatId': chId})
item:"qrc:/qml/ChatView.qml",
properties: {chatId: chId}
})
} }
function refreshUnread()
{
rsApi.request("/chat/unread_msgs", "", function(par)
{
var jsonData = JSON.parse(par.response).data
var dataLen = jsonData.length
if(JSON.stringify(unreadMessages) != JSON.stringify(jsonData))
{
unreadMessages = {}
for ( var i=0; i<dataLen; ++i)
{
var el = jsonData[i]
if(el.is_distant_chat_id)
unreadMessages[el.remote_author_id] = el.unread_count
}
visualModel.resetSorting()
}
})
}
/** This must be equivalent to
p3GxsTunnelService::makeGxsTunnelId(...) */
function getChatId(from_gxs, to_gxs)
{
return from_gxs < to_gxs ? from_gxs + to_gxs : to_gxs + from_gxs
}
onFocusChanged: focus && refreshData() onFocusChanged: focus && refreshData()
JSONListModel JSONListModel
{ {
id: locationsModel id: gxsIdsModel
query: "$.data[*]" query: "$.data[*]"
} }
DelegateModel
{
/* More documentation about this is available at:
* http://doc.qt.io/qt-5/qml-qtqml-models-delegatemodel.html
* http://doc.qt.io/qt-5/qtquick-tutorials-dynamicview-dynamicview4-example.html
* http://imaginativethinking.ca/use-qt-quicks-delegatemodelgroup/
*/
id: visualModel
model: gxsIdsModel.model
property var lessThan:
[
function(left, right)
{
var lfun = unreadMessages.hasOwnProperty(left.gxs_id) ?
unreadMessages[left.gxs_id] : 0
var rgun = unreadMessages.hasOwnProperty(right.gxs_id) ?
unreadMessages[right.gxs_id] : 0
if( lfun !== rgun ) return lfun > rgun
if(left.name !== right.name) return left.name < right.name
return left.gxs_id < right.gxs_id
},
function(left, right)
{
if(searchText.length > 0)
{
var mtc = searchText.text.toLowerCase()
var lfn = left.name.toLowerCase()
var rgn = right.name.toLowerCase()
var lfml = lfn.indexOf(mtc)
var rgml = rgn.indexOf(mtc)
if ( lfml !== rgml )
{
lfml = lfml >= 0 ? lfml : Number.MAX_VALUE
rgml = rgml >= 0 ? rgml : Number.MAX_VALUE
return lfml < rgml
}
}
return lessThan[0](left, right)
}
]
property int sortOrder: contactsView.searching ? 1 : 0
onSortOrderChanged: resetSorting()
property bool isSorting: false
function insertPosition(lessThan, item)
{
var lower = 0
var upper = items.count
while (lower < upper)
{
var middle = Math.floor(lower + (upper - lower) / 2)
var result = lessThan(item.model, items.get(middle).model);
if (result) upper = middle
else lower = middle + 1
}
return lower
}
function resetSorting() { items.setGroups(0, items.count, "unsorted") }
function sort()
{
while (unsortedItems.count > 0)
{
var item = unsortedItems.get(0)
var index = insertPosition(lessThan[visualModel.sortOrder],
item)
item.groups = ["items"]
items.move(item.itemsIndex, index)
}
}
items.includeByDefault: false
groups:
[
DelegateModelGroup
{
id: unsortedItems
name: "unsorted"
includeByDefault: true
onChanged: visualModel.sort()
}
]
delegate: GxsIdentityDelegate {}
}
ListView ListView
{ {
id: locationsListView id: locationsListView
width: parent.width width: parent.width
height: 300 height: contactsView.searching ?
model: locationsModel.model parent.height - searchBox.height : parent.height
delegate: Item model: visualModel
anchors.top: contactsView.searching ? searchBox.bottom : parent.top
}
Rectangle
{
id: searchBox
visible: contactsView.searching
height: searchText.height
width: searchText.width
anchors.right: parent.right
anchors.top: parent.top
Image
{ {
height: 40 id: searchIcon
width: parent.width height: searchText.height - 4
width: searchText.height - 4
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/qml/icons/edit-find.png"
}
MouseArea TextField
{ {
anchors.fill: parent id: searchText
onClicked: anchors.left: searchIcon.right
{ anchors.verticalCenter: parent.verticalCenter
console.log("Contacts view onclicked:", model.name, onTextChanged: visualModel.resetSorting()
model.gxs_id)
if(model.own) contactsView.own_gxs_id = model.gxs_id
else
{
var jsonData = { "own_gxs_hex": contactsView.own_gxs_id,
"remote_gxs_hex": model.gxs_id }
rsApi.request("/chat/initiate_distant_chat",
JSON.stringify(jsonData),
contactsView.startChatCallback)
}
}
Rectangle
{
id: colorHash
height: parent.height - 4
width: height
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 2
color: "white"
property int childHeight : height/2
Rectangle
{
color: '#' + model.gxs_id.substring(1, 9)
height: parent.childHeight
width: height
anchors.top: parent.top
anchors.left: parent.left
}
Rectangle
{
color: '#' + model.gxs_id.substring(9, 17)
height: parent.childHeight
width: height
anchors.top: parent.top
anchors.right: parent.right
}
Rectangle
{
color: '#' + model.gxs_id.substring(17, 25)
height: parent.childHeight
width: height
anchors.bottom: parent.bottom
anchors.left: parent.left
}
Rectangle
{
color: '#' + model.gxs_id.slice(-8)
height: parent.childHeight
width: height
anchors.bottom: parent.bottom
anchors.right: parent.right
}
MouseArea
{
anchors.fill: parent
onPressAndHold:
{
fingerPrintDialog.nick = model.name
fingerPrintDialog.gxs_id = model.gxs_id
fingerPrintDialog.visible = true
}
}
}
Text
{
id: nickText
color: model.own ? "blue" : "black"
text: model.name
anchors.left: colorHash.right
anchors.leftMargin: 5
anchors.verticalCenter: parent.verticalCenter
}
}
} }
} }
@ -179,11 +243,16 @@ Item
id: refreshTimer id: refreshTimer
interval: 5000 interval: 5000
repeat: true repeat: true
onTriggered: if(contactsView.visible) contactsView.refreshData() triggeredOnStart: true
onTriggered:
if(contactsView.visible)
{
contactsView.refreshUnread()
contactsView.refreshData()
}
Component.onCompleted: start() Component.onCompleted: start()
} }
Dialog Dialog
{ {
id: createIdentityDialog id: createIdentityDialog
@ -207,14 +276,17 @@ Item
} }
} }
Dialog Popup
{ {
id: fingerPrintDialog id: fingerPrintDialog
visible: false visible: false
property string nick property string nick
property string gxs_id property string gxs_id
title: nick + " fingerprint:" width: fingerPrintText.contentWidth + 20
standardButtons: StandardButton.NoButton height: fingerPrintText.contentHeight + 20
x: parent.x + parent.width/2 - width/2
y: parent.y + parent.height/2 - height/2
Text Text
{ {
id: fingerPrintText id: fingerPrintText

View file

@ -0,0 +1,139 @@
/*
* RetroShare Android QML App
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
Item
{
height: 40
width: parent.width
MouseArea
{
anchors.fill: parent
onClicked:
{
console.log("Contacts view onclicked:", model.name,
model.gxs_id)
contactsView.searching = false
if(model.own) contactsView.own_gxs_id = model.gxs_id
else
{
var jsonData = { "own_gxs_hex": contactsView.own_gxs_id,
"remote_gxs_hex": model.gxs_id }
rsApi.request("/chat/initiate_distant_chat",
JSON.stringify(jsonData),
contactsView.startChatCallback)
}
}
Rectangle
{
id: colorHash
height: parent.height - 4
width: height
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 2
color: "white"
property int childHeight : height/2
Image
{
source: "qrc:/qml/icons/edit-image-face-detect.png"
anchors.fill: parent
}
Rectangle
{
color: '#' + model.gxs_id.substring(1, 9)
height: parent.childHeight
width: height
anchors.top: parent.top
anchors.left: parent.left
}
Rectangle
{
color: '#' + model.gxs_id.substring(9, 17)
height: parent.childHeight
width: height
anchors.top: parent.top
anchors.right: parent.right
}
Rectangle
{
color: '#' + model.gxs_id.substring(17, 25)
height: parent.childHeight
width: height
anchors.bottom: parent.bottom
anchors.left: parent.left
}
Rectangle
{
color: '#' + model.gxs_id.slice(-8)
height: parent.childHeight
width: height
anchors.bottom: parent.bottom
anchors.right: parent.right
}
MouseArea
{
anchors.fill: parent
onPressAndHold:
{
fingerPrintDialog.nick = model.name
fingerPrintDialog.gxs_id = model.gxs_id
fingerPrintDialog.visible = true
}
}
}
Text
{
id: nickText
color: model.own ? "blue" : "black"
text: model.name
anchors.left: colorHash.right
anchors.leftMargin: 5
anchors.verticalCenter: parent.verticalCenter
}
Rectangle
{
visible: contactsView.unreadMessages.hasOwnProperty(model.gxs_id)
anchors.right: parent.right
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
color: "cornflowerblue"
antialiasing: true
border.color: "blue"
border.width: 1
radius: height/2
height: parent.height - 10
width: height
Text
{
color: "white"
font.bold: true
text: contactsView.unreadMessages.hasOwnProperty(model.gxs_id) ?
contactsView.unreadMessages[model.gxs_id] : ''
anchors.centerIn: parent
}
}
}
}

View file

@ -17,7 +17,7 @@
*/ */
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 1.4 import QtQuick.Controls 2.0
import org.retroshare.qml_components.LibresapiLocalClient 1.0 import org.retroshare.qml_components.LibresapiLocalClient 1.0
Item Item
@ -182,6 +182,7 @@ Item
id: attemptTimer id: attemptTimer
interval: 500 interval: 500
repeat: true repeat: true
triggeredOnStart: true
onTriggered: onTriggered:
{ {
if(locationView.focus) if(locationView.focus)

View file

@ -18,7 +18,7 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Controls 1.4 import QtQuick.Controls 2.0
import QtQml 2.2 import QtQml 2.2
import org.retroshare.qml_components.LibresapiLocalClient 1.0 import org.retroshare.qml_components.LibresapiLocalClient 1.0

View file

@ -17,7 +17,7 @@
*/ */
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 1.4 import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2 import QtQuick.Dialogs 1.2
import "jsonpath.js" as JSONPath import "jsonpath.js" as JSONPath
@ -40,8 +40,10 @@ Item
function isOnline(pgpId) function isOnline(pgpId)
{ {
var qr = "$.data[?(@.pgp_id=='"+pgpId+"')].locations[*].is_online" var qr = "$.data[?(@.pgp_id=='"+pgpId+"')].locations[*].is_online"
var locArr = JSONPath.jsonPath(JSON.parse(jsonModel.json), qr) var locOn = JSONPath.jsonPath(JSON.parse(jsonModel.json), qr)
return locArr.reduce(function(cur,acc){return cur || acc}, false) if (Array.isArray(locOn))
return locOn.reduce(function(cur,acc){return cur || acc}, false)
return Boolean(locOn)
} }
} }
@ -65,7 +67,8 @@ Item
height: parent.height - 4 height: parent.height - 4
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
anchors.leftMargin: 6 anchors.left: parent.left
anchors.leftMargin: 3
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Text Text
@ -130,7 +133,7 @@ Item
id: bottomButton id: bottomButton
text: "Add Trusted Node" text: "Add Trusted Node"
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
onClicked: stackView.push({item:"qrc:/qml/AddTrustedNode.qml"}) onClicked: stackView.push("qrc:/qml/AddTrustedNode.qml")
width: parent.width width: parent.width
} }
@ -138,6 +141,7 @@ Item
{ {
interval: 800 interval: 800
repeat: true repeat: true
triggeredOnStart: true
onTriggered: if(trustedNodesView.visible) trustedNodesView.refreshData() onTriggered: if(trustedNodesView.visible) trustedNodesView.refreshData()
Component.onCompleted: start() Component.onCompleted: start()
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -17,7 +17,7 @@
*/ */
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.4 import QtQuick.Controls 2.0
import org.retroshare.qml_components.LibresapiLocalClient 1.0 import org.retroshare.qml_components.LibresapiLocalClient 1.0
ApplicationWindow ApplicationWindow
@ -28,8 +28,10 @@ ApplicationWindow
width: 400 width: 400
height: 400 height: 400
toolBar: ToolBar header: ToolBar
{ {
id: toolBar
Image Image
{ {
id: rsIcon id: rsIcon
@ -45,22 +47,48 @@ ApplicationWindow
anchors.left: rsIcon.right anchors.left: rsIcon.right
anchors.leftMargin: 20 anchors.leftMargin: 20
} }
} MouseArea
menuBar: MenuBar
{
Menu
{ {
MenuItem height: parent.height
width: parent.height
anchors.right: parent.right
anchors.rightMargin: 2
anchors.verticalCenter: parent.verticalCenter
onClicked: menu.open()
Image
{ {
text: "Trusted Nodes" source: "qrc:/qml/icons/application-menu.png"
onTriggered: height: parent.height - 10
stackView.push({item:"qrc:/qml/TrustedNodesView.qml"}) width: parent.height - 10
anchors.centerIn: parent
} }
MenuItem
Menu
{ {
text: "StackView State" id: menu
onTriggered: console.log(stackView.state, stackView.enabled) y: parent.y + parent.height
MenuItem
{
text: qsTr("Trusted Nodes")
//iconSource: "qrc:/qml/icons/document-share.png"
onTriggered:
stackView.push("qrc:/qml/TrustedNodesView.qml")
}
MenuItem
{
text: qsTr("Search Contacts")
onTriggered:
stackView.push(
"qrc:/qml/Contacts.qml", {'searching': true} )
}
MenuItem
{
text: "Terminate Core"
onTriggered: rsApi.request("/control/shutdown")
}
} }
} }
} }
@ -102,6 +130,7 @@ ApplicationWindow
id: refreshTimer id: refreshTimer
interval: 800 interval: 800
repeat: true repeat: true
triggeredOnStart: true
onTriggered: if(stackView.visible) stackView.checkCoreStatus() onTriggered: if(stackView.visible) stackView.checkCoreStatus()
Component.onCompleted: start() Component.onCompleted: start()
} }
@ -123,7 +152,7 @@ ApplicationWindow
{ {
console.log("StateChangeScript waiting_account_select") console.log("StateChangeScript waiting_account_select")
stackView.clear() stackView.clear()
stackView.push({item:"qrc:/qml/Locations.qml"}) stackView.push("qrc:/qml/Locations.qml")
} }
} }
}, },
@ -137,7 +166,7 @@ ApplicationWindow
{ {
console.log("StateChangeScript waiting_account_select") console.log("StateChangeScript waiting_account_select")
stackView.clear() stackView.clear()
stackView.push({item: "qrc:/qml/Contacts.qml"}) stackView.push("qrc:/qml/Contacts.qml")
} }
} }
}, },

View file

@ -12,6 +12,8 @@ RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model # Additional import path used to resolve QML modules in Qt Creator's code model
#QML_IMPORT_PATH = #QML_IMPORT_PATH =
#QML2_IMPORT_PATH =
# Default rules for deployment. # Default rules for deployment.
include(deployment.pri) include(deployment.pri)