From 533b0d3fe09c4dd0394624e648f22c5c9a701d19 Mon Sep 17 00:00:00 2001 From: erinn Date: Tue, 19 May 2020 18:39:57 -0700 Subject: [PATCH] restoring cwtch-specific widgets that were accidentally moved to opaque --- qml/widgets/ContactList.qml | 171 ++++++++++++++++++++++++++++++++ qml/widgets/ContactRow.qml | 102 +++++++++++++++++++ qml/widgets/Message.qml | 192 ++++++++++++++++++++++++++++++++++++ qml/widgets/MyProfile.qml | 155 +++++++++++++++++++++++++++++ qml/widgets/ProfileList.qml | 124 +++++++++++++++++++++++ qml/widgets/ProfileRow.qml | 56 +++++++++++ 6 files changed, 800 insertions(+) create mode 100644 qml/widgets/ContactList.qml create mode 100644 qml/widgets/ContactRow.qml create mode 100644 qml/widgets/Message.qml create mode 100644 qml/widgets/MyProfile.qml create mode 100644 qml/widgets/ProfileList.qml create mode 100644 qml/widgets/ProfileRow.qml diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml new file mode 100644 index 00000000..cfac63ba --- /dev/null +++ b/qml/widgets/ContactList.qml @@ -0,0 +1,171 @@ +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 "theme" + +ColumnLayout { + id: root + + property alias dualPane: myprof.dualPane + + spacing: 10 + + MouseArea { + anchors.fill: parent + + onClicked: { + forceActiveFocus() + } + } + + MyProfile { // CURRENT PROFILE INFO AND CONTROL BAR + id: myprof + } + + + IconTextField { + id: searchAddText + anchors.horizontalCenter: parent.horizontalCenter + + Layout.minimumWidth: parent.width - 60 + Layout.maximumWidth: parent.width - 60 + + + + + //: ex: "... paste an address here to add a contact ..." + placeholderText: qsTr("paste-address-to-add-contact") + horizontalAlignment: TextInput.AlignHCenter + icon: gcd.assetPath + "core/search-24px.svg" + + + onTextChanged: { + if (text != "") { + gcd.importString(text) + text = "" + } + } + } + + + Flickable { // THE ACTUAL CONTACT LIST + id: sv + //Layout.alignment: Qt.AlignLeft | Qt.AlignTop + clip: true + Layout.minimumHeight: 100 + //Layout.maximumHeight: parent.height - 30 + Layout.fillHeight: true + Layout.minimumWidth: parent.width + Layout.maximumWidth: parent.width + contentWidth: colContacts.width + contentHeight: colContacts.height + boundsBehavior: Flickable.StopAtBounds + maximumFlickVelocity: 400 + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AsNeeded + background: Rectangle { + implicitWidth: 6 + + color: Theme.backgroundMainColor + } + contentItem: Rectangle { + implicitWidth: 6 + implicitHeight:1 + color: Theme.backgroundPaneColor + } + } + + ColumnLayout { + id: colContacts + width: root.width + spacing: 0 + + Connections { // ADD/REMOVE CONTACT ENTRIES + target: gcd + + onAddContact: function(handle, displayName, image, badge, status, blocked, loading, lastMsgTs) { + + for (var i = 0; i < contactsModel.count; i++) { + if (contactsModel.get(i)["_handle"] == handle) { + return + } + } + + var index = contactsModel.count + for (var i = 0; i < contactsModel.count; i++) { + if (contactsModel.get(i)["_lastMsgTs"] < lastMsgTs) { + index = i + break + } + } + + var newContact = { + "_handle": handle, + "_displayName": displayName + (blocked ? " (blocked)" : "" ), + "_image": image, + "_badge": badge, + "_status": status, + "_blocked": blocked, + "_loading": loading, + "_loading": loading, + "_lastMsgTs": lastMsgTs + } + + contactsModel.insert(index, newContact) + } + + onRemoveContact: function(handle) { + for(var i = 0; i < contactsModel.count; i++){ + if(contactsModel.get(i)["_handle"] == handle) { + console.log("deleting contact " + contactsModel.get(i)["_handle"]) + contactsModel.remove(i) + return + } + } + } + + onIncContactUnreadCount: function(handle) { + var ts = Math.round((new Date()).getTime() / 1000); + for(var i = 0; i < contactsModel.count; i++){ + if(contactsModel.get(i)["_handle"] == handle) { + var contact = contactsModel.get(i) + contact["_lastMsgTs"] = ts + contactsModel.move(i, 0, 1) + } + } + } + + onResetProfile: function() { + contactsModel.clear() + } + } + + ListModel { // CONTACT OBJECTS ARE STORED HERE ... + id: contactsModel + } + + Repeater { + model: contactsModel // ... AND DISPLAYED HERE + delegate: ContactRow { + handle: _handle + displayName: _displayName + image: _image + badge: _badge + status: _status + blocked: _blocked + loading: _loading + } + } + + + } + } + + +} + + + diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml new file mode 100644 index 00000000..cee9f203 --- /dev/null +++ b/qml/widgets/ContactRow.qml @@ -0,0 +1,102 @@ +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 "." as Widgets +import "theme" +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +PortraitRow { + + property int status: 0 + property int badge + property bool loading + + badgeColor: Theme.portraitContactBadgeColor + badgeVisible: badge > 0 + + badgeContent: Label { + id: lblUnread + color: Theme.portraitContactBadgeTextColor + font.pixelSize: Theme.badgeTextSize * gcd.themeScale + font.weight: Font.Bold + text: badge > 99 ? "99+" : badge + } + + ProgressBar { // LOADING ? + id: loadingProgress + property bool running + running: loading + visible: loading + + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 1 * gcd.themeScale + anchors.rightMargin: 25 * gcd.themeScale + + 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++ + } + } + } +} diff --git a/qml/widgets/Message.qml b/qml/widgets/Message.qml new file mode 100644 index 00000000..d9f6e3c6 --- /dev/null +++ b/qml/widgets/Message.qml @@ -0,0 +1,192 @@ +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 "controls" as Awesome + +Item { + id: root + + anchors.left: fromMe ? undefined : parent.left + anchors.right: fromMe ? parent.right : undefined + height: Math.max(imgProfile.height, rectMessageBubble.height) + + property string message + property string rawMessage + property string from + property string handle + property string displayName + property string messageID + property bool fromMe + property bool ackd + property alias timestamp: ts.text + property alias image: imgProfile.source + property string error + + Connections { + target: gcd + + onAcknowledged: function(mid) { + if (mid == messageID) { + root.ackd = true + } + } + + onGroupSendError: function(mid, error) { + if (mid == messageID) { + root.error = error + } + } + } + + + Portrait { + id: imgProfile + anchors.left: parent.left + // TODO: currently unused? + //handle: root.from + visible: !fromMe + //showStatus: false + //highlight: ima.containsMouse + + ToolTip.visible: ima.containsMouse + //: Click to DM + ToolTip.text: qsTr("dm-tooltip") + + MouseArea { + id: ima + anchors.fill: parent + hoverEnabled: overlay.inGroup + + + onClicked: { + gcd.createContact(from) + gcd.broadcast("ResetMessagePane") + theStack.pane = theStack.messagePane + gcd.loadMessagesPane(from) + overlayStack.overlay = overlayStack.chatOverlay + } + } + + } + + Rectangle { // THIS IS JUST A PRETTY MESSAGE-HOLDING RECTANGLE + id: rectMessageBubble + height: colMessageBubble.height + 8 + width: colMessageBubble.width + 6 + color: fromMe ? "#B09CBC" : "#4B3557" + radius: 5 + + // the console will complain constantly about me setting these anchors, but qt only allows margins if they've been set to something + // a kludge to fix this would be to have spacers before/after and set the widths according to the side they're on ^ea + anchors.left: fromMe ? undefined : imgProfile.right //parent.left + anchors.right: fromMe ? parent.right : undefined + anchors.leftMargin: 5 + anchors.rightMargin: 9 + anchors.topMargin: 5 + + + ColumnLayout { + id: colMessageBubble + + + Column { // combine these into one element or else childrenRect won't play nicely + TextEdit { // this is used as a helper to calculate the message box width + id: dummy + visible: false + padding: 6 + leftPadding: 10 + font.pixelSize: gcd.themeScale * 12 + wrapMode: TextEdit.NoWrap + text: lbl.text + textFormat: Text.RichText + } + + TextEdit { // this is the actual text display + id: lbl + text: parse(message, 12, true) + color: "#FFFFFF" + padding: 6 + leftPadding: 10 + font.pixelSize: gcd.themeScale * 12 + selectByMouse: gcd.os != "android" + readOnly: true + width: Math.min(dummy.width, root.parent.width - (imgProfile.visible ? imgProfile.width : 0) - 40) + wrapMode: TextEdit.Wrap + textFormat: Text.RichText + } + } + + RowLayout { + id: rowBottom + anchors.left: parent.left + anchors.right: parent.right + + + Label { // TIMESTAMP + id: ts + color: "#FFFFFF" + font.pixelSize: 10 * gcd.themeScale + anchors.left: parent.left + leftPadding: 10 + } + + Label { // DISPLAY NAME FOR GROUPS + color: "#FFFFFF" + font.pixelSize: 10 * gcd.themeScale + anchors.right: parent.right + text: displayName.length > 12 ? displayName.substr(0,12) + "..." : displayName + visible: !fromMe + ToolTip.text: from + ToolTip.visible: ma2.containsMouse + ToolTip.delay: 200 + + MouseArea { + id: ma2 + anchors.fill: parent + hoverEnabled: true + } + } + + Image { // ACKNOWLEDGEMENT ICON + id: ack + anchors.right: parent.right + source: root.error != "" ? gcd.assetPath + "fontawesome/regular/window-close.svg" : (root.ackd ? gcd.assetPath + "fontawesome/regular/check-circle.svg" : gcd.assetPath + "fontawesome/regular/hourglass.svg") + height: 10 * gcd.themeScale + sourceSize.height: 10 * gcd.themeScale + visible: fromMe + ToolTip.visible: ma.containsMouse + ToolTip.delay: 200 + //: Could not send this message + ToolTip.text: root.error != "" ? qsTr("could-not-send-msg-error") + ":" + root.error : (root.ackd ? qsTr("acknowledged-label") : qsTr("pending-label")) + + + MouseArea { + id: ma + anchors.fill: parent + hoverEnabled: true + } + } + } + } + + TextEdit { + id: copyhelper + visible: false + text: root.rawMessage + } + + MouseArea { + anchors.fill: gcd.os == "android" ? parent : null + + + onPressAndHold: { + copyhelper.selectAll() + copyhelper.copy() + gcd.popup("message copied") + } + } + } +} diff --git a/qml/widgets/MyProfile.qml b/qml/widgets/MyProfile.qml new file mode 100644 index 00000000..0dbea35a --- /dev/null +++ b/qml/widgets/MyProfile.qml @@ -0,0 +1,155 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.11 +import QtQuick.Controls 1.4 + +import "." as Widgets +import "styles" +import "theme" + +Item { + id: root + anchors.fill: parent + width: parent.width + + height: profile.height + implicitHeight: profile.height + + property string image + property string nick + property string onion + property string tag + property bool dualPane: false + + property real logscale: 4 * Math.log10(gcd.themeScale + 1) + + onDualPaneChanged: { realignProfile() } + + function realignProfile() { + if (dualPane) { + profile.height = 78 * logscale + + portrait.baseWidth = 78 * logscale + portrait.height = 78 * logscale + + portrait.anchors.horizontalCenter = undefined + portrait.anchors.left = profile.left + portrait.anchors.leftMargin = 25 * logscale + + nameRow.anchors.right = undefined + nameRow.anchors.left = portrait.right + + nameRow.anchors.top = undefined + nameRow.anchors.verticalCenter = portrait.verticalCenter + + nameCenter.anchors.horizontalCenter = undefined + nameCenter.anchors.left = nameRow.left + } else { + profile.height = (150 * logscale) + + portrait.baseWidth = 100 * logscale + portrait.height = 100 * logscale + + portrait.anchors.left = undefined + portrait.anchors.leftMargin = undefined + portrait.anchors.horizontalCenter = profile.horizontalCenter + + nameRow.anchors.left = profile.left + nameRow.anchors.right = profile.right + + nameRow.anchors.verticalCenter = undefined + nameRow.anchors.top = portrait.bottom + + nameCenter.anchors.left = undefined + nameCenter.anchors.horizontalCenter = nameRow.horizontalCenter + + } + } + + Rectangle { + + anchors.left: parent.left + anchors.right: parent.right + width: parent.width + id: profile + color: Theme.backgroundMainColor + + Portrait { + id: portrait + + source: root.image + + badgeColor: Theme.portraitProfileBadgeColor + portraitBorderColor: Theme.portraitOnlineBorderColor + portraitColor: Theme.portraitOnlineBackgroundColor + + 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 + } + } + + Rectangle { + id: nameRow + height: name.height + onWidthChanged: { name.textResize() } + color: Theme.backgroundMainColor + + Rectangle { + id: nameCenter + width: name.width + addBtn.width + + EllipsisLabel { + id: name + + anchors.right: undefined + anchors.left: undefined + + color: Theme.portraitOnlineTextColor + pixelSize: Theme.usernameSize * gcd.themeScale + weight: Font.Bold + text: nick + extraPadding: addBtn.width + 30 + container: nameRow + } + + Widgets.Button { // Add Button + id: addBtn + + anchors.left: name.right + anchors.top: name.top + + icon: "solid/plus" + + height: name.height + width: height + radius: width * 0.3 + onClicked: { + + } + } + } + } + + } + + Connections { + target: gcd + + onUpdateMyProfile: function(_nick, _onion, _image, _tag) { + nick = _nick + onion = _onion + image = _image + tag = _tag + //realignProfile() + } + + onResetProfile: { realignProfile() } + } +} diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml new file mode 100644 index 00000000..6e8364e3 --- /dev/null +++ b/qml/widgets/ProfileList.qml @@ -0,0 +1,124 @@ +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 "theme" + +ColumnLayout { + id: root + + MouseArea { + anchors.fill: parent + + onClicked: { + forceActiveFocus() + } + } + + Flickable { // Profile List + id: sv + clip: true + Layout.minimumHeight: 100 + Layout.fillHeight: true + Layout.minimumWidth: parent.width + Layout.maximumWidth: parent.width + contentWidth: colContacts.width + contentHeight: colContacts.height + boundsBehavior: Flickable.StopAtBounds + maximumFlickVelocity: 400 + + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AlwaysOn + } + + ColumnLayout { + id: colContacts + width: root.width + spacing: 0 + + Connections { // ADD/REMOVE CONTACT ENTRIES + target: gcd + + onAddProfile: function(handle, displayName, image, tag) { + + // don't add duplicates + for (var i = 0; i < profilesModel.count; i++) { + if (profilesModel.get(i)["_handle"] == handle) { + return + } + } + + // find index for insert (sort by onion) + var index = profilesModel.count + for (var i = 0; i < profilesModel.count; i++) { + if (profilesModel.get(i)["_handle"] > handle) { + index = i + break + } + } + + profilesModel.insert(index, + { + _handle: handle, + _displayName: displayName, + _image: image, + _tag: tag, + _status: 4, + }) + } + + /* + onRemoveProfile: function(handle) { + for(var i = 0; i < profilesModel.count; i++){ + if(profilesModel.get(i)["_handle"] == handle) { + console.log("deleting contact " + profilesModel.get(i)["_handle"]) + profilesModel.remove(i) + return + } + } + }*/ + + onResetProfileList: function() { + profilesModel.clear() + } + } + + ListModel { // Profile OBJECTS ARE STORED HERE ... + id: profilesModel + } + + Repeater { + id: profileList + model: profilesModel // ... AND DISPLAYED HERE + delegate: ProfileRow { + handle: _handle + displayName: _displayName + image: _image + blocked: false + tag: _tag + } + } + + PortraitRow { + handle: "" + displayName: qsTr("add-new-profile-btn") + nameColor: Theme.mainTextColor + image: "/fontawesome/regular/user.svg" + tag: "" + portraitBorderColor: Theme.portraitOnlineBorderColor + portraitColor: Theme.portraitOnlineBackgroundColor + badgeVisible: true + badgeContent: Image { + source: gcd.assetPath + "/fontawesome/solid/plus.svg" + height: Theme.badgeTextSize * gcd.themeScale + width: height + } + badgeColor: Theme.defaultButtonColor + + 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..4accf5d0 --- /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 "." 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 + } +}