diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 7114b3ec..b4ebe0dc 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -48,14 +48,14 @@ type GrandCentralDispatcher struct { _ func(failed bool) `signal:"ChangePasswordResponse"` // contact list stuff - _ func(handle, displayName, image, server string, badge, status int, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"` - _ func(handle, displayName string) `signal:"UpdateContactDisplayName"` - _ func(handle, image string) `signal:"UpdateContactPicture"` - _ func(handle string, status int, loading bool) `signal:"UpdateContactStatus"` - _ func(handle string, blocked bool) `signal:"UpdateContactBlocked"` - _ func(handle string) `signal:"IncContactUnreadCount"` - _ func(handle string) `signal:"RemoveContact"` - _ func(handle, key, value string) `signal:"UpdateContactAttribute"` + _ func(handle, displayName, image string, badge, status int, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"` + _ func(handle, displayName string) `signal:"UpdateContactDisplayName"` + _ func(handle, image string) `signal:"UpdateContactPicture"` + _ func(handle string, status int, loading bool) `signal:"UpdateContactStatus"` + _ func(handle string, blocked bool) `signal:"UpdateContactBlocked"` + _ func(handle string) `signal:"IncContactUnreadCount"` + _ func(handle string) `signal:"RemoveContact"` + _ func(handle, key, value string) `signal:"UpdateContactAttribute"` // messages pane stuff _ func(handle, from, displayName, message, image string, mID string, fromMe bool, ts string, ackd bool, error bool) `signal:"AppendMessage"` @@ -590,6 +590,9 @@ func (this *GrandCentralDispatcher) unlockProfiles(password string) { func (this *GrandCentralDispatcher) loadProfile(onion string) { the.Peer = the.CwtchApp.GetPeer(onion) + if the.Peer == nil { + return + } the.EventBus = the.CwtchApp.GetEventBus(onion) picVal, exists := the.Peer.GetAttribute(attr.GetPublicScope(constants.Picture)) diff --git a/go/ui/manager.go b/go/ui/manager.go index d5355c4d..3ad2fe73 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -242,7 +242,7 @@ func (this *manager) AddContact(handle string) { unread := countUnread(group.Timeline.GetMessages(), lastRead) picture := getProfilePic(handle) - this.gcd.AddContact(handle, getNick(handle), picture, group.GroupServer, unread, int(connections.ConnectionStateToType[group.State]), false, false, getLastMessageTime(&group.Timeline)) + this.gcd.AddContact(handle, getNick(handle), picture, unread, int(connections.ConnectionStateToType[group.State]), false, false, getLastMessageTime(&group.Timeline)) } return } else if !isPeer(handle) { @@ -257,7 +257,7 @@ func (this *manager) AddContact(handle string) { unread := countUnread(contact.Timeline.GetMessages(), lastRead) picture := getProfilePic(handle) - this.gcd.AddContact(handle, getNick(handle), picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false, getLastMessageTime(&contact.Timeline)) + this.gcd.AddContact(handle, getNick(handle), picture, unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false, getLastMessageTime(&contact.Timeline)) } }) } diff --git a/qml.qrc b/qml.qrc index 7fa985ce..c524d5b1 100644 --- a/qml.qrc +++ b/qml.qrc @@ -18,7 +18,10 @@ qml/styles/CwtchTextAreaStyle.qml qml/styles/CwtchTextFieldStyle.qml qml/widgets/ContactList.qml - qml/widgets/ContactPicture.qml + qml/widgets/Portrait.qml + qml/widgets/Badge.qml + qml/widgets/PortraitRow.qml + qml/widgets/ProfileRow.qml qml/widgets/ContactRow.qml qml/widgets/EmojiDrawer.qml qml/widgets/FontAwesome.qml diff --git a/qml/main.qml b/qml/main.qml index 9de89540..c1a58094 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -126,21 +126,15 @@ ApplicationWindow { RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR - /* anchors.left: ratio >= 0.92 ? parent.left : toolbar.right - anchors.top: ratio >= 0.92 ? toolbar.bottom : parent.top - anchors.right: parent.right - anchors.bottom: parent.bottom */ anchors.fill: parent spacing: 0 - - Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS color: "#D2C0DD" Layout.fillHeight: true Layout.minimumWidth: Layout.maximumWidth - Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : 450 - visible: (ratio <= 1.08 && windowItem.width >= 700 && !Qt.inputMethod.visible) || theStack.pane == theStack.emptyPane + Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : Theme.sidePaneMinSize + visible: (windowItem.width >= Theme.doublePaneMinSize && !Qt.inputMethod.visible) || theStack.pane == theStack.emptyPane ContactList{ diff --git a/qml/overlays/MembershipOverlay.qml b/qml/overlays/MembershipOverlay.qml index b9297717..586b2d6f 100644 --- a/qml/overlays/MembershipOverlay.qml +++ b/qml/overlays/MembershipOverlay.qml @@ -106,7 +106,6 @@ ColumnLayout { displayName: _displayName image: _image blocked: false - background: false } } } diff --git a/qml/theme/CwtchDark.qml b/qml/theme/CwtchDark.qml index f52cc6cc..14d0ed2b 100644 --- a/qml/theme/CwtchDark.qml +++ b/qml/theme/CwtchDark.qml @@ -6,12 +6,28 @@ ThemeType { readonly property color purple: "#DFB9DE" readonly property color whitePurple: "#FFFDFF" readonly property color softPurple: "#FDF3FC" - readonly property color hotPink: "#d01972" + readonly property color pink: "#E85DA1" + readonly property color hotPink: "#D01972" backgroundMainColor: darkGrayPurple backgroundPaneColor: mauvePurple mainTextColor: whitePurple defaultButtonColor: hotPink + defaultButtonActiveColor: pink defaultButtonTextColor: whitePurple + + portraitOnlineBorderColor: whitePurple + portraitOnlineBackgroundColor: darkGrayPurple + portraitOnlineTextColor: whitePurple + portraitConnectingBorderColor: mauvePurple + portraitConnectingBackgroundColor: darkGrayPurple + portraitConnectingTextColor: whitePurple + portraitOfflineBorderColor: deepPurple + portraitOfflineBackgroundColor: darkGrayPurple + portraitOfflineTextColor: softPurple + + portraitContactBadgeColor: hotPink + portraitContactBadgeTextColor: whitePurple + portraitProfileBadgeColor: mauvePurple } \ No newline at end of file diff --git a/qml/theme/CwtchLight.qml b/qml/theme/CwtchLight.qml index ce1635ec..aad84706 100644 --- a/qml/theme/CwtchLight.qml +++ b/qml/theme/CwtchLight.qml @@ -2,16 +2,32 @@ ThemeType { readonly property color whitePurple: "#FFFDFF" readonly property color softPurple: "#FDF3FC" - readonly property color purple: "#FDF3FC" + readonly property color purple: "#DFB9DE" readonly property color brightPurple: "#760388" readonly property color darkPurple: "#350052" readonly property color greyPurple: "#775F84" - readonly property color hotPink: "#d01972" + readonly property color pink: "#E85DA1" + readonly property color hotPink: "#D01972" backgroundMainColor: whitePurple - backgroundPaneColor: purple + backgroundPaneColor: softPurple mainTextColor: darkPurple defaultButtonColor: hotPink + defaultButtonActiveColor: pink defaultButtonTextColor: whitePurple + + portraitOnlineBorderColor: darkPurple + portraitOnlineBackgroundColor: darkPurple + portraitOnlineTextColor: darkPurple + portraitConnectingBorderColor: greyPurple + portraitConnectingBackgroundColor: greyPurple + portraitConnectingTextColor: greyPurple + portraitOfflineBorderColor: purple + portraitOfflineBackgroundColor: purple + portraitOfflineTextColor: purple + + portraitContactBadgeColor: hotPink + portraitContactBadgeTextColor: whitePurple + portraitProfileBadgeColor: brightPurple } \ No newline at end of file diff --git a/qml/theme/Theme.qml b/qml/theme/Theme.qml index 1023db74..5fc5685b 100644 --- a/qml/theme/Theme.qml +++ b/qml/theme/Theme.qml @@ -8,7 +8,33 @@ Item { readonly property color mainTextColor: theme.mainTextColor readonly property color defaultButtonColor: theme.defaultButtonColor + readonly property color defaultButtonActiveColor: theme.defaultButtonActiveColor readonly property color defaultButtonTextColor: theme.defaultButtonTextColor + readonly property color portraitOnlineBorderColor: theme.portraitOnlineBorderColor + readonly property color portraitOnlineBackgroundColor: theme.portraitOnlineBackgroundColor + readonly property color portraitOnlineTextColor: theme.portraitOnlineTextColor + readonly property color portraitConnectingBorderColor: theme.portraitConnectingBorderColor + readonly property color portraitConnectingBackgroundColor: theme.portraitConnectingBackgroundColor + readonly property color portraitConnectingTextColor: theme.portraitConnectingTextColor + readonly property color portraitOfflineBorderColor: theme.portraitOfflineBorderColor + readonly property color portraitOfflineBackgroundColor: theme.portraitOfflineBackgroundColor + readonly property color portraitOfflineTextColor: theme.portraitOfflineTextColor + + readonly property color portraitContactBadgeColor: theme.portraitContactBadgeColor + readonly property color portraitContactBadgeTextColor: theme.portraitContactBadgeTextColor + readonly property color portraitProfileBadgeColor: theme.portraitProfileBadgeColor + + readonly property int headerSize: 50 + readonly property int usernameSize: 30 + readonly property int tabSize: 25 + readonly property int chatSize: 20 + readonly property int secondaryTextSize: 20 // address + readonly property int chatMetaTextSize: 15 + readonly property int badgeTextSize: 12 + + readonly property int sidePaneMinSize: 700 + readonly property int doublePaneMinSize: 1000 + property ThemeType theme: CwtchLight { } } \ No newline at end of file diff --git a/qml/theme/ThemeType.qml b/qml/theme/ThemeType.qml index cd8aa725..174c190a 100644 --- a/qml/theme/ThemeType.qml +++ b/qml/theme/ThemeType.qml @@ -6,7 +6,23 @@ QtObject { property color mainTextColor: "red" property color defaultButtonColor: "red" + property color defaultButtonActiveColor: "red" property color defaultButtonTextColor: "red" + + property color portraitOnlineBorderColor: "red" + property color portraitOnlineBackgroundColor: "red" + property color portraitOnlineTextColor: "red" + property color portraitConnectingBorderColor: "red" + property color portraitConnectingBackgroundColor: "red" + property color portraitConnectingTextColor: "red" + property color portraitOfflineBorderColor: "red" + property color portraitOfflineBackgroundColor: "red" + property color portraitOfflineTextColor: "red" + + property color portraitContactBadgeColor: "red" + property color portraitContactBadgeTextColor: "red" + property color portraitProfileBadgeColor: "red" + // ... more to come diff --git a/qml/widgets/Badge.qml b/qml/widgets/Badge.qml new file mode 100644 index 00000000..7cf04d36 --- /dev/null +++ b/qml/widgets/Badge.qml @@ -0,0 +1,24 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import CustomQmlTypes 1.0 +import "../theme" + +Rectangle { + width: parent.width * 0.25 + height: width + radius: width/2 + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: parent.width * 0.15 + property alias content: container.children + + Column { + id: container + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + +} \ No newline at end of file diff --git a/qml/widgets/Button.qml b/qml/widgets/Button.qml index 64502b90..dacb9c17 100644 --- a/qml/widgets/Button.qml +++ b/qml/widgets/Button.qml @@ -15,7 +15,7 @@ Rectangle { height: 20 * gcd.themeScale Layout.minimumHeight: height Layout.maximumHeight: height - color: mousedown ? Qt.lighter(Theme.defaultButtonColor, 1.5) : Theme.defaultButtonColor + color: mousedown ? Theme.defaultButtonActiveColor : Theme.defaultButtonColor border.color: Theme.defaultButtonColor border.width: 1 radius: (height / 2.0) diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index 1552475d..13630fbb 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -47,7 +47,7 @@ ColumnLayout { Connections { // ADD/REMOVE CONTACT ENTRIES target: gcd - onAddContact: function(handle, displayName, image, server, badge, status, blocked, loading, lastMsgTs) { + onAddContact: function(handle, displayName, image, badge, status, blocked, loading, lastMsgTs) { for (var i = 0; i < contactsModel.count; i++) { if (contactsModel.get(i)["_handle"] == handle) { @@ -67,7 +67,6 @@ ColumnLayout { "_handle": handle, "_displayName": displayName + (blocked ? " (blocked)" : "" ), "_image": image, - "_server": server, "_badge": badge, "_status": status, "_blocked": blocked, @@ -95,7 +94,6 @@ ColumnLayout { if(contactsModel.get(i)["_handle"] == handle) { var contact = contactsModel.get(i) contact["_lastMsgTs"] = ts - console.log("Found at " + i + " contact: " + contact) contactsModel.move(i, 0, 1) } } @@ -116,12 +114,10 @@ ColumnLayout { handle: _handle displayName: _displayName image: _image - server: _server badge: _badge status: _status blocked: _blocked loading: _loading - type: "contact" } } diff --git a/qml/widgets/ContactPicture.qml b/qml/widgets/ContactPicture.qml deleted file mode 100644 index ca7b9925..00000000 --- a/qml/widgets/ContactPicture.qml +++ /dev/null @@ -1,85 +0,0 @@ -import QtGraphicalEffects 1.0 -import QtQuick 2.7 -import QtQuick.Controls 2.4 -import QtQuick.Controls.Material 2.0 -import QtQuick.Layouts 1.3 -import CustomQmlTypes 1.0 - -Item { - id: imgProfile - implicitWidth: baseWidth - implicitHeight: baseWidth - anchors.margins: 5 - - property string handle - property string source - property int status - property bool isGroup - property bool showStatus - property bool highlight - property bool button - property real logscale: 4 * Math.log10(gcd.themeScale + 1) - property int baseWidth: 48 * logscale - - Rectangle { - id: mainImage - width: baseWidth - height: baseWidth - color: "#350052" //: "#FFFFFF" //windowItem.cwtch_dark_color: "#FFFFFF" - radius: width / 2 - - Rectangle { - width: highlight ? baseWidth - 4 : baseWidth - height: highlight ? baseWidth - 4 : baseWidth - color: "#350052" //: "#FFFFFF" // windowItem.cwtch_dark_color: "#FFFFFF" - radius: width / 2 - anchors.centerIn:parent - - - Image { // PROFILE IMAGE - id: img - source: gcd.assetPath + imgProfile.source - anchors.fill: parent - fillMode: Image.PreserveAspectFit - visible: false - } - - Image { // CIRCLE MASK - id: mask - fillMode: Image.PreserveAspectFit - visible: false - source: "qrc:/qml/images/extra/clipcircle.png" - } - - OpacityMask { - anchors.fill: img - source: img - maskSource: mask - } - - } - - Rectangle { // PRESENCE INDICATOR - visible: showStatus - color: "#FFFFFF" - width: 8 * logscale - height: 8 * logscale - radius: 2 * logscale - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 4 * logscale - - - Rectangle { //-2:WtfCodeError,-1:Error,0:Disconnected,1:Connecting,2:Connected,3:Authenticated,4:Synced,5:Failed,6:Killed - color: status == 4 ? "green" : status == 3 ? "green" : status == -1 ? "blue" : status == 1 ? "orange" : status == 2 ? "orange" : "red" - width: 5 * logscale - height: 5 * logscale - radius: 2 * logscale - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 1.5 * logscale - } - } - - } -} \ No newline at end of file diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 2da8f502..ce315713 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -6,255 +6,97 @@ import QtQuick.Layouts 1.3 import CustomQmlTypes 1.0 import "../styles" import "../widgets" as Widgets +import "../theme" import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 +PortraitRow { -Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY - id: crItem - anchors.left: parent.left - anchors.right: parent.right - height: 48 * logscale + 3 - implicitHeight: height - - property real logscale: 4 * Math.log10(gcd.themeScale + 1) - property alias displayName: cn.text - property alias image: imgProfile.source - property string handle - property int badge - property bool isActive - property bool isHover - property bool background: true - property string type // profile or contact or button - property string tag // profile version/type - - // Profile - property bool defaultPassword - - // Contact - property bool blocked + property int status: 0 + property int badge property bool loading - property alias status: imgProfile.status - property string server + badgeColor: Theme.portraitContactBadgeColor + badgeVisible: badge > 0 - Rectangle { // CONTACT ENTRY BACKGROUND COLOR - id: crRect - anchors.left: parent.left - anchors.right: parent.right - height: 48 * logscale + 3 - width: parent.width - color: background ? (isHover ? "#D2D2F3" : (isActive ? "#D2D2F3" : "#D2C0DD")) : windowItem.cwtch_background_color - - ContactPicture { - id: imgProfile - showStatus: type == "contact" - button: type == "button" - } - - ColumnLayout { - - anchors.left: imgProfile.right - anchors.right: loading ? loadingProgress.left : rectUnread.left - anchors.verticalCenter: parent.verticalCenter - - - Label { // CONTACT NAME - id: cn - leftPadding: 10 - rightPadding: 10 - //wrapMode: Text.WordWrap - font.pixelSize: 16 * gcd.themeScale - font.strikeout: blocked - textFormat: Text.PlainText - //fontSizeMode: Text.HorizontalFit - elide: Text.ElideRight - color: "#000000" - } - - Label { // Onion - id: onion - text: handle - leftPadding: 10 - rightPadding: 10 - font.pixelSize: 10 * gcd.themeScale - font.strikeout: blocked - textFormat: Text.PlainText - //fontSizeMode: Text.HorizontalFit - elide: Text.ElideRight - color: "#000000" - } - - } - - Rectangle { // UNREAD MESSAGES? - id: rectUnread - height: txtmetric.tightBoundingRect.height + 8 * gcd.themeScale - width: txtmetric.tightBoundingRect.width + 8 * gcd.themeScale - radius: 8 * gcd.themeScale - color: "#4B3557" - visible: badge != 0 - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 9 * gcd.themeScale - - Label { - id: lblUnread - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - color: "#FFFFFF" - font.pixelSize: 12 * gcd.themeScale - text: txtmetric.text - } - - TextMetrics { - id: txtmetric - text: badge - font: lblUnread.font - } - } - - // Profile - - Image {// Profle Type - id: profiletype - - source: tag == "v1-userPassword" ? gcd.assetPath + "/fontawesome/solid/lock.svg" : gcd.assetPath + "/fontawesome/solid/lock-open.svg" - - anchors.right: parent.right - - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 1 * gcd.themeScale - anchors.rightMargin: (20 * gcd.themeScale) + parent.height - height: parent.height * 0.5 - width: height - - visible: type == "profile" - - } - - - // Contact - ProgressBar { // LOADING ? - id: loadingProgress - property bool running - running: loading - visible: loading - - anchors.right: rectUnread.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 1 * gcd.themeScale - anchors.rightMargin: 1 * gcd.themeScale - - - height: cn.height/2 - width: 100 * gcd.themeScale - - - - indeterminate: true - - style: ProgressBarStyle { - progress: CwtchProgress { running: loadingProgress.running} - } - } - - } - - MouseArea { // ONCLICK: LOAD CONVERSATION WITH THIS CONTACT - anchors.fill: parent - hoverEnabled: true - - onClicked: { - if (type == "contact") { - if (displayName != "me") { - gcd.broadcast("ResetMessagePane") - isActive = true - theStack.pane = theStack.messagePane - gcd.loadMessagesPane(handle) - badge = 0 - if (handle.length == 32) { - gcd.requestGroupSettings(handle) - } - } - } else if (type == "profile") { - gcd.broadcast("ResetMessagePane") - gcd.broadcast("ResetProfile") - gcd.selectedProfile = handle - gcd.loadProfile(handle) - parentStack.pane = parentStack.profilePane - } else if (type == "button") { // Add profile button - profileAddEditPane.reset() - parentStack.pane = parentStack.addEditProfilePane - } - } - - onEntered: { - isHover = true - } - - onExited: { - isHover = false - } - } - - Widgets.Button {// Edit BUTTON - id: btnEdit - icon: "solid/user-edit" - - anchors.right: parent.right - - //rectUnread.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 1 * gcd.themeScale - anchors.rightMargin: 20 * gcd.themeScale - height: parent.height * 0.75 - - visible: type == "profile" - - - onClicked: { - profileAddEditPane.load(handle, displayName, tag) - parentStack.pane = parentStack.addEditProfilePane - } + badgeContent: Label { + id: lblUnread + color: Theme.portraitContactBadgeTextColor + font.pixelSize: Theme.badgeTextSize * gcd.themeScale + font.weight: Font.Bold + text: badge > 99 ? "99+" : badge } - Connections { // UPDATE UNREAD MESSAGES COUNTER - target: gcd + ProgressBar { // LOADING ? + id: loadingProgress + property bool running + running: loading + visible: loading - onResetMessagePane: function() { - isActive = false - } + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 1 * gcd.themeScale + anchors.rightMargin: 25 * gcd.themeScale - onUpdateContactStatus: function(_handle, _status, _loading) { - if (handle == _handle) { - status = _status - loadingProgress.visible = loadingProgress.running = loading = _loading + height: parent.height * .1 + width: 100 * gcd.themeScale + + indeterminate: true + + style: ProgressBarStyle { + progress: CwtchProgress { running: loadingProgress.running} + } + } + + onClicked: function(handle) { + gcd.broadcast("ResetMessagePane") + isActive = true + theStack.pane = theStack.messagePane + gcd.loadMessagesPane(handle) + badge = 0 + if (handle.length == 32) { + gcd.requestGroupSettings(handle) + } + } + + Component.onCompleted: { setColors(status) } + + onStatusChanged: { setColors(status) } + + function setColors(status) { + //-2:WtfCodeError,-1:Error,0:Disconnected,1:Connecting,2:Connected,3:Authenticated,4:Synced,5:Failed,6:Killed + if (status == 4 || status == 3) { + portraitBorderColor = Theme.portraitOnlineBorderColor + portraitColor = Theme.portraitOnlineBackgroundColor + nameColor = Theme.portraitOnlineTextColor + onionColor = Theme.portraitOnlineTextColor + } else if (status == 2 || status == 1) { + portraitBorderColor = Theme.portraitConnectingBorderColor + portraitColor = Theme.portraitConnectingBackgroundColor + nameColor = Theme.portraitConnectingTextColor + onionColor = Theme.portraitConnectingTextColor + } else { + portraitBorderColor = Theme.portraitOfflineBorderColor + portraitColor = Theme.portraitOfflineBackgroundColor + nameColor = Theme.portraitOfflineTextColor + onionColor = Theme.portraitOfflineTextColor + } + } + + Connections { // UPDATE UNREAD MESSAGES COUNTER + target: gcd + + onUpdateContactStatus: function(_handle, _status, _loading) { + if (handle == _handle) { + status = _status + loadingProgress.visible = loadingProgress.running = loading = _loading + } + } + + onIncContactUnreadCount: function(handle) { + if (handle == _handle && gcd.selectedConversation != handle) { + badge++ } } - - onUpdateContactBlocked: function(_handle, _blocked) { - if (handle == _handle) { - blocked = _blocked - } - } - - onUpdateContactDisplayName: function(_handle, _displayName) { - if (handle == _handle) { - displayName = _displayName + (_blocked == true ? " (blocked)" : "") - } - } - - onUpdateContactPicture: function(_handle, _image) { - if (handle == _handle) { - image = _image - } - } - - onIncContactUnreadCount: function(handle) { - if (handle == _handle && gcd.selectedConversation != handle) { - badge++ - } - } - } -} + } +} \ No newline at end of file diff --git a/qml/widgets/Message.qml b/qml/widgets/Message.qml index d59b2fe1..ee97fd07 100644 --- a/qml/widgets/Message.qml +++ b/qml/widgets/Message.qml @@ -23,7 +23,6 @@ Item { property bool ackd property alias timestamp: ts.text property alias image: imgProfile.source - property alias status: imgProfile.status property string error Connections { @@ -43,13 +42,13 @@ Item { } - ContactPicture { + Portrait { id: imgProfile anchors.left: parent.left handle: root.from visible: !fromMe - showStatus: false - highlight: ima.containsMouse + //showStatus: false + //highlight: ima.containsMouse ToolTip.visible: ima.containsMouse //: Click to DM diff --git a/qml/widgets/Portrait.qml b/qml/widgets/Portrait.qml new file mode 100644 index 00000000..d405c032 --- /dev/null +++ b/qml/widgets/Portrait.qml @@ -0,0 +1,68 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import CustomQmlTypes 1.0 +import "../theme" + +Item { + id: imgProfile + implicitWidth: baseWidth + implicitHeight: baseWidth + + property string handle + property string source + property alias badgeColor: badge.color + + property real logscale: 4 * Math.log10(gcd.themeScale + 1) + property int baseWidth: parent.height + + property alias portraitBorderColor: mainImage.color + property alias portraitColor: imageInner.color + property alias badgeVisible: badge.visible + property alias badgeContent: badge.content + + Rectangle { + id: mainImage + width: baseWidth * 0.8 + height: width + anchors.verticalCenter: parent.verticalCenter + color: Theme.portraitOfflineBorderColor + radius: width / 2 + + Rectangle { + id: imageInner + width: parent.width - 4 + height: width + color: Theme.portraitOfflineBorderColor + radius: width / 2 + anchors.centerIn:parent + + Image { // PROFILE IMAGE + id: img + source: gcd.assetPath + imgProfile.source + anchors.fill: parent + fillMode: Image.PreserveAspectFit + visible: false + } + + Image { // CIRCLE MASK + id: mask + fillMode: Image.PreserveAspectFit + visible: false + source: "qrc:/qml/images/extra/clipcircle.png" + } + + OpacityMask { + anchors.fill: img + source: img + maskSource: mask + } + } + } + + Badge { + id: badge + } +} \ No newline at end of file diff --git a/qml/widgets/PortraitRow.qml b/qml/widgets/PortraitRow.qml new file mode 100644 index 00000000..d506ce58 --- /dev/null +++ b/qml/widgets/PortraitRow.qml @@ -0,0 +1,160 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import CustomQmlTypes 1.0 +import "../styles" +import "../widgets" as Widgets +import "../theme" +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY + id: crItem + anchors.left: parent.left + anchors.right: parent.right + height: 78 * logscale + 3 + implicitHeight: 78 * logscale + 3 //height + + property real logscale: 4 * Math.log10(gcd.themeScale + 1) + property string displayName //: nameTxtmetric.text + property alias image: portrait.source + property string handle + property bool isActive + property bool isHover + property string tag // profile version/type + + property alias badgeColor: portrait.badgeColor + property alias portraitBorderColor: portrait.portraitBorderColor + property alias portraitColor: portrait.portraitColor + property alias nameColor: cn.color + property alias onionColor: onion.color + property alias badgeVisible: portrait.badgeVisible + property alias badgeContent: portrait.badgeContent + + + // TODO: should be in ContactRow + property bool blocked + + signal clicked(string handle) + + Rectangle { // CONTACT ENTRY BACKGROUND COLOR + id: crRect + anchors.left: parent.left + anchors.right: parent.right + height: crItem.height + width: parent.width + color: isHover ? Theme.backgroundPaneColor : (isActive ? Theme.backgroundPaneColor : Theme.backgroundMainColor) + + Portrait { + id: portrait + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 25 * logscale + } + + ColumnLayout { + + anchors.left: portrait.right + anchors.leftMargin: 4 * logscale + anchors.verticalCenter: parent.verticalCenter + + Label { // CONTACT NAME + id: cn + leftPadding: 10 + rightPadding: 10 + //wrapMode: Text.WordWrap + font.pixelSize: Theme.usernameSize * gcd.themeScale + font.weight: Font.Bold + font.strikeout: blocked + elide: Text.ElideRight + text: nameTxtmetric.text + } + + TextMetrics { + id: nameTxtmetric + text: displayName + font: cn.font + } + + + Label { // Onion + id: onion + text: onionTxtmetric.text + leftPadding: 10 + rightPadding: 10 + font.pixelSize: Theme.secondaryTextSize * gcd.themeScale + font.strikeout: blocked + textFormat: Text.PlainText + elide: Text.ElideRight + } + + TextMetrics { + id: onionTxtmetric + text: handle + font: onion.font + } + + } + + onWidthChanged: { + nameTxtmetric.text = displayName + onionTxtmetric.text = handle + var i = 2 + var maxWidth = Math.max(200, width - portrait.width - (50 * logscale)) + + while (nameTxtmetric.width > maxWidth) { + nameTxtmetric.text = displayName.slice(0, displayName.length - (i * 3)) + "..." + i++ + } + i = 2 + while (onionTxtmetric.width > maxWidth) { + onionTxtmetric.text = handle.slice(0, handle.length - (i * 3)) + "..." + i++ + } + } + } + + MouseArea { // Full row mouse area triggering onClick + id: buttonMA + anchors.fill: parent + hoverEnabled: true + + onClicked: { crItem.clicked(crItem.handle) } + + onEntered: { + isHover = true + } + + onExited: { + isHover = false + } + } + + Connections { // UPDATE UNREAD MESSAGES COUNTER + target: gcd + + onResetMessagePane: function() { + isActive = false + } + + onUpdateContactBlocked: function(_handle, _blocked) { + if (handle == _handle) { + blocked = _blocked + } + } + + onUpdateContactDisplayName: function(_handle, _displayName) { + if (handle == _handle) { + displayName = _displayName + (_blocked == true ? " (blocked)" : "") + } + } + + onUpdateContactPicture: function(_handle, _image) { + if (handle == _handle) { + image = _image + } + } + } +} diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml index 8c69e28b..512ab35f 100644 --- a/qml/widgets/ProfileList.qml +++ b/qml/widgets/ProfileList.qml @@ -3,11 +3,11 @@ import QtQuick 2.7 import QtQuick.Controls 2.4 import QtQuick.Controls.Material 2.0 import QtQuick.Layouts 1.3 +import "../theme" ColumnLayout { id: root - MouseArea { anchors.fill: parent @@ -44,7 +44,6 @@ ColumnLayout { onAddProfile: function(handle, displayName, image, tag) { // don't add duplicates - console.log("ProfileList onAddProfile for: " + handle) for (var i = 0; i < profilesModel.count; i++) { if (profilesModel.get(i)["_handle"] == handle) { return @@ -52,8 +51,8 @@ ColumnLayout { } // find index for insert (sort by onion) - var index = profilesModel.count-1 - for (var i = 0; i < profilesModel.count-1; i++) { + var index = profilesModel.count + for (var i = 0; i < profilesModel.count; i++) { if (profilesModel.get(i)["_handle"] > handle) { index = i break @@ -62,11 +61,11 @@ ColumnLayout { profilesModel.insert(index, { - "_handle": handle, - "_displayName": displayName, - "_image": image, - "_type": "profile", - "_tag": tag + _handle: handle, + _displayName: displayName, + _image: image, + _tag: tag, + _status: 4, }) } @@ -83,43 +82,41 @@ ColumnLayout { onResetProfileList: function() { profilesModel.clear() - profilesModel.append({ - _handle: "", - _displayName: qsTr("add-new-profile-btn"), - _image: "/fontawesome/solid/user-plus.svg", - _type: "button", - _tag: "," - }) } } ListModel { // Profile OBJECTS ARE STORED HERE ... id: profilesModel - - ListElement { - _handle: "" - _displayName: qsTr("add-new-profile-btn") - _image: "/fontawesome/solid/user-plus.svg" - _type: "button" - _tag: "" - } } Repeater { + id: profileList model: profilesModel // ... AND DISPLAYED HERE - delegate: ContactRow { + delegate: ProfileRow { handle: _handle displayName: _displayName image: _image - server: "" - badge: 0 - status: 0 blocked: false - loading: false - type: _type tag: _tag } } - } + + PortraitRow { + handle: "" + displayName: qsTr("add-new-profile-btn") + image: "/fontawesome/regular/user.svg" + tag: "" + portraitBorderColor: Theme.defaultButtonColor + portraitColor: Theme.defaultButtonColor + badgeVisible: true + badgeContent: Image { + source: gcd.assetPath + "/fontawesome/solid/plus.svg" + height: Theme.badgeTextSize * gcd.themeScale + width: height + } + badgeColor: Theme.portraitOnlineBorderColor + onClicked: function(handle) { profileAddEditPane.reset(); parentStack.pane = parentStack.addEditProfilePane } + } + } } } diff --git a/qml/widgets/ProfileRow.qml b/qml/widgets/ProfileRow.qml new file mode 100644 index 00000000..6a48be9b --- /dev/null +++ b/qml/widgets/ProfileRow.qml @@ -0,0 +1,56 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import CustomQmlTypes 1.0 +import "../styles" +import "../widgets" as Widgets +import "../theme" +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +PortraitRow { + + badgeColor: Theme.portraitProfileBadgeColor + + portraitBorderColor: Theme.portraitOnlineBorderColor + portraitColor: Theme.portraitOnlineBackgroundColor + nameColor: Theme.portraitOnlineTextColor + onionColor: Theme.portraitOnlineTextColor + + badgeContent: Image {// Profle Type + id: profiletype + source: tag == "v1-userPassword" ? gcd.assetPath + "/fontawesome/solid/lock.svg" : gcd.assetPath + "/fontawesome/solid/lock-open.svg" + height: Theme.badgeTextSize * gcd.themeScale + width: height + } + + Widgets.Button {// Edit BUTTON + id: btnEdit + icon: "solid/user-edit" + + anchors.right: parent.right + + //rectUnread.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 1 * gcd.themeScale + anchors.rightMargin: 20 * gcd.themeScale + height: parent.height * 0.75 + + + + onClicked: { + profileAddEditPane.load(handle, displayName, tag) + parentStack.pane = parentStack.addEditProfilePane + } + } + + onClicked: function openClick(handle) { + gcd.broadcast("ResetMessagePane"); + gcd.broadcast("ResetProfile"); + gcd.selectedProfile = handle + gcd.loadProfile(handle) + parentStack.pane = parentStack.profilePane + } +} \ No newline at end of file