From 094dee492825520b47d3f4941a6720020dcbe6d3 Mon Sep 17 00:00:00 2001
From: Alexandra Betouni <31625338+alexandraB99@users.noreply.github.com>
Date: Thu, 20 Jan 2022 00:33:29 +0200
Subject: [PATCH] feat(StatusQ.Components): Adding StatusTagSelector component
Added StatusTagSelector component needed for
creating new chat channels, either ono on
one or group based on updated designs on
Figma
Also added corresponding page in API Documentation
Closes #526
---
sandbox/demoapp/ChatChannelView.qml | 103 ++++++++
sandbox/demoapp/CreateChatView.qml | 232 +++++++++++++++++++
sandbox/demoapp/StatusAppChatView.qml | 225 +++---------------
sandbox/demoapp/data/Models.qml | 52 +++++
sandbox/main.qml | 5 +
sandbox/pages/StatusTagSelectorPage.qml | 56 +++++
src/StatusQ/Components/StatusTagSelector.qml | 126 ++++++++++
src/StatusQ/Components/qmldir | 1 +
statusq.qrc | 1 +
9 files changed, 613 insertions(+), 188 deletions(-)
create mode 100644 sandbox/demoapp/ChatChannelView.qml
create mode 100644 sandbox/demoapp/CreateChatView.qml
create mode 100644 sandbox/pages/StatusTagSelectorPage.qml
create mode 100644 src/StatusQ/Components/StatusTagSelector.qml
diff --git a/sandbox/demoapp/ChatChannelView.qml b/sandbox/demoapp/ChatChannelView.qml
new file mode 100644
index 00000000..25f41203
--- /dev/null
+++ b/sandbox/demoapp/ChatChannelView.qml
@@ -0,0 +1,103 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import QtQml.Models 2.2
+
+import StatusQ.Controls 0.1
+import StatusQ.Popups 0.1
+import StatusQ.Components 0.1
+import StatusQ.Core 0.1
+import StatusQ.Core.Theme 0.1
+
+ListView {
+ id: messageList
+ anchors.fill: parent
+ anchors.margins: 15
+ clip: true
+ delegate: StatusMessage {
+ id: delegate
+ width: parent.width
+
+ audioMessageInfoText: "Audio Message"
+ cancelButtonText: "Cancel"
+ saveButtonText: "Save"
+ loadingImageText: "Loading image..."
+ errorLoadingImageText: "Error loading the image"
+ resendText: "Resend"
+ pinnedMsgInfoText: "Pinned by"
+
+ messageDetails: StatusMessageDetails {
+ contentType: model.contentType
+ messageContent: model.messageContent
+ amISender: model.amIsender
+ displayName: model.userName
+ secondaryName: model.localName !== "" && model.ensName.startsWith("@") ? model.ensName: ""
+ chatID: model.chatKey
+ profileImage: StatusImageSettings {
+ width: 40
+ height: 40
+ source: model.profileImage
+ isIdenticon: model.isIdenticon
+ }
+ messageText: model.message
+ hasMention: model.hasMention
+ contactType: model.contactType
+ isPinned: model.isPinned
+ pinnedBy: model.pinnedBy
+ hasExpired: model.hasExpired
+ }
+ timestamp.text: "10:00 am"
+ timestamp.tooltip.text: "10:01 am"
+ // reply related data
+ isAReply: model.isReply
+ replyDetails: StatusMessageDetails {
+ amISender: model.isReply ? model.replyAmISender : ""
+ displayName: model.isReply ? model.replySenderName: ""
+ profileImage: StatusImageSettings {
+ width: 20
+ height: 20
+ source: model.isReply ? model.replyProfileImage: ""
+ isIdenticon: model.isReply ? model.replyIsIdenticon: ""
+ }
+ messageText: model.isReply ? model.replyMessageText: ""
+ contentType: model.replyContentType
+ messageContent: model.replyMessageContent
+ }
+ quickActions: [
+ StatusFlatRoundButton {
+ id: emojiBtn
+ width: 32
+ height: 32
+ icon.name: "reaction-b"
+ type: StatusFlatRoundButton.Type.Tertiary
+ tooltip.text: "Add reaction"
+ },
+ StatusFlatRoundButton {
+ id: replyBtn
+ width: 32
+ height: 32
+ icon.name: "reply"
+ type: StatusFlatRoundButton.Type.Tertiary
+ tooltip.text: "Reply"
+ },
+ StatusFlatRoundButton {
+ width: 32
+ height: 32
+ icon.name: "tiny/edit"
+ type: StatusFlatRoundButton.Type.Tertiary
+ tooltip.text: "Edit"
+ onClicked: {
+ delegate.editMode = !delegate.editMode
+ }
+ },
+ StatusFlatRoundButton {
+ id: otherBtn
+ width: 32
+ height: 32
+ icon.name: "more"
+ type: StatusFlatRoundButton.Type.Tertiary
+ tooltip.text: "More"
+ }
+ ]
+ }
+}
diff --git a/sandbox/demoapp/CreateChatView.qml b/sandbox/demoapp/CreateChatView.qml
new file mode 100644
index 00000000..b8de5912
--- /dev/null
+++ b/sandbox/demoapp/CreateChatView.qml
@@ -0,0 +1,232 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import QtQml.Models 2.2
+import QtGraphicalEffects 1.0
+
+import StatusQ.Controls 0.1
+import StatusQ.Components 0.1
+import StatusQ.Core 0.1
+import StatusQ.Core.Theme 0.1
+
+Page {
+ id: root
+ anchors.fill: parent
+ anchors.margins: 16
+ property ListModel contactsModel: null
+ background: null
+
+ header: RowLayout {
+ id: headerRow
+ width: parent.width
+ height: tagSelector.height
+ anchors.right: parent.right
+ anchors.rightMargin: 8
+
+ StatusTagSelector {
+ id: tagSelector
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
+ Layout.leftMargin: 17
+ implicitHeight: 44
+ toLabelText: qsTr("To: ")
+ warningText: qsTr("5 USER LIMIT REACHED")
+ //simulate model filtering, TODO this
+ //makes more sense to be provided by the backend
+ //figure how real implementation should look like
+ property ListModel sortedList: ListModel { }
+ onTextChanged: {
+ sortedList.clear();
+ if (text !== "") {
+ for (var i = 0; i < contactsModel.count; i++ ) {
+ var entry = contactsModel.get(i);
+ if (entry.name.toLowerCase().includes(text.toLowerCase())) {
+ sortedList.insert(sortedList.count, {"publicId": entry.publicId, "name": entry.name,
+ "icon": entry.icon, "isIdenticon": entry.isIdenticon,
+ "onlineStatus": entry.onlineStatus});
+ userListView.model = sortedList;
+ }
+ }
+ } else {
+ userListView.model = contactsModel;
+ }
+ }
+ }
+
+ StatusButton {
+ implicitHeight: 44
+ enabled: (tagSelector.namesModel.count > 0)
+ text: "Confirm"
+ }
+ }
+
+ contentItem: Item {
+ anchors.fill: parent
+ anchors.topMargin: headerRow.height + 16
+
+ Item {
+ anchors.fill: parent
+ visible: (contactsModel.count > 0)
+
+ StatusBaseText {
+ id: contactsLabel
+ font.pixelSize: 15
+ color: Theme.palette.baseColor1
+ text: "Contacts"
+ }
+ Control {
+ width: 360
+ anchors {
+ top: contactsLabel.bottom
+ topMargin: 8//Style.current.padding
+ bottom: parent.bottom
+ bottomMargin: 20//Style.current.bigPadding
+ }
+ background: Rectangle {
+ id: statusPopupMenuBackgroundContent
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: (userListView.height + 8)
+ visible: (tagSelector.sortedList.count > 0)
+ color: Theme.palette.statusPopupMenu.backgroundColor
+ radius: 8
+ layer.enabled: true
+ layer.effect: DropShadow {
+ width: statusPopupMenuBackgroundContent.width
+ height: statusPopupMenuBackgroundContent.height
+ x: statusPopupMenuBackgroundContent.x
+ visible: statusPopupMenuBackgroundContent.visible
+ source: statusPopupMenuBackgroundContent
+ horizontalOffset: 0
+ verticalOffset: 4
+ radius: 12
+ samples: 25
+ spread: 0.2
+ color: Theme.palette.dropShadow
+ }
+ }
+ contentItem: ListView {
+ id: userListView
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: (count * 64) > parent.height ? parent.height : (count * 64)
+ clip: true
+ model: contactsModel
+ ScrollBar.vertical: ScrollBar {
+ policy: ScrollBar.AsNeeded
+ }
+ boundsBehavior: Flickable.StopAtBounds
+ delegate: Item {
+ id: wrapper
+ anchors.right: parent.right
+ anchors.left: parent.left
+ height: 64
+ property bool hovered: false
+ Rectangle {
+ id: rectangle
+ anchors.fill: parent
+ anchors.topMargin: 8
+ anchors.rightMargin: 8
+ anchors.leftMargin: 8
+ radius: 8
+ visible: (tagSelector.sortedList.count > 0)
+ color: (wrapper.hovered) ? Theme.palette.baseColor2 : "transparent"
+ }
+
+ StatusSmartIdenticon {
+ id: contactImage
+ anchors.left: parent.left
+ anchors.leftMargin: 16//Style.current.padding
+ anchors.verticalCenter: parent.verticalCenter
+ name: model.name
+ icon: StatusIconSettings {
+ width: 28
+ height: 28
+ letterSize: 15
+ }
+ image: StatusImageSettings {
+ width: 28
+ height: 28
+ source: model.icon
+ isIdenticon: model.isIdenticon
+ }
+ }
+
+ StatusBaseText {
+ id: contactInfo
+ text: model.name
+ anchors.right: parent.right
+ anchors.rightMargin: 8
+ anchors.left: contactImage.right
+ anchors.leftMargin: 16
+ anchors.verticalCenter: parent.verticalCenter
+ elide: Text.ElideRight
+ color: Theme.palette.directColor1
+ font.weight: Font.Medium
+ font.pixelSize: 15
+ }
+
+ StatusBadge {
+ id: statusBadge
+ width: 15
+ height: 15
+ anchors.left: contactImage.right
+ anchors.leftMargin: -8
+ anchors.bottom: contactImage.bottom
+ border.width: 3
+ border.color: Theme.palette.statusAppNavBar.backgroundColor
+ color: {
+ if (model.onlineStatus === 1)
+ return Theme.palette.successColor1;
+ else if (model.onlineStatus === 2)
+ return Theme.palette.pinColor1;
+ else if (model.onlineStatus === 3)
+ return Theme.palette.dangerColor1;
+
+ return "transparent"
+ }
+ }
+
+ MouseArea {
+ cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: {
+ wrapper.hovered = true;
+ }
+ onExited: {
+ wrapper.hovered = false;
+ }
+ onClicked: {
+ tagSelector.insertTag(model.name, model.publicId);
+ }
+ }
+ }
+ }
+ }
+ Component.onCompleted: {
+ if (visible) {
+ tagSelector.textEdit.forceActiveFocus();
+ }
+ }
+ }
+
+ StatusBaseText {
+ visible: (contactsModel.count === 0)
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: 15
+ color: Theme.palette.baseColor1
+ text: qsTr("You can only send direct messages to your Contacts. \n\n
+Send a contact request to the person you would like to chat with, you will be\n able to
+chat with them once they have accepted your contact request.")
+ Component.onCompleted: {
+ if (visible) {
+ tagSelector.enabled = false;
+ }
+ }
+ }
+ }
+}
diff --git a/sandbox/demoapp/StatusAppChatView.qml b/sandbox/demoapp/StatusAppChatView.qml
index 19cf4a06..a59a2c46 100644
--- a/sandbox/demoapp/StatusAppChatView.qml
+++ b/sandbox/demoapp/StatusAppChatView.qml
@@ -1,4 +1,5 @@
import QtQuick 2.12
+import QtQuick.Layouts 1.12
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
@@ -11,6 +12,7 @@ import "data" 1.0
StatusAppThreePanelLayout {
id: root
+ property bool createChat: false
leftPanel: Item {
anchors.fill: parent
@@ -23,113 +25,35 @@ StatusAppThreePanelLayout {
text: "Chat"
}
- Item {
+ RowLayout {
id: searchInputWrapper
- anchors.top: headline.bottom
- anchors.topMargin: 16
width: parent.width
height: searchInput.height
+ anchors.top: headline.bottom
+ anchors.topMargin: 16
+ anchors.right: parent.right
+ anchors.rightMargin: 8
StatusBaseInput {
id: searchInput
-
- anchors.verticalCenter: parent.verticalCenter
- anchors.left: parent.left
- anchors.right: actionButton.left
- anchors.leftMargin: 16
- anchors.rightMargin: 16
-
- height: 36
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
+ Layout.leftMargin: 17
+ implicitHeight: 36
topPadding: 8
bottomPadding: 0
placeholderText: "Search"
icon.name: "search"
}
- StatusRoundButton {
- id: actionButton
- anchors.verticalCenter: parent.verticalCenter
- anchors.right: parent.right
- anchors.rightMargin: 8
- width: 32
- height: 32
+ StatusIconTabButton {
+ icon.name: "public-chat"
+ }
- type: StatusRoundButton.Type.Secondary
- icon.name: "add"
- state: "default"
-
- onClicked: chatContextMenu.popup(actionButton.width-chatContextMenu.width, actionButton.height + 4)
- states: [
- State {
- name: "default"
- PropertyChanges {
- target: actionButton
- icon.rotation: 0
- highlighted: false
- }
- },
- State {
- name: "pressed"
- PropertyChanges {
- target: actionButton
- icon.rotation: 45
- highlighted: true
- }
- }
- ]
-
- transitions: [
- Transition {
- from: "default"
- to: "pressed"
-
- RotationAnimation {
- duration: 150
- direction: RotationAnimation.Clockwise
- easing.type: Easing.InCubic
- }
- },
- Transition {
- from: "pressed"
- to: "default"
- RotationAnimation {
- duration: 150
- direction: RotationAnimation.Counterclockwise
- easing.type: Easing.OutCubic
- }
- }
- ]
-
- StatusPopupMenu {
- id: chatContextMenu
-
- onOpened: {
- actionButton.state = "pressed"
- }
-
- onClosed: {
- actionButton.state = "default"
- }
-
- StatusMenuItem {
- text: "Start new chat"
- icon.name: "private-chat"
- }
-
- StatusMenuItem {
- text: "Start group chat"
- icon.name: "group-chat"
- }
-
- StatusMenuItem {
- text: "Join public chat"
- icon.name: "public-chat"
- }
-
- StatusMenuItem {
- text: "Communities"
- icon.name: "communities"
- }
+ StatusIconTabButton {
+ icon.name: "edit"
+ onClicked: {
+ root.createChat = !root.createChat;
}
}
}
@@ -201,6 +125,25 @@ StatusAppThreePanelLayout {
}
}
+ centerPanel: Loader {
+ anchors.fill: parent
+ sourceComponent: root.createChat ? createChatView : chatChannelView
+ }
+
+ Component {
+ id: createChatView
+ CreateChatView {
+ contactsModel: Models.dummyContactsModel
+ }
+ }
+
+ Component {
+ id: chatChannelView
+ ChatChannelView {
+ model: Models.chatMessagesModel
+ }
+ }
+
rightPanel: Item {
anchors.fill: parent
@@ -248,98 +191,4 @@ StatusAppThreePanelLayout {
}
}
}
-
- centerPanel: ListView {
- id: messageList
- anchors.fill: parent
- anchors.margins: 15
- clip: true
- model: Models.chatMessagesModel
- delegate: StatusMessage {
- id: delegate
- width: parent.width
-
- audioMessageInfoText: "Audio Message"
- cancelButtonText: "Cancel"
- saveButtonText: "Save"
- loadingImageText: "Loading image..."
- errorLoadingImageText: "Error loading the image"
- resendText: "Resend"
- pinnedMsgInfoText: "Pinned by"
-
- messageDetails: StatusMessageDetails {
- contentType: model.contentType
- messageContent: model.messageContent
- amISender: model.amIsender
- displayName: model.userName
- secondaryName: model.localName !== "" && model.ensName.startsWith("@") ? model.ensName: ""
- chatID: model.chatKey
- profileImage: StatusImageSettings {
- width: 40
- height: 40
- source: model.profileImage
- isIdenticon: model.isIdenticon
- }
- messageText: model.message
- hasMention: model.hasMention
- contactType: model.contactType
- isPinned: model.isPinned
- pinnedBy: model.pinnedBy
- hasExpired: model.hasExpired
- }
- timestamp.text: "10:00 am"
- timestamp.tooltip.text: "10:01 am"
- // reply related data
- isAReply: model.isReply
- replyDetails: StatusMessageDetails {
- amISender: model.isReply ? model.replyAmISender : ""
- displayName: model.isReply ? model.replySenderName: ""
- profileImage: StatusImageSettings {
- width: 20
- height: 20
- source: model.isReply ? model.replyProfileImage: ""
- isIdenticon: model.isReply ? model.replyIsIdenticon: ""
- }
- messageText: model.isReply ? model.replyMessageText: ""
- contentType: model.replyContentType
- messageContent: model.replyMessageContent
- }
- quickActions: [
- StatusFlatRoundButton {
- id: emojiBtn
- width: 32
- height: 32
- icon.name: "reaction-b"
- type: StatusFlatRoundButton.Type.Tertiary
- tooltip.text: "Add reaction"
- },
- StatusFlatRoundButton {
- id: replyBtn
- width: 32
- height: 32
- icon.name: "reply"
- type: StatusFlatRoundButton.Type.Tertiary
- tooltip.text: "Reply"
- },
- StatusFlatRoundButton {
- width: 32
- height: 32
- icon.name: "tiny/edit"
- type: StatusFlatRoundButton.Type.Tertiary
- tooltip.text: "Edit"
- onClicked: {
- delegate.editMode = !delegate.editMode
- }
- },
- StatusFlatRoundButton {
- id: otherBtn
- width: 32
- height: 32
- icon.name: "more"
- type: StatusFlatRoundButton.Type.Tertiary
- tooltip.text: "More"
- }
- ]
- }
- }
}
diff --git a/sandbox/demoapp/data/Models.qml b/sandbox/demoapp/data/Models.qml
index 91695fd6..2ee36aba 100644
--- a/sandbox/demoapp/data/Models.qml
+++ b/sandbox/demoapp/data/Models.qml
@@ -4,6 +4,58 @@ import StatusQ.Components 0.1
QtObject {
+ property ListModel dummyContactsModel: ListModel {
+ ListElement {
+ publicId: "0x0"
+ name: "Maria"
+ icon: ""
+ isIdenticon: false
+ onlineStatus: 3
+ }
+ ListElement {
+ publicId: "0x1"
+ name: "James"
+ icon: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
+ isIdenticon: false
+ onlineStatus: 1
+ }
+ ListElement {
+ publicId: "0x2"
+ name: "Paul"
+ icon: ""
+ isIdenticon: false
+ onlineStatus: 2
+ }
+ ListElement {
+ publicId: "0x3"
+ name: "Tracy"
+ icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
+ isIdenticon: true
+ onlineStatus: 3
+ }
+ ListElement {
+ publicId: "0x4"
+ name: "Nick"
+ icon: ""
+ isIdenticon: false
+ onlineStatus: 3
+ }
+ ListElement {
+ publicId: "0x5"
+ name: "Steven"
+ icon: ""
+ isIdenticon: false
+ onlineStatus: 2
+ }
+ ListElement {
+ publicId: "0x6"
+ name: "Helen"
+ icon: ""
+ isIdenticon: false
+ onlineStatus: 3
+ }
+ }
+
property var demoChatListItems: ListModel {
id: demoChatListItems
ListElement {
diff --git a/sandbox/main.qml b/sandbox/main.qml
index f9c5ce7b..15acad15 100644
--- a/sandbox/main.qml
+++ b/sandbox/main.qml
@@ -255,6 +255,11 @@ StatusWindow {
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page("StatusExpandableSettingsItem");
}
+ StatusNavigationListItem {
+ title: "StatusTagSelector"
+ selected: viewLoader.source.toString().includes(title)
+ onClicked: mainPageView.page(title);
+ }
StatusListSectionHeadline { text: "StatusQ.Popup" }
StatusNavigationListItem {
title: "StatusPopupMenu"
diff --git a/sandbox/pages/StatusTagSelectorPage.qml b/sandbox/pages/StatusTagSelectorPage.qml
new file mode 100644
index 00000000..3de2c69e
--- /dev/null
+++ b/sandbox/pages/StatusTagSelectorPage.qml
@@ -0,0 +1,56 @@
+import QtQuick 2.14
+
+import StatusQ.Components 0.1
+
+Item {
+ id: root
+ anchors.fill: parent
+
+ property ListModel asortedContacts: ListModel {
+ ListElement {
+ publicId: "0x0"
+ name: "Maria"
+ icon: ""
+ isIdenticon: false
+ onlineStatus: 3
+ }
+ ListElement {
+ publicId: "0x1"
+ name: "James"
+ icon: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
+ isIdenticon: false
+ onlineStatus: 1
+ }
+ ListElement {
+ publicId: "0x2"
+ name: "Paul"
+ icon: ""
+ isIdenticon: false
+ onlineStatus: 2
+ }
+ ListElement {
+ publicId: "0x3"
+ name: "Tracy"
+ icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
+ isIdenticon: true
+ onlineStatus: 3
+ }
+ ListElement {
+ publicId: "0x4"
+ name: "Nick"
+ icon: ""
+ isIdenticon: false
+ onlineStatus: 3
+ }
+ }
+
+ StatusTagSelector {
+ id: tagSelector
+ width: 650
+ height: 44
+ anchors.centerIn: parent
+ namesModel: root.asortedContacts
+ toLabelText: qsTr("To: ")
+ warningText: qsTr("5 USER LIMIT REACHED")
+ }
+}
diff --git a/src/StatusQ/Components/StatusTagSelector.qml b/src/StatusQ/Components/StatusTagSelector.qml
new file mode 100644
index 00000000..03634955
--- /dev/null
+++ b/src/StatusQ/Components/StatusTagSelector.qml
@@ -0,0 +1,126 @@
+import QtQuick 2.14
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.14
+
+import StatusQ.Core 0.1
+import StatusQ.Core.Theme 0.1
+
+Item {
+ id: root
+
+ implicitWidth: 448
+ implicitHeight: 44
+
+ property alias textEdit: edit
+ property alias text: edit.text
+ property string warningText: ""
+ property string toLabelText: ""
+ property int nameCountLimit: 5
+ property ListModel namesModel: ListModel { }
+
+ function find(model, criteria) {
+ for (var i = 0; i < model.count; ++i) if (criteria(model.get(i))) return model.get(i);
+ return null;
+ }
+
+ function insertTag(name, id) {
+ if (!find(namesModel, function(item) { return item.publicId === id }) && namesModel.count < root.nameCountLimit) {
+ namesModel.insert(namesModel.count, {"name": name, "publicId": id});
+ edit.clear();
+ }
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ radius: 8
+ color: Theme.palette.baseColor2
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: 16
+ anchors.rightMargin: 16
+ spacing: 8
+ StatusBaseText {
+ Layout.preferredWidth: 22
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
+ color: Theme.palette.baseColor1
+ text: root.toLabelText
+ }
+
+ ListView {
+ id: namesList
+ Layout.preferredWidth: (count >= 5) ? (parent.width - warningTextLabel.width - 30) : childrenRect.width
+ implicitHeight: 30
+ visible: (count > 0)
+ Layout.alignment: Qt.AlignVCenter
+ model: namesModel
+ orientation: ListView.Horizontal
+ spacing: 8
+ clip: true
+ onWidthChanged: {
+ positionViewAtEnd();
+ }
+
+ delegate: Rectangle {
+ id: nameDelegate
+ width: (nameText.contentWidth + 34)
+ height: 30
+ color: mouseArea.containsMouse ? Theme.palette.miscColor1 : Theme.palette.primaryColor1
+ radius: 8
+ StatusBaseText {
+ id: nameText
+ anchors.left: parent.left
+ anchors.leftMargin: 8
+ anchors.verticalCenter: parent.verticalCenter
+ color: Theme.palette.indirectColor1
+ text: name
+ }
+ StatusIcon {
+ anchors.left: nameText.right
+ anchors.verticalCenter: parent.verticalCenter
+ color: Theme.palette.indirectColor1
+ icon: "close"
+ }
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onClicked: {
+ namesModel.remove(index, 1);
+ }
+ }
+ }
+ }
+
+ TextEdit {
+ id: edit
+ Layout.fillWidth: true
+ Layout.preferredHeight: 44
+ verticalAlignment: Text.AlignVCenter
+ visible: (namesModel.count < 5)
+ enabled: visible
+ focus: true
+ font.pixelSize: 15
+ font.family: Theme.palette.baseFont.name
+ color: Theme.palette.directColor1
+ Keys.onPressed: {
+ if ((event.key === Qt.Key_Backspace || event.key === Qt.Key_Escape)
+ && getText(cursorPosition, (cursorPosition-1)) === "") {
+ namesModel.remove((namesList.count-1), 1);
+ }
+ }
+ }
+
+ StatusBaseText {
+ id: warningTextLabel
+ visible: (namesModel.count === 5)
+ Layout.preferredWidth: 120
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
+ font.pixelSize: 10
+ color: Theme.palette.dangerColor1
+ text: root.warningText
+ }
+ }
+ }
+}
diff --git a/src/StatusQ/Components/qmldir b/src/StatusQ/Components/qmldir
index 568befdd..51c84c91 100644
--- a/src/StatusQ/Components/qmldir
+++ b/src/StatusQ/Components/qmldir
@@ -26,3 +26,4 @@ StatusExpandableItem 0.1 StatusExpandableItem.qml
StatusSmartIdenticon 0.1 StatusSmartIdenticon.qml
StatusMessage 0.1 StatusMessage.qml
StatusMessageDetails 0.1 StatusMessageDetails.qml
+StatusTagSelector 0.1 StatusTagSelector.qml
diff --git a/statusq.qrc b/statusq.qrc
index 58346703..3e118610 100644
--- a/statusq.qrc
+++ b/statusq.qrc
@@ -320,5 +320,6 @@
src/StatusQ/Controls/StatusProgressBar.qml
src/StatusQ/Controls/StatusPasswordStrengthIndicator.qml
src/StatusQ/Components/StatusMemberListItem.qml
+ src/StatusQ/Components/StatusTagSelector.qml