Compare commits

..
Author SHA1 Message Date
Alexandra Betouni 8865afa4db fix(DemoApp) Results popup component position
Fixed alignment when results are being filtered.
If the x position that the popup should be aligned
goes off the screen, the popup appears back on
position 0. Also fixed result list item was not
vertically centered.

Closes #531
2022-02-11 19:10:29 +02:00
10241 changed files with 1247 additions and 38141 deletions
-17
View File
@@ -1,17 +0,0 @@
### Checklist
- [ ] follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)
- the scope should be the component's name e.g: `feat(StatusListItem): ... `
- when adding new components, the scope is the module e.g: `feat(StatusQ.Controls): ...`
- [ ] add documentation if necessary (new component, new feature)
- [ ] update sandbox app
- in case of new component, add new component page
- in case of new features, add variation to existing component page
- nice to have: add it to the demo application as well
- [ ] test changes in both light and dark theme?
- [ ] is this a breaking change?
- [ ] use the dedicated `BREAKING CHANGE` commit message section
- [ ] resolve breaking changes in [status-desktop](https://github.com/status-im/status-desktop)
- [ ] (pre-merge) adapt code to breaking changes
- [ ] (post-merge) update StatusQ submodule pointer
- [ ] test changes in [status-desktop](https://github.com/status-im/status-desktop)
-4
View File
@@ -13,7 +13,3 @@ Makefile
*_qml.cpp
*_qmlcache.qrc
sandbox/qmlcache_loader.cpp
doc/html
CMakeLists.txt.user
.vscode
build/
-40
View File
@@ -1,43 +1,3 @@
<a name=""></a>
## 0.25.0 (2022-03-07)
#### Bug Fixes
* **@StatusListItem:** fix left padding ([f574e390](f574e390))
* **StatusListItem:** add propagateTitleClicks property to StatusListItem ([8495fae8](8495fae8))
* **StatusQ.Popups:** removed overlay setting (#557) ([665141f8](665141f8))
* **StatusTagSelector:** Updates and fixes in the component ([07a5dc09](07a5dc09))
* **build:** fix linux build ([9d06aae1](9d06aae1), closes [#413](413))
* **sandbox:** Fix results popup component position ([cdf51fef](cdf51fef))
#### Features
* **@StatusListItem:** add option for tags ([381150a7](381150a7))
* **StatusChatList:** Highlight chat item at creation time ([9e6fbe95](9e6fbe95))
* **StatusChatListItem:** enable assigning emoji to chat item ([5fac8774](5fac8774))
* **StatusContactVerificationIcons:** Create new row control that includes mutual connect and trust indicator icons (#559) ([c0f825c6](c0f825c6), closes [#542](542))
* **StatusQ:** Moving docs outside sandbox ([825999b1](825999b1))
* **StatusQ.Core:** add new arrow-left icon ([e9f20be1](e9f20be1))
* **StatusSmartIdenticon:** Add support for color rings in StatusSmartIdenticon (#553) ([3b86d13a](3b86d13a), closes [#517](517))
#### Breaking Changes
* **StatusContactVerificationIcons:** Create new row control that includes mutual connect and trust indicator icons (#559) ([c0f825c6](c0f825c6), closes [#542](542))
<a name=""></a>
## 0.24.0 (2022-02-14)
#### Features
* **StatusListItem:** add highlighted property ([5780f183](5780f183))
* **StatusQ.Components:** Adding StatusWizardSteper component ([b7d6554b](b7d6554b), closes [#522](522))
<a name=""></a>
## Version 0.23.0 (2022-02-08)
-5
View File
@@ -1,5 +0,0 @@
cmake_minimum_required(VERSION 3.19)
project(StatusQ)
add_subdirectory(tests)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

-3
View File
@@ -7,8 +7,6 @@ Item {
id: themeSwitchWrapper
signal checkedChanged()
property alias lightThemeEnabled: switchControl.checked
width: themeSwitch.width
height: themeSwitch.height
@@ -28,7 +26,6 @@ Item {
}
StatusSwitch {
id: switchControl
onCheckedChanged: themeSwitchWrapper.checkedChanged()
StatusToolTip {
+4 -65
View File
@@ -4,7 +4,6 @@ import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Utils 0.1
GridLayout {
columns: 1
@@ -26,7 +25,7 @@ GridLayout {
badge.value: 1
}
StatusNavigationListItem {
title: "Menu Item (selected) with very long text"
title: "Menu Item (selected)"
selected: true
icon.name: "info"
badge.value: 1
@@ -65,12 +64,6 @@ GridLayout {
type: StatusChatListItem.Type.CommunityChat
}
StatusChatListItem {
name: "community-channel-emoji"
type: StatusChatListItem.Type.CommunityChat
icon.emoji: "😁"
}
StatusChatListItem {
name: "community-channel-with-image"
image.source: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
@@ -329,43 +322,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
badge.icon.isLetterIdenticon: true
}
StatusListItem {
title: "List Item with Tags"
icon.isLetterIdenticon: true
bottomModel: 3
bottomDelegate: StatusListItemTag {
title: "tag"
icon.isLetterIdenticon: true
}
}
StatusListItem {
title: "List Item with Inline Tags"
icon.isLetterIdenticon: true
tagsModel : 2
tagsDelegate: StatusListItemTag {
color: "blue"
height: 24
radius: 6
closeButtonVisible: false
icon.emoji: "😁"
icon.emojiSize: Emoji.size.verySmall
icon.isLetterIdenticon: true
title: "helloworld.eth"
titleText.font.pixelSize: 12
titleText.color: Theme.palette.indirectColor1
}
}
StatusListItem {
title: "List Item with Emoji"
subTitle: "Emoji"
icon.emoji: "😁"
icon.color: "yellow"
icon.letterSize: 14
icon.isLetterIdenticon: true
}
StatusDescriptionListItem {
title: "Title"
subTitle: "Subtitle"
@@ -395,41 +351,24 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
nickName: "This is an example"
userName: "annabelle"
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusContactVerificationIcons.TrustedType.Verified
trustIndicator: StatusMemberListItem.TrustedType.Verified
isMutualContact: true
image.source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
image.isIdenticon: true
isOnline: true
ringSettings.ringSpecModel:
ListModel {
ListElement {colorId: 13; segmentLength: 5}
ListElement {colorId: 31; segmentLength: 5}
ListElement {colorId: 10; segmentLength: 1}
ListElement {colorId: 2; segmentLength: 5}
ListElement {colorId: 26; segmentLength: 2}
ListElement {colorId: 19; segmentLength: 4}
ListElement {colorId: 28; segmentLength: 3}
}
ringSettings.distinctiveColors: Theme.palette.identiconRingColors
}
StatusMemberListItem {
nickName: "carmen.eth"
isOnline: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
}
StatusMemberListItem {
nickName: "very-long-annoying-nickname.eth"
isOnline: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
trustIndicator: StatusMemberListItem.TrustedType.Untrustworthy
}
StatusMemberListItem {
nickName: "This girl I know from work"
userName: "annabelle"
isOnline: true
isOnline: true
}
StatusMemberListItem {
+29 -49
View File
@@ -4,58 +4,38 @@ import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
ColumnLayout {
GridLayout {
Layout.fillWidth: true
Layout.fillHeight: true
GridLayout {
columns: 6
columnSpacing: 5
rowSpacing: 5
columns: 6
columnSpacing: 5
rowSpacing: 5
StatusLoadingIndicator {
color: Theme.palette.directColor4
}
StatusLetterIdenticon {
name: "#status"
}
StatusRoundedImage {
image.source: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
}
StatusBadge {}
StatusBadge {
value: 1
}
StatusBadge {
value: 10
}
StatusBadge {
value: 100
}
StatusRoundIcon {
icon.name: "info"
}
StatusLoadingIndicator {
color: Theme.palette.directColor4
}
Flow {
Layout.fillWidth: true
spacing: 4
StatusLetterIdenticon {
name: "#status"
}
Repeater {
model: 12
StatusLetterIdenticon {
name: "A"
color: Theme.palette.userCustomizationColors[index]
letterSize: 16
}
}
StatusRoundedImage {
image.source: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
}
StatusBadge {}
StatusBadge {
value: 1
}
StatusBadge {
value: 10
}
StatusBadge {
value: 100
}
StatusRoundIcon {
icon.name: "info"
}
}
-41
View File
@@ -78,11 +78,6 @@ Column {
onClicked: editTitleModal.open()
}
StatusButton {
text: "Modal with Advanced Header/Footer"
onClicked: advancedHeaderFooterModal.open()
}
StatusModal {
id: simpleModal
anchors.centerIn: parent
@@ -358,42 +353,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
header.editable: true
}
StatusModal {
id: advancedHeaderFooterModal
anchors.centerIn: parent
showHeader: false
showFooter: false
showAdvancedHeader: true
showAdvancedFooter: true
height: 200
advancedHeaderComponent: Rectangle {
width: parent.width
height: 50
color: Theme.palette.baseColor1
border.width: 1
StatusBaseText {
anchors.centerIn: parent
text: "Add any header here"
font.pixelSize: 15
color: Theme.palette.directColor1
}
}
advancedFooterComponent: Rectangle {
width: parent.width
height: 50
color: Theme.palette.baseColor1
border.width: 1
StatusBaseText {
anchors.centerIn: parent
text: "Add any footer here"
font.pixelSize: 15
color: Theme.palette.directColor1
}
}
}
ListModel {
id: dummyAccountsModel
ListElement{name: "Account 1"; iconName: "filled-account"}
+1 -2
View File
@@ -41,8 +41,7 @@ ListView {
}
messageText: model.message
hasMention: model.hasMention
isMutualContact: model.isMutualContact
trustIndicator: model.trustIndicator
contactType: model.contactType
isPinned: model.isPinned
pinnedBy: model.pinnedBy
hasExpired: model.hasExpired
+172 -35
View File
@@ -1,6 +1,8 @@
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
@@ -26,50 +28,33 @@ Page {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.leftMargin: 17
maxHeight: root.height
implicitHeight: 44
toLabelText: qsTr("To: ")
warningText: qsTr("USER LIMIT REACHED")
listLabel: qsTr("Contacts")
ringSpecModelGetter: function(pubKey) {
//for simulation purposes only, in real app
//this would be Utils.getColorHashAsJson(pubKey)
var index = -1;
if (!!contactsModel) {
for (var i = 0; i < contactsModel.count; i++) {
if (contactsModel.get(i).publicId === pubKey) {
index = i;
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;
}
}
return contactsModel.get(index).ringSpecModel;
} else {
return null;
userListView.model = contactsModel;
}
}
compressedKeyGetter: function(pubKey) {
//for simulation purposes only, in real app
//this would be Utils.getCompressedPk(pubKey);
var possibleCharacters = pubKey.split('');
var randomStringLength = 12; // assuming you want random strings of 12 characters
var randomString = [];
for (var i=0; i<randomStringLength; ++i) {
var index = (Math.random() * possibleCharacters.length).toFixed(0);
var nextChar = possibleCharacters[index];
randomString.push(nextChar);
}
return randomString.join().toString().replace(/,/g, '');
}
onTextChanged: {
sortModel(root.contactsModel);
}
Component.onCompleted: {
textEdit.forceActiveFocus();
sortModel(root.contactsModel);
}
}
StatusButton {
implicitHeight: 44
Layout.alignment: Qt.AlignTop
enabled: (tagSelector.namesModel.count > 0)
text: "Confirm"
}
@@ -77,7 +62,159 @@ Page {
contentItem: Item {
anchors.fill: parent
anchors.topMargin: 68
anchors.topMargin: headerRow.height + 16
Item {
anchors.fill: parent
visible: (contactsModel.count > 0)
StatusBaseText {
id: contactsLabel
font.pixelSize: 15
anchors.left: parent.left
anchors.leftMargin: 8
color: Theme.palette.baseColor1
text: qsTr("Contacts")
}
Control {
width: 360
anchors {
top: contactsLabel.bottom
topMargin: 8//Style.current.padding
bottom: !statusPopupMenuBackgroundContent.visible ? parent.bottom : undefined
bottomMargin: 20//Style.current.bigPadding
}
height: 16 + (!statusPopupMenuBackgroundContent.visible ? parent.height :
(((userListView.count * 64) > parent.height) ? parent.height : (userListView.count * 64)))
x: (statusPopupMenuBackgroundContent.visible && (tagSelector.namesModel.count > 0) &&
((tagSelector.textEdit.x + 24 + statusPopupMenuBackgroundContent.width) < parent.width))
? (tagSelector.textEdit.x + 24) : 0
background: Rectangle {
id: statusPopupMenuBackgroundContent
anchors.fill: parent
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.fill: parent
anchors.topMargin: 8
anchors.bottomMargin: 8
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.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)
+20 -17
View File
@@ -27,19 +27,21 @@ StatusAppThreePanelLayout {
RowLayout {
id: searchInputWrapper
width: 288
width: parent.width
height: searchInput.height
anchors.top: headline.bottom
anchors.topMargin: 16
anchors.horizontalCenter: parent.horizontalCenter
anchors.right: parent.right
anchors.rightMargin: 8
StatusBaseInput {
id: searchInput
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.leftMargin: 17
implicitHeight: 36
topPadding: 8
bottomPadding: 8
bottomPadding: 0
placeholderText: "Search"
icon.name: "search"
}
@@ -59,7 +61,7 @@ StatusAppThreePanelLayout {
Column {
anchors.top: searchInputWrapper.bottom
anchors.topMargin: 16
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
spacing: 8
StatusContactRequestsIndicatorListItem {
@@ -70,8 +72,8 @@ StatusAppThreePanelLayout {
}
StatusChatList {
anchors.horizontalCenter: parent.horizontalCenter
model: Models.demoChatListItems
highlightItem: !root.createChat
onChatItemUnmuted: {
for (var i = 0; i < Models.demoChatListItems.count; i++) {
let item = Models.demoChatListItems.get(i);
@@ -126,18 +128,19 @@ StatusAppThreePanelLayout {
centerPanel: Loader {
anchors.fill: parent
sourceComponent: root.createChat ? createChatView : chatChannelView
Component {
id: createChatView
CreateChatView {
contactsModel: Models.membersListModel
}
}
}
Component {
id: chatChannelView
ChatChannelView {
model: Models.chatMessagesModel
}
Component {
id: createChatView
CreateChatView {
contactsModel: Models.dummyContactsModel
}
}
Component {
id: chatChannelView
ChatChannelView {
model: Models.chatMessagesModel
}
}
+4 -5
View File
@@ -259,14 +259,13 @@ StatusAppThreePanelLayout {
delegate: StatusMemberListItem {
implicitWidth: parent.width
nickName: model.nickName
userName: model.name
chatKey: model.publicId
userName: model.userName
chatKey: model.chatKey
trustIndicator: model.trustIndicator
isMutualContact: model.isMutualContact
image.source: model.icon
image.source: model.source
image.isIdenticon: model.isIdenticon
isOnline: model.onlineStatus
ringSettings.ringSpecModel: model.ringSpecModel
isOnline: model.isOnline
}
}
}
+101 -391
View File
@@ -4,15 +4,65 @@ 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 {
itemId: "x012340000"
name: "#status"
icon: ""
emoji: ""
isIdenticon: false
colorHash: []
color: "blue"
description: ""
type: StatusChatListItem.Type.PublicChat
@@ -28,9 +78,7 @@ QtObject {
itemId: "x012340001"
name: "status-desktop"
icon: ""
emoji: ""
isIdenticon: false
colorHash: []
color: "red"
description: ""
type: StatusChatListItem.Type.PublicChat
@@ -47,9 +95,7 @@ QtObject {
name: "Amazing Funny Squirrel"
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0Bh
CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
emoji: ""
isIdenticon: true
colorHash: []
color: "green"
description: ""
type: StatusChatListItem.Type.OneToOneChat
@@ -65,9 +111,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
itemId: "x012340003"
name: "Black Ops"
icon: ""
emoji: ""
isIdenticon: false
colorHash: []
color: "purple"
description: ""
type: StatusChatListItem.Type.OneToOneChat
@@ -83,9 +127,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
itemId: "x012340004"
name: "Spectacular Growing Otter"
icon: ""
emoji: ""
isIdenticon: false
colorHash: []
color: "orange"
description: ""
type: StatusChatListItem.Type.OneToOneChat
@@ -101,9 +143,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
itemId: "x012340005"
name: "channel-with-a-super-duper-long-name"
icon: ""
emoji: ""
isIdenticon: false
colorHash: []
color: "green"
description: ""
type: StatusChatListItem.Type.PublicChat
@@ -357,6 +397,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: ""
iconName: "chat"
iconColor: ""
isIdenticon: false
subItems: [
ListElement {
value: "sub_item_1_1"
@@ -364,6 +405,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
iconName: ""
iconColor: ""
isIdenticon: false
},
ListElement {
value: "sub_item_1_2"
@@ -371,6 +413,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
iconName: ""
iconColor: ""
isIdenticon: true
}]}
ListElement {
value: "item_2"
@@ -378,6 +421,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
iconName: ""
iconColor: ""
isIdenticon: false
subItems: [
ListElement {
value: "sub_item_2_1"
@@ -385,6 +429,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: ""
iconName: "channel"
iconColor: ""
isIdenticon: false
},
ListElement {
value: "sub_item_2_2"
@@ -392,6 +437,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: ""
iconName: "channel"
iconColor: ""
isIdenticon: false
},
ListElement {
value: "sub_item_2_3"
@@ -399,6 +445,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: ""
iconName: "channel"
iconColor: ""
isIdenticon: false
}]}
ListElement {
value: "item_3"
@@ -406,6 +453,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: "";
iconName: "info"
iconColor: ""
isIdenticon: false
subItems: [
ListElement {
value: "sub_item_3_1"
@@ -413,6 +461,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: ""
iconName: "channel"
iconColor: ""
isIdenticon: false
}]}
ListElement {
value: "item_4"
@@ -420,6 +469,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: "";
iconName: ""
iconColor: "red"
isIdenticon: false
subItems: [
ListElement {
value: "sub_item_4_1"
@@ -427,6 +477,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
imageSource: ""
iconName: "channel"
iconColor: ""
isIdenticon: false
}]}
}
@@ -446,8 +497,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f5789"
isMutualContact: true
trustIndicator: StatusContactVerificationIcons.TrustedType.None
contactType: 2
hasMention: false
editMode: false
isReply: false
@@ -475,8 +525,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f2333"
isMutualContact: true
trustIndicator: StatusContactVerificationIcons.TrustedType.Verified
contactType: 3
hasMention: false
editMode: false
isReply: false
@@ -503,8 +552,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAkUlEQVR4nOzYwQmDQBQG4SSkl3SQyizDyuzAavTi0cPK8sPwM9/Rw8rw4KH7eZUwhMYQGkNoDKGpCfmmDl63/bh7vvx/78T7aiZiCI0hNMMbJL2FZs+vmYghNIbQTG+cp9smtf1qJmIIjSEKqZmIITSG0Azfa6W/qfxDvBhCY4hCaiZiCI0hNIbQGEJzBgAA///3pSBjX42sYAAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f2333"
isMutualContact: true
trustIndicator: StatusContactVerificationIcons.TrustedType.Verified
contactType: 3
hasMention: false
editMode: false
isReply: false
@@ -531,8 +579,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f6789"
isMutualContact: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
contactType: 4
hasMention: false
editMode: false
isReply: false
@@ -560,8 +607,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: true
trustIndicator: StatusContactVerificationIcons.TrustedType.None
contactType: 0
hasMention: false
editMode: false
isReply: false
@@ -589,8 +635,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
contactType: 0
hasMention: false
editMode: false
isReply: false
@@ -618,8 +663,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
contactType: 0
hasMention: true
editMode: false
isReply: false
@@ -648,8 +692,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
contactType: 0
hasMention: false
editMode: false
isReply: true
@@ -678,8 +721,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
contactType: 0
hasMention: false
editMode: false
isReply: true
@@ -708,8 +750,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
contactType: 0
hasMention: false
editMode: false
isReply: true
@@ -738,8 +779,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
contactType: 0
hasMention: false
editMode: false
isReply: true
@@ -767,8 +807,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Verified
contactType: 0
hasMention: false
editMode: false
isReply: false
@@ -795,8 +834,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: true
trustIndicator: StatusContactVerificationIcons.TrustedType.None
contactType: 0
hasMention: false
editMode: false
isReply: false
@@ -823,8 +861,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
profileImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDKExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
chatKey: "0x04d1bed19c523299cbd07ddec7b8949ad7dd923819a68e0b733c9c0bc38cde276bd256f098e755f8f028395c2c91f438a22adaff6caded060b7cc0ef3f470f1234"
isMutualContact: true
trustIndicator: StatusContactVerificationIcons.TrustedType.None
contactType: 0
hasMention: false
editMode: false
isReply: false
@@ -844,68 +881,46 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
id: membersList
ListElement {
nickName: "This is an example"
name: "Maria"
publicId: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusContactVerificationIcons.TrustedType.Verified
userName: "annabelle"
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusMemberListItem.TrustedType.Verified
isMutualContact: true
onlineStatus: true
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
isOnline: true
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
isIdenticon: true
ringSpecModel: [ ListElement {colorId: 13; segmentLength: 5},
ListElement {colorId: 31; segmentLength: 5},
ListElement {colorId: 10; segmentLength: 1},
ListElement {colorId: 2; segmentLength: 5},
ListElement {colorId: 26; segmentLength: 2},
ListElement {colorId: 19; segmentLength: 4},
ListElement {colorId: 28; segmentLength: 3} ]
}
ListElement {
nickName: ""
name: "carmen.eth"
publicId: "0x043a7ed78362567894688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
nickName: "carmen.eth"
userName: ""
chatKey: ""
trustIndicator: StatusMemberListItem.TrustedType.Untrustworthy
isMutualContact: false
onlineStatus: false
icon: ""
isIdenticon: false
isOnline: false
source: ""
isIdenticon: false
}
ListElement {
nickName: "This girl I know from work"
name: "annabelle"
publicId: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486dsfkjghyu2cf04"
trustIndicator: StatusContactVerificationIcons.TrustedType.None
userName: "annabelle"
chatKey: ""
trustIndicator: StatusMemberListItem.TrustedType.None
isMutualContact: false
onlineStatus: true
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDK
isOnline: true
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDK
ExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
ringSpecModel: [ ListElement {colorId: 11; segmentLength: 1},
ListElement {colorId: 23; segmentLength: 5},
ListElement {colorId: 23; segmentLength: 5},
ListElement {colorId: 10; segmentLength: 4},
ListElement {colorId: 15; segmentLength: 3},
ListElement {colorId: 26; segmentLength: 2},
ListElement {colorId: 29; segmentLength: 5} ]
}
ListElement {
nickName: "Mark Cuban"
name: "mark.eth"
publicId: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc79872cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
userName: "annabelle"
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusMemberListItem.TrustedType.Untrustworthy
isMutualContact: true
onlineStatus: false
icon: ""
isIdenticon: false
ringSpecModel: [ ListElement {colorId: 0; segmentLength: 1},
ListElement {colorId: 28; segmentLength: 1},
ListElement {colorId: 31; segmentLength: 1},
ListElement {colorId: 22; segmentLength: 4},
ListElement {colorId: 28; segmentLength: 3},
ListElement {colorId: 27; segmentLength: 5},
ListElement {colorId: 7; segmentLength: 5},
ListElement {colorId: 13; segmentLength: 1},
ListElement {colorId: 25; segmentLength: 4}]
isOnline: false
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
isIdenticon: true
}
}
@@ -1001,309 +1016,4 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
notificationsCount: 0
}
}
property ListModel currencyPickerModel: ListModel {
ListElement {
key: 0
name: "United States Dollar"
shortName: "USD"
symbol: "$"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: ""
selected: false
}
ListElement {
key: 1
name: "British Pound"
shortName: "GBP"
symbol: "£"
imageSource: "../../assets/twemoji/26x26/1f4b5.png"
category: ""
selected: false
}
ListElement {
key: 2
name: "Euro"
shortName: "EUR"
symbol: "€"
imageSource: "../../assets/twemoji/26x26/1f4b6.png"
category: ""
selected: true
}
ListElement {
key: 3
name: "Shout Korean Won"
shortName: "KRW"
symbol: "₩"
imageSource: "../../assets/twemoji/26x26/1f4b8.png"
category: ""
selected: false
}
ListElement {
key: 4
name: "Ethereum"
shortName: "ETH"
symbol: "Ξ"
imageSource: "../../assets/twemoji/26x26/1f4b7.png"
category: "Tokens"
selected: true
}
ListElement {
key: 5
name: "Bitcoin"
shortName: "BTC"
symbol: "฿"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: "Tokens"
selected: false
}
ListElement {
key: 6
name: "Status Network Token"
shortName: "SNT"
symbol: ""
imageSource: "../../assets/twemoji/26x26/1f4b8.png"
category: "Tokens"
selected: false
}
ListElement {
key: 7
name: "Emirati Dirham"
shortName: "AED"
symbol: "د.إ"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: "Other Fiat"
selected: false
}
ListElement {
key: 8
name: "Afghani"
shortName: "AFN"
symbol: "؋"
imageSource: "../../assets/twemoji/26x26/1f4b7.png"
category: "Other Fiat"
selected: false
}
ListElement {
key: 9
name: "Argentine Peso"
shortName: "AFN"
symbol: "$"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: "Other Fiat"
selected: false
}
}
property ListModel currencyPickerModel2: ListModel {
ListElement {
key: 0
name: "United States Dollar"
shortName: "USD"
symbol: "$"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: ""
selected: false
}
ListElement {
key: 1
name: "British Pound"
shortName: "GBP"
symbol: "£"
imageSource: "../../assets/twemoji/26x26/1f4b5.png"
category: ""
selected: false
}
ListElement {
key: 2
name: "Euro"
shortName: "EUR"
symbol: "€"
imageSource: "../../assets/twemoji/26x26/1f4b6.png"
category: ""
selected: true
}
ListElement {
key: 3
name: "Shout Korean Won"
shortName: "KRW"
symbol: "₩"
imageSource: "../../assets/twemoji/26x26/1f4b8.png"
category: ""
selected: false
}
ListElement {
key: 4
name: "Ethereum"
shortName: "ETH"
symbol: "Ξ"
imageSource: "../../assets/twemoji/26x26/1f4b7.png"
category: "Tokens"
selected: true
}
ListElement {
key: 5
name: "Bitcoin"
shortName: "BTC"
symbol: "฿"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: "Tokens"
selected: false
}
ListElement {
key: 6
name: "Status Network Token"
shortName: "SNT"
symbol: ""
imageSource: "../../assets/twemoji/26x26/1f4b8.png"
category: "Tokens"
selected: false
}
ListElement {
key: 7
name: "Emirati Dirham"
shortName: "AED"
symbol: "د.إ"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: "Other Fiat"
selected: false
}
ListElement {
key: 8
name: "Afghani"
shortName: "AFN"
symbol: "؋"
imageSource: "../../assets/twemoji/26x26/1f4b7.png"
category: "Other Fiat"
selected: false
}
ListElement {
key: 9
name: "Argentine Peso"
shortName: "AFN"
symbol: "$"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: "Other Fiat"
selected: false
}
}
property ListModel languagePickerModel: ListModel {
ListElement {
key: 0
name: "English"
shortName: "English"
imageSource: "../../assets/twemoji/26x26/1f1ec-1f1e7.png"
category: ""
selected: false
}
ListElement {
key: 1
name: "Korean"
shortName: "한국어"
imageSource: "../../assets/twemoji/26x26/1f1f0-1f1f7.png"
category: ""
selected: false
}
ListElement {
key: 2
name: "Portuguese (Brazilian)"
shortName: "Português"
imageSource: "../../assets/twemoji/26x26/1f1e7-1f1f7.png"
category: ""
selected: true
}
ListElement {
key: 3
name: "Dutch"
shortName: "Nederlands"
imageSource: "../../assets/twemoji/26x26/1f1f3-1f1f1.png"
category: "Beta Languages"
selected: false
}
ListElement {
key: 4
name: "Indonesian"
shortName: "Bahasa Indonesia"
imageSource: "../../assets/twemoji/26x26/1f1ee-1f1e9.png"
category: "Beta Languages"
selected: false
}
ListElement {
key: 5
name: "Spanish"
shortName: "Español"
imageSource: "../../assets/twemoji/26x26/1f1ea-1f1e6.png"
category: "Beta Languages"
selected: false
}
}
property ListModel languageNoImagePickerModel: ListModel {
ListElement {
key: 0
name: "Chinese (Mainland China)"
shortName: "普通话"
category: ""
selected: true
}
ListElement {
key: 1
name: "Russian"
shortName: "Русский Язык"
category: ""
selected: false
}
ListElement {
key: 2
name: "Arabic"
shortName: "اَلْعَرَبِيَّةُ"
category: "Beta Languages"
selected: true
}
ListElement {
key: 3
name: "Chinese (Taiwan)"
shortName: "臺灣華語"
category: "Beta Languages"
selected: false
}
ListElement {
key: 4
name: "Filipino"
shortName: "Wikang Filipino"
category: "Beta Languages"
selected: false
}
ListElement {
key: 5
name: "French"
shortName: "Français"
category: "Beta Languages"
selected: false
}
ListElement {
key: 6
name: "Italian"
shortName: "Italiano"
category: "Beta Languages"
selected: false
}
ListElement {
key: 7
name: "Turkish"
shortName: "Türkçe"
category: "Beta Languages"
selected: false
}
ListElement {
key: 8
name: "Urdu"
shortName: "اُردُو"
category: "Beta Languages"
selected: false
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

@@ -22,7 +22,6 @@
\li \l{StatusDescriptionListItem}
\li \l{StatusLetterIdenticon}
\li \l{StatusListItem}
\li \l{StatusListPicker}
\li \l{StatusListSectionHeadline}
\li \l{StatusLoadingIndicator}
\li \l{StatusMemberListItem}
@@ -38,6 +37,5 @@
\li \l{StatusMessageDetails}
\li \l{StatusTagSelector}
\li \l{StatusToastMessage}
\li \l{StatusWizardStepper}
\endlist
*/
@@ -30,11 +30,8 @@
\li \l{StatusSlider}
\li \l{StatusSelect}
\li \l{StatusBaseInput}
\li \l{StatusIdenticonRing}
\li \l{StatusInput}
\li \l{StatusItemPicker}
\li \l{StatusPickerButton}
\li \l{StatusPinInput}
\li \l{StatusProgressBar}
\li \l{StatusPasswordStrengthIndicator}
\li \l{StatusSwitchTabButton}
@@ -42,8 +39,5 @@
\li \l{StatusSelectableText}
\li \l{StatusWalletColorButton}
\li \l{StatusWalletColorSelect}
\li \l{StatusRegularExpressionValidator}
\li \l{StatusIntValidator}
\li \l{StatusSeedPhraseInput}
\endlist
*/
@@ -15,7 +15,6 @@
\li \l{StatusIcon}
\li \l{StatusIconBackgroundSettings}
\li \l{StatusIconSettings}
\li \l{StatusIdenticonRingSettings}
\li \l{StatusImageSettings}
\li \l{StatusModalHeaderSettings}
\li \l{StatusTooltipSettings}
@@ -5,16 +5,16 @@ url = https://github.com/status-im/StatusQ
#These are set implictly by qdoc as well, but only with these the module header can be found
includepaths = -I . \
-I $PWD/../src/StatusQ
-I $PWD/../../src/StatusQ
imagedirs = src/images
sourcedirs += src \
$PWD/../src/StatusQ/Components \
$PWD/../src/StatusQ/Controls \
$PWD/../src/StatusQ/Core \
$PWD/../src/StatusQ/Layout \
$PWD/../src/StatusQ/Platform \
$PWD/../src/StatusQ/Popups \
$PWD/../../src/StatusQ/Components \
$PWD/../../src/StatusQ/Controls \
$PWD/../../src/StatusQ/Core \
$PWD/../../src/StatusQ/Layout \
$PWD/../../src/StatusQ/Platform \
$PWD/../../src/StatusQ/Popups \
sources.fileextensions = "*.cpp *.qdoc *.qml"
-5
View File
@@ -11,11 +11,6 @@ int main(int argc, char *argv[])
#endif
SandboxApp app(argc, argv);
app.setOrganizationName("Status");
app.setOrganizationDomain("status.im");
app.setApplicationName("Sandbox");
app.startEngine();
return app.exec();
+13 -58
View File
@@ -2,8 +2,6 @@ import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.14
import QtGraphicalEffects 1.13
import QtQuick.Layouts 1.14
import Qt.labs.settings 1.0
import Sandbox 0.1
@@ -33,7 +31,10 @@ StatusWindow {
property real factor: 1.0
Component.onCompleted: rootWindow.updatePosition()
Component.onCompleted: {
Theme.palette = lightTheme
rootWindow.updatePosition();
}
QtObject {
id: appSectionType
@@ -109,10 +110,12 @@ StatusWindow {
anchors.topMargin: 32
anchors.right: parent.right
anchors.rightMargin: 32
lightThemeEnabled: storeSettings.lightTheme
onLightThemeEnabledChanged: {
Theme.palette = lightThemeEnabled ? rootWindow.darkTheme : rootWindow.lightTheme
storeSettings.lightTheme = lightThemeEnabled
onCheckedChanged: {
if (Theme.palette === rootWindow.lightTheme) {
Theme.palette = rootWindow.darkTheme
} else {
Theme.palette = rootWindow.lightTheme
}
}
}
}
@@ -123,13 +126,11 @@ StatusWindow {
StatusAppTwoPanelLayout {
id: mainPageView
function page(name, fillPage) {
storeSettings.fillPage = fillPage ? true : false
function page(name) {
viewLoader.source = Qt.resolvedUrl("./pages/" + name + "Page.qml");
}
function control(name) {
viewLoader.source = Qt.resolvedUrl("./controls/" + name + ".qml");
storeSettings.fillPage = false
}
leftPanel: Item {
@@ -183,11 +184,6 @@ StatusWindow {
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.control(title);
}
StatusNavigationListItem {
title: "StatusTabBarButton"
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title);
}
StatusNavigationListItem {
title: "StatusTabBarIconButton"
selected: viewLoader.source.toString().includes(title)
@@ -233,11 +229,6 @@ StatusWindow {
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title);
}
StatusNavigationListItem {
title: "StatusPinInput"
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title);
}
StatusListSectionHeadline { text: "StatusQ.Components" }
StatusNavigationListItem {
title: "StatusAddress"
@@ -279,11 +270,6 @@ StatusWindow {
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title);
}
StatusNavigationListItem {
title: "StatusListPicker"
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title);
}
StatusListSectionHeadline { text: "StatusQ.Popup" }
StatusNavigationListItem {
title: "StatusPopupMenu"
@@ -302,16 +288,6 @@ StatusWindow {
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title);
}
StatusNavigationListItem {
title: "StatusColorSelectorGrid"
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title);
}
StatusNavigationListItem {
title: "StatusImageCropPanel"
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title, true);
}
}
}
}
@@ -319,11 +295,8 @@ StatusWindow {
rightPanel: Item {
id: rightPanel
anchors.fill: parent
ScrollView {
visible: !storeSettings.fillPage
anchors.fill: parent
anchors.topMargin: 64
contentHeight: (pageWrapper.height + pageWrapper.anchors.topMargin) * rootWindow.factor
contentWidth: (pageWrapper.width * rootWindow.factor)
clip: true
@@ -332,16 +305,14 @@ StatusWindow {
id: pageWrapper
width: rightPanel.width
anchors.top: parent.top
anchors.topMargin: 64
height: Math.max(rootWindow.height, viewLoader.height + 128)
scale: rootWindow.factor
Loader {
id: viewLoader
active: !storeSettings.fillPage
anchors.centerIn: parent
source: storeSettings.selected.length === 0 ? mainPageView.control("Icons") : storeSettings.selected
source: mainPageView.control("Icons")
onSourceChanged: {
storeSettings.selected = viewLoader.source
if (source.toString().includes("Icons")) {
item.iconColor = Theme.palette.primaryColor1;
}
@@ -349,15 +320,6 @@ StatusWindow {
}
}
}
Loader {
active: storeSettings.fillPage
anchors.fill: parent
anchors.topMargin: 64
visible: storeSettings.fillPage
clip: true
source: viewLoader.source
}
}
}
}
@@ -475,11 +437,4 @@ StatusWindow {
rootWindow.toggleFullScreen()
}
}
Settings {
id: storeSettings
property string selected: ""
property bool lightTheme: true
property bool fillPage: false
}
}
@@ -1,36 +0,0 @@
import QtQuick 2.14
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
Item {
id: root
width: 800
height: 100
Row {
id: selectedColor
anchors.top: parent.top
anchors.left: colorSelectionGrid.left
spacing: 10
StatusBaseText {
text: "SelectedColor is"
}
Rectangle {
width: 100
height: 20
radius: width/2
color: colorSelectionGrid.selectedColor
}
}
StatusColorSelectorGrid {
id: colorSelectionGrid
anchors.top: selectedColor.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
titleText: "COLOR"
selectedColorIndex: 2
}
}
@@ -1,94 +0,0 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import QtGraphicalEffects 1.14
Item {
id: root
implicitWidth: mainLayout.implicitWidth
implicitHeight: mainLayout.implicitHeight
property var testControls: [ctrl1, ctrl2, ctrl3]
property bool globalStylePreferRound: true
ColumnLayout {
id: mainLayout
anchors.fill: parent
RowLayout {
ColumnLayout {
Text {
text: `AR: ${ctrl1.aspectRatio.toFixed(2)}`
}
StatusImageCropPanel {
id: ctrl1
Layout.fillWidth: true
Layout.fillHeight: true
source: "qrc:/demoapp/data/logo-test-image.png"
windowStyle: globalStylePreferRound ? StatusImageCrop.WindowStyle.Rounded : StatusImageCrop.WindowStyle.Rectangular
}
Text {
text: `AR: ${ctrl2.aspectRatio.toFixed(2)}`
}
StatusImageCropPanel {
id: ctrl2
Layout.fillWidth: true
Layout.fillHeight: true
source: "qrc:/demoapp/data/logo-test-image.png"
windowStyle: globalStylePreferRound ? StatusImageCrop.WindowStyle.Rectangular : StatusImageCrop.WindowStyle.Rounded
aspectRatio: 16/9
enableCheckers: true
}
}
ColumnLayout {
Text {
text: `AR: ${ctrl3.aspectRatio.toFixed(2)}`
}
StatusImageCropPanel {
id: ctrl3
Layout.fillWidth: true
Layout.fillHeight: true
source: "qrc:/demoapp/data/logo-test-image.png"
windowStyle: globalStylePreferRound ? StatusImageCrop.WindowStyle.Rounded : StatusImageCrop.WindowStyle.Rectangular
aspectRatio: 0.8
}
}
}
Shortcut {
sequence: StandardKey.ZoomIn
onActivated: {
for(let i in testControls) {
const c = testControls[i]
c.setCropRect(ctrl1.inflateRectBy(c.cropRect, -0.05))
}
}
}
Shortcut {
sequence: StandardKey.ZoomOut
onActivated: {
for(let i in testControls) {
const c = testControls[i]
c.setCropRect(ctrl1.inflateRectBy(c.cropRect, 0.05))
}
}
}
Shortcut {
sequences: ["Ctrl+W"]
onActivated: globalStylePreferRound = !globalStylePreferRound
}
}
}
+2 -84
View File
@@ -57,38 +57,11 @@ Column {
input.clearable: true
}
Item {
implicitWidth: 480
implicitHeight: 82
z: 100000
StatusSeedPhraseInput {
id: statusSeedInput
anchors.left: parent.left
anchors.right: parent.right
height: parent.height
textEdit.input.anchors.leftMargin: 16
textEdit.input.anchors.rightMargin: 16
textEdit.input.anchors.topMargin: 11
textEdit.label: "Input with drop down selection list"
leftComponentText: "1"
inputList: ListModel {
ListElement {
seedWord: "panda"
}
ListElement {
seedWord: "posible"
}
ListElement {
seedWord: "wing"
}
}
}
}
StatusInput {
label: "Label"
charLimit: 30
errorMessage: "Error message"
input.clearable: true
input.valid: false
input.placeholderText: "Placeholder"
@@ -129,24 +102,10 @@ Column {
]
}
StatusInput {
label: "Input with <i>StatusRegularExpressionValidator</i>"
charLimit: 30
input.placeholderText: `Must match regex(${validators[0].regularExpression.toString()}) and <= 30 chars`
validationMode: StatusInput.ValidationMode.IgnoreInvalidInput
validators: [
StatusRegularExpressionValidator {
regularExpression: /^[0-9A-Za-z_\$-\s]*$/
errorMessage: "Bad input!"
}
]
}
StatusInput {
label: "Label"
input.placeholderText: "Input width component (right side)"
input.rightComponent: StatusIcon {
input.component: StatusIcon {
icon: "cancel"
height: 16
color: Theme.palette.dangerColor1
@@ -171,45 +130,4 @@ Column {
input.minimumHeight: 80
input.maximumHeight: 200
}
StatusInput {
property bool toggled: true
label: "Input with emoji icon"
input.placeholderText: "Enter Name"
input.icon.emoji: toggled ? "😁" : "🧸"
input.icon.color: "blue"
input.isIconSelectable: true
onIconClicked: {
toggled = !toggled
}
}
StatusInput {
property bool toggled: true
label: "Input with selectable icon which is not an emoji"
input.placeholderText: "Enter Name"
input.icon.emoji: ""
input.icon.name: toggled ? "filled-account" : "image"
input.icon.color: "blue"
input.isIconSelectable: true
onIconClicked: {
toggled = !toggled
}
}
StatusInput {
label: "Input with inline token selector"
input.leftComponent: StatusTokenInlineSelector {
tokens: [{amount: 0.1, token: "ETH"},
{amount: 10, token: "SNT"},
{amount: 15, token: "MANA"}]
StatusToolTip {
id: toolTip
text: "posted"
}
onTriggered: toolTip.visible = true
}
input.edit.readOnly: true
}
}
-81
View File
@@ -1,81 +0,0 @@
import QtQuick 2.0
import QtQuick.Layouts 1.14
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1
import "../demoapp/data" 1.0
GridLayout {
id: root
columns: 1
rowSpacing: 150
GridLayout {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
rows: 4
columns: 2
rowSpacing: 170
columnSpacing: 150
z: 100
StatusListPicker {
id: languagePicker
z: 100
inputList: Models.languagePickerModel
placeholderSearchText: qsTr("Search Languages")
menuAlignment: StatusListPicker.MenuAlignment.Left
}
StatusListPicker {
id: languagePicker2
z: 100
inputList: Models.languageNoImagePickerModel
placeholderSearchText: qsTr("Search Languages")
menuAlignment: StatusListPicker.MenuAlignment.Center
}
StatusListPicker {
id: currencyPicker
inputList: Models.currencyPickerModel
placeholderSearchText: qsTr("Search Currencies")
multiSelection: true
}
StatusListPicker {
id: currencyPicker2
inputList: Models.currencyPickerModel2
placeholderSearchText: qsTr("Search Currencies")
multiSelection: true
printSymbol: true
enableSelectableItem: false
}
}
StatusBaseText {
id: pageDesc
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
height: 100
width: 500
text: "4 different configurations for the `StatusListPicker` component:\n
* Single selection. \n
* Single selection but dynamically changed to multiple selection (model provides multiple selected items).\n
* Multiple selection.\n
* Multiple selection and displayed name is the symbol + shortName\n"
color: Theme.palette.baseColor1
font.pixelSize: 15
}
// Outsite area
MouseArea {
height: root.height
width: root.width
onClicked: {
languagePicker.close()
languagePicker2.close()
currencyPicker.close()
currencyPicker2.close()
}
}
}
-75
View File
@@ -1,75 +0,0 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
Column {
id: root
spacing: 25
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
// PIN input that accepts only numbers
StatusBaseText {
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.palette.directColor1
text: "Enter Keycard PIN"
font.pixelSize: 30
font.bold: true
}
StatusPinInput {
id: numbersPinInput
anchors.horizontalCenter: parent.horizontalCenter
validator: StatusIntValidator{bottom: 0; top: 999999;}
}
StatusBaseText {
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.palette.dangerColor1
text: "Only numbers allowed"
font.pixelSize: 16
}
StatusBaseText {
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.palette.directColor1
text: "Introduced PIN: " + numbersPinInput.pinInput
font.pixelSize: 12
}
// PIN input that accepts input depending on the regular expression definition
StatusBaseText {
topPadding: 100
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.palette.directColor1
text: "Enter another Keycard PIN"
font.pixelSize: 30
font.bold: true
}
StatusPinInput {
id: regexPinInput
anchors.horizontalCenter: parent.horizontalCenter
validator: StatusRegularExpressionValidator { regularExpression: /[0-9A-Za-z@]+/ }
circleDiameter: 22
circleSpacing: 22
pinLen: 7
}
StatusBaseText {
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.palette.dangerColor1
text: "Only alphanumeric characters and '@' allowed"
font.pixelSize: 16
}
StatusBaseText {
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.palette.directColor1
text: "Introduced PIN: " + regexPinInput.pinInput
font.pixelSize: 12
}
}
-27
View File
@@ -1,27 +0,0 @@
import QtQuick 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import Sandbox 0.1
Column {
spacing: 8
StatusTabBar {
StatusTabButton {
width: implicitWidth
text: "Button 1"
}
StatusTabButton {
width: implicitWidth
text: "Button 2"
}
StatusTabButton {
width: implicitWidth
text: "Button 3"
badge.value: 42
}
}
}
+1 -1
View File
@@ -51,6 +51,6 @@ Item {
anchors.centerIn: parent
namesModel: root.asortedContacts
toLabelText: qsTr("To: ")
warningText: qsTr("USER LIMIT REACHED")
warningText: qsTr("5 USER LIMIT REACHED")
}
}
+7 -8
View File
@@ -15,11 +15,10 @@ Item {
width: parent.width
height: childrenRect.height
model: [
{"title":"anna.eth wants to verify your identity", "subTitle":"Provide the code in the letter I sent to you on February 1st.", "icon":"contact", "loading":false, "type":0,"url":"", "duration":0},
{"title":"Verification Request Sent", "subTitle":"", "icon":"checkmark-circle", "loading":false, "type":1,"url":"", "duration":4000},
{"title":"Collectible is being minted...", "subTitle":"View on Etherscan", "icon":"", "loading":true, "type":0,"url":"http://google.com", "duration":0},
{"title":"Contact request sent", "subTitle":"", "icon":"checkmark-circle", "loading":false, "type":1,"url":"", "duration":4000},
{"title":"Test User", "subTitle":"Hello message...", "icon":"", "loading":false, "type":0,"url":"", "duration":4000}
{"title":"anna.eth wants to verify your identity", "subTitle":"Provide the code in the letter I sent to you on February 1st.", "icon":"contact", "loading":false, "type":0,"url":""},
{"title":"Verification Request Sent", "subTitle":"", "icon":"checkmark-circle", "loading":false, "type":1,"url":""},
{"title":"Collectible is being minted...", "subTitle":"View on Etherscan", "icon":"", "loading":true, "type":0,"url":"http://google.com"},
{"title":"Contact request sent", "subTitle":"", "icon":"checkmark-circle", "loading":false, "type":1,"url":""}
]
delegate: StatusToastMessage {
primaryText: modelData.title
@@ -28,12 +27,12 @@ Item {
loading: modelData.loading
type: modelData.type
linkUrl: modelData.url
duration: modelData.duration
onLinkActivated: {
Qt.openUrlExternally(link);
}
onClose: {
console.warn("toast closed: ", modelData.title)
//simulate open
Component.onCompleted: {
open = true;
}
}
}
@@ -12,6 +12,6 @@ Column {
spacing: 8
StatusWalletColorSelect {
model: Theme.palette.userCustomizationColors
model: Theme.palette.accountColors
}
}
+2 -14
View File
@@ -16,7 +16,7 @@
<file>pages/StatusTabSwitchPage.qml</file>
<file>pages/StatusWalletColorButtonPage.qml</file>
<file>pages/StatusWalletColorSelectPage.qml</file>
<file>pages/StatusPasswordStrengthIndicatorPage.qml</file>
<file>pages/StatusPasswordStrengthIndicatorPage.qml</file>
<file>controls/Buttons.qml</file>
<file>controls/Controls.qml</file>
<file>controls/Icons.qml</file>
@@ -24,8 +24,6 @@
<file>controls/ListItems.qml</file>
<file>controls/Others.qml</file>
<file>controls/Popups.qml</file>
<file>demoapp/ChatChannelView.qml</file>
<file>demoapp/CreateChatView.qml</file>
<file>demoapp/DemoCommunityDetailModal.qml</file>
<file>demoapp/DemoContactRequestsModal.qml</file>
<file>demoapp/StatusAppChatView.qml</file>
@@ -33,15 +31,5 @@
<file>demoapp/StatusAppProfileSettingsView.qml</file>
<file>demoapp/data/Models.qml</file>
<file>demoapp/data/qmldir</file>
<file>demoapp/data/logo-test-image.png</file>
<file>DemoApp.qml</file>
<file>ThemeSwitch.qml</file>
<file>pages/StatusColorSelectorGridPage.qml</file>
<file>pages/StatusListPickerPage.qml</file>
<file>pages/StatusPinInputPage.qml</file>
<file>pages/StatusTagSelectorPage.qml</file>
<file>pages/StatusToastMessagePage.qml</file>
<file>pages/StatusWizardStepperPage.qml</file>
<file>pages/StatusTabBarButtonPage.qml</file>
</qresource>
</RCC>
</RCC>
+3 -9
View File
@@ -1,10 +1,4 @@
#!/bin/bash
make clean
if [[ $OSTYPE == 'darwin'* ]]
then
qmake sandbox.pro -spec macx-clang CONFIG+=debug CONFIG+=x86_64 && /usr/bin/make qmake_all
else
qmake sandbox.pro CONFIG+=debug CONFIG+=x86_64 && /usr/bin/make qmake_all
fi
make
qmake sandbox.pro -spec macx-clang CONFIG+=debug CONFIG+=x86_64 && /usr/bin/make qmake_all
make
+1 -1
View File
@@ -32,7 +32,7 @@ Rectangle {
anchors.centerIn: parent
text: {
if (statusBadge.value > 99) {
return qsTr("99+");
return "99+";
}
return statusBadge.value;
}
@@ -27,6 +27,7 @@ Item {
id: statusChatInfoButton
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 5
anchors.right: (implicitWidth > parent.width - 50) ? statusMenuButton.left : undefined
anchors.rightMargin: 5
type: StatusChatInfoButton.Type.OneToOneChat
@@ -36,6 +37,7 @@ Item {
StatusRoundButton {
id: statusMenuButton
anchors.right: parent.right
anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter
visible: popupMenuSlot.active
@@ -102,4 +104,5 @@ Item {
}
}
}
}
+5 -12
View File
@@ -15,9 +15,8 @@ Column {
width: 288
property string categoryId: ""
property var model: null
property var model: []
property bool draggableItems: false
property bool highlightItem: true
property alias statusChatListItems: statusChatListItems
@@ -91,8 +90,8 @@ Column {
}
StatusChatListItem {
id: statusChatListItem
id: statusChatListItem
opacity: dragSensor.active ? 0.0 : 1.0
originalOrder: model.position
chatId: model.itemId
@@ -102,19 +101,13 @@ Column {
muted: model.muted
hasUnreadMessages: model.hasUnreadMessages
notificationsCount: model.notificationsCount
highlightWhenCreated: !!model.highlight
selected: (model.active && statusChatList.highlightItem)
icon.emoji: model.emoji
icon.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId]
image.isIdenticon: false
selected: model.active
icon.color: model.color
image.isIdenticon: model.isIdenticon
image.source: model.icon
ringSettings.ringSpecModel: model.colorHash
sensor.cursorShape: dragSensor.cursorShape
onClicked: {
highlightWhenCreated = false
if (mouse.button === Qt.RightButton && !!statusChatList.popupMenu) {
statusChatListItem.highlighted = true
@@ -14,10 +14,10 @@ Item {
implicitWidth: chatListsAndCategories.width
property StatusTooltipSettings categoryAddButtonToolTip: StatusTooltipSettings {
text: qsTr("Add channel inside category")
text: "Add channel inside category"
}
property StatusTooltipSettings categoryMenuButtonToolTip: StatusTooltipSettings {
text: qsTr("More")
text: "More"
}
property var model: []
@@ -68,6 +68,7 @@ Item {
StatusChatList {
id: statusChatList
anchors.horizontalCenter: parent.horizontalCenter
visible: statusChatList.model.count > 0
onChatItemSelected: statusChatListAndCategories.chatItemSelected(categoryId, id)
onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id)
@@ -48,6 +48,7 @@ Column {
popupMenuSlot.item.popup(mouse.x + 4, mouse.y + 6);
return
}
statusChatListCategory.opened = !opened;
}
}
onTitleClicked: statusChatListCategory.opened = !opened
@@ -39,21 +39,21 @@ StatusListItem {
id: addButton
icon.name: "add"
icon.width: 20
visible: statusChatListCategoryItem.showAddButton &&
visible: statusChatListCategoryItem.showAddButton &&
(statusChatListCategoryItem.highlighted ||
statusChatListCategoryItem.sensor.containsMouse)
onClicked: statusChatListCategoryItem.addButtonClicked(mouse)
tooltip.text: qsTr("Add channel inside category")
tooltip.text: "Add channel inside category"
},
StatusChatListCategoryItemButton {
id: menuButton
icon.name: "more"
icon.width: 21
visible: statusChatListCategoryItem.showMenuButton &&
visible: statusChatListCategoryItem.showMenuButton &&
(statusChatListCategoryItem.highlighted ||
statusChatListCategoryItem.sensor.containsMouse)
onClicked: statusChatListCategoryItem.menuButtonClicked(mouse)
tooltip.text: qsTr("More")
tooltip.text: "More"
},
StatusChatListCategoryItemButton {
id: toggleButton
+31 -37
View File
@@ -5,12 +5,11 @@ import QtGraphicalEffects 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
Rectangle {
id: root
id: statusChatListItem
objectName: "chatItem"
property int originalOrder: -1
@@ -29,13 +28,10 @@ Rectangle {
width: 24
height: 24
color: Theme.palette.miscColor5
emoji: ""
charactersLen: root.type === StatusChatListItem.Type.OneToOneChat ? 2 : 1
letterSize: 15
}
property alias ringSettings: identicon.ringSettings
property int type: StatusChatListItem.Type.PublicChat
property bool highlighted: false
property bool highlightWhenCreated: false
property bool selected: false
property bool dragged: false
property alias sensor: sensor
@@ -78,16 +74,16 @@ Rectangle {
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: root.clicked(mouse)
onClicked: statusChatListItem.clicked(mouse)
StatusSmartIdenticon {
id: identicon
anchors.left: parent.left
anchors.leftMargin: 8
anchors.verticalCenter: parent.verticalCenter
image: root.image
icon: root.icon
name: root.name
image: statusChatListItem.image
icon: statusChatListItem.icon
name: statusChatListItem.name
}
StatusIcon {
@@ -97,21 +93,21 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 14
visible: root.type !== StatusChatListItem.Type.OneToOneChat
visible: statusChatListItem.type !== StatusChatListItem.Type.OneToOneChat
opacity: {
if (root.muted && !hoverHander.hovered && !root.highlighted) {
if (statusChatListItem.muted && !hoverHander.hovered && !statusChatListItem.highlighted) {
return 0.4
}
return root.hasUnreadMessages ||
root.notificationsCount > 0 ||
root.selected ||
root.highlighted ||
return statusChatListItem.hasUnreadMessages ||
statusChatListItem.notificationsCount > 0 ||
statusChatListItem.selected ||
statusChatListItem.highlighted ||
statusBadge.visible ||
hoverHander.hovered ? 1.0 : 0.7
}
icon: {
switch (root.type) {
switch (statusChatListItem.type) {
case StatusChatListItem.Type.PublicCat:
return Theme.palette.name == "light" ? "tiny/public-chat" : "tiny/public-chat-white"
break;
@@ -136,27 +132,25 @@ Rectangle {
anchors.rightMargin: 6
anchors.verticalCenter: parent.verticalCenter
text: (root.type === StatusChatListItem.Type.PublicChat &&
!root.name.startsWith("#") ?
"#" + root.name :
root.name)
text: (statusChatListItem.type === StatusChatListItem.Type.PublicChat &&
!statusChatListItem.name.startsWith("#") ?
"#" + statusChatListItem.name :
statusChatListItem.name)
elide: Text.ElideRight
color: {
if (root.muted && !hoverHander.hovered && !root.highlighted) {
if (statusChatListItem.muted && !hoverHander.hovered && !statusChatListItem.highlighted) {
return Theme.palette.directColor5
}
return root.hasUnreadMessages ||
root.notificationsCount > 0 ||
root.selected ||
root.highlighted ||
root.highlightWhenCreated ||
return statusChatListItem.hasUnreadMessages ||
statusChatListItem.notificationsCount > 0 ||
statusChatListItem.selected ||
statusChatListItem.highlighted ||
hoverHander.hovered ||
statusBadge.visible ? Theme.palette.directColor1 : Theme.palette.directColor4
}
font.weight: !root.muted &&
(root.hasUnreadMessages ||
root.notificationsCount > 0 ||
root.highlightWhenCreated ||
font.weight: !statusChatListItem.muted &&
(statusChatListItem.hasUnreadMessages ||
statusChatListItem.notificationsCount > 0 ||
statusBadge.visible) ? Font.Bold : Font.Medium
font.pixelSize: 15
}
@@ -169,18 +163,18 @@ Rectangle {
width: 14
opacity: mutedIconSensor.containsMouse ? 1.0 : 0.2
icon: Theme.palette.name === "light" ? "tiny/muted" : "tiny/muted-white"
visible: root.muted
visible: statusChatListItem.muted
MouseArea {
id: mutedIconSensor
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: root.unmute()
onClicked: statusChatListItem.unmute()
}
StatusToolTip {
text: qsTr("Unmute")
text: "Unmute"
visible: mutedIconSensor.containsMouse
}
}
@@ -192,11 +186,11 @@ Rectangle {
anchors.right: parent.right
anchors.rightMargin: 8
color: root.muted ? Theme.palette.primaryColor2 : Theme.palette.primaryColor1
color: statusChatListItem.muted ? Theme.palette.primaryColor2 : Theme.palette.primaryColor1
border.width: 4
border.color: color
value: root.notificationsCount
visible: root.notificationsCount > 0
value: statusChatListItem.notificationsCount
visible: statusChatListItem.notificationsCount > 0
}
}
}
+4 -4
View File
@@ -58,7 +58,7 @@ Rectangle {
onClicked: statusChatToolBar.searchButtonClicked()
// initializing the tooltip
tooltip.text: qsTr("Search")
tooltip.text: "Search"
tooltip.orientation: StatusToolTip.Orientation.Bottom
tooltip.y: parent.height + 12
}
@@ -72,7 +72,7 @@ Rectangle {
onClicked: statusChatToolBar.membersButtonClicked()
// initializing the tooltip
tooltip.text: qsTr("Members")
tooltip.text: "Members"
tooltip.orientation: StatusToolTip.Orientation.Bottom
tooltip.y: parent.height + 12
}
@@ -87,7 +87,7 @@ Rectangle {
// initializing the tooltip
tooltip.visible: !!tooltip.text && menuButton.hovered && !popupMenuSlot.item.opened
tooltip.text: qsTr("More")
tooltip.text: "More"
tooltip.orientation: StatusToolTip.Orientation.Bottom
tooltip.y: parent.height + 12
@@ -141,7 +141,7 @@ Rectangle {
type: StatusFlatRoundButton.Type.Secondary
// initializing the tooltip
tooltip.text: qsTr("Activity")
tooltip.text: "Activity"
tooltip.orientation: StatusToolTip.Orientation.Bottom
tooltip.y: parent.height + 12
@@ -14,7 +14,6 @@ StatusListItem {
components: [
StatusBadge {
visible: statusContactRequestsListItem.requestsCount > 0
value: statusContactRequestsListItem.requestsCount
anchors.verticalCenter: parent.verticalCenter
border.width: 4
@@ -1,79 +0,0 @@
import QtQuick 2.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
Row {
id: root
property bool isMutualContact: false
property var trustIndicator: StatusContactVerificationIcons.TrustedType.None
property StatusIconSettings mutualConnectionIcon: StatusIconSettings {
name: "tiny/tiny-contact"
color: Theme.palette.indirectColor1
width: dummyImage.width
height: dummyImage.height
background: StatusIconBackgroundSettings {
width: 10
height: 10
color: Theme.palette.primaryColor1
}
// Only used to get implicit width and height from the actual image
property Image dummyImage: Image {
source: mutualConnectionIcon.name ? "../../assets/img/icons/" + mutualConnectionIcon.name + ".svg": ""
visible: false
}
}
property StatusIconSettings trustContactIcon: StatusIconSettings {
// None and Untrustworthy types, same aspect (Icon will not be visible in case of None type):
name: root.trustIndicator === StatusContactVerificationIcons.TrustedType.Verified ? "tiny/tiny-checkmark" : "tiny/subtract"
color: Theme.palette.indirectColor1
width: dummyImage.width
height: dummyImage.height
background: StatusIconBackgroundSettings {
width: 10
height: 10
color: root.trustIndicator === StatusContactVerificationIcons.TrustedType.Verified ? Theme.palette.primaryColor1 : Theme.palette.dangerColor1
}
// Only used to get implicit width and height from the actual image
property Image dummyImage: Image {
source: trustContactIcon.name ? "../../assets/img/icons/" + trustContactIcon.name + ".svg": ""
visible: false
}
}
enum TrustedType {
None, //0
Verified, //1
Untrustworthy //2
}
spacing: 4
visible: root.isMutualContact || (root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None)
StatusRoundIcon {
visible: root.isMutualContact
icon.name: root.mutualConnectionIcon.name
icon.width: root.mutualConnectionIcon.width
icon.height: root.mutualConnectionIcon.height
icon.rotation: root.mutualConnectionIcon.rotation
icon.color: root.mutualConnectionIcon.color
icon.background.color: root.mutualConnectionIcon.background.color
icon.background.width: root.mutualConnectionIcon.background.width
icon.background.height: root.mutualConnectionIcon.background.height
}
StatusRoundIcon {
visible: root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None
icon.name: root.trustContactIcon.name
icon.width: root.trustContactIcon.width
icon.height: root.trustContactIcon.height
icon.rotation: root.trustContactIcon.rotation
icon.color: root.trustContactIcon.color
icon.background.color: root.trustContactIcon.background.color
icon.background.width: root.trustContactIcon.background.width
icon.background.height: root.trustContactIcon.background.height
}
}
-19
View File
@@ -1,19 +0,0 @@
import QtQuick 2.13
import StatusQ.Core 0.1
Image {
id: root
property string emojiId: ""
width: 14
height: 14
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
mipmap: true
antialiasing: true
source: emojiId ? `../../assets/twemoji/svg/${emojiId}.svg` : ""
}
@@ -12,7 +12,6 @@ Rectangle {
property alias additionalText: additionalText.text
property alias button: button
property alias expandableComponent: expandableRegion.sourceComponent
property alias expandableItem: expandableRegion.item
property int type: StatusExpandableItem.Type.Primary
property bool expandable: true
@@ -186,7 +185,7 @@ Rectangle {
anchors.leftMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 48 : 0
anchors.right: parent.right
anchors.rightMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 16 : 0
visible: false
active: false
}
onExpandedChanged: {
@@ -203,13 +202,13 @@ Rectangle {
name: "EXPANDED"
PropertyChanges {target: expandImage; icon: "chevron-up"}
PropertyChanges {target: statusExpandableItem; height: 82 + expandableRegion.height + 22}
PropertyChanges {target: expandableRegion; visible: true}
PropertyChanges {target: expandableRegion; active: true}
},
State {
name: "COLLAPSED"
PropertyChanges {target: expandImage; icon: "chevron-down"}
PropertyChanges {target: statusExpandableItem; height: 82}
PropertyChanges {target: expandableRegion; visible: false}
PropertyChanges {target: expandableRegion; active: false}
}
]
@@ -1,281 +0,0 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
/*!
\qmltype StatusImageCropPanel
\inherits Item
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Draw a crop-window onto an image and allows manipulating the position of the crop-window. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-item.html}{Item}.
Adds mouse pan and zoom functionality on top of StatusImageCrop functionality. Also adds small
practical features and optimizes drawing by minimizing StatusImageCrop area
\sa StatusImageCrop for more details
Example of how to use it:
\qml
StatusImageCropPanel {
width: 400
height: 200
source: "qrc:/demoapp/data/logo-test-image.png"
windowStyle: StatusImageCrop.WindowStyle.Rectangular
Component.onCompleted: setCropRect(Qt.rect(10, 0, sourceSize.width - 20, sourceSize.height))
}
\endqml
For a list of components available see StatusQ.
*/
Item {
id: root
implicitWidth: mainLayout.implicitWidth
implicitHeight: mainLayout.implicitHeight
/*!
\qmlproperty real StatusImageCrop::aspectRatio
Initial aspect-ratio of the crop-window
*/
property real aspectRatio: 1
/*!
\qmlproperty bool StatusImageCrop::interactive
If true allows user to interact with the image. Set to false for previewing existing crop-data
*/
property bool interactive: true
/*!
\qmlproperty bool StatusImageCrop::margins
Space to keep around the control borders and crop area
*/
property int margins: 10
/*!
\qmlproperty url StatusImageCropPanel::source
\sa StatusImageCrop::source
*/
/*required*/ property alias source: cropEditor.source
/*!
\qmlproperty WindowStyle StatusImageCropPanel::windowStyle
\sa StatusImageCrop::windowStyle
*/
property alias windowStyle: cropEditor.windowStyle
/*!
\qmlproperty int StatusImageCropPanel::radius
\sa StatusImageCrop::radius
*/
property alias radius: cropEditor.radius
/*!
\qmlproperty color StatusImageCropPanel::wallColor
\sa StatusImageCrop::wallColor
*/
property alias wallColor: cropEditor.wallColor
/*!
\qmlproperty real StatusImageCropPanel::wallTransparency
\sa StatusImageCrop::wallTransparency
*/
property alias wallTransparency: cropEditor.wallTransparency
/*!
\qmlproperty rect StatusImageCropPanel::cropRect
\sa StatusImageCrop::cropRect
*/
property alias cropRect: cropEditor.cropRect
/*!
\qmlproperty rect StatusImageCropPanel::cropRect
\sa StatusImageCrop::cropRect
*/
readonly property alias cropWindow: cropEditor.cropWindow
/*!
\qmlproperty real StatusImageCrop::scrollZoomFactor
How fast is image scaled (zoomed) when using mouse scroll
*/
property real scrollZoomFactor: 0.5
/*!
\qmlproperty bool StatusImageCropPanel::enableCheckers
Shows helper guiding checkers pattern where image is not covering
*/
property bool enableCheckers: root.interactive
/*!
\qmlproperty size StatusImageCropPanel::sourceSize
\sa StatusImageCrop::sourceSize
*/
property alias sourceSize: cropEditor.sourceSize
/*
\qmlmethod StatusImageCropPanel::setCropRect(rect)
\sa StatusImageCrop::cropRect
*/
function setCropRect(newRect) {
cropEditor.setCropRect(newRect)
aspectRatio = cropEditor.aspectRatio
}
QtObject {
id: d
function updateAspectRatio(newAR) {
// Keep width and adjust height
const eW = cropEditor.cropRect.width
const w = (eW <= 0) ? cropEditor.sourceSize.width : eW
const h = w/newAR
const c = (eW <= 0)
? Qt.point(cropEditor.sourceSize.width/2, cropEditor.sourceSize.height/2)
: Qt.point(cropEditor.cropRect.x + w/2, cropEditor.cropRect.y + cropEditor.cropRect.height/2)
const nR = Qt.rect(c.x - w/2, c.y-h/2, w, h)
cropEditor.setCropRect(nR)
}
}
Component.onCompleted: d.updateAspectRatio(root.aspectRatio)
onAspectRatioChanged: d.updateAspectRatio(root.aspectRatio)
onSourceSizeChanged: d.updateAspectRatio(root.aspectRatio)
ColumnLayout {
id: mainLayout
anchors.fill: parent
Item {
id: cropSpaceItem
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
Rectangle {
id: leftOverlay
anchors.left: parent.left
anchors.top: parent.top
anchors.right: cropEditor.left
anchors.bottom: parent.bottom
color: wallColor
opacity: wallTransparency
z: cropEditor.z + 1
}
Rectangle {
id: topOverlay
anchors.left: leftOverlay.right
anchors.top: parent.top
anchors.right: rightOverlay.left
anchors.bottom: cropEditor.top
color: wallColor
opacity: wallTransparency
z: cropEditor.z + 1
}
StatusImageCrop {
id: cropEditor
anchors.centerIn: parent
width: aspectRatio < cropSpaceItem.width/cropSpaceItem.height ? cropSpaceItem.height * aspectRatio : cropSpaceItem.width - root.margins * 2
height: aspectRatio < cropSpaceItem.width/cropSpaceItem.height ? cropSpaceItem.height - root.margins * 2 : cropSpaceItem.width / aspectRatio
}
Rectangle {
id: rightOverlay
anchors.left: cropEditor.right
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
color: wallColor
opacity: wallTransparency
z: cropEditor.z + 1
}
Rectangle {
id: bottomOverlay
anchors.left: leftOverlay.right
anchors.top: cropEditor.bottom
anchors.right: rightOverlay.left
anchors.bottom: parent.bottom
color: wallColor
opacity: wallTransparency
z: cropEditor.z + 1
}
// Checkers
Canvas {
visible: root.enableCheckers
anchors.fill: parent
onPaint: {
var ctx = getContext("2d")
for(let xI = 0; xI < Math.ceil(width/10); xI++) {
for(let yI = 0; yI < Math.ceil(height/10); yI++) {
ctx.fillStyle = (xI % 2) === (yI % 2) ? "#FFFFFE" : "#DBDBDB"
ctx.fillRect(xI * 10, yI * 10, 10, 10)
}
}
}
z: cropEditor.z - 1
}
// Drag and zoom
MouseArea {
anchors.fill: parent
enabled: root.interactive
property var lastDragPoint: null
onReleased: lastDragPoint = null
onMouseXChanged: updateDrag(Qt.point(mouse.x, mouse.y))
onMouseYChanged: updateDrag(Qt.point(mouse.x, mouse.y))
onWheel: {
const delta = wheel.angleDelta.y / 120
cropEditor.setCropRect(cropEditor.getZoomRect(cropEditor.zoomScale + delta * root.scrollZoomFactor))
}
function moveRect(r /*rect*/, delta /*real*/) {
return Qt.rect(r.x + delta.x, r.y + delta.y, r.width, r.height)
}
function scaleSize(sz /*size*/, s /*size*/) {
return Qt.point(sz.width * s, sz.height * s)
}
function updateDrag(p) {
let delta = (lastDragPoint ? Qt.size(lastDragPoint.x - p.x, lastDragPoint.y - p.y) : Qt.size(0, 0))
delta = scaleSize(delta, 1/cropEditor.scrToImgScale)
cropEditor.setCropRect(moveRect(cropEditor.cropRect, delta))
lastDragPoint = p
}
}
}
RowLayout {
visible: root.interactive
StatusIcon {
icon: "remove-circle"
Layout.preferredWidth: 20
Layout.preferredHeight: 20
}
StatusSlider {
Layout.fillWidth: true
Layout.topMargin: 20
Layout.bottomMargin: 25
Layout.alignment: Qt.AlignVCenter
enabled: root.interactive
from: cropEditor.minZoomScale
to: cropEditor.maxZoomScale
value: cropEditor.zoomScale
live: false
onMoved: cropEditor.setCropRect(cropEditor.getZoomRect(valueAt(visualPosition)))
}
StatusIcon {
icon: "add-circle"
Layout.preferredWidth: 20
Layout.preferredHeight: 20
}
}
}
}
@@ -1,60 +1,30 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
Rectangle {
id: root
id: statusLetterIdenticon
property alias identiconText: identiconText
property string name
property string emoji
property string emojiSize: Emoji.size.small
property int letterSize: 21
property int charactersLen: 1
color: Theme.palette.miscColor5
width: 40
height: 40
radius: width / 2
StatusEmoji {
visible: root.emoji
anchors.centerIn: parent
width: Math.round(parent.width / 2)
height: Math.round(parent.height / 2)
emojiId: Emoji.iconId(root.emoji, root.emojiSize) || ""
}
StatusBaseText {
id: identiconText
visible: !root.emoji
anchors.alignWhenCentered: false
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: parent.width
height: parent.height
text: ((statusLetterIdenticon.name.charAt(0) === "#")
|| (statusLetterIdenticon.name.charAt(0) === "@") ?
statusLetterIdenticon.name.charAt(1) : statusLetterIdenticon.name.charAt(0)).toUpperCase()
font.weight: Font.Bold
font.pixelSize: root.letterSize
color: d.luminance(root.color) > 0.5 ? Qt.rgba(0, 0, 0, 0.5) : Qt.rgba(255, 255, 255, 0.7)
text: {
const shift = (root.name.charAt(0) === "#") ||
(root.name.charAt(0) === "@")
return root.name.substring(shift, shift + charactersLen).toUpperCase()
}
}
QtObject {
id: d
function luminance(color) {
let r = Math.pow(color.r, 2.2) * 0.2126
let g = Math.pow(color.g, 2.2) * 0.7151
let b = Math.pow(color.b, 2.2) * 0.0721
return r + g + b
}
font.pixelSize: statusLetterIdenticon.letterSize
color: Qt.rgba(255, 255, 255, 0.7)
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
}
+81 -102
View File
@@ -13,20 +13,17 @@ Rectangle {
property string titleId: ""
property string title: ""
property string titleAsideText: ""
property bool titleIcon1Visible
property bool titleIcon2Visible
property string subTitle: ""
property string tertiaryTitle: ""
property string tertiaryTitle: ""
property string label: ""
property real leftPadding: 16
property real rightPadding: 16
property bool enabled: true
property bool highlighted: false
property bool propagateTitleClicks: true
property int type: StatusListItem.Type.Primary
property list<Item> components
property var bottomModel: []
property Component bottomDelegate
property var tagsModel: []
property Component tagsDelegate
property StatusIconSettings icon: StatusIconSettings {
height: isLetterIdenticon ? 40 : 20
@@ -34,7 +31,6 @@ Rectangle {
rotation: 0
isLetterIdenticon: false
letterSize: 21
charactersLen: 1
color: isLetterIdenticon ? background.color : type === StatusListItem.Type.Danger ?
Theme.palette.dangerColor1 : Theme.palette.primaryColor1
background: StatusIconBackgroundSettings {
@@ -56,10 +52,31 @@ Rectangle {
height: 40
isIdenticon: false
}
property StatusIdenticonRingSettings ringSettings: StatusIdenticonRingSettings {
initalAngleRad: 0
ringPxSize: 1.5
distinctiveColors: Theme.palette.identiconRingColors
property StatusIconSettings titleIcon1: StatusIconSettings {
width: dummyImage.width
height: dummyImage.height
background: StatusIconBackgroundSettings {
width: 10
height: 10
}
// Only used to get implicit width and height from the actual image
property Image dummyImage: Image {
source: titleIcon1.name ? "../../assets/img/icons/" + titleIcon1.name + ".svg": ""
visible: false
}
}
property StatusIconSettings titleIcon2: StatusIconSettings {
width: dummyImage.width
height: dummyImage.height
background: StatusIconBackgroundSettings {
width: 10
height: 10
}
// Only used to get implicit width and height from the actual image
property Image dummyImage: Image {
source: titleIcon2.name ? "../../assets/img/icons/" + titleIcon2.name + ".svg": ""
visible: false
}
}
property alias sensor: sensor
@@ -67,13 +84,10 @@ Rectangle {
property alias statusListItemIcon: iconOrImage
property alias statusListItemTitle: statusListItemTitle
property alias statusListItemTitleAside: statusListItemTitleAsideText
property alias statusListItemTitleIcons: titleIconsRow
property alias statusListItemTitleArea: statusListItemTitleArea
property alias statusListItemSubTitle: statusListItemSubTitle
property alias statusListItemTertiaryTitle: statusListItemTertiaryTitle
property alias statusListItemComponentsSlot: statusListItemComponentsSlot
property alias statusListItemTagsSlot: statusListItemTagsSlot
property alias statusListItemInlineTagsSlot: statusListItemTagsSlotInline
signal clicked(string itemId)
signal titleClicked(string titleId)
@@ -85,12 +99,7 @@ Rectangle {
}
implicitWidth: 448
implicitHeight: {
if (bottomModel.length === 0) {
return Math.max(64, statusListItemTitleArea.height + 16)
}
return Math.max(64, statusListItemTitleArea.height + 90)
}
implicitHeight: Math.max(64, statusListItemTitleArea.height + 16)
color: {
if (sensor.containsMouse || statusListItem.highlighted) {
switch(type) {
@@ -139,29 +148,18 @@ Rectangle {
name: statusListItem.title
active: statusListItem.icon.isLetterIdenticon ||
!!statusListItem.icon.name ||
!!statusListItem.image.source.toString() ||
!!statusListItem.icon.emoji
!!statusListItem.image.source.toString()
badge.border.color: statusListItem.color
ringSettings: statusListItem.ringSettings
}
Item {
id: statusListItemTitleArea
function getStatusListItemTitleAnchorsRight() {
let isIconsRowVisible = false
if(titleIconsRow.item) {
isIconsRowVisible = true//titleIconsRow.item.visible
}
return !statusListItem.titleAsideText && !isIconsRowVisible ? statusListItemTitleArea.right : undefined
}
anchors.left: iconOrImage.active ? iconOrImage.right : parent.left
anchors.right: statusListItemLabel.visible ? statusListItemLabel.left : statusListItemComponentsSlot.left
anchors.leftMargin: iconOrImage.active ? 16 : statusListItem.leftPadding
anchors.right: statusListItemLabel.visible ?
statusListItemLabel.left : statusListItemComponentsSlot.left
anchors.leftMargin: statusListItem.leftPadding
anchors.rightMargin: statusListItem.rightPadding
anchors.verticalCenter: bottomModel.length === 0 ? parent.verticalCenter : undefined
anchors.verticalCenter: parent.verticalCenter
height: childrenRect.height
StatusBaseText {
@@ -169,39 +167,29 @@ Rectangle {
text: statusListItem.title
font.pixelSize: 15
height: visible ? contentHeight : 0
elide: Text.ElideRight
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
anchors.left: parent.left
anchors.top: bottomModel.length === 0 ? undefined: parent.top
anchors.topMargin: bottomModel.length === 0 ? undefined : 20
width: Math.min(implicitWidth, parent.width)
anchors.right: !statusListItem.titleAsideText && !statusListItem.titleIcon1Visible && !statusListItem.titleIcon2Visible
? parent.right : undefined
color: {
if (!statusListItem.enabled) {
return Theme.palette.baseColor1
}
switch (statusListItem.type) {
case StatusListItem.Type.Primary:
return Theme.palette.directColor1
case StatusListItem.Type.Secondary:
return Theme.palette.primaryColor1
case StatusListItem.Type.Danger:
return Theme.palette.dangerColor1
}
}
StatusToolTip {
id: statusListItemTitleTooltip
text: statusListItemTitle.text
delay: 0
visible: statusListItemTitle.truncated && statusListItemTitleMouseArea.containsMouse
if (!statusListItem.enabled) {
return Theme.palette.baseColor1
}
switch (statusListItem.type) {
case StatusListItem.Type.Primary:
return Theme.palette.directColor1
case StatusListItem.Type.Secondary:
return Theme.palette.primaryColor1
case StatusListItem.Type.Danger:
return Theme.palette.dangerColor1
}
}
MouseArea {
id: statusListItemTitleMouseArea
anchors.fill: parent
enabled: statusListItem.enabled
cursorShape: sensor.enabled && containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
cursorShape: containsMouse? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true
propagateComposedEvents: statusListItem.propagateTitleClicks
propagateComposedEvents: true
onClicked: {
statusListItem.titleClicked(statusListItem.titleId)
mouse.accepted = false
@@ -214,20 +202,43 @@ Rectangle {
anchors.left: statusListItemTitle.right
anchors.leftMargin: 4
anchors.verticalCenter: statusListItemTitle.verticalCenter
anchors.top: bottomModel.length === 0 ? undefined: parent.top
anchors.topMargin: bottomModel.length === 0 ? undefined : 20
text: statusListItem.titleAsideText
font.pixelSize: 10
color: Theme.palette.baseColor1
visible: !!statusListItem.titleAsideText
}
Loader {
Row {
id: titleIconsRow
spacing: 4
anchors.left: !statusListItem.titleAsideText ? statusListItemTitle.right : statusListItemTitleAsideText.right
anchors.verticalCenter: statusListItemTitle.verticalCenter
anchors.leftMargin: 4
}
anchors.leftMargin: titleIconsRow.spacing
StatusRoundIcon {
visible: statusListItem.titleIcon1Visible
icon.name: statusListItem.titleIcon1.name
icon.width: statusListItem.titleIcon1.width
icon.height: statusListItem.titleIcon1.height
icon.rotation: statusListItem.titleIcon1.rotation
icon.color: statusListItem.titleIcon1.color
icon.background.color: statusListItem.titleIcon1.background.color
icon.background.width: statusListItem.titleIcon1.background.width
icon.background.height: statusListItem.titleIcon1.background.height
}
StatusRoundIcon {
visible: statusListItem.titleIcon2Visible
icon.name: statusListItem.titleIcon2.name
icon.width: statusListItem.titleIcon2.width
icon.height: statusListItem.titleIcon2.height
icon.rotation: statusListItem.titleIcon2.rotation
icon.color: statusListItem.titleIcon2.color
icon.background.color: statusListItem.titleIcon2.background.color
icon.background.width: statusListItem.titleIcon2.background.width
icon.background.height: statusListItem.titleIcon2.background.height
}
}
StatusBaseText {
id: statusListItemSubTitle
@@ -259,42 +270,11 @@ Rectangle {
width: contentItem.width
implicitHeight: visible ? 22 : 0
}
Row {
id: statusListItemTagsSlotInline
anchors.top: statusListItemTertiaryTitle.bottom
width: parent.width
spacing: 10
clip: true
Repeater {
model: tagsModel
delegate: tagsDelegate
}
}
}
Row {
id: statusListItemTagsSlot
anchors.topMargin: 16
anchors.top: iconOrImage.bottom
anchors.left: parent.left
anchors.leftMargin: 16
width: statusListItemBadge.width
spacing: 10
anchors.verticalCenter: parent.verticalCenter
Repeater {
model: bottomModel
delegate: bottomDelegate
}
}
StatusBaseText {
id: statusListItemLabel
anchors.verticalCenter: bottomModel.length === 0 ? parent.verticalCenter : undefined
anchors.top: bottomModel.length === 0 ? undefined: parent.top
anchors.topMargin: bottomModel.length === 0 ? undefined : 16
anchors.verticalCenter: parent.verticalCenter
anchors.right: statusListItemComponentsSlot.left
anchors.rightMargin: statusListItemComponentsSlot.width > 0 ? 10 : 0
@@ -304,13 +284,12 @@ Rectangle {
visible: !!statusListItem.label
}
Row {
id: statusListItemComponentsSlot
anchors.right: parent.right
anchors.rightMargin: statusListItem.rightPadding
anchors.verticalCenter: bottomModel.length === 0 ? parent.verticalCenter : undefined
anchors.top: bottomModel.length === 0 ? undefined: parent.top
anchors.topMargin: bottomModel.length === 0 ? undefined : 12
anchors.verticalCenter: parent.verticalCenter
spacing: 10
}
}
@@ -57,8 +57,6 @@ Control {
visible: root.icon.isLetterIdenticon
color: root.icon.color
name: root.primaryText
emoji: root.icon.emoji
emojiSize: root.icon.emojiSize
}
StatusBaseText {
font.weight: Font.Medium
@@ -1,81 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
Rectangle {
id: root
width: layout.width
height: 30
color: Theme.palette.primaryColor3
radius: 15
property alias titleText: titleText
property string title: ""
property bool closeButtonVisible: true
signal clicked()
property StatusImageSettings image: StatusImageSettings {
width: 20
height: 20
isIdenticon: false
}
property StatusIconSettings icon: StatusIconSettings {
height: 20
width: 20
rotation: 0
isLetterIdenticon: false
letterSize: 10
color: Theme.palette.primaryColor1
background: StatusIconBackgroundSettings {
width: 15
height: 15
color: Theme.palette.primaryColor3
}
}
RowLayout {
id: layout
height: parent.height
spacing: 5
StatusSmartIdenticon {
id: iconOrImage
Layout.leftMargin: 5
image: root.image
icon: root.icon
name: root.title
active: root.icon.isLetterIdenticon ||
!!root.icon.name ||
!!root.image.source.toString()
}
StatusBaseText {
id: titleText
color: Theme.palette.primaryColor1
text: root.title
Layout.rightMargin: closeButtonVisible ? 0 : 5
}
StatusIcon {
id: closeIcon
color: Theme.palette.primaryColor1
icon: "close-circle"
visible: closeButtonVisible
Layout.rightMargin: 5
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.clicked()
}
}
}
}
}
-392
View File
@@ -1,392 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
/*!
\qmltype StatusListPicker
\inherits Item
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief It is a combination of StatusPickerButton and a drop-down list component that provides a way of presenting a list of selectable options to the user. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-item.html}{Item}.
The \c StatusListPicker is populated with a data model. The data model is commonly a JavaScript array or a ListModel object.
StatusListPicker can be made as a single or multiple options picker by setting its StatusListPicker::multiSelection property properly or will be auto-set if the model contains / provides more than one selected items.
The StatusPickerButton text holds the text of the current item selected in the list picker. If there is more than one item selected, a string composed will be displayed.
The drop-down list incorporates a searcher by the following model roles: `name` or / and `shortName`.
NOTE: Make sure to set the appropriate z-index when instantiating the component in order to place it on top of your view in order to display the drop-down data properly.
Make sure to position the component inside its parent also when it is spanned in order to achieve a better user experience.
Example of how the component looks like:
\image status_list_picker.png
Example of how to use it:
\qml
StatusListPicker {
id: currencyPicker
inputList: Models.currencyPickerModel
searchText: qsTr("Search Language")
multiSelection: true
printSymbol: true
}
\endqml
For a list of components available see StatusQ.
*/
Item {
id: root
/*!
\qmlproperty string StatusListPicker::inputList
This property holds the data that will be populated in the list picker.
NOTE: This model property should not change so it is an in / out property where the selected role will be dynamically changed according to user actions.
Here an example of the model roles expected:
\qml
inputList: ListModel {
ListElement {
key: 0
name: "United States Dollar"
shortName: "USD"
symbol: "$"
imageSource: "../../assets/twemoji/26x26/1f4b4.png"
category: "fiat"
selected: true
}
ListElement {
key: 1
name: "British Pound"
shortName: "GBP"
symbol: "£"
imageSource: "../../assets/twemoji/26x26/1f4b5.png"
category: "fiat"
selected: false
}
}
\endqml
*/
property ListModel inputList: ListModel { }
/*!
\qmlproperty string StatusListPicker::searchText
This property holds the search text the searcher input displays by default.
*/
property string searchText: ""
/*!
\qmlproperty string StatusListPicker::placeholderSearchText
This property holds the placeholder text the searcher input displays by default.
*/
property string placeholderSearchText: qsTr("Search")
/*!
\qmlproperty string StatusListPicker::multiSelection
This property holds whether the list picker is a single or a multiple picker option by displaying a StatusRadioButton or StatusCheckBox instead.
*/
property bool multiSelection: false
/*!
\qmlproperty string StatusListPicker::printSymbol
This property holds whether the selected items will display a composition of the role `symbol` in the StatusPickerButton text or just only the role `shortName`.
*/
property bool printSymbol: false
/*!
\qmlproperty string StatusListPicker::maxPickerHeight
This property holds the maximum drop-down list height allowed. The drop-down list height will be set as the minimum value between the list content height and the maxPickerHeight property.
*/
property int maxPickerHeight: 718
/*!
\qmlproperty string StatusListPicker::enableSelectableItem
This property holds if the item in the list will be highlighted when hovering and clickable in its complete area or just not highlighted and able to be selected by clicking only the checbox or radiobutton area.
*/
property bool enableSelectableItem: true
/*!
\qmlproperty string StatusListPicker::menuAlignment
This property holds the allignment of the menu in terms of the button
*/
property int menuAlignment: StatusListPicker.MenuAlignment.Right
/*!
\qmlproperty enum StatusListPicker::MenuAlignment
This property holds the allignment of the menu in terms of the button
values can be Left, Right or Center
*/
enum MenuAlignment {
Left,
Right,
Center
}
/*
\qmlmethod StatusListPicker::close()
It can be used to force to close the drop-down picker list whenever the consumer needs it. For example by adding an outside MouseArea to close the picker when user clicks outsite the component:
\qml
// Outsite area
MouseArea {
height: root.height
width: root.width
onClicked: { currencyPicker.close() }
}
\endqml
*/
function close() {
picker.visible = false
// Reset searcher:
root.searchText = ""
}
/*!
\qmlsignal StatusListPicker::itemPickerChanged(string key, bool selected)
This signal is emitted when an item changes its selected value.
*/
signal itemPickerChanged(string key, bool selected)
QtObject {
id: d
property var filteredModel: ListModel { }
// Used to set up component and the needed private properties
function initialize() {
if(filteredModel.count === 0) {
// It is necessary to load the model, not loaded yet!
var selected = 0
var selectionText = ""
for(var i = 0; i < root.inputList.count; i++) {
var item = root.inputList.get(i)
d.filteredModel.append(item)
if(item.selected) selected++;
}
// If the given model has more than one selected elements, the behaviour of the list picker will be as a multiple selector with checkboxes although it is
// set in the radiobutton mode. Radiobutton mode is only for mutial-exclusion.
if(selected > 1)
multiSelection = true
// Update selected items text:
d.getSelectedItemsText()
}
}
// Used to update model elements given a specific text by filtering them for its `name` and / or `shortName`.
function applyFilter(text) {
const input = text.toLowerCase()
filteredModel.clear()
for(var i = 0; i < root.inputList.count; i++) {
let item = root.inputList.get(i)
if(item.name.toLowerCase().includes(input) || item.shortName.toLowerCase().includes(input))
filteredModel.append(item)
}
}
function formatSymbolShortNameText(symbol, shortName) {
var formattedText = ""
if(root.printSymbol && symbol)
formattedText = symbol + shortName
else
formattedText = shortName
return formattedText
}
function getSelectedItemsText() {
var res = ""
for(var i = 0; i < root.inputList.count; i++) {
var item = root.inputList.get(i)
if(item.selected) {
if(res != "")
res += ", "
res += d.formatSymbolShortNameText(item.symbol, item.shortName)
}
}
return res
}
// Used to update the base input list model with the new selected property
function updateInputListModel(key, selected, isSingleSelection) {
// If it is a single selection we must ensure that when a new key is selected others are cleared.
// It must be done manually because as there is the option of filterning, the model visualized changes
// and it could be possible to have a filtered list without an element selected (but in the base model
// it is) so, the binding to not selected will not be executed, as in the current filtered model
// there are no changes to update, just only the selected one.
for(var i = 0; i < root.inputList.count; i++) {
if(root.inputList.get(i).key === key) {
root.inputList.get(i).selected = selected
}
else if(isSingleSelection && selected) {
// Clear all the list
root.inputList.get(i).selected = false
}
}
}
}
width: 110
height: 38
Component.onCompleted: d.initialize()
StatusPickerButton {
id: btn
anchors.fill: parent
bgColor: Theme.palette.primaryColor3
contentColor: Theme.palette.primaryColor1
text: picker.selectedItemsText
textPixelSize: 13
type: StatusPickerButton.Type.Down
onClicked: {
picker.visible = !picker.visible
// Restart list position:
content.positionViewAtBeginning()
content.forceActiveFocus()
}
}
Rectangle {
id: picker
property string selectedItemsText: ""
width: content.itemWidth
height: Math.min(content.contentHeight + content.anchors.topMargin + content.anchors.bottomMargin, root.maxPickerHeight)
anchors.left: root.menuAlignment === StatusListPicker.MenuAlignment.Left ? btn.left : undefined
anchors.right: root.menuAlignment === StatusListPicker.MenuAlignment.Right ? btn.right : undefined
anchors.horizontalCenter: root.menuAlignment === StatusListPicker.MenuAlignment.Center ? btn.horizontalCenter : undefined
anchors.top: btn.bottom
anchors.topMargin: 4
visible: false
color: Theme.palette.statusPopupMenu.backgroundColor
radius: 8
layer.enabled: true
layer.effect: DropShadow {
source: picker
horizontalOffset: 0
verticalOffset: 4
radius: 12
samples: 25
spread: 0.2
color: Theme.palette.dropShadow
}
ListView {
id: content
property int itemHeight: 40
property int itemWidth: 360
model: d.filteredModel
width: itemWidth
anchors.top: parent.top
anchors.topMargin: 8
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
currentIndex: -1
clip: true
headerPositioning: ListView.OverlayHeader
header: Rectangle {
id: header
width: content.itemWidth
height: searchInput.height + 24
color: Theme.palette.statusPopupMenu.backgroundColor
z: 3 // Above delegate (z=1) and above section.delegate (z = 2)
StatusBaseInput {
id: searchInput
implicitHeight: 36
width: content.itemWidth - 2 * 18
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
topPadding: 0
bottomPadding: 0
placeholderText: root.placeholderSearchText
text: root.searchText
icon.name: "search"
onTextChanged: {
d.applyFilter(text)
root.searchText = text
}
}
}// End of search input item
delegate: StatusItemPicker {
width: content.itemWidth
height: content.itemHeight
color: mouseArea.containsMouse ? Theme.palette.baseColor4 : "transparent"
image: StatusImageSettings {
source: model.imageSource ? model.imageSource : ""
width: 15
height: 15
isIdenticon: false
}
name: model.name
shortName: model.shortName
selectorType: root.multiSelection ? StatusItemPicker.SelectorType.CheckBox : StatusItemPicker.SelectorType.RadioButton
selected: model.selected
radioGroup: radioBtnGroup
onCheckedChanged: {
d.updateInputListModel(model.key, checked, selectorType === StatusItemPicker.SelectorType.RadioButton)
if(selectorType === StatusItemPicker.SelectorType.RadioButton && checked) {
// Update selected item text
picker.selectedItemsText = d.formatSymbolShortNameText(model.symbol, model.shortName)
}
else {
// Update selected items text (multiple selection, text chain).
picker.selectedItemsText = d.getSelectedItemsText()
}
// Used to notify selected property changes in the specific item picker.
itemPickerChanged(model.key, checked)
}
MouseArea {
id: mouseArea
enabled: root.enableSelectableItem
anchors.fill: parent
cursorShape: root.enableSelectableItem ? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true
onClicked: {
if(selectorType === StatusItemPicker.SelectorType.RadioButton) {
// Just only allow to select, not unselect in case of single selection (radiobutton)
if(!selected) selected = !selected
}
else
// In case of multiple selections you can both select and unselect.
selected = !selected
}
}
}
section.property: "category"
section.criteria: ViewSection.FullString
section.delegate: Item {
width: content.itemWidth
height: content.itemHeight
StatusBaseText {
anchors.leftMargin: 18
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: section
color: Theme.palette.baseColor1
font.pixelSize: 15
elide: Text.ElideRight
}
}// End of Category item
onActiveFocusChanged: { if(!activeFocus) root.close() }
// Not visual element to control mutual-exclusion of radiobuttons that are not sharing the same parent (inside list view)
ButtonGroup {
id: radioBtnGroup
}
}// End of Content
}// End of Rectangle picker
}
+35 -86
View File
@@ -2,116 +2,56 @@ import QtQuick 2.0
import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1
/*!
\qmltype StatusMemberListItem
\inherits StatusListItem
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief It is a list item with a specific format to display members of a community or chat. Inherits from \c StatusListItem.
The \c StatusMemberListItem is a clickable / hovering list item with a specific format to display members of a community or chat.
It displays the member avatar, trust and mutual contact indicators, nick name, user name, chat key and information about the member connectivity.
Example of how the control looks like:
\image status_member_list_item.png
Example of how to use it:
\qml
StatusMemberListItem {
nickName: "carmen.eth"
isOnline: false
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
}
StatusMemberListItem {
nickName: "This girl I know from work"
userName: "annabelle"
isOnline: true
}
\endqml
For a list of components available see StatusQ.
*/
StatusListItem {
id: root
/*!
\qmlproperty string StatusMemberListItem::nickName
This property holds the nick name of the member represented.
*/
property string nickName: ""
/*!
\qmlproperty string StatusMemberListItem::userName
This property holds the user name of the member represented.
*/
property string userName: ""
/*!
\qmlproperty string StatusMemberListItem::chatKey
This property holds the chat key of the member represented.
*/
property string chatKey: ""
/*!
\qmlproperty string StatusMemberListItem::isMutualContact
This property holds if the member represented is a mutual contact.
*/
property bool isMutualContact: false
/*!
\qmlproperty string StatusMemberListItem::trustIndicator
This property holds the trust indicator of the member represented. Possible values are:
\list
\li StatusContactVerificationIcons.TrustedType.None
\li StatusContactVerificationIcons.TrustedType.Verified
\li StatusContactVerificationIcons.TrustedType.Untrustworthy
\endlist
*/
property var trustIndicator: StatusContactVerificationIcons.TrustedType.None
/*!
\qmlproperty string StatusMemberListItem::isOnline
This property holds the connectivity status of the member represented.
*/
property var trustIndicator: StatusMemberListItem.TrustedType.None
property bool isOnline: false
QtObject {
id: d
enum TrustedType {
None, //0
Verified, //1
Untrustworthy //2
}
// Subtitle composition:
function composeSubtitile() {
var compose = ""
if(root.userName !== "" && root.nickName !== "")
compose = "(" + root.userName + ")"
// Subtitle composition:
function composeSubtitile() {
var compose = ""
if(root.userName !== "")
compose = "(" + root.userName + ")"
if(compose !== "" && root.chatKey !== "")
// Composition
compose += " • " + composeShortKeyChat(root.chatKey)
if(compose !== "" && root.chatKey !== "")
// Composition
compose += " • " + composeShortKeyChat(root.chatKey)
else if(root.chatKey !== "")
compose = composeShortKeyChat(root.chatKey)
else if(root.chatKey !== "")
compose = composeShortKeyChat(root.chatKey)
return compose
}
return compose
}
// Short keychat composition:
function composeShortKeyChat(chatKey) {
return chatKey.substring(0, 5) + "..." + chatKey.substring(chatKey.length - 3)
}
// Short keychat composition:
function composeShortKeyChat(chatKey) {
return chatKey.substring(0, 5) + "..." + chatKey.substring(chatKey.length - 3)
}
// root object settings:
title: (root.nickName === "") ? root.userName : root.nickName
statusListItemTitleIcons.sourceComponent: StatusContactVerificationIcons {
isMutualContact: root.isMutualContact
trustIndicator: root.trustIndicator
}
subTitle: d.composeSubtitile()
title: root.nickName
titleIcon1Visible: root.isMutualContact
titleIcon2Visible: root.trustIndicator !== StatusMemberListItem.TrustedType.None
subTitle: composeSubtitile()
statusListItemSubTitle.font.pixelSize: 10
icon.isLetterIdenticon: !root.image.source.toString()
statusListItemIcon.badge.visible: true
statusListItemIcon.badge.color: root.isOnline ? Theme.palette.successColor1 : Theme.palette.baseColor1
color: sensor.containsMouse ? Theme.palette.baseColor2 : Theme.palette.baseColor4
// Default sizes / positions by design
// Default sizes/positions by design
implicitWidth: 256
implicitHeight: Math.max(56, statusListItemTitleArea.height + leftPadding)
leftPadding: 8
@@ -124,4 +64,13 @@ StatusListItem {
statusListItemIcon.badge.border.width: 2
statusListItemIcon.badge.implicitHeight: 12 // 8 px + 2 px * 2 borders
statusListItemIcon.badge.implicitWidth: 12 // 8 px + 2 px * 2 borders
// Trusted type icons definition:
titleIcon1.name: "tiny/tiny-contact"
titleIcon1.color: Theme.palette.indirectColor1
titleIcon1.background.color: Theme.palette.primaryColor1
// None and Untrustworthy types, same aspect (Icon will not be visible in case of None type):
titleIcon2.name: trustIndicator === StatusMemberListItem.TrustedType.Verified ? "tiny/tiny-checkmark" : "tiny/subtract"
titleIcon2.color: Theme.palette.indirectColor1
titleIcon2.background.color: trustIndicator === StatusMemberListItem.TrustedType.Verified ? Theme.palette.primaryColor1 : Theme.palette.dangerColor1
}
+13 -2
View File
@@ -21,6 +21,14 @@ Rectangle {
Invitation = 7
}
enum ContactType {
STANDARD = 0,
RENAME = 1,
CONTACT = 2,
VERIFIED = 3,
UNTRUSTWORTHY = 4
}
property alias messageHeader: messageHeader
property alias quickActions:quickActionsPanel.quickActions
property alias statusChatInput: editComponent.inputComponent
@@ -100,8 +108,11 @@ Rectangle {
displayName: messageDetails.displayName
secondaryName: messageDetails.secondaryName
tertiaryDetail: messageDetails.chatID
isMutualContact: messageDetails.isMutualContact
trustIndicator: messageDetails.trustIndicator
icon1.name: messageDetails.contactType === StatusMessage.ContactType.CONTACT ? "tiny/tiny-contact" : ""
icon2.name: messageDetails.contactType === StatusMessage.ContactType.VERIFIED ? "tiny/tiny-checkmark" :
messageDetails.contactType === StatusMessage.ContactType.UNTRUSTWORTHY ? "tiny/subtract": ""
icon2.background.color: messageDetails.contactType === StatusMessage.ContactType.UNTRUSTWORTHY ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
icon2.color: Theme.palette.indirectColor1
resendText: statusMessage.resendText
showResendButton: messageDetails.hasExpired && messageDetails.amISender
onClicked: statusMessage.senderNameClicked()
@@ -17,13 +17,12 @@ QtObject {
property string messageText: ""
property int contentType: 0
property string messageContent: ""
property bool isMutualContact: false
property var trustIndicator: StatusContactVerificationIcons.TrustedType.None
property int contactType: 0
property bool hasMention: false
property bool isPinned: false
property string pinnedBy: ""
property bool hasExpired: false
property string timestamp: ""
property string timestamp: ""
}
@@ -1,13 +1,11 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
Loader {
id: statusSmartIdenticon
property string name: ""
property int dZ: 100
// Badge color properties must be set if badgeItem.visible = true
property alias badge: statusBadge
@@ -22,12 +20,6 @@ Loader {
height: 40
}
property StatusIdenticonRingSettings ringSettings: StatusIdenticonRingSettings {
initalAngleRad: 0
ringPxSize: Math.max(1.5, statusSmartIdenticon.image.width / 24.0)
distinctiveColors: Theme.palette.identiconRingColors
}
sourceComponent: statusSmartIdenticon.icon.isLetterIdenticon ? letterIdenticon :
!!statusSmartIdenticon.image.source.toString() ? roundedImage :
!!statusSmartIdenticon.icon.name.toString() ? roundedIcon : letterIdenticon
@@ -83,20 +75,10 @@ Loader {
height: statusSmartIdenticon.icon.height
color: statusSmartIdenticon.icon.color
name: statusSmartIdenticon.name
emoji: statusSmartIdenticon.icon.emoji
emojiSize: statusSmartIdenticon.icon.emojiSize
letterSize: statusSmartIdenticon.icon.letterSize
charactersLen: statusSmartIdenticon.icon.charactersLen
}
}
// Next components are painted above main sourceComponent
StatusIdenticonRing {
settings: statusSmartIdenticon.ringSettings
anchors.fill: parent
z: statusSmartIdenticon.dZ/2
}
// State component
StatusBadge {
id: statusBadge
@@ -106,6 +88,6 @@ Loader {
border.width: 3
implicitHeight: 15
implicitWidth: 15
z: statusSmartIdenticon.dZ
z: 100
}
}
+54 -315
View File
@@ -1,192 +1,40 @@
import QtQuick 2.14
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.14
import QtGraphicalEffects 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusTagSelector
\inherits Item
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Displays a tag selector component together with a list of where to select and add the tags from.
Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-item.html}{Item}.
The \c StatusTagSelector displays a list of asorted elements together with a text input where tags are added. As the user
types some text, the list elements are filtered and if the user selects any of those a new tag is created.
For example:
\qml
StatusTagSelector {
width: 650
height: 44
anchors.centerIn: parent
namesModel: 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
}
}
toLabelText: qsTr("To: ")
warningText: qsTr("USER LIMIT REACHED")
}
\endqml
\image status_tag_selector.png
For a list of components available see StatusQ.
*/
Item {
id: root
implicitWidth: 448
implicitHeight: (104 + contactsLabel.height + contactsLabel.anchors.topMargin + (userListView.model.count * 64)) > root.maxHeight ? root.maxHeight :
(104 + contactsLabel.height + contactsLabel.anchors.topMargin + (userListView.model.count * 64))
implicitHeight: 44
/*!
\qmlproperty real StatusTagSelector::maxHeight
This property holds the maximum height of the component.
*/
property real maxHeight: (488 + contactsLabel.height + contactsLabel.anchors.topMargin) //default min
/*!
\qmlproperty alias StatusTagSelector::textEdit
This property holds a reference to the TextEdit component.
*/
property alias textEdit: edit
/*!
\qmlproperty alias StatusTagSelector::text
This property holds a reference to the TextEdit's text property.
*/
property alias text: edit.text
/*!
\qmlproperty string StatusTagSelector::warningText
This property sets the warning text.
*/
property string warningText: ""
/*!
\qmlproperty string StatusTagSelector::toLabelText
This property sets the 'to' label text.
*/
property string toLabelText: ""
/*!
\qmlproperty string StatusTagSelector::listLabel
This property sets the elements list label text.
*/
property string listLabel: ""
/*!
\qmlproperty int StatusTagSelector::nameCountLimit
This property sets the tags count limit.
*/
property int nameCountLimit: 5
/*!
\qmlproperty var StatusTagSelector::ringSpecModel
This property holds the function to calculate the ring spec model
based on the public key.
*/
property var ringSpecModelGetter: (pubKey) => { /*return ringSpecModel*/ }
/*!
\qmlproperty var StatusTagSelector::compressKey
This property holds the function to calculate the compressed
key based on the public key.
*/
property var compressedKeyGetter: (pubKey) => { /*return compressed key;*/ }
/*!
\qmlproperty ListModel StatusTagSelector::sortedList
This property holds the sorted list model.
*/
property ListModel sortedList: ListModel { }
/*!
\qmlproperty ListModel StatusTagSelector::namesModel
This property holds the asorted names model.
*/
property ListModel namesModel: ListModel { }
/*!
\qmlmethod
This function is used to find an entry in a model.
*/
function find(model, criteria) {
for (var i = 0; i < model.count; ++i) if (criteria(model.get(i))) return model.get(i);
return null;
for (var i = 0; i < model.count; ++i) if (criteria(model.get(i))) return model.get(i);
return null;
}
/*!
\qmlmethod
This function is used to insert a new tag.
*/
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});
addMember(id);
edit.clear();
}
}
/*!
\qmlmethod
This function is used to sort the source model.
*/
function sortModel(inputModel) {
sortedList.clear();
if (text !== "") {
for (var i = 0; i < inputModel.count; i++ ) {
var entry = inputModel.get(i);
if (entry.name.toLowerCase().includes(text.toLowerCase()) &&
!find(namesModel, function(item) { return item.name === entry.name })) {
sortedList.append({"publicId": entry.publicId, "name": entry.name,
"nickName": entry.nickName, "trustIndicator": entry.trustIndicator,
"isMutualContact": entry.isMutualContact, "ringSpecModel": entry.ringSpecModel,
"icon": entry.icon, "isIdenticon": entry.isIdenticon,
"onlineStatus": entry.onlineStatus});
userListView.model = sortedList;
}
}
} else {
userListView.model = inputModel;
}
}
/*!
\qmlsignal
This signal is emitted when a new tag is created.
*/
signal addMember(string memberId)
/*!
\qmlsignal
This signal is emitted when a tag is removed.
*/
signal removeMember(string memberId)
Rectangle {
id: tagSelectorRect
width: parent.width
height: 44
anchors.fill: parent
radius: 8
color: Theme.palette.baseColor2
RowLayout {
anchors.fill: parent
anchors.leftMargin: 16
@@ -199,188 +47,79 @@ Item {
text: root.toLabelText
}
ScrollView {
Layout.preferredWidth: (namesList.contentWidth > (parent.width - 177)) ?
(parent.width - 177) : namesList.contentWidth
ListView {
id: namesList
Layout.preferredWidth: (count >= 5) ? (parent.width - warningTextLabel.width - 30) : childrenRect.width
implicitHeight: 30
visible: (count > 0)
Layout.alignment: Qt.AlignVCenter
visible: (namesList.count > 0)
contentWidth: namesList.contentWidth
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
model: namesModel
orientation: ListView.Horizontal
spacing: 8
clip: true
ListView {
id: namesList
anchors.fill: parent
model: namesModel
orientation: ListView.Horizontal
spacing: 8
function scrollToEnd() {
if (contentWidth > width) {
contentX = contentWidth;
}
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
}
onWidthChanged: { scrollToEnd(); }
onCountChanged: { scrollToEnd(); }
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: {
removeMember(publicId);
namesModel.remove(index, 1);
}
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);
}
}
}
}
TextInput {
TextEdit {
id: edit
verticalAlignment: Text.AlignVCenter
focus: true
color: Theme.palette.directColor1
clip: true
font.pixelSize: 15
wrapMode: TextEdit.NoWrap
font.family: Theme.palette.baseFont.name
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)) === ""
&& (namesList.count-1) >= 0) {
removeMember(namesModel.get(namesList.count-1).publicId);
&& getText(cursorPosition, (cursorPosition-1)) === "") {
namesModel.remove((namesList.count-1), 1);
}
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (sortedList.count > 0)) {
root.insertTag(sortedList.get(userListView.currentIndex).name, sortedList.get(userListView.currentIndex).publicId);
}
}
Keys.onUpPressed: { userListView.decrementCurrentIndex(); }
Keys.onDownPressed: { userListView.incrementCurrentIndex(); }
}
StatusBaseText {
id: warningTextLabel
visible: (namesModel.count === root.nameCountLimit)
Layout.preferredWidth: visible ? 120 : 0
visible: (namesModel.count === 5)
Layout.preferredWidth: 120
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
font.pixelSize: 10
color: Theme.palette.dangerColor1
text: root.nameCountLimit + " " + root.warningText
}
}
}
StatusBaseText {
id: contactsLabel
font.pixelSize: 15
anchors.left: parent.left
anchors.leftMargin: 8
anchors.top: tagSelectorRect.bottom
anchors.topMargin: visible ? 32 : 0
height: visible ? contentHeight : 0
visible: (root.sortedList.count === 0)
color: Theme.palette.baseColor1
text: root.listLabel
}
Control {
id: suggestionsContainer
width: 360
anchors {
top: (root.sortedList.count > 0) ? tagSelectorRect.bottom : contactsLabel.bottom
topMargin: 8//Style.current.halfPadding
bottom: parent.bottom
bottomMargin: 16//Style.current.padding
}
clip: true
visible: ((edit.text === "") || (root.sortedList.count > 0))
x: ((root.namesModel.count > 0) && (root.sortedList.count > 0) && ((edit.x + 8) <= (root.width - suggestionsContainer.width)))
? (edit.x + 8) : 0
background: Rectangle {
id: bgRect
anchors.fill: parent
anchors.margins: 8
visible: (root.sortedList.count > 0)
color: Theme.palette.statusPopupMenu.backgroundColor
radius: 8
layer.enabled: true
layer.effect: DropShadow {
source: bgRect
horizontalOffset: 0
verticalOffset: 4
radius: 12
samples: 25
spread: 0.2
color: Theme.palette.dropShadow
}
}
contentItem: ListView {
id: userListView
anchors {
fill: parent
topMargin: 16
leftMargin: 8
rightMargin: 8
}
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded
}
boundsBehavior: Flickable.StopAtBounds
onCountChanged: {
userListView.currentIndex = 0;
}
delegate: StatusMemberListItem {
anchors.left: parent.left
anchors.leftMargin: bgRect.visible ? 8 : 0
anchors.right: parent.right
anchors.rightMargin: bgRect.visible ? 8 : 0
height: visible ? 64 : 0
visible: {
for (let i = 0; i < namesModel.count; i++) {
if (namesModel.get(i).publicId === model.publicId) {
return false
}
}
return true
}
nickName: model.nickName
userName: model.name
chatKey: root.compressedKeyGetter(model.publicId)
trustIndicator: model.trustIndicator
isMutualContact: model.isMutualContact
image.source: model.icon
image.isIdenticon: model.isIdenticon
isOnline: model.onlineStatus
statusListItemIcon.badge.border.color: sensor.containsMouse ? Theme.palette.baseColor2 : Theme.palette.baseColor4
ringSettings.ringSpecModel: root.ringSpecModelGetter(publicId)
color: (sensor.containsMouse || highlighted) ? Theme.palette.baseColor2 : "transparent"
onClicked: {
root.insertTag(model.name, model.publicId);
}
text: root.warningText
}
}
}
+8 -124
View File
@@ -7,114 +7,30 @@ import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusToastMessage
\inherits Control
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Displays a toast message in the UI. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-controls2-control.html}{Control}.
The \c StatusToastMessage displays a toast message in the UI either standalone or as part
of a stack of toast messages.
For example:
\qml
StatusToastMessage {
primaryText: qsTr("Collectible is being minted...")
secondaryText: qsTr("View on Etherscan")
loading: true
type: 0
linkUrl: "http://google.com"
duration: 5000
}
\endqml
\image status_toast_message.png
For a list of components available see StatusQ.
*/
Control {
id: root
width: 343
height: !!secondaryText ? 68 : 48
anchors.right: parent.right
/*!
\qmlproperty bool StatusToastMessage::open
This property represents all steps and their descriptions as provided by the user.
*/
property bool open: true
/*!
\qmlproperty string StatusToastMessage::primaryText
This property represents the title text of the ToastMessage.
*/
property bool open: false
property string primaryText: ""
/*!
\qmlproperty string StatusToastMessage::secondaryText
This property represents the subtitle text of the ToastMessage.
*/
property string secondaryText: ""
/*!
\qmlproperty bool StatusToastMessage::loading
This property represents activates/deactivates the loading indicator of the ToastMessage.
*/
property bool loading: false
/*!
\qmlproperty string StatusToastMessage::linkUrl
This property represents all steps and their descriptions as provided by the user.
*/
property string iconName: ""
property string linkUrl: ""
/*!
\qmlproperty int StatusToastMessage::duration
This property represents duration in milliseconds, for how long a toast will be visible. If 0 is set for
duration, that means a toast won't dissapear without user's interaction (click on close).
*/
property int duration: 0
/*!
\qmlproperty StatusIconSettings StatusToastMessage::icon
This property holds a set of settings for the icon of the ToastMessage.
*/
property StatusIconSettings icon: StatusIconSettings {
width: 23
height: 23
}
/*!
\qmlproperty int StatusToastMessage::type
This property holds the type of the ToastMessage. Values are:
\list
\li Default
\li Success
\endlist
*/
property int type: StatusToastMessage.Type.Default
enum Type {
Default,
Success
}
/*!
\qmlmethod
This function is used to open the ToastMessage setting all its properties.
Examples of usage:
\qml
StatusToastMessage {
id: toastMessage
}
MouseArea {
anchors.fill: parent
onClicked: {
toastMessage.open(qsTr("Verification Request Sent"), "", "checkmark-circle", 1, false,"");
}
}
\endqml
*/
function open(title, subTitle, iconName, type, loading, url) {
root.primaryText = title;
root.secondaryText = subTitle;
@@ -125,36 +41,12 @@ Control {
root.open = true;
}
/*!
\qmlsignal
This signal is emitted when the ToastMessage is closed (after animation).
*/
signal close()
/*!
\qmlsignal
This signal is emitted when the ToastMessage contains a url and this url
is clicked by the user.
*/
signal linkActivated(var link)
QtObject {
id: d
readonly property string openedState: "opened"
readonly property string closedState: "closed"
}
Timer {
interval: root.duration
running: root.duration > 0
onTriggered: {
root.open = false;
}
}
states: [
State {
name: d.openedState
name: "opened"
when: root.open
PropertyChanges {
target: root
@@ -163,13 +55,16 @@ Control {
}
},
State {
name: d.closedState
name: "closed"
when: !root.open
PropertyChanges {
target: root
anchors.rightMargin: -width
opacity: 0.0
}
StateChangeScript {
script: { root.close(); }
}
}
]
@@ -177,12 +72,6 @@ Control {
Transition {
to: "*"
NumberAnimation { properties: "anchors.rightMargin,opacity"; duration: 400 }
onRunningChanged: {
if(!running && state == d.closedState) {
root.close();
}
}
}
]
@@ -225,14 +114,9 @@ Control {
radius: (root.width/2)
color: (root.type === StatusToastMessage.Type.Success) ?
Theme.palette.successColor2 : Theme.palette.primaryColor3
visible: loader.sourceComponent != undefined
Loader {
id: loader
anchors.centerIn: parent
sourceComponent: root.loading ? loadingInd :
root.icon.name != ""? statusIcon :
undefined
sourceComponent: root.loading ? loadingInd : statusIcon
Component {
id: loadingInd
StatusLoadingIndicator {
+2 -33
View File
@@ -6,42 +6,10 @@ import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusWizardStepper
\inherits Item
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief Displays a step sequence provided by the stepsModel. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-item.html}{Item}.
The \c StatusWizardStepper steps and their descriptions as provided by the stepsModel.
For example:
\qml
StatusWizardStepper {
id: wizardStepper
stepsModel: ListModel {
ListElement {description: qsTr("Send Request"); loadingTime: 0; stepCompleted: false}
ListElement {description: qsTr("Receive Response"); loadingTime: 0; stepCompleted: false}
ListElement {description: qsTr("Confirm Identity"); loadingTime: 0; stepCompleted: false}
}
}
\endqml
\image status_wizard_stepper.png
For a list of components available see StatusQ.
*/
Item {
property int maxDuration: 2000
id: wizardWrapper
width: parent.width
height: 56
/*!
\qmlproperty ListModel StatusWizardStepper::stepsModel
This property represents all steps and their descriptions as provided by the user.
*/
property ListModel stepsModel: ListModel { }
ListView {
@@ -67,7 +35,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -12
from: 0
to: wizardWrapper.maxDuration
//TODO what is to: here?
to: 2000
value: loadingTime
backgroundColor: "transparent"
backgroundBorderColor: Theme.palette.primaryColor1
@@ -19,8 +19,38 @@ Item {
property string tertiaryDetail: ""
property string resendText: ""
property bool showResendButton: false
property bool isMutualContact: false
property var trustIndicator: StatusContactVerificationIcons.TrustedType.None
property StatusIconSettings icon1: StatusIconSettings {
width: dummyImage.width
height: dummyImage.height
rotation: 0
color: Theme.palette.indirectColor1
background: StatusIconBackgroundSettings {
width: 10
height: 10
color: Theme.palette.primaryColor1
}
// only used to get implicit width and height from the actual image
property Image dummyImage: Image {
source: icon1.name ? "../../../../assets/img/icons/" + icon1.name + ".svg": ""
visible: false
}
}
property StatusIconSettings icon2: StatusIconSettings {
width: dummyImage.width
height: dummyImage.height
rotation: 0
color: Theme.palette.primaryColor1
background: StatusIconBackgroundSettings {
width: 10
height: 10
color: Theme.palette.indirectColor1
}
// only used to get implicit width and height from the actual image
property Image dummyImage: Image {
source: icon2.name ? "../../../../assets/img/icons/" + icon2.name + ".svg": ""
visible: false
}
}
signal clicked()
signal resendClicked()
@@ -53,10 +83,28 @@ Item {
}
}
Layout.alignment: Qt.AlignBottom
}
StatusContactVerificationIcons {
isMutualContact: statusMessageHeader.isMutualContact
trustIndicator: statusMessageHeader.trustIndicator
}
StatusRoundIcon {
icon.background.width: icon1.background.width
icon.background.height: icon1.background.height
icon.background.color: icon1.background.color
icon.width: icon1.width
icon.height: icon1.height
icon.name: icon1.name
icon.rotation: icon1.rotation
icon.color: icon1.color
visible: !!icon.name
}
StatusRoundIcon {
icon.background.width: icon2.background.width
icon.background.height: icon2.background.height
icon.background.color: icon2.background.color
icon.width: icon2.width
icon.height: icon2.height
icon.name: icon2.name
icon.rotation: icon2.rotation
icon.color: icon2.color
visible: !!icon.name
}
StatusBaseText {
id: secondaryDisplayName
-5
View File
@@ -10,8 +10,6 @@ StatusChatListCategoryItem 0.1 StatusChatListCategoryItem.qml
StatusChatListAndCategories 0.1 StatusChatListAndCategories.qml
StatusChatToolBar 0.1 StatusChatToolBar.qml
StatusContactRequestsIndicatorListItem 0.1 StatusContactRequestsIndicatorListItem.qml
StatusEmoji 0.1 StatusEmoji.qml
StatusContactVerificationIcons 0.1 StatusContactVerificationIcons.qml
StatusDescriptionListItem 0.1 StatusDescriptionListItem.qml
StatusLetterIdenticon 0.1 StatusLetterIdenticon.qml
StatusListItem 0.1 StatusListItem.qml
@@ -24,8 +22,6 @@ StatusRoundIcon 0.1 StatusRoundIcon.qml
StatusRoundedImage 0.1 StatusRoundedImage.qml
StatusMacWindowButtons 0.1 StatusMacWindowButtons.qml
StatusListItemBadge 0.1 StatusListItemBadge.qml
StatusListItemTag 0.1 StatusListItemTag.qml
StatusListPicker 0.1 StatusListPicker.qml
StatusExpandableItem 0.1 StatusExpandableItem.qml
StatusSmartIdenticon 0.1 StatusSmartIdenticon.qml
StatusMessage 0.1 StatusMessage.qml
@@ -33,4 +29,3 @@ StatusMessageDetails 0.1 StatusMessageDetails.qml
StatusTagSelector 0.1 StatusTagSelector.qml
StatusToastMessage 0.1 StatusToastMessage.qml
StatusWizardStepper 0.1 StatusWizardStepper.qml
StatusImageCropPanel 0.1 StatusImageCropPanel.qml
@@ -55,7 +55,7 @@ Item {
return
}
if (selectedAccount.color) {
selectedIconImg.color = Utils.getThemeAccountColor(selectedAccount.color, Theme.palette.userCustomizationColors) || Theme.palette.userCustomizationColors[0]
selectedIconImg.color = Utils.getThemeAccountColor(selectedAccount.color, Theme.palette.accountColors) || Theme.palette.accountColors[0]
}
if (selectedAccount.name) {
selectedTextField.text = selectedAccount.name
@@ -208,7 +208,7 @@ Item {
width: 20
height: 20
icon: "filled-account"
color: Utils.getThemeAccountColor(model.color, Theme.palette.userCustomizationColors) || Theme.palette.userCustomizationColors[0]
color: Utils.getThemeAccountColor(model.color, Theme.palette.accountColors) || Theme.palette.accountColors[0]
}
Column {
+1 -51
View File
@@ -3,61 +3,13 @@ import QtQuick 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusBanner
\inherits Column
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief It displays a banner with a custom text, size and type. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-column.html}{Column}.
The \c StatusBanner displays a banner with a custom text, size and type (Info, Danger, Success or Warning).
Example of how the control looks like:
\image status_banner.png
Example of how to use it:
\qml
StatusBanner {
width: parent.width
visible: popup.userIsBlocked
type: StatusBanner.Type.Danger
statusText: qsTr("Blocked")
}
\endqml
For a list of components available see StatusQ.
*/
Column {
id: statusBanner
width: parent.width
/*!
\qmlproperty string StatusBanner::statusText
This property holds the text the banner will display.
*/
property string statusText
/*!
\qmlproperty string StatusBanner::type
This property holds type of banner. Possible values are:
\qml
enum Type {
Info, // 0
Danger, // 1
Success, // 2
Warning // 3
}
\endqml
*/
property int type: StatusBanner.Type.Info
/*!
\qmlproperty string StatusBanner::textPixels
This property holds the pixels size of the text inside the banner.
*/
property int textPixels: 15
/*!
\qmlproperty string StatusBanner::statusBannerHeight
This property holds the height of the banner rectangle.
*/
property int statusBannerHeight: 38
// "private" properties
@@ -75,8 +27,6 @@ Column {
Warning // 3
}
width: parent.width
// Component definition
Rectangle {
id: topDiv
+15 -14
View File
@@ -40,9 +40,11 @@ Rectangle {
return 5
case StatusBaseButton.Size.Small:
return 10
break;
case StatusBaseButton.Size.Large:
default:
return 11
break;
}
}
property real defaultBottomPadding: {
@@ -51,9 +53,11 @@ Rectangle {
return 5
case StatusBaseButton.Size.Small:
return 10
break;
case StatusBaseButton.Size.Large:
default:
return 11
break;
}
}
@@ -105,17 +109,6 @@ Rectangle {
hoverEnabled: !loading
enabled: !loading
Loader {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
active: loading
sourceComponent: StatusLoadingIndicator {
color: statusBaseButton.enabled ? textColor
: disabledTextColor
} // Indicator
} // Loader
Row {
id: layout
anchors.left: parent.left
@@ -128,20 +121,28 @@ Rectangle {
height: statusBaseButton.icon.height
icon: statusBaseButton.icon.name
anchors.verticalCenter: parent.verticalCenter
opacity: !loading && statusBaseButton.icon.name !== ""
visible: statusBaseButton.icon.name !== ""
visible: !loading && statusBaseButton.icon.name !== ""
color: statusBaseButton.enabled ? textColor
: disabledTextColor
} // Icon
StatusBaseText {
id: label
opacity: !loading
visible: !loading
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: size === StatusBaseButton.Size.Large ? 15 : 13 // by design
color: statusBaseButton.enabled ? textColor
: disabledTextColor
} // Text
Loader {
anchors.verticalCenter: parent.verticalCenter
active: loading
sourceComponent: StatusLoadingIndicator {
color: statusBaseButton.enabled ? textColor
: disabledTextColor
} // Indicator
} // Loader
} // Ro
+154 -410
View File
@@ -1,470 +1,252 @@
import QtQuick 2.14
import QtQuick.Controls 2.14 as QC
import QtQuick.Layouts 1.14
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusBaseInput
\inherits Item
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief Displays a customized TextInput component.
Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-item.html}{Item}.
The \c StatusBaseInput displays an enhanced TextInput for users to type text.
For example:
\qml
StatusBaseInput {
implicitWidth: parent.width
implicitHeight: 36
topPadding: 8
bottomPadding: 8
clearable: true
placeholderText: qsTr("Search")
icon.name: "search"
}
\endqml
\image status_base_input.png
For a list of components available see StatusQ.
*/
Item {
id: root
id: statusBaseInput
/*!
\qmlproperty bool StatusBaseInput::multiline
This property indicates whether the StatusBaseInput allows multiline text. Default value is false.
*/
property bool multiline: false
/*!
\qmlproperty bool StatusBaseInput::acceptReturn
This property indicates whether the StatusBaseInput allows pressing Enter or Return.
This is used in case multiline is false and we still want to interact using Enter or Return. Default value is false.
*/
property bool acceptReturn: false
/*!
\qmlproperty bool StatusBaseInput::clearable
This property indicates whether the StatusBaseInput allows clearing all text. Default value is false.
*/
property bool clearable: false
/*!
\qmlproperty alias StatusBaseInput::inputMethodHints
This property holds a reference to the TextEdit component's inputMethodHints property.
*/
property alias inputMethodHints: edit.inputMethodHints
/*!
\qmlproperty alias StatusBaseInput::selectedText
This property holds a reference to the TextEdit component's selectedText property.
*/
property alias selectedText: edit.selectedText
/*!
\qmlproperty alias StatusBaseInput::selectedTextColor
This property holds a reference to the TextEdit component's selectedTextColor property.
*/
property alias selectedTextColor: edit.selectedTextColor
/*!
\qmlproperty alias StatusBaseInput::selectionStart
This property holds a reference to the TextEdit component's selectionStart property.
*/
property alias selectionStart: edit.selectionStart
/*!
\qmlproperty alias StatusBaseInput::selectionEnd
This property holds a reference to the TextEdit component's selectionEnd property.
*/
property alias selectionEnd: edit.selectionEnd
/*!
\qmlproperty alias StatusBaseInput::cursorPosition
This property holds a reference to the TextEdit component's cursorPosition property.
*/
property alias cursorPosition: edit.cursorPosition
/*!
\qmlproperty alias StatusBaseInput::edit
This property holds a reference to the TextEdit component.
*/
property alias edit: edit
/*!
\qmlproperty alias StatusBaseInput::text
This property holds a reference to the TextEdit component's text property.
*/
property alias text: edit.text
/*!
\qmlproperty alias StatusBaseInput::background
This property holds a reference to the background component.
*/
property alias background: background
/*!
\qmlproperty alias StatusBaseInput::color
This property holds a reference to the TextEdit component's color property.
*/
property alias color: edit.color
/*!
\qmlproperty alias StatusBaseInput::font
This property holds a reference to the TextEdit component's font property.
*/
property alias font: edit.font
/*!
\qmlproperty alias StatusBaseInput::focussed
This property holds a reference to the TextEdit component's activeFocus property.
*/
property alias focussed: edit.activeFocus
/*!
\qmlproperty alias StatusBaseInput::verticalAlignment
This property holds a reference to the TextEdit component's verticalAlignment property.
*/
property alias verticalAlignment: edit.verticalAlignment
/*!
\qmlproperty alias StatusBaseInput::horizontalAlignment
This property holds a reference to the TextEdit component's horizontalAlignment property.
*/
property alias verticalAlignmet: edit.verticalAlignment
property alias horizontalAlignment: edit.horizontalAlignment
/*!
\qmlproperty alias StatusBaseInput::placeholderText
This property holds a reference to the placeholder component's placeholderText property.
*/
property alias placeholderText: placeholder.text
/*!
\qmlproperty alias StatusBaseInput::placeholderTextColor
This property holds a reference to the placeholder component's placeholderTextColor property.
*/
property alias placeholderTextColor: placeholder.color
/*!
\qmlproperty alias StatusBaseInput::placeholderFont
This property holds a reference to the placeholder component's font property.
*/
property alias placeholderFont: placeholder.font
/*!
\qmlproperty real StatusBaseInput::leftPadding
This property sets the leftComponentLoader's left padding.
*/
property real leftPadding: leftComponentLoader.item ? 8 : 16
/*!
\qmlproperty real StatusBaseInput::rightPadding
This property sets the right padding.
*/
property real leftPadding: 16
property real rightPadding: 16
/*!
\qmlproperty real StatusBaseInput::topPadding
This property sets the top padding.
*/
property real topPadding: 12
/*!
\qmlproperty alias StatusBaseInput::bottomPadding
This property sets the bottom padding.
*/
property real bottomPadding: 12
/*!
\qmlproperty var StatusBaseInput::tabNavItem
This property sets the tab key navigation item.
*/
property var tabNavItem: null
/*!
\qmlproperty real StatusBaseInput::minimumHeight
This property sets the minimum height.
*/
property real minimumHeight: 0
/*!
\qmlproperty alias StatusBaseInput::maximumHeight
This property sets the maximum height.
*/
property real maximumHeight: 0
/*!
\qmlproperty int StatusBaseInput::maximumLength
This property sets the text's maximum length.
*/
property int maximumLength: 0
/*!
\qmlproperty bool StatusBaseInput::valid
This property sets the valid state. Default value is true.
*/
property bool valid: true
/*!
\qmlproperty bool StatusBaseInput::pristine
This property sets the pristine. Default value is true.
*/
property bool pristine: true
/*!
\qmlproperty bool StatusBaseInput::dirty
This property sets the dirty property. Default value is false.
*/
property bool dirty: false
/*!
\qmlproperty bool StatusBaseInput::pending
This property holds the maximum height of the component. Default value is false.
*/
property bool pending: false
/*!
\qmlproperty bool StatusBaseInput::leftIcon
This property sets the visibility of the left icon. Default value is true.
*/
property bool leftIcon: true
/*!
\qmlproperty bool StatusBaseInput::isIconSelectable
This property indicates whether icon is selectable. Default value is false.
*/
property bool isIconSelectable: false
/*!
\qmlproperty bool StatusBaseInput::showBackground
This property sets the visibility of the background. Default value is true.
*/
property bool showBackground: true
/*!
\qmlproperty StatusIconSettings StatusBaseInput::icon
This property holds a set of settings for the icon of the StatusBaseInput.
*/
property StatusIconSettings icon: StatusIconSettings {
width: 24
height: 24
name: ""
color: Theme.palette.baseColor1
emoji: ""
letterSize: emoji ? 11 : 14
background: StatusIconBackgroundSettings {
width: 30
height: 30
color: Theme.palette.indirectColor1
}
property Item component
onClearableChanged: {
if (clearable && !component) {
clearButtonLoader.active = true
clearButtonLoader.parent = statusBaseInputComponentSlot
} else {
clearButtonLoader.active = false
}
}
/*!
\qmlproperty string StatusBaseInput::letterIconName
This property holds the letter that should be displayed in the letter identicon component.
*/
property string letterIconName: ""
/*!
\qmlproperty Component StatusBaseInput::leftComponent
This property sets the left component to be displayed inside the input.
*/
property Component leftComponent
/*!
\qmlproperty Component StatusBaseInput::selectedText
This property sets the right component to be displayed inside the input.
*/
property Component rightComponent
/*!
\qmlsignal
This signal is emitted when the icon is clicked.
*/
signal iconClicked
/*!
\qmlsignal
This signal is emitted when a hard key is pressed passing as parameter the keyboard event.
*/
signal keyPressed(var event)
/*!
\qmlsignal
This signal is emitted when the text edit is clicked.
*/
signal editClicked()
onComponentChanged: {
if (!!component) {
component.parent = statusBaseInputComponentSlot
}
}
implicitWidth: 448
implicitHeight: multiline ? Math.min(Math.max(
(edit.implicitHeight + topPadding + bottomPadding),
44, root.minimumHeight), root.maximumHeight) : 44
implicitHeight: multiline ? Math.max((edit.implicitHeight + topPadding + bottomPadding), 44) : 44
Rectangle {
id: background
width: parent.width
height: maximumHeight != 0 ? Math.min(
minimumHeight
!= 0 ? Math.max(
root.implicitHeight,
minimumHeight) : root.implicitHeight,
maximumHeight) : parent.height
color: root.showBackground ? Theme.palette.baseColor2
: "transparent"
minimumHeight != 0 ? Math.max(statusBaseInput.implicitHeight, minimumHeight)
: statusBaseInput.implicitHeight, maximumHeight) : parent.height
color: Theme.palette.baseColor2
radius: 8
clip: true
border.width: 1
border.color: {
if (!root.showBackground) {
return "transparent"
}
if (!root.valid && root.dirty) {
return Theme.palette.dangerColor1
if (!statusBaseInput.valid && statusBaseInput.dirty) {
return Theme.palette.dangerColor1;
}
if (edit.activeFocus) {
return Theme.palette.primaryColor1
return Theme.palette.primaryColor1;
}
return sensor.containsMouse ? Theme.palette.primaryColor2 : "transparent"
}
MouseArea {
id: sensor
enabled: !edit.activeFocus
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.IBeamCursor
onClicked: {
if (edit.activeFocus)
return;
edit.forceActiveFocus()
root.editClicked()
}
RowLayout {
spacing: 10
anchors {
fill: parent
leftMargin: root.leftPadding
rightMargin: root.rightPadding
}
StatusIcon {
id: statusIcon
anchors.topMargin: 10
anchors.left: statusBaseInput.leftIcon ? parent.left : undefined
anchors.right: !statusBaseInput.leftIcon ? parent.right : undefined
anchors.leftMargin: 10
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
icon: statusBaseInput.icon.name
width: statusBaseInput.icon.width
height: statusBaseInput.icon.height
color: statusBaseInput.icon.color
visible: !!statusBaseInput.icon.name
}
Flickable {
id: flick
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: (statusIcon.visible && statusBaseInput.leftIcon) ?
statusIcon.right : parent.left
anchors.right: {
if (!!statusBaseInput.component) {
return statusBaseInputComponentSlot.left
}
return statusIcon.visible && !statusBaseInput.leftIcon ? statusIcon.left : parent.right
}
anchors.leftMargin: statusIcon.visible && statusBaseInput.leftIcon ? 8
: statusBaseInput.leftPadding
anchors.rightMargin: {
return clearable ? clearButtonLoader.width + 12 :
(statusIcon.visible && !leftIcon) || !!statusBaseInput.component ? 8 : 0
}
contentWidth: edit.paintedWidth
contentHeight: edit.paintedHeight
boundsBehavior: Flickable.StopAtBounds
QC.ScrollBar.vertical: QC.ScrollBar { interactive: multiline; enabled: multiline }
clip: true
Loader {
id: leftComponentLoader
sourceComponent: {
if (root.leftComponent) return root.leftComponent
if (!root.leftIcon) return undefined
if (root.icon.emoji || root.icon.isLetterIdenticon) return identiconComponent
if (root.icon.name) return isIconSelectable ? identiconComponent : iconComponent
return undefined
}
function ensureVisible(r) {
if (contentX >= r.x)
contentX = r.x;
else if (contentX+width <= r.x+r.width)
contentX = r.x+r.width-width;
if (contentY >= r.y)
contentY = r.y;
else if (contentY+height <= r.y+r.height)
contentY = r.y+r.height-height;
}
Flickable {
id: flick
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: root.topPadding
Layout.bottomMargin: root.bottomPadding
contentWidth: edit.paintedWidth
contentHeight: edit.paintedHeight
boundsBehavior: Flickable.StopAtBounds
QC.ScrollBar.vertical: QC.ScrollBar {
interactive: multiline
enabled: multiline
}
clip: true
function ensureVisible(r) {
if (contentX >= r.x)
contentX = r.x
else if (contentX + width <= r.x + r.width)
contentX = r.x + r.width - width
if (contentY >= r.y)
contentY = r.y
else if (contentY + height <= r.y + r.height)
contentY = r.y + r.height - height
}
TextEdit {
id: edit
property string previousText: text
property var keyEvent
width: flick.width
height: flick.height
verticalAlignment: TextEdit.AlignVCenter
selectByMouse: true
selectionColor: Theme.palette.primaryColor2
selectedTextColor: color
focus: true
font.pixelSize: 15
font.family: Theme.palette.baseFont.name
color: Theme.palette.directColor1
wrapMode: root.multiline ? Text.WrapAtWordBoundaryOrAnywhere : TextEdit.NoWrap
Keys.onReturnPressed: event.accepted = !multiline && !acceptReturn
Keys.onEnterPressed: event.accepted = !multiline && !acceptReturn
Keys.forwardTo: [root]
KeyNavigation.priority: !!root.tabNavItem ? KeyNavigation.BeforeItem : KeyNavigation.AfterItem
KeyNavigation.tab: root.tabNavItem
Keys.onPressed: {
edit.keyEvent = event.key
root.keyPressed(event);
TextEdit {
id: edit
property string previousText: text
width: flick.width
height: flick.height
verticalAlignment: Text.AlignVCenter
selectByMouse: true
selectionColor: Theme.palette.primaryColor2
selectedTextColor: color
focus: true
font.pixelSize: 15
font.family: Theme.palette.baseFont.name
color: Theme.palette.directColor1
onCursorRectangleChanged: { flick.ensureVisible(cursorRectangle); }
wrapMode: statusBaseInput.multiline ? Text.WrapAtWordBoundaryOrAnywhere : TextEdit.NoWrap
onActiveFocusChanged: {
if (statusBaseInput.pristine) {
statusBaseInput.pristine = false
}
}
onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)
onActiveFocusChanged: if (root.pristine) root.pristine = false
onTextChanged: {
if (previousText === text) {
// Not sure why, but the textChanged event was triggered even if it didn't really
return
}
root.dirty = true
if (root.maximumLength > 0) {
if (text.length > root.maximumLength) {
var cursor = cursorPosition
text = previousText
if (cursor > text.length) {
cursorPosition = text.length
} else {
cursorPosition = cursor - 1
}
Keys.onReturnPressed: {
if (multiline) {
event.accepted = false
} else {
event.accepted = true
}
}
Keys.onEnterPressed: {
if (multiline) {
event.accepted = false
} else {
event.accepted = true
}
}
Keys.forwardTo: [statusBaseInput]
onTextChanged: {
statusBaseInput.dirty = true
if (statusBaseInput.maximumLength > 0) {
if (text.length > statusBaseInput.maximumLength) {
var cursor = cursorPosition;
text = previousText;
if (cursor > text.length) {
cursorPosition = text.length;
} else {
cursorPosition = cursor-1;
}
previousText = text
}
}
cursorDelegate: Rectangle {
color: Theme.palette.primaryColor1
implicitWidth: 2
implicitHeight: 22
radius: 1
visible: edit.cursorVisible
SequentialAnimation on visible {
loops: Animation.Infinite
running: edit.cursorVisible
PropertyAnimation { to: false; duration: 600; }
PropertyAnimation { to: true; duration: 600; }
}
}
StatusBaseText {
id: placeholder
visible: (edit.text.length === 0)
anchors.fill: parent
verticalAlignment: parent.verticalAlignment
font.pixelSize: 15
elide: StatusBaseText.ElideRight
font.family: Theme.palette.baseFont.name
color: root.enabled ? Theme.palette.baseColor1 : Theme.palette.directColor6
previousText = text
}
}
} // Flickable
Loader {
sourceComponent: {
if (root.rightComponent) return root.rightComponent
if (root.clearable) return clearButton
if (root.leftIcon) return undefined
if (root.icon.emoji) return identiconComponent
if (root.icon.name) return isIconSelectable ? identiconComponent : iconComponent
return undefined
StatusBaseText {
id: placeholder
visible: (edit.text.length === 0)
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: statusBaseInput.rightPadding
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 15
elide: StatusBaseText.ElideRight
font.family: Theme.palette.baseFont.name
color: statusBaseInput.enabled ? Theme.palette.baseColor1 :
Theme.palette.directColor6
}
}
} // Flickable
Item {
id: statusBaseInputComponentSlot
anchors.right: parent.right
anchors.rightMargin: 12
width: childrenRect.width
height: childrenRect.height
anchors.verticalCenter: parent.verticalCenter
}
}
} // Rectangle
Component {
id: clearButton
StatusFlatRoundButton {
visible: edit.text.length != 0 && root.clearable && !root.multiline
&& edit.activeFocus
Loader {
id: clearButtonLoader
sourceComponent: StatusFlatRoundButton {
id: clearButton
visible: edit.text.length != 0 &&
statusBaseInput.clearable &&
!statusBaseInput.multiline &&
edit.activeFocus
type: StatusFlatRoundButton.Type.Secondary
width: 24
height: 24
@@ -478,43 +260,5 @@ Item {
}
}
Component {
id: identiconComponent
StatusSmartIdenticon {
id: identicon
icon.width: 24
icon.height: 24
icon.background: root.icon.background
icon.color: root.icon.color
icon.letterSize: root.icon.letterSize
icon.emoji: root.icon.emoji
icon.name: !root.icon.emoji ? root.icon.name : ""
icon.isLetterIdenticon: root.icon.isLetterIdenticon
name: root.letterIconName
MouseArea {
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
hoverEnabled: true
enabled: identicon.visible
onClicked: root.iconClicked()
}
}
}
Component {
id: iconComponent
StatusIcon {
id: statusIcon
icon: root.icon.name
width: root.icon.width
height: root.icon.height
color: root.icon.color
}
}
}
@@ -25,10 +25,8 @@ Rectangle {
property StatusIconSettings icon: StatusIconSettings {
width: 36
height: 36
charactersLen: 2
letterSize: 20
}
property alias ringSettings: identicon.ringSettings
property int type: StatusChatInfoButton.Type.PublicChat
property alias tooltip: statusToolTip
property alias sensor: sensor
@@ -1,32 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.14
import StatusQ.Core.Theme 0.1
RadioButton {
id: control
property string radioButtonColor: ""
property string selectionColor: StatusColors.colors['white']
implicitWidth: 48
implicitHeight: 48
indicator: Rectangle {
implicitWidth: 48
implicitHeight: 48
radius: width/2
color: radioButtonColor
Rectangle {
width: 20
height: 20
radius: width/2
color: selectionColor
border.color: StatusColors.colors['grey3']
visible: control.checked
anchors.centerIn: parent
}
}
}
@@ -1,56 +0,0 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import "./"
Column {
id: root
property alias titleText: title.text
property alias title: title
property int selectedColorIndex: 0
property string selectedColor: ""
property var model: [StatusColors.colors['black'],
StatusColors.colors['grey'],
StatusColors.colors['blue2'],
StatusColors.colors['purple'],
StatusColors.colors['cyan'],
StatusColors.colors['violet'],
StatusColors.colors['red2'],
StatusColors.colors['yellow'],
StatusColors.colors['green2'],
StatusColors.colors['moss'],
StatusColors.colors['brown'],
StatusColors.colors['brown2']]
spacing: 16
StatusBaseText {
id: title
verticalAlignment: Text.AlignVCenter
font.pixelSize: 13
color: Theme.palette.baseColor1
}
Grid {
columns: 6
rowSpacing: 16
columnSpacing: 32
Repeater {
model: root.model
delegate: StatusColorRadioButton {
checked: index === selectedColorIndex
radioButtonColor: root.model[index] || "transparent"
onCheckedChanged: {
if(checked) {
selectedColorIndex = index
selectedColor = root.model[index]
}
}
}
}
}
}
+1 -2
View File
@@ -11,8 +11,6 @@ TabButton {
property string name: ""
property bool highlighted: false
property alias identicon: identicon
implicitWidth: 40
implicitHeight: 40
@@ -34,6 +32,7 @@ TabButton {
icon.name: statusIconTabButton.icon.name
icon.color: (statusIconTabButton.hovered || highlighted || statusIconTabButton.checked) ? Theme.palette.primaryColor1 : statusIconTabButton.icon.color
icon.isLetterIdenticon: statusIconTabButton.name !== "" && !statusIconTabButton.icon.source.toString()
icon.letterSize: 15
name: statusIconTabButton.name
}
}
@@ -1,151 +0,0 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import StatusQ.Core 0.1
/*!
\qmltype StatusIdenticonRing
\inherits Item
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief It provides lines wrapped into a ring around another component like avatars.
The \c StatusIdenticonRing item displays lines wrapped into a ring. The line is rendered clockwise.
N segments and M distinctive colors compose identicon lines.
Example of how the component looks like:
\image status_identicon_ring.png
Example of how to use it:
\qml
StatusIdenticonRing {
anchors.fill: parent
settings: StatusIdenticonRingSettings {
initalAngleRad: 0
ringPxSize: 1.5
ringSpecModel: [ ListElement {colorId: 0; segmentLength: 1},
ListElement {colorId: 28; segmentLength: 1},
ListElement {colorId: 31; segmentLength: 1},
ListElement {colorId: 22; segmentLength: 4},
ListElement {colorId: 28; segmentLength: 3},
ListElement {colorId: 27; segmentLength: 5},
ListElement {colorId: 7; segmentLength: 5},
ListElement {colorId: 13; segmentLength: 1},
ListElement {colorId: 25; segmentLength: 4}]
distinctiveColors: ["#000000", "#726F6F", "#C4C4C4",
"#E7E7E7", "#FFFFFF", "#00FF00",
"#009800", "#B8FFBB", "#FFC413",
"#9F5947", "#FFFF00", "#A8AC00",
"#FFFFB0", "#FF5733", "#FF0000",
"#9A0000", "#FF9D9D", "#FF0099",
"#C80078", "#FF00FF", "#900090",
"#FFB0FF", "#9E00FF", "#0000FF",
"#000086", "#9B81FF", "#3FAEF9",
"#9A6600", "#00FFFF", "#008694",
"#C2FFFF", "#00F0B6"]
}
}
\endqml
For a list of components available see StatusQ.
*/
Item {
id: root
/*!
\qmlproperty StatusIdenticonRingSettings StatusIdenticonRing::settings
This property holds a set of settings for building the ring.
*/
property StatusIdenticonRingSettings settings: StatusIdenticonRingSettings {
initalAngleRad: 0
ringPxSize: 1.5
}
visible: settings.ringSpecModel !== undefined
Loader {
anchors.fill: parent
active: root.visible
sourceComponent: Canvas {
function printArcSegment(context, xPos, yPos, radius, color, startAngle, arcAngleLen, lineWidth) {
context.beginPath()
context.arc(xPos, yPos, radius, startAngle, startAngle + arcAngleLen, false/*anticlockwise*/)
context.strokeStyle = color
context.lineWidth = lineWidth
context.stroke()
}
function getSegmentsCount() {
if (typeof settings.ringSpecModel.rowCount !== "undefined") {
return settings.ringSpecModel.rowCount()
}
if (typeof settings.ringSpecModel.count !== "undefined") {
return settings.ringSpecModel.count
}
return settings.ringSpecModel.length
}
function getSegment(i) {
if (typeof settings.ringSpecModel.rowCount !== "undefined") {
return abstactItemModelWrapper.itemAt(i)
}
if (typeof settings.ringSpecModel.count !== "undefined") {
return settings.ringSpecModel.get(i)
}
return settings.ringSpecModel[i]
}
function totalRingUnits() {
var units = 0
for (let i=0; i < getSegmentsCount(); i++) {
const segment = getSegment(i)
units += segment.segmentLength
}
return Math.max(1, units)
}
Repeater {
id: abstactItemModelWrapper
model: typeof settings.ringSpecModel.rowCount !== "undefined" ? settings.ringSpecModel : null
delegate: Item {
readonly property int segmentLength: model.segmentLength
readonly property int colorId: model.colorId
}
}
onPaint: {
const context = getContext("2d")
const radius = (height - settings.ringPxSize) / 2
const xPos = width / 2
const yPos = height / 2
const unitRadLen = 2 * Math.PI / totalRingUnits()
const segmentsCount = getSegmentsCount()
let arcPos = settings.initalAngleRad
context.reset()
if(settings.ringSpecModel) {
for (let i=0; i < segmentsCount; i++) {
const segment = getSegment(i)
printArcSegment(context,
xPos,
yPos,
radius,
settings.distinctiveColors[segment.colorId],
arcPos,
segment.segmentLength * unitRadLen,
settings.ringPxSize)
arcPos += segment.segmentLength * unitRadLen
}
}
}
Connections {
target: root.settings
onRingSpecModelChanged: requestPaint()
}
}
}
}
-359
View File
@@ -1,359 +0,0 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.1
import StatusQ.Core.Utils 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusImageCrop
\inherits Item
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief Draw a crop-window onto an image. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-item.html}{Item}.
Sizing
User resizes the control as needed. Control adapts and centers the crop
window inside the available space
Performance
The \c StatusImageCrop uses Canvas to draw the overlay in multiple steps.
Drawing with canvas is inefficient therefore keep the usage to a minimum,
as in the required space
API
The setCropRect is the only way to set the content shown in the crop window.
/note "Fit window" used here reffer to optimum zoom to fit all the content
in the view window; produces window borders if AR of the image is different
than the window. This ia opposite to "Fill window" reffers to optimum zoom
to fill all the view; parts of the content won't be visible if AR of the
image is different than the AR of window
/note The math is based on pixels so no subpixel accuracy. Didn't find it necessary for the current needs
/note windowRect aspect ratio and cropRect aspect ratio are keept in sync
Example of two controls; top WindowStyle.Rounded, bottom WindowStyle.Rectangular
\image eg-StatusImageCrop.png
Example of how to use it:
\qml
StatusImageCrop {
anchors.fill: parent
width: 100
height: 100 * aspectRatio
source: "qrc:/demoapp/data/logo-test-image.png"
windowStyle: StatusImageCrop.WindowStyle.Rounded
}
\endqml
For a list of components available see StatusQ.
*/
Item {
id: root
implicitWidth: 400
implicitHeight: 400 / aspectRatio
/*!
\qmlproperty url StatusImageCrop::source
Path to the image to be cropped
*/
/*required*/ property alias source: mainImage.source
enum WindowStyle {
Rounded,
Rectangular
}
/*!
\qmlproperty WindowStyle StatusImageCrop::windowStyle
Crop window styles
\value Rounded ellipses bounded by cropRect rectangle
\value Rectangular with rounded corners specified by radius
*/
property int windowStyle: StatusImageCrop.WindowStyle.Rectangular
/*!
\qmlproperty int StatusImageCrop::radius
Valid only when windowStyle is Rounded
*/
property int radius: 8
/*!
\qmlproperty color StatusImageCrop::wallColor
Color used outside the crop-rect. Tells user which part won't be available after cropping
*/
property color wallColor: Theme.palette.black
/*!
\qmlproperty real StatusImageCrop::wallTransparency
Transparency outside the crop-rect 0.0 - 1.0 (inclusive). Tells user which part won't
be available after cropping
*/
property real wallTransparency: 0.7
/*!
\qmlproperty bool StatusImageCrop::allowZoomToFitAllContent
\c true to limit the content to fit at minimum zoom; that's it, the user will see the entire image
in the crop window, in case of aspect-ratio mismatch, allowing transparent/no-content borders in the crop window.
\c false to limit the content to fill at minimum zoom; that's it, the user will see only part of the image
in the crop window in case of aspect-ratio mismatch. This way, we don't allow transparent/no-content space
*/
property bool allowZoomToFitAllContent: false
/*!
\qmlproperty real StatusImageCrop::minZoomScale
Minimum allowed zoom. This is 1 if \c allowZoomToFitAllContent is true else depends on the
source aspect ratio.
*/
readonly property real minZoomScale: allowZoomToFitAllContent ? 1 : d.zoomToFill
/*!
\qmlproperty real StatusImageCrop::maxZoomScale
Don't allow to zoom in more than maxZoomScale factor
*/
property real maxZoomScale: 10
/*!
\qmlproperty rect StatusImageCrop::cropRect
The content shown in the crop window. Values are expected to be in image coordinates.
The crop-window aspect-ratio is adjusted to match the cropRect value.
The default value shows an 1.0 AR to Fit Window
e.g. to set the crop window to show and match image's AR use Qt.rect(0, 0, sourceSize.width, sourceSize.height)
*/
readonly property rect cropRect: d.cropRect
/*!
\qmlproperty rect StatusImageCrop::cropRect
The position and zis of the crop rectangle in item's space; without extra spacing
*/
readonly property alias cropWindow: d.cropWindow
/*!
\qmlproperty real StatusImageCrop::aspectRatio
*/
readonly property real aspectRatio: d.cropRect.height !== 0 ? d.cropRect.width/d.cropRect.height : 0
/*!
\qmlproperty real StatusImageCrop::zoomScale
*/
readonly property real zoomScale: d.currentZoom(sourceSize, Qt.size(d.cropRect.width, d.cropRect.height))
/*!
\qmlproperty size StatusImageCrop::sourceSize
Image size if one is set. StatusImageCrop::cropRect is relative to the rect with top-corner
(0,0) and StatusImageCrop::sourceSize
\sa StatusImageCrop::source
*/
readonly property size sourceSize: mainImage.sourceSize
/*!
\qmlproperty real StatusImageCrop::scrToImgScale
Screen crop window to \a cropRect ratio. Can be used to translate from
screen coordinates into image coordinates e.g. to adjust \a cropRect accordingly
\note windowRect aspect-ratio and d.cropRect aspect-ratio are the same
\sa StatusImageCrop::cropRect
*/
readonly property real scrToImgScale: windowRect.wW/d.cropRect.width
/*!
\qmlmethod StatusImageCrop::setCropRect(rect)
The only way to set StatusImageCrop::cropRect from outside
The new rect will be adjusted to account for the zoom [1, StatusImageCrop::maxZoomScale]
\note the source image must be set and \c sourceSize valid otherwise, an error is logged to \c console
\note If the new rect has a diferent area the crop window will adjust to the new AR
*/
function setCropRect(newRect /*rect*/) {
if(newRect.width === 0 || newRect.height === 0)
return
if(root.sourceSize.width === 0 || root.sourceSize.height === 0)
console.error("Wrong source size. Ensure source is set")
let n = newRect
const s = root.sourceSize
let nZoom = d.currentZoom(s, Qt.size(n.width, n.height))
if(nZoom > root.maxZoomScale) {
nZoom = root.maxZoomScale
n = root.getZoomRect(nZoom)
}
else if(nZoom < root.minZoomScale) {
nZoom = root.minZoomScale
n = root.getZoomRect(nZoom)
}
// Limit panning
if((n.width/n.height) < (s.width/s.height)) {
// Crop window narrower than source
if(n.x < 0)
n.x = 0
const upBoundY = n.height > s.height ? s.height - n.height : 0
if(n.y < upBoundY)
n.y = upBoundY
if((n.x + n.width) > s.width)
n.x = s.width - n.width
const loBoundY = n.height > s.height ? 0 : s.height - n.height
if(n.y > loBoundY)
n.y = loBoundY
}
else {
// Crop window wider than source
const leftBoundX = n.width > s.width ? s.width - n.width : 0
if(n.x < leftBoundX)
n.x = leftBoundX
if(n.y < 0)
n.y = 0
const rightBoundY = n.width > s.width ? 0 : s.width - n.width
if(n.x > rightBoundY)
n.x = rightBoundY
if((n.y + n.height) > s.height)
n.y = s.height - n.height
}
d.cropRect = n
}
function getZoomRect(scale /*real*/) {
const oldCenter = root.rectCenter(root.cropRect)
const inflatedRect = root.inflateRectBy(root.minimumCropRect(), 1/scale)
return root.recenterRect(inflatedRect, oldCenter);
}
/*!
\qmlmethod StatusImageCrop::inflateRectBy(rect target, real scale)
Inflates the curren \a target rectangle with the \a scale while keeping the center fixed
*/
function inflateRectBy(target /*rect*/, scale /*real*/) {
const inflatedWidth = target.width * scale
const inflatedHeight = target.height * scale
return Qt.rect(target.x - (inflatedWidth - target.width)/2, target.y - (inflatedHeight - target.height)/2, inflatedWidth, inflatedHeight)
}
/*!
\qmlmethod StatusImageCrop::rectCenter(rect target)
Returns the center point of the \a target rectangle as a Qt.point
*/
function rectCenter(target /*rect*/) /*Qt.point*/ {
return Qt.point(target.x + target.width/2, target.y + target.height/2)
}
/*!
\qmlmethod StatusImageCrop::recenterRect(rect target)
Move the \a target rectangle's center to a /a newCenter
*/
function recenterRect(target /*rect*/, newCenter/*point*/) {
return Qt.rect(newCenter.x - target.width/2 , newCenter.y - target.height/2, target.width, target.height)
}
//> cropRect for minimum zoom: 1.0
function minimumCropRect() {
const sourceAR = root.sourceSize.width/root.sourceSize.height
const widthBound = sourceAR > root.aspectRatio
const minCropSize = widthBound ? Qt.size(root.sourceSize.width, root.sourceSize.width/root.aspectRatio)
: Qt.size(root.sourceSize.height * root.aspectRatio, root.sourceSize.height)
let res = Qt.rect(widthBound ? 0 : -(root.sourceSize.width - minCropSize.width)/2, // x
widthBound ? -(root.sourceSize.height - minCropSize.height)/2 : 0, // y
minCropSize.width, minCropSize.height)
return res
}
QtObject {
id: d
property rect cropRect: fillContentInSquaredWindow(root.sourceSize)
onCropRectChanged: windowRect.requestPaint()
readonly property real zoomToFill: {
const rectangle = fillContentInSquaredWindow(root.sourceSize)
return d.currentZoom(root.sourceSize, Qt.size(rectangle.width, rectangle.height))
}
property rect cropWindow
// Probably called from render thread, run async
signal updateCropWindow(rect newRect)
onUpdateCropWindow: cropWindow = newRect
function fillContentInSquaredWindow(c /*size*/) {
if(c.width > c.height) {
const border = (c.width - c.height)/2
return Qt.rect(border, 0, c.height, c.height)
}
else {
const border = (c.height - c.width)/2
return Qt.rect(0, border, c.width, c.width)
}
}
//> 1.0 is the content represented by w fully inscribed in c
function currentZoom(c /*size*/, w /*size*/) {
const wScale = c.width/w.width
const hScale = c.height/w.height
return Math.max(wScale, hScale)
}
}
onWindowStyleChanged: windowRect.requestPaint()
onSourceSizeChanged: d.cropRect = d.fillContentInSquaredWindow(sourceSize)
Canvas {
id: windowRect
anchors.fill: parent
layer.enabled: true
property bool widthFit: (root.width / root.aspectRatio) <= root.height
// Window width
property real wW: widthFit ? root.width : root.height * root.aspectRatio
// Window height
property real wH: widthFit ? root.width / root.aspectRatio : root.height
onPaint: {
var ctx = getContext("2d")
ctx.save()
// TODO: observed drawing artefacts at the edge. Draw one pixel more for now until root cause is found
ctx.clearRect(0, 0, width + 1, height + 1)
// Fill all with wallColor in order to clip the window from it
ctx.fillStyle = Qt.rgba(root.wallColor.r, root.wallColor.g, root.wallColor.b, root.wallTransparency)
ctx.fillRect(0, 0, width + 1, height + 1)
// Cut opaque new pixels from background
ctx.globalCompositeOperation = "source-out"
// Draw the window
ctx.beginPath()
const cW = Qt.rect((width - wW)/2, (height - wH)/2, wW, wH)
if(root.windowStyle === StatusImageCrop.WindowStyle.Rounded)
ctx.ellipse(cW.x, cW.y, cW.width, cW.height)
else if(root.windowStyle === StatusImageCrop.WindowStyle.Rectangular)
ctx.roundedRect(cW.x, cW.y, cW.width, cW.height, root.radius, root.radius)
ctx.fill()
ctx.restore()
d.updateCropWindow(cW)
}
}
Image {
id: mainImage
fillMode: Image.PreserveAspectFit
z: windowRect.z - 1
// Transform to keep the center of the image window in x and y coordinates
transform: [
Translate {
readonly property real s: mainImage.scale
x: -(d.cropRect.x + d.cropRect.width/2)
y: -(d.cropRect.y + d.cropRect.height/2)
},
Scale {
xScale: root.scrToImgScale
yScale: root.scrToImgScale
}
]
// Align window center to window rect
x: windowRect.x + windowRect.width/2
y: windowRect.y + windowRect.height/2
}
}
+18 -215
View File
@@ -6,231 +6,56 @@ import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
/*!
\qmltype StatusInput
\inherits Item
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief The StatusInput control provides a generic user text input
Example of how to use it:
\qml
StatusInput {
label: "Label"
charLimit: 30
errorMessage: qsTr("Input doesn't match validator")
input.clearable: true
input.placeholderText: qsTr("Placeholder text")
}
\endqml
For a list of components available see StatusQ.
*/
Item {
id: root
implicitWidth: 480
implicitHeight: (label.visible ?
label.anchors.topMargin +
label.height :
charLimitLabel.visible ?
charLimitLabel.anchors.topMargin +
charLimitLabel.height :
0) +
statusBaseInput.anchors.topMargin +
statusBaseInput.height +
(errorMessage.visible ?
errorMessage.anchors.topMargin +
errorMessage.height :
0) + 8
height: (label.visible ?
label.anchors.topMargin +
label.height :
charLimitLabel.visible ?
charLimitLabel.anchors.topMargin +
charLimitLabel.height :
0) +
statusBaseInput.anchors.topMargin +
statusBaseInput.height +
(errorMessage.visible ?
errorMessage.anchors.topMargin +
errorMessage.height :
0) + 8
height: implicitHeight
width: implicitWidth
/*!
\qmlproperty alias StatusInput::input
This property holds a reference to the TextEdit component.
*/
property alias input: statusBaseInput
/*!
\qmlproperty string StatusInput::letterIconName
This property holds a reference to the StatusBaseInput's letterIconName property.
*/
property alias letterIconName: statusBaseInput.letterIconName
/*!
\qmlproperty alias StatusInput::valid
This property holds a reference to the TextEdit's valid property.
*/
property alias valid: statusBaseInput.valid
/*!
\qmlproperty alias StatusInput::pending
This property holds a reference to the TextEdit's pending property.
*/
property alias pending: statusBaseInput.pending
/*!
\qmlproperty alias StatusInput::text
This property holds a reference to the TextEdit's text property.
*/
property alias text: statusBaseInput.text
/*!
\qmlproperty errorMessageCmp
This property represents the errorMessage shown on statusInput in cases one of the validators fails
This can be used to control the error message's look and position from the outside.
Examples of usage
\qml
StatusInput {
errorMessageCmp.font.pixelSize: 15
errorMessageCmp.font.weight: Font.Medium
}
\endqml
*/
property alias errorMessageCmp: errorMessage
/*!
\qmlproperty string StatusInput::label
This property sets the label text.
*/
property string label: ""
/*!
\qmlproperty string StatusInput::secondaryLabel
This property sets the secondary label text.
*/
property string secondaryLabel: ""
/*!
\qmlproperty int StatusInput::charLimit
This property sets the character limit of the text input.
*/
property int charLimit: 0
/*!
\qmlproperty string StatusInput::errorMessage
This property sets the error message text.
*/
property string errorMessage: ""
/*!
\qmlproperty real StatusBaseInput::leftPadding
This property sets the leftComponentLoader's left padding.
*/
property real leftPadding: 16
/*!
\qmlproperty real StatusBaseInput::rightPadding
This property sets the right padding.
*/
property real rightPadding: 16
/*!
\qmlproperty list StatusBaseInput::validators
This property sets the list of validators to be considered.
*/
property list<StatusValidator> validators
/*!
\qmlproperty list StatusBaseInput::validators
This property sets the list of async validators to be considered.
*/
property list<StatusAsyncValidator> asyncValidators
/*!
\qmlproperty int StatusInput::validationMode
This property describes the validation mode
Note that this property should be always strictly compared with one of ValidationMode's value
Default value: StatusInput.ValidationMode.OnlyWhenDirty
Examples of usage
> To ignore invalid input
\qml
StatusInput {
validationMode: StatusInput.ValidationMode.IgnoreInvalidInput
validators: [
StatusMinLengthValidator {
minLength: 15
}
]
}
\endqml
*/
property int validationMode: StatusInput.ValidationMode.OnlyWhenDirty
/*!
\qmlproperty string StatusBaseInput::validatedValue
This property sets the validated value text.
*/
property string validatedValue
/*!
\qmlproperty var StatusBaseInput::pendingValidators
This property sets the pending validators to be considered.
*/
property var pendingValidators: []
/*!
\qmlsignal
This signal is emitted when the icon is clicked.
*/
signal iconClicked()
/*!
\qmlsignal
This signal is emitted when a hard key is pressed passing as parameter the keyboard event.
*/
signal keyPressed(var event)
/*!
\qmlsignal
This signal is emitted when the text edit is clicked.
*/
signal editClicked()
/*!
\qmltype ValidationMode
\brief Available validation modes supported by StatusInput
Values:
- OnlyWhenDirty validates input only after it has become dirty
- Always validates input even before it has become dirty
- IgnoreInvalidInput ignore the new content if it doesn't match validators
Has no effect without at least a validator
\note that it represents a QML enumeration and can't be used as a property type, we use int instead
\see validationMode for a usage example
\see validators
*/
enum ValidationMode {
OnlyWhenDirty, // validates input only after it has become dirty
Always, // validates input even before it has become dirty
IgnoreInvalidInput // ignore the new content if it doesn't match
Always // validates input even before it has become dirty
}
/*!
\qmlproperty var StatusBaseInput::errors
This property holds the validation errors.
*/
property var errors: ({})
/*!
\qmlproperty var StatusBaseInput::errors
This property holds the validation async errors.
*/
property var asyncErrors: ({})
/*!
\qmlmethod
This function resets the text input validation and text.
*/
function reset() {
statusBaseInput.valid = true
statusBaseInput.valid = false
statusBaseInput.pristine = true
statusBaseInput.text = ""
root.errorMessage = ""
}
property string _previousText: text
/*!
\qmlmethod
This function validates the text input's text.
*/
function validate() {
if (!statusBaseInput.dirty && validationMode === StatusInput.ValidationMode.OnlyWhenDirty) {
return
}
@@ -260,23 +85,11 @@ Item {
if (errors){
let errs = Object.values(errors)
if (errs && errs[0]) {
if(validationMode === StatusInput.ValidationMode.IgnoreInvalidInput
&& text.length > _previousText.length) {
// Undo the last input
const cursor = statusBaseInput.cursorPosition;
statusBaseInput.text = _previousText;
if (statusBaseInput.cursor > statusBaseInput.text.length) {
statusBaseInput.cursorPosition = statusBaseInput.text.length;
} else {
statusBaseInput.cursorPosition = cursor-1;
}
}
errorMessage.text = errs[0].errorMessage || root.errorMessage;
} else {
errorMessage.text = ""
}
}
_previousText = text
}
if (asyncValidators.length && !Object.values(errors).length) {
@@ -293,10 +106,7 @@ Item {
root.validatedValue = root.text
}
}
/*!
\qmlmethod
This function updates the text input async validation.
*/
function updateAsyncValidity(validatorName, value, result) {
if (!asyncErrors) {
asyncErrors = {}
@@ -379,13 +189,6 @@ Item {
onTextChanged: root.validate()
Keys.forwardTo: [root]
onIconClicked: root.iconClicked()
onKeyPressed: {
root.keyPressed(event);
}
onEditChanged: {
root.editClicked();
}
}
StatusBaseText {
-189
View File
@@ -1,189 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 2.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
/*!
\qmltype StatusItemPicker
\inherits Item
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief It presents a selectable item to the user. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-rectangle.html}{Rectangle}.
The \c StatusItemPicker is populated with the given properties data.
StatusItemPicker can be made as a RadioButton or CheckBox selectable item.
Example of how the component looks like:
\image status_item_picker.png
Example of how to use it:
\qml
StatusItemPicker {
width: 300
height: 40
imageSource: model.imageSource
name: model.name
shortName: model.shortName
selectorType: root.multiSelection ? StatusItemPicker.SelectorType.CheckBox : StatusItemPicker.SelectorType.RadioButton
selected: model.selected
radioGroup: content.radioGroup
onCheckedChanged: { // Some updates }
}
\endqml
For a list of components available see StatusQ.
*/
Rectangle {
id: root
/*!
\qmlproperty string StatusItemPicker::image
This property holds the image settings information.
*/
property StatusImageSettings image
/*!
\qmlproperty string StatusItemPicker::name
This property holds the main text or name to be displayed.
*/
property string name
/*!
\qmlproperty string StatusItemPicker::shortName
This property holds the secondary text or short name to be displayed.
*/
property string shortName
/*!
\qmlproperty string StatusItemPicker::selectorType
This property holds the selector type. Possible options are:
\qml
enum SelectorType {
RadioButton,
CheckBox
}
\endqml
By default, RadioButton.
*/
property int selectorType: StatusItemPicker.SelectorType.RadioButton
/*!
\qmlproperty string StatusItemPicker::selected
This property holds if the item is selected.
*/
property bool selected
/*!
\qmlproperty string StatusItemPicker::radioGroup
This property holds the button group object the radiobutton belongs to.
*/
property ButtonGroup radioGroup
/*!
\qmlsignal StatusItemPicker::checkedChanged(bool checked)
This signal is emitted when the item is selected by pressing the radiobutton or the checkbox.
*/
signal checkedChanged(bool checked)
enum SelectorType {
RadioButton,
CheckBox
}
QtObject {
id: d
property int minShortNameWidth: 50
function availableTextWidth() {
return root.width - imageItem.width - row.spacing - shortNameItem.anchors.rightMargin - selector.width - selector.anchors.rightMargin - 24/*Margin between both texts*/
}
}
Row {
id: row
anchors.left: parent.left
anchors.leftMargin: 18
anchors.verticalCenter: parent.verticalCenter
spacing: 8
StatusIcon {
id: imageItem
anchors.verticalCenter: parent.verticalCenter
source: root.image.source ? root.image.source : ""
width: root.image.width
height: root.image.height
visible: root.image.source !== undefined
}
StatusBaseText {
id: nameItem
anchors.verticalCenter: parent.verticalCenter
width: dummyNameItem.width > d.availableTextWidth() - d.minShortNameWidth ?
d.availableTextWidth() - d.minShortNameWidth :
dummyNameItem.width
text: root.name
color: Theme.palette.directColor1
font.pixelSize: 15
clip: true
elide: Text.ElideRight
}
// Dummy object just to exactly know the width needed by `name` and dynamically set a limit for nameItem and shortNameItem components
StatusBaseText {
id: dummyNameItem
visible: false
text: root.name
font.pixelSize: 15
}
}
StatusBaseText {
id: shortNameItem
width: d.availableTextWidth() - nameItem.width < d.minShortNameWidth ?
d.minShortNameWidth :
d.availableTextWidth() - nameItem.width
anchors.right: selector.left
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
text: root.shortName
color: Theme.palette.baseColor1
font.pixelSize: 15
clip: true
elide: Text.ElideRight
horizontalAlignment: Text.AlignRight
}
// 2 different options: Or with radio buttons or with checkboxes:
Loader {
id: selector
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 18
sourceComponent: root.selectorType === StatusItemPicker.SelectorType.RadioButton ? radioBtn : checkbox
}
Component {
id: radioBtn
StatusRadioButton {
ButtonGroup.group: root.radioGroup
checked: root.selected
onCheckedChanged: {
root.selected = checked
root.checkedChanged(checked)
}
}
}
Component {
id: checkbox
StatusCheckBox {
checked: root.selected
onCheckedChanged: {
root.selected = checked
root.checkedChanged(checked)
}
}
}
}// End of Content item
@@ -4,110 +4,27 @@ import QtQuick.Controls 2.14
import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1
/*!
\qmltype StatusPasswordStrengthIndicator
\inherits StatusProgressBar
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief It allows visualizing the password strength. Inherits from StatusProgressBar.
The \c StatusPasswordStrengthIndicator changes its text and color depending on the selected StatusPasswordStrengthIndicator::strength.
Since it inherits from \c StatusProgressBar, `value` property will change the length of the bar accordingly.
When `value` property is 0, StatusPasswordStrengthIndicator::strength is automatically set to StatusPasswordStrengthIndicator.Strength.None and no text is displayed.
Example of how the control looks like:
\image status_password_strength_indicator.png
Example of how to use it:
\qml
StatusPasswordStrengthIndicator {
id: weakPw
strength: StatusPasswordStrengthIndicator.Strength.Weak
value: 0.5
}
StatusPasswordStrengthIndicator {
id: sosoPw
strength: StatusPasswordStrengthIndicator.Strength.SoSo
value: 0.25
}
\endqml
For a list of components available see StatusQ.
*/
StatusProgressBar {
id: control
/*!
\qmlproperty string StatusPasswordStrengthIndicator::strength
This property holds the password strength value. Possible values are:
\list
\li StatusPasswordStrengthIndicator.Strength.None
\li StatusPasswordStrengthIndicator.Strength.VeryWeak
\li StatusPasswordStrengthIndicator.Strength.Weak
\li StatusPasswordStrengthIndicator.Strength.SoSo
\li StatusPasswordStrengthIndicator.Strength.Good
\li StatusPasswordStrengthIndicator.Strength.Great
\endlist
*/
property var strength
/*!
\qmlproperty string StatusPasswordStrengthIndicator::labelVeryWeak
This property holds the text shown when the strength is StatusPasswordStrengthIndicator.Strength.VeryWeak.
Default value: "Very weak"
*/
property string labelVeryWeak: qsTr("Very weak")
/*!
\qmlproperty string StatusPasswordStrengthIndicator::labelWeak
This property holds the text shown when the strength is StatusPasswordStrengthIndicator.Strength.Weak.
Default value: "Weak"
*/
property string labelWeak: qsTr("Weak")
/*!
\qmlproperty string StatusPasswordStrengthIndicator::labelSoso
This property holds the text shown when the strength is StatusPasswordStrengthIndicator.Strength.SoSo.
Default value: "So-so"
*/
property string labelSoso: qsTr("So-so")
/*!
\qmlproperty string StatusPasswordStrengthIndicator::labelGood
This property holds the text shown when the strength is StatusPasswordStrengthIndicator.Strength.Good.
Default value: "Good"
*/
property string labelGood: qsTr("Good")
/*!
\qmlproperty string StatusPasswordStrengthIndicator::labelGreat
This property holds the text shown when the strength is StatusPasswordStrengthIndicator.Strength.Great.
Default value: "Great"
*/
property string labelGreat: qsTr("Great")
property string labelVeryWeak: "Very weak"
property string labelWeak: "Weak"
property string labelSoso: "So-so"
property string labelGood: "Good"
property string labelGreat: "Great"
enum Strength {
None, // 0
VeryWeak, // 1
Weak, // 2
SoSo, // 3
Good, // 4
Great // 5
VeryWeak, // 0
Weak, // 1
SoSo, // 2
Good, // 3
Great // 4
}
onValueChanged: if(value === 0) control.strength = StatusPasswordStrengthIndicator.Strength.None
// Behavior:
states: [
// Strength states definition:
State {
when: control.strength === StatusPasswordStrengthIndicator.Strength.None
PropertyChanges { target: control; text: ""}
},
State {
when: control.strength === StatusPasswordStrengthIndicator.Strength.VeryWeak
PropertyChanges { target: control; fillColor : Theme.palette.dangerColor1}
+19 -50
View File
@@ -11,14 +11,7 @@ Button {
property color bgColor: Theme.palette.baseColor2
property color contentColor: Theme.palette.baseColor1
property var type: StatusPickerButton.Type.Next
property int lateralMargins: 16
property int textPixelSize: 15
enum Type {
Next,
Down
}
signal clicked()
background: Item {
anchors.fill: parent
@@ -27,57 +20,31 @@ Button {
anchors.fill: parent
radius: 8
color: root.bgColor
}
}
StatusIcon {
id: nextIcon
anchors.right: parent.right
anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter
icon: "next"
color: !Qt.colorEqual(root.contentColor, Theme.palette.baseColor1)
? root.contentColor : Theme.palette.directColor1
}
}
contentItem: Item {
anchors.fill: parent
state: root.type === StatusPickerButton.Type.Next ? "NEXT" : "DOWN"
StatusBaseText {
id: textLabel
anchors.verticalCenter: parent.verticalCenter
width: parent.width - icon.width - anchors.rightMargin - anchors.leftMargin - icon.anchors.rightMargin - icon.anchors.leftMargin
anchors {
fill: parent
leftMargin: 16
}
verticalAlignment: Text.AlignVCenter
font.pixelSize: root.textPixelSize
font.pixelSize: 15
color: root.contentColor
text: root.text
clip: true
elide: Text.ElideRight
}
StatusIcon {
id: icon
anchors.verticalCenter: parent.verticalCenter
color: !Qt.colorEqual(root.contentColor, Theme.palette.baseColor1) ? root.contentColor : Theme.palette.directColor1
}
states: [
State {
name: "NEXT"
PropertyChanges {target: icon; icon: "next"}
PropertyChanges {target: icon; anchors.left: undefined }
PropertyChanges {target: icon; anchors.right: parent.right }
PropertyChanges {target: icon; anchors.rightMargin: root.lateralMargins / 2 }
PropertyChanges {target: icon; anchors.leftMargin: root.lateralMargins / 2 }
PropertyChanges {target: textLabel; anchors.left: parent.left }
PropertyChanges {target: textLabel; anchors.right: undefined }
PropertyChanges {target: textLabel; anchors.rightMargin: undefined }
PropertyChanges {target: textLabel; anchors.leftMargin: root.lateralMargins }
},
State {
name: "DOWN"
PropertyChanges {target: icon; icon: "chevron-down"}
PropertyChanges {target: icon; anchors.left: parent.left }
PropertyChanges {target: icon; anchors.right: undefined }
PropertyChanges {target: icon; anchors.rightMargin: root.lateralMargins / 2 }
PropertyChanges {target: icon; anchors.leftMargin: root.lateralMargins }
PropertyChanges {target: textLabel; anchors.left: icon.right }
PropertyChanges {target: textLabel; anchors.right: undefined }
PropertyChanges {target: textLabel; anchors.rightMargin: root.lateralMargins / 2 }
PropertyChanges {target: textLabel; anchors.leftMargin: undefined }
}
]
}
MouseArea {
@@ -85,6 +52,8 @@ Button {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: { root.clicked() }
onClicked: {
root.clicked();
}
}
}
-234
View File
@@ -1,234 +0,0 @@
import QtQuick 2.0
import StatusQ.Core.Theme 0.1
import StatusQ.Controls.Validators 0.1
/*!
\qmltype StatusPinInput
\inherits Item
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief It allows entering an N pin length.
The \c StatusPinInput displays N visual circles corresponging with the pin length to introduce.
It runs a blinking animation when the control is focused and ready to introduce the pin, as well as, an hovering feedback when user is in the MouseArea where is able to click into the control.
This pin input control allows introducing validators.
Example of how the control looks like:
\image status_pin_input.png
Example of how to use it:
\qml
StatusPinInput {
id: regexPinInput
validator: StatusRegularExpressionValidator { regularExpression: /[0-9A-Za-z@]+/ }
circleDiameter: 22
circleSpacing: 22
pinLen: 7
}
\endqml
For a list of components available see StatusQ.
*/
Item {
id: root
/*!
\qmlproperty string StatusPinInput::pinInput
This property holds the introduced user pin.
*/
property alias pinInput: inputText.text
/*!
\qmlproperty Validator StatusPinInput::validator
This property allows you to set a validator on the StatusPinInput. When a validator is set the StatusPinInput will only accept
input which leaves the pinInput property in an acceptable state.
Currently supported validators are qml StatusIntValidator and StatusRegularExpressionValidator.
An example of using validators is shown below, which allows input of integers between 0 and 999999 into the pin input
\qml
StatusPinInput {
id: numbersPinInput
validator: StatusIntValidator{bottom: 0; top: 999999;}
}
\endqml
*/
property alias validator: d.statusValidator
/*!
\qmlproperty int StatusPinInput::pinLen
This property allows you to set a specific pin input length. The default value is 6.
*/
property int pinLen: 6
/*!
\qmlproperty int StatusPinInput::circleSpacing
This property allows you to customize spacing between pin circles. The default value is 16 pixels.
*/
property int circleSpacing: 16
/*!
\qmlproperty int StatusPinInput::circleDiameter
This property allows you to customize pin circle diameter. The default value is 16 pixels.
*/
property int circleDiameter: 16
QtObject {
id: d
property int currentPinIndex: 0
property StatusValidator statusValidator
function activateBlink () {
const currItem = repeater.itemAt(d.currentPinIndex)
if(currItem) {
if((currItem.innerState === "NEXT") && (!currItem.blinkingAnimation.running)) {
currItem.blinkingAnimation.start()
}
}
}
function deactivateBlink () {
const currItem = repeater.itemAt(d.currentPinIndex)
if(currItem) {
if((currItem.innerState === "NEXT") && (currItem.blinkingAnimation.running)) {
currItem.blinkingAnimation.stop()
// To ensure that the opacity does not remain in an intermediate state when forcing the animation to stop
currItem.innerOpacity = 1
}
}
}
}
/*
\qmlmethod StatusPinInput::statesInitialization()
Initializes pin input bringing it to its initial state.
It is directly called in Component.onCompleted slot and can be called whenever you need resetting it.
*/
function statesInitialization() {
d.currentPinIndex = 0
repeater.itemAt(d.currentPinIndex).innerState = "NEXT"
for (var i = 1; i < root.pinLen; i++) {
repeater.itemAt(i).innerState = "EMPTY"
}
}
width: (root.circleDiameter + root.circleSpacing) * root.pinLen
height: root.circleDiameter
Component.onCompleted: { statesInitialization() }
// Pin input data management object:
TextInput {
id: inputText
visible: false
focus: true
maximumLength: root.pinLen
validator: d.statusValidator.validatorObj
onTextChanged: {
// Modify state of current introduced character position:
if(text.length >= (d.currentPinIndex + 1)) {
repeater.itemAt(d.currentPinIndex).innerState = "FILLED"
// Update next:
d.currentPinIndex++
if(d.currentPinIndex < root.pinLen)
repeater.itemAt(d.currentPinIndex).innerState = "NEXT"
}
// Modify state of current removed character position:
else if (text.length <= (d.currentPinIndex + 1)) {
if(d.currentPinIndex < root.pinLen)
repeater.itemAt(d.currentPinIndex).innerState = "EMPTY"
d.currentPinIndex--
repeater.itemAt(d.currentPinIndex).innerState = "NEXT"
}
// Some component validations:
if(text.length !== d.currentPinIndex)
console.error("StatusPinInput input management error. Current pin length must be "+ text.length + "and is " + d.currentPinIndex)
}
onFocusChanged: { if(!focus) { d.deactivateBlink () } }
}
// Pin input visual objects:
Row {
spacing: root.circleSpacing
Repeater {
id: repeater
model: root.pinLen
Rectangle {
id: background
property string innerState: "EMPTY"
property alias blinkingAnimation: blinkingAnimation
property alias innerOpacity: inner.opacity
width: root.circleDiameter
height: width
color: Theme.palette.primaryColor2
radius: 0.5 * width
Rectangle {
id: inner
state: background.innerState
anchors.centerIn: parent
height: width
color: Theme.palette.primaryColor1
radius: 0.5 * width
states: [
State {
name: "NEXT"
StateChangeScript { script: { if(inputText.focus) blinkingAnimation.start() } }
PropertyChanges {target: inner; width: root.circleDiameter / 2}
},
State {
name: "FILLED"
StateChangeScript { script: if(blinkingAnimation.running) blinkingAnimation.stop() }
PropertyChanges {target: inner; width: root.circleDiameter}
PropertyChanges {target: inner; opacity: 1}
},
State {
name: "EMPTY"
StateChangeScript { script: if(blinkingAnimation.running) blinkingAnimation.stop() }
PropertyChanges {target: inner; width: 0}
PropertyChanges {target: inner; opacity: 1}
}
]
// Animation on transitions
Behavior on width { NumberAnimation { duration: 200 } }
// Animation on "cursor" blinking
SequentialAnimation {
id: blinkingAnimation
loops: Animation.Infinite
NumberAnimation { target: inner; property: "opacity"; to: 0; duration: 800 }
NumberAnimation { target: inner; property: "opacity"; to: 1; duration: 800 }
}
}
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
// MouseArea behavior:
onClicked: {
inputText.forceActiveFocus()
d.activateBlink ()
}
onContainsMouseChanged: { if(containsMouse) { cursorShape = Qt.PointingHandCursor } }
}
}
@@ -34,24 +34,10 @@ ProgressBar {
radius: 5
StatusBaseText {
id: textItem
anchors.centerIn: parent
property bool _fittedInBar: width < bar.width ? true : false
text: control.text
font.pixelSize: 12
color: Theme.palette.indirectColor1
Component.onCompleted: opacity = width < bar.width ? 1 : 0
on_FittedInBarChanged: {
if (_fittedInBar && opacity == 0) {
fadeIn.start();
}
else if (!_fittedInBar) {
fadeIn.stop();
opacity = 0;
}
}
OpacityAnimator { id: fadeIn; target: textItem; from: 0; to: 1; duration: 250 }
}
}
}
@@ -1,257 +0,0 @@
import QtQuick 2.12
import QtGraphicalEffects 1.13
import QtQuick.Controls 2.12
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusSeedPhraseInput
\inherits Item
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief Displays a text input with suggestions. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-item.html}{Item}.
The \c StatusSeedPhraseInput item displays a text input with suggestions filtered based on the typed text.
For example:
\qml
StatusSeedPhraseInput {
id: statusSeedInput
anchors.left: parent.left
anchors.right: parent.right
textEdit.input.anchors.leftMargin: 16
textEdit.input.anchors.rightMargin: 16
textEdit.input.anchors.topMargin: 11
leftComponentText: "1"
inputList: ListModel {
ListElement {
seedWord: "panda"
}
ListElement {
seedWord: "posible"
}
ListElement {
seedWord: "wing"
}
}
}
\endqml
\image status_seed_phrase_input.png
For a list of components available see StatusQ.
*/
Item {
id: root
width: 162
height: 44
/*!
\qmlproperty alias StatusSeedPhraseInput::textEdit
This property is an alias to the StatusInput's textEdit property.
*/
property alias textEdit: seedWordInput
/*!
\qmlproperty alias StatusSeedPhraseInput::text
This property is an alias to the StatusInput's text property.
*/
property alias text: seedWordInput.text
/*!
\qmlproperty string StatusSeedPhraseInput::leftComponentText
This property sets the StatusInput's left component's text.
*/
property string leftComponentText: ""
/*!
\qmlproperty ListModel StatusSeedPhraseInput::inputList
This property holds the filtered words list based on the user's
input text.
*/
property ListModel inputList: ListModel { }
/*!
\qmlproperty ListModel StatusSeedPhraseInput::filteredList
This signal is emitted when the user selects a word from
the suggestions list, either by clicking on it or by completing
typing 4 charactersnd passes as a parameter the selected word.
The corresponding handler is \c onDoneInsertingWord
*/
property ListModel filteredList: ListModel { }
/*!
\qmlsignal doneInsertingWord
This signal is emitted when the user selects a word from the suggestions list
either by mouse click or by typing 4 characters that match and passes as a parameter
the selected word. The corresponding handler is \c onDoneInsertingWord.
*/
signal doneInsertingWord(string word)
/*!
\qmlsignal keyPressed
This signal is emitted when the user presses a keyboard key and passes as a
parameter the event. The corresponding handler is \c onKeyPressed.
*/
signal keyPressed(var event)
/*!
\qmlsignal editClicked
This signal is emitted when the user clicks inside the StatusInput.
The corresponding handler is \c onEditClicked
*/
signal editClicked()
function setWord(word) {
seedWordInput.ignoreTextChange = true
seedWordInput.input.edit.text = word
}
onActiveFocusChanged: {
if (root.activeFocus) {
seedWordInput.input.edit.forceActiveFocus();
}
}
StatusInput {
property bool ignoreTextChange: false
id: seedWordInput
implicitWidth: parent.width
implicitHeight: parent.height
input.anchors.topMargin: 0
input.anchors.leftMargin: 0
input.anchors.rightMargin: 0
input.anchors.bottom: input.parent.bottom
input.leftComponent: StatusBaseText {
text: root.leftComponentText
color: seedWordInput.input.edit.activeFocus ?
Theme.palette.primaryColor1 : Theme.palette.baseColor1
font.pixelSize: 15
}
input.acceptReturn: true
onTextChanged: {
if (ignoreTextChange) {
ignoreTextChange = false
return
}
filteredList.clear();
let textToCheck = text.trim()
if (textToCheck !== "") {
for (var i = 0; i < inputList.count; i++) {
if (inputList.get(i).seedWord.startsWith(textToCheck)) {
filteredList.insert(filteredList.count, {"seedWord": inputList.get(i).seedWord});
}
}
seedSuggestionsList.model = filteredList;
if (filteredList.count === 1 && input.edit.keyEvent !== Qt.Key_Backspace
&& input.edit.keyEvent !== Qt.Key_Delete
&& filteredList.get(0).seedWord.trim() === textToCheck) {
seedWordInput.input.edit.cursorPosition = textToCheck.length;
seedSuggestionsList.model = 0;
root.doneInsertingWord(textToCheck);
}
} else {
seedSuggestionsList.model = 0;
}
}
onKeyPressed: {
if (input.edit.keyEvent === Qt.Key_Tab || input.edit.keyEvent === Qt.Key_Return || input.edit.keyEvent === Qt.Key_Enter) {
if (!!text && seedSuggestionsList.count > 0) {
seedSuggestionsList.completeWordFill(filteredList.get(seedSuggestionsList.currentIndex).seedWord)
event.accepted = true
return
}
}
if (input.edit.keyEvent === Qt.Key_Down) {
seedSuggestionsList.incrementCurrentIndex()
}
if (input.edit.keyEvent === Qt.Key_Up) {
seedSuggestionsList.decrementCurrentIndex()
}
root.keyPressed(event);
}
onEditClicked: {
root.editClicked();
}
}
Item {
id: suggListContainer
width: seedSuggestionsList.width
height: (((seedSuggestionsList.count <= 5) ? seedSuggestionsList.count : 5) *34) + 16
anchors.left: parent.left
anchors.leftMargin: 16
anchors.top: seedWordInput.bottom
anchors.topMargin: 4
visible: ((seedSuggestionsList.count > 0) && seedWordInput.input.edit.activeFocus)
Rectangle {
id: statusPopupMenuBackgroundContent
anchors.fill: parent
color: Theme.palette.statusPopupMenu.backgroundColor
radius: 8
layer.enabled: true
layer.effect: DropShadow {
anchors.fill: parent
source: statusPopupMenuBackgroundContent
horizontalOffset: 0
verticalOffset: 4
radius: 12
samples: 25
spread: 0.2
color: Theme.palette.dropShadow
}
}
ListView {
id: seedSuggestionsList
width: ((seedSuggestionsList.contentItem.childrenRect.width + 24) > root.width) ? root.width
: (seedSuggestionsList.contentItem.childrenRect.width + 24)
anchors.top: parent.top
anchors.topMargin: 8
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
onCountChanged: {
seedSuggestionsList.currentIndex = 0
}
function completeWordFill(seedWord) {
let seedWordTrimmed = seedWord.trim();
root.setWord(seedWordTrimmed);
seedWordInput.input.edit.cursorPosition = seedWordInput.text.length;
seedSuggestionsList.model = 0;
root.doneInsertingWord(seedWordTrimmed);
}
clip: true
ScrollBar.vertical: ScrollBar { }
delegate: Item {
id: txtDelegate
width: suggWord.contentWidth
height: 34
Rectangle {
width: seedSuggestionsList.width
height: parent.height
color: mouseArea.containsMouse || index === seedSuggestionsList.currentIndex ?
Theme.palette.primaryColor1 : "transparent"
}
StatusBaseText {
id: suggWord
anchors.left: parent.left
anchors.leftMargin: 14
anchors.verticalCenter: parent.verticalCenter
text: seedWord
color: mouseArea.containsMouse || index === seedSuggestionsList.currentIndex ? Theme.palette.indirectColor1 : Theme.palette.directColor1
font.pixelSize: 13
elide: Text.ElideRight
}
MouseArea {
id: mouseArea
width: seedSuggestionsList.width
height: parent.height
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
seedSuggestionsList.completeWordFill(seedWord)
}
}
}
}
}
}
@@ -23,7 +23,7 @@ Item {
property alias color: edit.color
property alias font: edit.font
property alias focussed: edit.activeFocus
property alias verticalAlignment: edit.verticalAlignment
property alias verticalAlignmet: edit.verticalAlignment
property alias horizontalAlignment: edit.horizontalAlignment
signal linkActivated(string link)
+16 -14
View File
@@ -6,35 +6,37 @@ import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
/*!
/note beware, the slider processes the mouse events only in its control space. So it must be at least as high
as the /c handle so user can grab it fully.
*/
Slider {
id: statusSlider
implicitWidth: 360
implicitHeight: Math.max(handle.implicitHeight, background.implicitHeight)
implicitHeight: 4
leftPadding: 0
background: Rectangle {
id: bgRect
x: statusSlider.leftPadding
y: statusSlider.topPadding + statusSlider.availableHeight / 2 - height / 2
implicitWidth: 100
implicitHeight: 4
width: statusSlider.availableWidth
height: implicitHeight
color: Theme.palette.primaryColor3
color: {
if (statusSlider.value === statusSlider.to) {
return Theme.palette.primaryColor1
}
return Theme.palette.primaryColor3
}
radius: 2
Rectangle {
width: statusSlider.visualPosition * parent.width
height: parent.height
color: Theme.palette.primaryColor1
radius: 2
anchors.fill: parent
visible: statusSlider.value > statusSlider.from && statusSlider.value < statusSlider.to
gradient: Gradient {
GradientStop { color: Theme.palette.primaryColor1 ; position: 0 }
GradientStop { color: Theme.palette.primaryColor3 ; position: (((statusSlider.value - statusSlider.from) * 100) / (statusSlider.to - statusSlider.from) / 100).toFixed(2) }
GradientStop { color: Theme.palette.primaryColor1 ; position: (((statusSlider.value - statusSlider.from) * 100) / (statusSlider.to - statusSlider.from) / 100).toFixed(2) }
GradientStop { color: Theme.palette.primaryColor3 ; position: 1 }
orientation: Gradient.Horizontal
}
}
} // background
+1 -1
View File
@@ -8,7 +8,7 @@ TabBar {
background: Rectangle {
implicitHeight: 36
color: Theme.palette.statusSwitchTab.barBackgroundColor
color: Theme.palette.directColor7
radius: 8
}
}
@@ -22,9 +22,7 @@ TabButton {
StatusBaseText {
id: label
text: statusSwitchTabButton.text
color: statusSwitchTabButton.checked ?
Theme.palette.statusSwitchTab.selectedTextColor :
Theme.palette.statusSwitchTab.textColor
color: Theme.palette.primaryColor1
font.weight: Font.Medium
font.pixelSize: 15
horizontalAlignment: Text.AlignHCenter
@@ -38,7 +36,7 @@ TabButton {
implicitHeight: 36
implicitWidth: 148
color: statusSwitchTabButton.checked ?
Theme.palette.statusSwitchTab.buttonBackgroundColor :
Theme.palette.statusSwitchTab.backgroundColor :
"transparent"
radius: 8
layer.enabled: true
-18
View File
@@ -1,18 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 2.13
/*!
\qmltype StatusTabButton
\inherits TabButton
\inqmlmodule StatusQ.Controls
\since StatusQ.Controls 0.1
\brief StatusTabBar provides a tab-based navigation model
It's customized from Qt's \l{https://doc.qt.io/qt-6/qml-qtquick-controls2-tabbar.html}{TabBar},
adding a transparent background.
*/
TabBar {
background: Item { }
}

Some files were not shown because too many files have changed in this diff Show More