Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bdc330c3f | ||
|
|
485647d1c3 | ||
|
|
e1db639340 | ||
|
|
b918897241 | ||
|
|
6ba2d8f3e6 | ||
|
|
c01f623305 | ||
|
|
6dc6ebb2fb | ||
|
|
2d031244ca | ||
|
|
9498be05ef | ||
|
|
1b25ddd2b5 | ||
|
|
9741f98b70 | ||
|
|
02923f7947 | ||
|
|
6c5957a492 | ||
|
|
eb96e5e08d | ||
|
|
457e5fe4af | ||
|
|
1c0d4b0133 | ||
|
|
ba79f62fda | ||
|
|
a9c79b0359 | ||
|
|
a668afef35 | ||
|
|
deb8b38b1f | ||
|
|
b4ebc520e1 | ||
|
|
5def2102c7 | ||
|
|
0f511ebc60 | ||
|
|
b09210cf1f | ||
|
|
3bdfc3e528 | ||
|
|
baf5948c90 | ||
|
|
4b1516c990 | ||
|
|
1a5990f24b | ||
|
|
82e1845056 | ||
|
|
75a76afcf1 | ||
|
|
0922cf507a | ||
|
|
32c5f7f8df | ||
|
|
148505ba6b | ||
|
|
d960c6dcec | ||
|
|
bb9f08155f | ||
|
|
934db52126 | ||
|
|
dcef08f3b3 | ||
|
|
20e7b345e1 | ||
|
|
5f8453659d | ||
|
|
c7ff011649 | ||
|
|
2b71ccf50f | ||
|
|
310be728a3 | ||
|
|
e2728285e1 | ||
|
|
1d9abf8e95 | ||
|
|
31e90c2d47 | ||
|
|
ae1e72fb23 | ||
|
|
7710470559 | ||
|
|
2069399979 | ||
|
|
29dd4da2e4 | ||
|
|
394040a039 | ||
|
|
a8e1ed863c | ||
|
|
d7b1ae9fbc | ||
|
|
f0c7d950ea | ||
|
|
1e5d389b53 | ||
|
|
1cda85fc0e | ||
|
|
4999368dfe | ||
|
|
7e4d69be05 | ||
|
|
3abbd3d4cd | ||
|
|
8fc5276090 | ||
|
|
cf4b092112 | ||
|
|
ddf4a8d4f1 | ||
|
|
9c1edc80db | ||
|
|
20bf5b07a6 | ||
|
|
ade6bd2d0d | ||
|
|
125760b934 | ||
|
|
e231c0a6cb | ||
|
|
a16bc6bb6e | ||
|
|
51989c0f5d | ||
|
|
88b7f6eab8 | ||
|
|
2df3c0a03e | ||
|
|
1e9cc07e2d | ||
|
|
6221c0a540 | ||
|
|
ddbffa7f28 | ||
|
|
555ad8bea8 | ||
|
|
a561a3fff8 | ||
|
|
70ec009edd | ||
|
|
31a1c60eb1 | ||
|
|
0a2cf6ab6e | ||
|
|
07fa2a1b0c | ||
|
|
43b8e43303 | ||
|
|
2cec51ff8d | ||
|
|
684f45cc08 | ||
|
|
eae4e0f405 | ||
|
|
b2ac2794bb | ||
|
|
b565a32c7e | ||
|
|
a1835164d9 | ||
|
|
06e931d749 | ||
|
|
b9aa49853a | ||
|
|
03eb6302d5 | ||
|
|
5f8f798cf2 | ||
|
|
bbc0177a66 | ||
|
|
6ce9f9bd86 | ||
|
|
46254f79c4 | ||
|
|
ad659a5dc2 | ||
|
|
41f67b59e9 | ||
|
|
aa91a88be7 | ||
|
|
f299c9df3a | ||
|
|
b6c3231e30 | ||
|
|
496ad05247 | ||
|
|
93f9214aad | ||
|
|
22bbf9b177 | ||
|
|
7301323ed7 | ||
|
|
c6e4cf627c | ||
|
|
5c1c9852ae | ||
|
|
be2b2c3df7 | ||
|
|
1362a94448 | ||
|
|
189f66225c | ||
|
|
40d8b9c2f6 | ||
|
|
8af7028efc | ||
|
|
f1eff6700a | ||
|
|
082bb8ef45 | ||
|
|
7609450068 | ||
|
|
6570275176 | ||
|
|
4b579cddbe | ||
|
|
a84026d74d | ||
|
|
428b165198 | ||
|
|
56b2663e4d | ||
|
|
9dbb7ec53f | ||
|
|
7e9c7a52fd | ||
|
|
2e33fb9f81 | ||
|
|
208825807b | ||
|
|
381150a7b5 | ||
|
|
5fac8774f4 | ||
|
|
e9f20be15a | ||
|
|
9d06aae1f4 |
@@ -0,0 +1,17 @@
|
||||
### 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)
|
||||
@@ -13,4 +13,7 @@ Makefile
|
||||
*_qml.cpp
|
||||
*_qmlcache.qrc
|
||||
sandbox/qmlcache_loader.cpp
|
||||
doc/html
|
||||
doc/html
|
||||
CMakeLists.txt.user
|
||||
.vscode
|
||||
build/
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
<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)
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
project(StatusQ)
|
||||
|
||||
add_subdirectory(tests)
|
||||
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 49 KiB |
@@ -22,6 +22,7 @@
|
||||
\li \l{StatusDescriptionListItem}
|
||||
\li \l{StatusLetterIdenticon}
|
||||
\li \l{StatusListItem}
|
||||
\li \l{StatusListPicker}
|
||||
\li \l{StatusListSectionHeadline}
|
||||
\li \l{StatusLoadingIndicator}
|
||||
\li \l{StatusMemberListItem}
|
||||
@@ -37,5 +38,6 @@
|
||||
\li \l{StatusMessageDetails}
|
||||
\li \l{StatusTagSelector}
|
||||
\li \l{StatusToastMessage}
|
||||
\li \l{StatusWizardStepper}
|
||||
\endlist
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
\li \l{StatusBaseInput}
|
||||
\li \l{StatusIdenticonRing}
|
||||
\li \l{StatusInput}
|
||||
\li \l{StatusItemPicker}
|
||||
\li \l{StatusPickerButton}
|
||||
\li \l{StatusPinInput}
|
||||
\li \l{StatusProgressBar}
|
||||
@@ -43,5 +44,6 @@
|
||||
\li \l{StatusWalletColorSelect}
|
||||
\li \l{StatusRegularExpressionValidator}
|
||||
\li \l{StatusIntValidator}
|
||||
\li \l{StatusSeedPhraseInput}
|
||||
\endlist
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,8 @@ Item {
|
||||
id: themeSwitchWrapper
|
||||
signal checkedChanged()
|
||||
|
||||
property alias lightThemeEnabled: switchControl.checked
|
||||
|
||||
width: themeSwitch.width
|
||||
height: themeSwitch.height
|
||||
|
||||
@@ -26,6 +28,7 @@ Item {
|
||||
}
|
||||
|
||||
StatusSwitch {
|
||||
id: switchControl
|
||||
onCheckedChanged: themeSwitchWrapper.checkedChanged()
|
||||
|
||||
StatusToolTip {
|
||||
|
||||
@@ -4,6 +4,7 @@ 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
|
||||
@@ -25,7 +26,7 @@ GridLayout {
|
||||
badge.value: 1
|
||||
}
|
||||
StatusNavigationListItem {
|
||||
title: "Menu Item (selected)"
|
||||
title: "Menu Item (selected) with very long text"
|
||||
selected: true
|
||||
icon.name: "info"
|
||||
badge.value: 1
|
||||
@@ -64,6 +65,12 @@ 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"
|
||||
@@ -322,6 +329,43 @@ 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"
|
||||
@@ -368,7 +412,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
ListElement {colorId: 28; segmentLength: 3}
|
||||
}
|
||||
ringSettings.distinctiveColors: Theme.palette.identiconRingColors
|
||||
ringSettings.totalRingUnits: 25
|
||||
}
|
||||
|
||||
StatusMemberListItem {
|
||||
@@ -377,10 +420,16 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
|
||||
}
|
||||
|
||||
StatusMemberListItem {
|
||||
nickName: "very-long-annoying-nickname.eth"
|
||||
isOnline: false
|
||||
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
|
||||
}
|
||||
|
||||
StatusMemberListItem {
|
||||
nickName: "This girl I know from work"
|
||||
userName: "annabelle"
|
||||
isOnline: true
|
||||
isOnline: true
|
||||
}
|
||||
|
||||
StatusMemberListItem {
|
||||
|
||||
@@ -4,38 +4,58 @@ import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
GridLayout {
|
||||
columns: 6
|
||||
columnSpacing: 5
|
||||
rowSpacing: 5
|
||||
ColumnLayout {
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
StatusLoadingIndicator {
|
||||
color: Theme.palette.directColor4
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
StatusLetterIdenticon {
|
||||
name: "#status"
|
||||
}
|
||||
Flow {
|
||||
Layout.fillWidth: true
|
||||
spacing: 4
|
||||
|
||||
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"
|
||||
Repeater {
|
||||
model: 12
|
||||
StatusLetterIdenticon {
|
||||
name: "A"
|
||||
color: Theme.palette.userCustomizationColors[index]
|
||||
letterSize: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,11 @@ Column {
|
||||
onClicked: editTitleModal.open()
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: "Modal with Advanced Header/Footer"
|
||||
onClicked: advancedHeaderFooterModal.open()
|
||||
}
|
||||
|
||||
StatusModal {
|
||||
id: simpleModal
|
||||
anchors.centerIn: parent
|
||||
@@ -353,6 +358,42 @@ 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,8 +1,6 @@
|
||||
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
|
||||
@@ -28,33 +26,50 @@ Page {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.leftMargin: 17
|
||||
implicitHeight: 44
|
||||
maxHeight: root.height
|
||||
toLabelText: qsTr("To: ")
|
||||
warningText: qsTr("5 USER LIMIT REACHED")
|
||||
//simulate model filtering, TODO this
|
||||
//makes more sense to be provided by the backend
|
||||
//figure how real implementation should look like
|
||||
property ListModel sortedList: ListModel { }
|
||||
onTextChanged: {
|
||||
sortedList.clear();
|
||||
if (text !== "") {
|
||||
for (var i = 0; i < contactsModel.count; i++ ) {
|
||||
var entry = contactsModel.get(i);
|
||||
if (entry.name.toLowerCase().includes(text.toLowerCase())) {
|
||||
sortedList.insert(sortedList.count, {"publicId": entry.publicId, "name": entry.name,
|
||||
"icon": entry.icon, "isIdenticon": entry.isIdenticon,
|
||||
"onlineStatus": entry.onlineStatus});
|
||||
userListView.model = sortedList;
|
||||
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;
|
||||
}
|
||||
}
|
||||
return contactsModel.get(index).ringSpecModel;
|
||||
} else {
|
||||
userListView.model = contactsModel;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
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"
|
||||
}
|
||||
@@ -62,138 +77,7 @@ Page {
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
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
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.topMargin: 68
|
||||
|
||||
StatusBaseText {
|
||||
visible: (contactsModel.count === 0)
|
||||
|
||||
@@ -27,21 +27,19 @@ StatusAppThreePanelLayout {
|
||||
|
||||
RowLayout {
|
||||
id: searchInputWrapper
|
||||
width: parent.width
|
||||
width: 288
|
||||
height: searchInput.height
|
||||
anchors.top: headline.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
StatusBaseInput {
|
||||
id: searchInput
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.leftMargin: 17
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitHeight: 36
|
||||
topPadding: 8
|
||||
bottomPadding: 0
|
||||
bottomPadding: 8
|
||||
placeholderText: "Search"
|
||||
icon.name: "search"
|
||||
}
|
||||
@@ -61,7 +59,7 @@ StatusAppThreePanelLayout {
|
||||
Column {
|
||||
anchors.top: searchInputWrapper.bottom
|
||||
anchors.topMargin: 16
|
||||
width: parent.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 8
|
||||
|
||||
StatusContactRequestsIndicatorListItem {
|
||||
@@ -72,8 +70,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);
|
||||
@@ -128,19 +126,18 @@ StatusAppThreePanelLayout {
|
||||
centerPanel: Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: root.createChat ? createChatView : chatChannelView
|
||||
}
|
||||
|
||||
Component {
|
||||
id: createChatView
|
||||
CreateChatView {
|
||||
contactsModel: Models.dummyContactsModel
|
||||
Component {
|
||||
id: createChatView
|
||||
CreateChatView {
|
||||
contactsModel: Models.membersListModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: chatChannelView
|
||||
ChatChannelView {
|
||||
model: Models.chatMessagesModel
|
||||
Component {
|
||||
id: chatChannelView
|
||||
ChatChannelView {
|
||||
model: Models.chatMessagesModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -259,16 +259,14 @@ StatusAppThreePanelLayout {
|
||||
delegate: StatusMemberListItem {
|
||||
implicitWidth: parent.width
|
||||
nickName: model.nickName
|
||||
userName: model.userName
|
||||
chatKey: model.chatKey
|
||||
userName: model.name
|
||||
chatKey: model.publicId
|
||||
trustIndicator: model.trustIndicator
|
||||
isMutualContact: model.isMutualContact
|
||||
image.source: model.source
|
||||
image.source: model.icon
|
||||
image.isIdenticon: model.isIdenticon
|
||||
isOnline: model.isOnline
|
||||
isOnline: model.onlineStatus
|
||||
ringSettings.ringSpecModel: model.ringSpecModel
|
||||
ringSettings.distinctiveColors: Theme.palette.identiconRingColors
|
||||
ringSettings.totalRingUnits: model.totalRingUnits
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,65 +4,15 @@ 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
|
||||
@@ -78,7 +28,9 @@ QtObject {
|
||||
itemId: "x012340001"
|
||||
name: "status-desktop"
|
||||
icon: ""
|
||||
emoji: ""
|
||||
isIdenticon: false
|
||||
colorHash: []
|
||||
color: "red"
|
||||
description: ""
|
||||
type: StatusChatListItem.Type.PublicChat
|
||||
@@ -95,7 +47,9 @@ 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
|
||||
@@ -111,7 +65,9 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
itemId: "x012340003"
|
||||
name: "Black Ops"
|
||||
icon: ""
|
||||
emoji: ""
|
||||
isIdenticon: false
|
||||
colorHash: []
|
||||
color: "purple"
|
||||
description: ""
|
||||
type: StatusChatListItem.Type.OneToOneChat
|
||||
@@ -127,7 +83,9 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
itemId: "x012340004"
|
||||
name: "Spectacular Growing Otter"
|
||||
icon: ""
|
||||
emoji: ""
|
||||
isIdenticon: false
|
||||
colorHash: []
|
||||
color: "orange"
|
||||
description: ""
|
||||
type: StatusChatListItem.Type.OneToOneChat
|
||||
@@ -143,7 +101,9 @@ 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
|
||||
@@ -397,7 +357,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: ""
|
||||
iconName: "chat"
|
||||
iconColor: ""
|
||||
isIdenticon: false
|
||||
subItems: [
|
||||
ListElement {
|
||||
value: "sub_item_1_1"
|
||||
@@ -405,7 +364,6 @@ 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"
|
||||
@@ -413,7 +371,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
|
||||
iconName: ""
|
||||
iconColor: ""
|
||||
isIdenticon: true
|
||||
}]}
|
||||
ListElement {
|
||||
value: "item_2"
|
||||
@@ -421,7 +378,6 @@ 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"
|
||||
@@ -429,7 +385,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: ""
|
||||
iconName: "channel"
|
||||
iconColor: ""
|
||||
isIdenticon: false
|
||||
},
|
||||
ListElement {
|
||||
value: "sub_item_2_2"
|
||||
@@ -437,7 +392,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: ""
|
||||
iconName: "channel"
|
||||
iconColor: ""
|
||||
isIdenticon: false
|
||||
},
|
||||
ListElement {
|
||||
value: "sub_item_2_3"
|
||||
@@ -445,7 +399,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: ""
|
||||
iconName: "channel"
|
||||
iconColor: ""
|
||||
isIdenticon: false
|
||||
}]}
|
||||
ListElement {
|
||||
value: "item_3"
|
||||
@@ -453,7 +406,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: "";
|
||||
iconName: "info"
|
||||
iconColor: ""
|
||||
isIdenticon: false
|
||||
subItems: [
|
||||
ListElement {
|
||||
value: "sub_item_3_1"
|
||||
@@ -461,7 +413,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: ""
|
||||
iconName: "channel"
|
||||
iconColor: ""
|
||||
isIdenticon: false
|
||||
}]}
|
||||
ListElement {
|
||||
value: "item_4"
|
||||
@@ -469,7 +420,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: "";
|
||||
iconName: ""
|
||||
iconColor: "red"
|
||||
isIdenticon: false
|
||||
subItems: [
|
||||
ListElement {
|
||||
value: "sub_item_4_1"
|
||||
@@ -477,7 +427,6 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
imageSource: ""
|
||||
iconName: "channel"
|
||||
iconColor: ""
|
||||
isIdenticon: false
|
||||
}]}
|
||||
}
|
||||
|
||||
@@ -895,15 +844,14 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
id: membersList
|
||||
ListElement {
|
||||
nickName: "This is an example"
|
||||
userName: "annabelle"
|
||||
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
|
||||
name: "Maria"
|
||||
publicId: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
|
||||
trustIndicator: StatusContactVerificationIcons.TrustedType.Verified
|
||||
isMutualContact: true
|
||||
isOnline: true
|
||||
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
|
||||
onlineStatus: true
|
||||
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
|
||||
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
|
||||
isIdenticon: true
|
||||
totalRingUnits: 25
|
||||
ringSpecModel: [ ListElement {colorId: 13; segmentLength: 5},
|
||||
ListElement {colorId: 31; segmentLength: 5},
|
||||
ListElement {colorId: 10; segmentLength: 1},
|
||||
@@ -913,26 +861,25 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
ListElement {colorId: 28; segmentLength: 3} ]
|
||||
}
|
||||
ListElement {
|
||||
nickName: "carmen.eth"
|
||||
userName: ""
|
||||
chatKey: ""
|
||||
nickName: ""
|
||||
name: "carmen.eth"
|
||||
publicId: "0x043a7ed78362567894688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
|
||||
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
|
||||
isMutualContact: false
|
||||
isOnline: false
|
||||
source: ""
|
||||
onlineStatus: false
|
||||
icon: ""
|
||||
isIdenticon: false
|
||||
}
|
||||
ListElement {
|
||||
nickName: "This girl I know from work"
|
||||
userName: "annabelle"
|
||||
chatKey: ""
|
||||
name: "annabelle"
|
||||
publicId: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486dsfkjghyu2cf04"
|
||||
trustIndicator: StatusContactVerificationIcons.TrustedType.None
|
||||
isMutualContact: false
|
||||
isOnline: true
|
||||
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDK
|
||||
onlineStatus: true
|
||||
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDK
|
||||
ExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
|
||||
isIdenticon: true
|
||||
totalRingUnits: 25
|
||||
ringSpecModel: [ ListElement {colorId: 11; segmentLength: 1},
|
||||
ListElement {colorId: 23; segmentLength: 5},
|
||||
ListElement {colorId: 23; segmentLength: 5},
|
||||
@@ -943,14 +890,13 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
|
||||
}
|
||||
ListElement {
|
||||
nickName: "Mark Cuban"
|
||||
userName: "annabelle"
|
||||
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
|
||||
name: "mark.eth"
|
||||
publicId: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc79872cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
|
||||
trustIndicator: StatusContactVerificationIcons.TrustedType.Untrustworthy
|
||||
isMutualContact: true
|
||||
isOnline: false
|
||||
source: ""
|
||||
onlineStatus: false
|
||||
icon: ""
|
||||
isIdenticon: false
|
||||
totalRingUnits: 25
|
||||
ringSpecModel: [ ListElement {colorId: 0; segmentLength: 1},
|
||||
ListElement {colorId: 28; segmentLength: 1},
|
||||
ListElement {colorId: 31; segmentLength: 1},
|
||||
@@ -1055,4 +1001,309 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 190 KiB |
@@ -11,6 +11,11 @@ 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();
|
||||
|
||||
@@ -2,6 +2,8 @@ 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
|
||||
|
||||
@@ -31,10 +33,7 @@ StatusWindow {
|
||||
|
||||
property real factor: 1.0
|
||||
|
||||
Component.onCompleted: {
|
||||
Theme.palette = lightTheme
|
||||
rootWindow.updatePosition();
|
||||
}
|
||||
Component.onCompleted: rootWindow.updatePosition()
|
||||
|
||||
QtObject {
|
||||
id: appSectionType
|
||||
@@ -110,12 +109,10 @@ StatusWindow {
|
||||
anchors.topMargin: 32
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 32
|
||||
onCheckedChanged: {
|
||||
if (Theme.palette === rootWindow.lightTheme) {
|
||||
Theme.palette = rootWindow.darkTheme
|
||||
} else {
|
||||
Theme.palette = rootWindow.lightTheme
|
||||
}
|
||||
lightThemeEnabled: storeSettings.lightTheme
|
||||
onLightThemeEnabledChanged: {
|
||||
Theme.palette = lightThemeEnabled ? rootWindow.darkTheme : rootWindow.lightTheme
|
||||
storeSettings.lightTheme = lightThemeEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,11 +123,13 @@ StatusWindow {
|
||||
StatusAppTwoPanelLayout {
|
||||
id: mainPageView
|
||||
|
||||
function page(name) {
|
||||
function page(name, fillPage) {
|
||||
storeSettings.fillPage = fillPage ? true : false
|
||||
viewLoader.source = Qt.resolvedUrl("./pages/" + name + "Page.qml");
|
||||
}
|
||||
function control(name) {
|
||||
viewLoader.source = Qt.resolvedUrl("./controls/" + name + ".qml");
|
||||
storeSettings.fillPage = false
|
||||
}
|
||||
|
||||
leftPanel: Item {
|
||||
@@ -184,6 +183,11 @@ 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)
|
||||
@@ -275,6 +279,11 @@ 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"
|
||||
@@ -293,6 +302,16 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,8 +319,11 @@ 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
|
||||
@@ -310,14 +332,16 @@ 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: mainPageView.control("Icons")
|
||||
source: storeSettings.selected.length === 0 ? mainPageView.control("Icons") : storeSettings.selected
|
||||
onSourceChanged: {
|
||||
storeSettings.selected = viewLoader.source
|
||||
if (source.toString().includes("Icons")) {
|
||||
item.iconColor = Theme.palette.primaryColor1;
|
||||
}
|
||||
@@ -325,6 +349,15 @@ StatusWindow {
|
||||
}
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
active: storeSettings.fillPage
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 64
|
||||
visible: storeSettings.fillPage
|
||||
clip: true
|
||||
|
||||
source: viewLoader.source
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -442,4 +475,11 @@ StatusWindow {
|
||||
rootWindow.toggleFullScreen()
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
id: storeSettings
|
||||
property string selected: ""
|
||||
property bool lightTheme: true
|
||||
property bool fillPage: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,11 +57,38 @@ 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"
|
||||
@@ -102,10 +129,24 @@ 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.component: StatusIcon {
|
||||
input.rightComponent: StatusIcon {
|
||||
icon: "cancel"
|
||||
height: 16
|
||||
color: Theme.palette.dangerColor1
|
||||
@@ -130,4 +171,45 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,6 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
namesModel: root.asortedContacts
|
||||
toLabelText: qsTr("To: ")
|
||||
warningText: qsTr("5 USER LIMIT REACHED")
|
||||
warningText: qsTr("USER LIMIT REACHED")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@ 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":""},
|
||||
{"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":""}
|
||||
{"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}
|
||||
]
|
||||
delegate: StatusToastMessage {
|
||||
primaryText: modelData.title
|
||||
@@ -27,12 +28,12 @@ Item {
|
||||
loading: modelData.loading
|
||||
type: modelData.type
|
||||
linkUrl: modelData.url
|
||||
duration: modelData.duration
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link);
|
||||
}
|
||||
//simulate open
|
||||
Component.onCompleted: {
|
||||
open = true;
|
||||
onClose: {
|
||||
console.warn("toast closed: ", modelData.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@ Column {
|
||||
spacing: 8
|
||||
|
||||
StatusWalletColorSelect {
|
||||
model: Theme.palette.accountColors
|
||||
model: Theme.palette.userCustomizationColors
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,6 +24,8 @@
|
||||
<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>
|
||||
@@ -31,5 +33,15 @@
|
||||
<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>
|
||||
|
||||
@@ -32,7 +32,7 @@ Rectangle {
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
if (statusBadge.value > 99) {
|
||||
return "99+";
|
||||
return qsTr("99+");
|
||||
}
|
||||
return statusBadge.value;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ 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
|
||||
@@ -37,7 +36,6 @@ Item {
|
||||
StatusRoundButton {
|
||||
id: statusMenuButton
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
visible: popupMenuSlot.active
|
||||
@@ -104,5 +102,4 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ Column {
|
||||
width: 288
|
||||
|
||||
property string categoryId: ""
|
||||
property var model: []
|
||||
property var model: null
|
||||
property bool draggableItems: false
|
||||
property bool highlightItem: true
|
||||
|
||||
property alias statusChatListItems: statusChatListItems
|
||||
|
||||
@@ -90,8 +91,8 @@ Column {
|
||||
}
|
||||
|
||||
StatusChatListItem {
|
||||
|
||||
id: statusChatListItem
|
||||
|
||||
opacity: dragSensor.active ? 0.0 : 1.0
|
||||
originalOrder: model.position
|
||||
chatId: model.itemId
|
||||
@@ -101,11 +102,14 @@ Column {
|
||||
muted: model.muted
|
||||
hasUnreadMessages: model.hasUnreadMessages
|
||||
notificationsCount: model.notificationsCount
|
||||
highlightWhenCreated: model.highlight
|
||||
selected: model.active
|
||||
icon.color: model.color
|
||||
image.isIdenticon: model.isIdenticon
|
||||
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
|
||||
image.source: model.icon
|
||||
ringSettings.ringSpecModel: model.colorHash
|
||||
|
||||
sensor.cursorShape: dragSensor.cursorShape
|
||||
onClicked: {
|
||||
|
||||
@@ -14,10 +14,10 @@ Item {
|
||||
implicitWidth: chatListsAndCategories.width
|
||||
|
||||
property StatusTooltipSettings categoryAddButtonToolTip: StatusTooltipSettings {
|
||||
text: "Add channel inside category"
|
||||
text: qsTr("Add channel inside category")
|
||||
}
|
||||
property StatusTooltipSettings categoryMenuButtonToolTip: StatusTooltipSettings {
|
||||
text: "More"
|
||||
text: qsTr("More")
|
||||
}
|
||||
|
||||
property var model: []
|
||||
@@ -68,7 +68,6 @@ Item {
|
||||
|
||||
StatusChatList {
|
||||
id: statusChatList
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: statusChatList.model.count > 0
|
||||
onChatItemSelected: statusChatListAndCategories.chatItemSelected(categoryId, id)
|
||||
onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id)
|
||||
|
||||
@@ -48,7 +48,6 @@ 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: "Add channel inside category"
|
||||
tooltip.text: qsTr("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: "More"
|
||||
tooltip.text: qsTr("More")
|
||||
},
|
||||
StatusChatListCategoryItemButton {
|
||||
id: toggleButton
|
||||
|
||||
@@ -5,11 +5,12 @@ 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: statusChatListItem
|
||||
id: root
|
||||
|
||||
objectName: "chatItem"
|
||||
property int originalOrder: -1
|
||||
@@ -28,8 +29,10 @@ Rectangle {
|
||||
width: 24
|
||||
height: 24
|
||||
color: Theme.palette.miscColor5
|
||||
letterSize: 15
|
||||
emoji: ""
|
||||
charactersLen: root.type === StatusChatListItem.Type.OneToOneChat ? 2 : 1
|
||||
}
|
||||
property alias ringSettings: identicon.ringSettings
|
||||
property int type: StatusChatListItem.Type.PublicChat
|
||||
property bool highlighted: false
|
||||
property bool highlightWhenCreated: false
|
||||
@@ -75,16 +78,16 @@ Rectangle {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onClicked: statusChatListItem.clicked(mouse)
|
||||
onClicked: root.clicked(mouse)
|
||||
|
||||
StatusSmartIdenticon {
|
||||
id: identicon
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
image: statusChatListItem.image
|
||||
icon: statusChatListItem.icon
|
||||
name: statusChatListItem.name
|
||||
image: root.image
|
||||
icon: root.icon
|
||||
name: root.name
|
||||
}
|
||||
|
||||
StatusIcon {
|
||||
@@ -94,21 +97,21 @@ Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
width: 14
|
||||
visible: statusChatListItem.type !== StatusChatListItem.Type.OneToOneChat
|
||||
visible: root.type !== StatusChatListItem.Type.OneToOneChat
|
||||
opacity: {
|
||||
if (statusChatListItem.muted && !hoverHander.hovered && !statusChatListItem.highlighted) {
|
||||
if (root.muted && !hoverHander.hovered && !root.highlighted) {
|
||||
return 0.4
|
||||
}
|
||||
return statusChatListItem.hasUnreadMessages ||
|
||||
statusChatListItem.notificationsCount > 0 ||
|
||||
statusChatListItem.selected ||
|
||||
statusChatListItem.highlighted ||
|
||||
return root.hasUnreadMessages ||
|
||||
root.notificationsCount > 0 ||
|
||||
root.selected ||
|
||||
root.highlighted ||
|
||||
statusBadge.visible ||
|
||||
hoverHander.hovered ? 1.0 : 0.7
|
||||
}
|
||||
|
||||
icon: {
|
||||
switch (statusChatListItem.type) {
|
||||
switch (root.type) {
|
||||
case StatusChatListItem.Type.PublicCat:
|
||||
return Theme.palette.name == "light" ? "tiny/public-chat" : "tiny/public-chat-white"
|
||||
break;
|
||||
@@ -133,27 +136,27 @@ Rectangle {
|
||||
anchors.rightMargin: 6
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: (statusChatListItem.type === StatusChatListItem.Type.PublicChat &&
|
||||
!statusChatListItem.name.startsWith("#") ?
|
||||
"#" + statusChatListItem.name :
|
||||
statusChatListItem.name)
|
||||
text: (root.type === StatusChatListItem.Type.PublicChat &&
|
||||
!root.name.startsWith("#") ?
|
||||
"#" + root.name :
|
||||
root.name)
|
||||
elide: Text.ElideRight
|
||||
color: {
|
||||
if (statusChatListItem.muted && !hoverHander.hovered && !statusChatListItem.highlighted) {
|
||||
if (root.muted && !hoverHander.hovered && !root.highlighted) {
|
||||
return Theme.palette.directColor5
|
||||
}
|
||||
return statusChatListItem.hasUnreadMessages ||
|
||||
statusChatListItem.notificationsCount > 0 ||
|
||||
statusChatListItem.selected ||
|
||||
statusChatListItem.highlighted ||
|
||||
statusChatListItem.highlightWhenCreated ||
|
||||
return root.hasUnreadMessages ||
|
||||
root.notificationsCount > 0 ||
|
||||
root.selected ||
|
||||
root.highlighted ||
|
||||
root.highlightWhenCreated ||
|
||||
hoverHander.hovered ||
|
||||
statusBadge.visible ? Theme.palette.directColor1 : Theme.palette.directColor4
|
||||
}
|
||||
font.weight: !statusChatListItem.muted &&
|
||||
(statusChatListItem.hasUnreadMessages ||
|
||||
statusChatListItem.notificationsCount > 0 ||
|
||||
statusChatListItem.highlightWhenCreated ||
|
||||
font.weight: !root.muted &&
|
||||
(root.hasUnreadMessages ||
|
||||
root.notificationsCount > 0 ||
|
||||
root.highlightWhenCreated ||
|
||||
statusBadge.visible) ? Font.Bold : Font.Medium
|
||||
font.pixelSize: 15
|
||||
}
|
||||
@@ -166,18 +169,18 @@ Rectangle {
|
||||
width: 14
|
||||
opacity: mutedIconSensor.containsMouse ? 1.0 : 0.2
|
||||
icon: Theme.palette.name === "light" ? "tiny/muted" : "tiny/muted-white"
|
||||
visible: statusChatListItem.muted
|
||||
visible: root.muted
|
||||
|
||||
MouseArea {
|
||||
id: mutedIconSensor
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: statusChatListItem.unmute()
|
||||
onClicked: root.unmute()
|
||||
}
|
||||
|
||||
StatusToolTip {
|
||||
text: "Unmute"
|
||||
text: qsTr("Unmute")
|
||||
visible: mutedIconSensor.containsMouse
|
||||
}
|
||||
}
|
||||
@@ -189,11 +192,11 @@ Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
|
||||
color: statusChatListItem.muted ? Theme.palette.primaryColor2 : Theme.palette.primaryColor1
|
||||
color: root.muted ? Theme.palette.primaryColor2 : Theme.palette.primaryColor1
|
||||
border.width: 4
|
||||
border.color: color
|
||||
value: statusChatListItem.notificationsCount
|
||||
visible: statusChatListItem.notificationsCount > 0
|
||||
value: root.notificationsCount
|
||||
visible: root.notificationsCount > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ Rectangle {
|
||||
onClicked: statusChatToolBar.searchButtonClicked()
|
||||
|
||||
// initializing the tooltip
|
||||
tooltip.text: "Search"
|
||||
tooltip.text: qsTr("Search")
|
||||
tooltip.orientation: StatusToolTip.Orientation.Bottom
|
||||
tooltip.y: parent.height + 12
|
||||
}
|
||||
@@ -72,7 +72,7 @@ Rectangle {
|
||||
onClicked: statusChatToolBar.membersButtonClicked()
|
||||
|
||||
// initializing the tooltip
|
||||
tooltip.text: "Members"
|
||||
tooltip.text: qsTr("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: "More"
|
||||
tooltip.text: qsTr("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: "Activity"
|
||||
tooltip.text: qsTr("Activity")
|
||||
tooltip.orientation: StatusToolTip.Orientation.Bottom
|
||||
tooltip.y: parent.height + 12
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ StatusListItem {
|
||||
|
||||
components: [
|
||||
StatusBadge {
|
||||
visible: statusContactRequestsListItem.requestsCount > 0
|
||||
value: statusContactRequestsListItem.requestsCount
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
border.width: 4
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
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,6 +12,7 @@ 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
|
||||
@@ -185,7 +186,7 @@ Rectangle {
|
||||
anchors.leftMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 48 : 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 16 : 0
|
||||
active: false
|
||||
visible: false
|
||||
}
|
||||
|
||||
onExpandedChanged: {
|
||||
@@ -202,13 +203,13 @@ Rectangle {
|
||||
name: "EXPANDED"
|
||||
PropertyChanges {target: expandImage; icon: "chevron-up"}
|
||||
PropertyChanges {target: statusExpandableItem; height: 82 + expandableRegion.height + 22}
|
||||
PropertyChanges {target: expandableRegion; active: true}
|
||||
PropertyChanges {target: expandableRegion; visible: true}
|
||||
},
|
||||
State {
|
||||
name: "COLLAPSED"
|
||||
PropertyChanges {target: expandImage; icon: "chevron-down"}
|
||||
PropertyChanges {target: statusExpandableItem; height: 82}
|
||||
PropertyChanges {target: expandableRegion; active: false}
|
||||
PropertyChanges {target: expandableRegion; visible: false}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
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,30 +1,60 @@
|
||||
import QtQuick 2.13
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
Rectangle {
|
||||
id: statusLetterIdenticon
|
||||
id: root
|
||||
|
||||
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
|
||||
text: ((statusLetterIdenticon.name.charAt(0) === "#")
|
||||
|| (statusLetterIdenticon.name.charAt(0) === "@") ?
|
||||
statusLetterIdenticon.name.charAt(1) : statusLetterIdenticon.name.charAt(0)).toUpperCase()
|
||||
|
||||
visible: !root.emoji
|
||||
anchors.alignWhenCentered: false
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: statusLetterIdenticon.letterSize
|
||||
color: Qt.rgba(255, 255, 255, 0.7)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,15 +14,19 @@ Rectangle {
|
||||
property string title: ""
|
||||
property string titleAsideText: ""
|
||||
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 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
|
||||
@@ -30,6 +34,7 @@ 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 {
|
||||
@@ -52,9 +57,9 @@ Rectangle {
|
||||
isIdenticon: false
|
||||
}
|
||||
property StatusIdenticonRingSettings ringSettings: StatusIdenticonRingSettings {
|
||||
totalRingUnits: 1
|
||||
initalAngleRad: 0
|
||||
ringPxSize: 1.5
|
||||
distinctiveColors: Theme.palette.identiconRingColors
|
||||
}
|
||||
|
||||
property alias sensor: sensor
|
||||
@@ -67,6 +72,8 @@ Rectangle {
|
||||
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)
|
||||
@@ -78,7 +85,12 @@ Rectangle {
|
||||
}
|
||||
|
||||
implicitWidth: 448
|
||||
implicitHeight: Math.max(64, statusListItemTitleArea.height + 16)
|
||||
implicitHeight: {
|
||||
if (bottomModel.length === 0) {
|
||||
return Math.max(64, statusListItemTitleArea.height + 16)
|
||||
}
|
||||
return Math.max(64, statusListItemTitleArea.height + 90)
|
||||
}
|
||||
color: {
|
||||
if (sensor.containsMouse || statusListItem.highlighted) {
|
||||
switch(type) {
|
||||
@@ -127,7 +139,8 @@ Rectangle {
|
||||
name: statusListItem.title
|
||||
active: statusListItem.icon.isLetterIdenticon ||
|
||||
!!statusListItem.icon.name ||
|
||||
!!statusListItem.image.source.toString()
|
||||
!!statusListItem.image.source.toString() ||
|
||||
!!statusListItem.icon.emoji
|
||||
badge.border.color: statusListItem.color
|
||||
ringSettings: statusListItem.ringSettings
|
||||
}
|
||||
@@ -147,7 +160,8 @@ Rectangle {
|
||||
anchors.right: statusListItemLabel.visible ? statusListItemLabel.left : statusListItemComponentsSlot.left
|
||||
anchors.leftMargin: iconOrImage.active ? 16 : statusListItem.leftPadding
|
||||
anchors.rightMargin: statusListItem.rightPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenter: bottomModel.length === 0 ? parent.verticalCenter : undefined
|
||||
|
||||
height: childrenRect.height
|
||||
|
||||
StatusBaseText {
|
||||
@@ -155,26 +169,37 @@ Rectangle {
|
||||
text: statusListItem.title
|
||||
font.pixelSize: 15
|
||||
height: visible ? contentHeight : 0
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
elide: Text.ElideRight
|
||||
anchors.left: parent.left
|
||||
anchors.right: !statusListItem.titleAsideText && !titleIconsRow.sourceComponent ? parent.right : undefined
|
||||
anchors.top: bottomModel.length === 0 ? undefined: parent.top
|
||||
anchors.topMargin: bottomModel.length === 0 ? undefined : 20
|
||||
width: Math.min(implicitWidth, parent.width)
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: statusListItemTitleMouseArea
|
||||
anchors.fill: parent
|
||||
cursorShape: containsMouse? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
enabled: statusListItem.enabled
|
||||
cursorShape: sensor.enabled && containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: statusListItem.propagateTitleClicks
|
||||
onClicked: {
|
||||
@@ -189,6 +214,8 @@ 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
|
||||
@@ -232,11 +259,42 @@ 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: parent.verticalCenter
|
||||
anchors.verticalCenter: bottomModel.length === 0 ? parent.verticalCenter : undefined
|
||||
anchors.top: bottomModel.length === 0 ? undefined: parent.top
|
||||
anchors.topMargin: bottomModel.length === 0 ? undefined : 16
|
||||
anchors.right: statusListItemComponentsSlot.left
|
||||
anchors.rightMargin: statusListItemComponentsSlot.width > 0 ? 10 : 0
|
||||
|
||||
@@ -250,7 +308,9 @@ Rectangle {
|
||||
id: statusListItemComponentsSlot
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: statusListItem.rightPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenter: bottomModel.length === 0 ? parent.verticalCenter : undefined
|
||||
anchors.top: bottomModel.length === 0 ? undefined: parent.top
|
||||
anchors.topMargin: bottomModel.length === 0 ? undefined : 12
|
||||
spacing: 10
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ 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
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
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
|
||||
}
|
||||
@@ -2,45 +2,109 @@ 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 bool isOnline: false
|
||||
|
||||
// Subtitle composition:
|
||||
function composeSubtitile() {
|
||||
var compose = ""
|
||||
if(root.userName !== "")
|
||||
compose = "(" + root.userName + ")"
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
if(compose !== "" && root.chatKey !== "")
|
||||
// Composition
|
||||
compose += " • " + composeShortKeyChat(root.chatKey)
|
||||
// Subtitle composition:
|
||||
function composeSubtitile() {
|
||||
var compose = ""
|
||||
if(root.userName !== "" && root.nickName !== "")
|
||||
compose = "(" + root.userName + ")"
|
||||
|
||||
else if(root.chatKey !== "")
|
||||
compose = composeShortKeyChat(root.chatKey)
|
||||
if(compose !== "" && root.chatKey !== "")
|
||||
// Composition
|
||||
compose += " • " + composeShortKeyChat(root.chatKey)
|
||||
|
||||
return compose
|
||||
}
|
||||
else if(root.chatKey !== "")
|
||||
compose = composeShortKeyChat(root.chatKey)
|
||||
|
||||
// Short keychat composition:
|
||||
function composeShortKeyChat(chatKey) {
|
||||
return chatKey.substring(0, 5) + "..." + chatKey.substring(chatKey.length - 3)
|
||||
return compose
|
||||
}
|
||||
|
||||
// Short keychat composition:
|
||||
function composeShortKeyChat(chatKey) {
|
||||
return chatKey.substring(0, 5) + "..." + chatKey.substring(chatKey.length - 3)
|
||||
}
|
||||
}
|
||||
|
||||
// root object settings:
|
||||
title: root.nickName
|
||||
title: (root.nickName === "") ? root.userName : root.nickName
|
||||
statusListItemTitleIcons.sourceComponent: StatusContactVerificationIcons {
|
||||
isMutualContact: root.isMutualContact
|
||||
trustIndicator: root.trustIndicator
|
||||
}
|
||||
subTitle: composeSubtitile()
|
||||
subTitle: d.composeSubtitile()
|
||||
statusListItemSubTitle.font.pixelSize: 10
|
||||
icon.isLetterIdenticon: !root.image.source.toString()
|
||||
statusListItemIcon.badge.visible: true
|
||||
|
||||
@@ -23,9 +23,9 @@ Loader {
|
||||
}
|
||||
|
||||
property StatusIdenticonRingSettings ringSettings: StatusIdenticonRingSettings {
|
||||
totalRingUnits: 1
|
||||
initalAngleRad: 0
|
||||
ringPxSize: 1.5
|
||||
ringPxSize: Math.max(1.5, statusSmartIdenticon.image.width / 24.0)
|
||||
distinctiveColors: Theme.palette.identiconRingColors
|
||||
}
|
||||
|
||||
sourceComponent: statusSmartIdenticon.icon.isLetterIdenticon ? letterIdenticon :
|
||||
@@ -83,7 +83,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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,142 @@
|
||||
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: 44
|
||||
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))
|
||||
|
||||
/*!
|
||||
\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;
|
||||
}
|
||||
|
||||
/*!
|
||||
\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});
|
||||
@@ -31,14 +145,48 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\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 {
|
||||
anchors.fill: parent
|
||||
id: tagSelectorRect
|
||||
width: parent.width
|
||||
height: 44
|
||||
radius: 8
|
||||
color: Theme.palette.baseColor2
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 16
|
||||
@@ -52,7 +200,8 @@ Item {
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: (namesList.contentWidth > (parent.width - 177)) ?
|
||||
(parent.width - 177) : namesList.contentWidth
|
||||
implicitHeight: 30
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: (namesList.count > 0)
|
||||
@@ -66,9 +215,13 @@ Item {
|
||||
model: namesModel
|
||||
orientation: ListView.Horizontal
|
||||
spacing: 8
|
||||
onContentWidthChanged: {
|
||||
positionViewAtEnd();
|
||||
function scrollToEnd() {
|
||||
if (contentWidth > width) {
|
||||
contentX = contentWidth;
|
||||
}
|
||||
}
|
||||
onWidthChanged: { scrollToEnd(); }
|
||||
onCountChanged: { scrollToEnd(); }
|
||||
delegate: Rectangle {
|
||||
id: nameDelegate
|
||||
width: (nameText.contentWidth + 34)
|
||||
@@ -103,30 +256,36 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
TextEdit {
|
||||
TextInput {
|
||||
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)) === "") {
|
||||
&& getText(cursorPosition, (cursorPosition-1)) === ""
|
||||
&& (namesList.count-1) >= 0) {
|
||||
removeMember(namesModel.get(namesList.count-1).publicId);
|
||||
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: 120
|
||||
Layout.preferredWidth: visible ? 120 : 0
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
font.pixelSize: 10
|
||||
color: Theme.palette.dangerColor1
|
||||
@@ -134,4 +293,95 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,30 +7,114 @@ 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
|
||||
|
||||
property bool open: false
|
||||
/*!
|
||||
\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 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
|
||||
property string iconName: ""
|
||||
/*!
|
||||
\qmlproperty string StatusToastMessage::linkUrl
|
||||
This property represents all steps and their descriptions as provided by the user.
|
||||
*/
|
||||
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;
|
||||
@@ -41,12 +125,36 @@ 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: "opened"
|
||||
name: d.openedState
|
||||
when: root.open
|
||||
PropertyChanges {
|
||||
target: root
|
||||
@@ -55,16 +163,13 @@ Control {
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "closed"
|
||||
name: d.closedState
|
||||
when: !root.open
|
||||
PropertyChanges {
|
||||
target: root
|
||||
anchors.rightMargin: -width
|
||||
opacity: 0.0
|
||||
}
|
||||
StateChangeScript {
|
||||
script: { root.close(); }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -72,6 +177,12 @@ Control {
|
||||
Transition {
|
||||
to: "*"
|
||||
NumberAnimation { properties: "anchors.rightMargin,opacity"; duration: 400 }
|
||||
|
||||
onRunningChanged: {
|
||||
if(!running && state == d.closedState) {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -114,9 +225,14 @@ 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 : statusIcon
|
||||
sourceComponent: root.loading ? loadingInd :
|
||||
root.icon.name != ""? statusIcon :
|
||||
undefined
|
||||
|
||||
Component {
|
||||
id: loadingInd
|
||||
StatusLoadingIndicator {
|
||||
|
||||
@@ -6,10 +6,42 @@ 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 {
|
||||
@@ -35,8 +67,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
from: 0
|
||||
//TODO what is to: here?
|
||||
to: 2000
|
||||
to: wizardWrapper.maxDuration
|
||||
value: loadingTime
|
||||
backgroundColor: "transparent"
|
||||
backgroundBorderColor: Theme.palette.primaryColor1
|
||||
|
||||
@@ -10,6 +10,7 @@ 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
|
||||
@@ -23,6 +24,8 @@ 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
|
||||
@@ -30,3 +33,4 @@ 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.accountColors) || Theme.palette.accountColors[0]
|
||||
selectedIconImg.color = Utils.getThemeAccountColor(selectedAccount.color, Theme.palette.userCustomizationColors) || Theme.palette.userCustomizationColors[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.accountColors) || Theme.palette.accountColors[0]
|
||||
color: Utils.getThemeAccountColor(model.color, Theme.palette.userCustomizationColors) || Theme.palette.userCustomizationColors[0]
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
@@ -3,13 +3,61 @@ 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
|
||||
@@ -27,6 +75,8 @@ Column {
|
||||
Warning // 3
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
|
||||
// Component definition
|
||||
Rectangle {
|
||||
id: topDiv
|
||||
|
||||
@@ -40,11 +40,9 @@ Rectangle {
|
||||
return 5
|
||||
case StatusBaseButton.Size.Small:
|
||||
return 10
|
||||
break;
|
||||
case StatusBaseButton.Size.Large:
|
||||
default:
|
||||
return 11
|
||||
break;
|
||||
}
|
||||
}
|
||||
property real defaultBottomPadding: {
|
||||
@@ -53,11 +51,9 @@ Rectangle {
|
||||
return 5
|
||||
case StatusBaseButton.Size.Small:
|
||||
return 10
|
||||
break;
|
||||
case StatusBaseButton.Size.Large:
|
||||
default:
|
||||
return 11
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +105,17 @@ 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
|
||||
@@ -121,28 +128,20 @@ Rectangle {
|
||||
height: statusBaseButton.icon.height
|
||||
icon: statusBaseButton.icon.name
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !loading && statusBaseButton.icon.name !== ""
|
||||
opacity: !loading && statusBaseButton.icon.name !== ""
|
||||
visible: statusBaseButton.icon.name !== ""
|
||||
color: statusBaseButton.enabled ? textColor
|
||||
: disabledTextColor
|
||||
} // Icon
|
||||
StatusBaseText {
|
||||
id: label
|
||||
visible: !loading
|
||||
opacity: !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
|
||||
|
||||
|
||||
|
||||
@@ -1,252 +1,470 @@
|
||||
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: statusBaseInput
|
||||
id: root
|
||||
|
||||
/*!
|
||||
\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
|
||||
property alias verticalAlignmet: edit.verticalAlignment
|
||||
/*!
|
||||
\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 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
|
||||
|
||||
property real leftPadding: 16
|
||||
/*!
|
||||
\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 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
|
||||
}
|
||||
|
||||
property Item component
|
||||
|
||||
onClearableChanged: {
|
||||
if (clearable && !component) {
|
||||
clearButtonLoader.active = true
|
||||
clearButtonLoader.parent = statusBaseInputComponentSlot
|
||||
} else {
|
||||
clearButtonLoader.active = false
|
||||
emoji: ""
|
||||
letterSize: emoji ? 11 : 14
|
||||
background: StatusIconBackgroundSettings {
|
||||
width: 30
|
||||
height: 30
|
||||
color: Theme.palette.indirectColor1
|
||||
}
|
||||
}
|
||||
|
||||
onComponentChanged: {
|
||||
if (!!component) {
|
||||
component.parent = statusBaseInputComponentSlot
|
||||
}
|
||||
}
|
||||
/*!
|
||||
\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()
|
||||
|
||||
implicitWidth: 448
|
||||
implicitHeight: multiline ? Math.max((edit.implicitHeight + topPadding + bottomPadding), 44) : 44
|
||||
implicitHeight: multiline ? Math.min(Math.max(
|
||||
(edit.implicitHeight + topPadding + bottomPadding),
|
||||
44, root.minimumHeight), root.maximumHeight) : 44
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
width: parent.width
|
||||
height: maximumHeight != 0 ? Math.min(
|
||||
minimumHeight != 0 ? Math.max(statusBaseInput.implicitHeight, minimumHeight)
|
||||
: statusBaseInput.implicitHeight, maximumHeight) : parent.height
|
||||
color: Theme.palette.baseColor2
|
||||
minimumHeight
|
||||
!= 0 ? Math.max(
|
||||
root.implicitHeight,
|
||||
minimumHeight) : root.implicitHeight,
|
||||
maximumHeight) : parent.height
|
||||
color: root.showBackground ? Theme.palette.baseColor2
|
||||
: "transparent"
|
||||
radius: 8
|
||||
|
||||
clip: true
|
||||
|
||||
border.width: 1
|
||||
border.color: {
|
||||
if (!statusBaseInput.valid && statusBaseInput.dirty) {
|
||||
return Theme.palette.dangerColor1;
|
||||
if (!root.showBackground) {
|
||||
return "transparent"
|
||||
}
|
||||
if (!root.valid && root.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()
|
||||
}
|
||||
|
||||
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
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: root.leftPadding
|
||||
rightMargin: root.rightPadding
|
||||
}
|
||||
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
|
||||
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;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (multiline) {
|
||||
event.accepted = false
|
||||
} else {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
TextEdit {
|
||||
id: edit
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (multiline) {
|
||||
event.accepted = false
|
||||
} else {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
property string previousText: text
|
||||
property var keyEvent
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
id: placeholder
|
||||
visible: (edit.text.length === 0)
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: statusBaseInput.rightPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: flick.width
|
||||
height: flick.height
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
selectByMouse: true
|
||||
selectionColor: Theme.palette.primaryColor2
|
||||
selectedTextColor: color
|
||||
focus: true
|
||||
font.pixelSize: 15
|
||||
elide: StatusBaseText.ElideRight
|
||||
font.family: Theme.palette.baseFont.name
|
||||
color: statusBaseInput.enabled ? Theme.palette.baseColor1 :
|
||||
Theme.palette.directColor6
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
} // 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
|
||||
}
|
||||
}
|
||||
|
||||
} // Flickable
|
||||
|
||||
Item {
|
||||
id: statusBaseInputComponentSlot
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
} // Rectangle
|
||||
|
||||
Loader {
|
||||
id: clearButtonLoader
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
id: clearButton
|
||||
visible: edit.text.length != 0 &&
|
||||
statusBaseInput.clearable &&
|
||||
!statusBaseInput.multiline &&
|
||||
edit.activeFocus
|
||||
|
||||
Component {
|
||||
id: clearButton
|
||||
|
||||
StatusFlatRoundButton {
|
||||
visible: edit.text.length != 0 && root.clearable && !root.multiline
|
||||
&& edit.activeFocus
|
||||
type: StatusFlatRoundButton.Type.Secondary
|
||||
width: 24
|
||||
height: 24
|
||||
@@ -260,5 +478,43 @@ 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,8 +25,10 @@ Rectangle {
|
||||
property StatusIconSettings icon: StatusIconSettings {
|
||||
width: 36
|
||||
height: 36
|
||||
letterSize: 20
|
||||
charactersLen: 2
|
||||
}
|
||||
property alias ringSettings: identicon.ringSettings
|
||||
|
||||
property int type: StatusChatInfoButton.Type.PublicChat
|
||||
property alias tooltip: statusToolTip
|
||||
property alias sensor: sensor
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ TabButton {
|
||||
property string name: ""
|
||||
property bool highlighted: false
|
||||
|
||||
property alias identicon: identicon
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
@@ -32,7 +34,6 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import StatusQ.Core 0.1
|
||||
StatusIdenticonRing {
|
||||
anchors.fill: parent
|
||||
settings: StatusIdenticonRingSettings {
|
||||
totalRingUnits: 25
|
||||
initalAngleRad: 0
|
||||
ringPxSize: 1.5
|
||||
ringSpecModel: [ ListElement {colorId: 0; segmentLength: 1},
|
||||
@@ -52,56 +51,100 @@ import StatusQ.Core 0.1
|
||||
For a list of components available see StatusQ.
|
||||
*/
|
||||
Item {
|
||||
id: identiconRing
|
||||
id: root
|
||||
|
||||
/*!
|
||||
\qmlproperty StatusIdenticonRingSettings StatusIdenticonRing::settings
|
||||
This property holds a set of settings for building the ring.
|
||||
*/
|
||||
property StatusIdenticonRingSettings settings: StatusIdenticonRingSettings {
|
||||
totalRingUnits: 1
|
||||
initalAngleRad: 0
|
||||
ringPxSize: 1.5
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlproperty real StatusIdenticonRing::segmentRadLen
|
||||
This property holds the total ring line length in radians. It depends on totalRingUnits value of settings object.
|
||||
*/
|
||||
readonly property real segmentRadLen: 2 * Math.PI / settings.totalRingUnits
|
||||
|
||||
visible: settings.ringSpecModel !== undefined
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
const context = getContext("2d")
|
||||
const radius = (height - settings.ringPxSize) / 2
|
||||
const xPos = width / 2
|
||||
const yPos = height / 2
|
||||
let arcPos = settings.initalAngleRad
|
||||
context.reset()
|
||||
if(settings.ringSpecModel) {
|
||||
for (let i=0; i<settings.ringSpecModel.count; i++) {
|
||||
const segment = settings.ringSpecModel.get(i)
|
||||
printArcSegment(context,
|
||||
xPos,
|
||||
yPos,
|
||||
radius,
|
||||
settings.distinctiveColors[segment.colorId],
|
||||
arcPos,
|
||||
segment.segmentLength * identiconRing.segmentRadLen,
|
||||
settings.ringPxSize)
|
||||
arcPos += segment.segmentLength * identiconRing.segmentRadLen
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -6,56 +6,231 @@ 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
|
||||
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
|
||||
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: implicitHeight
|
||||
width: implicitWidth
|
||||
/*!
|
||||
\qmlproperty alias StatusInput::input
|
||||
This property holds a reference to the TextEdit component.
|
||||
*/
|
||||
property alias input: statusBaseInput
|
||||
property alias valid: statusBaseInput.valid
|
||||
property alias pending: statusBaseInput.pending
|
||||
property alias text: statusBaseInput.text
|
||||
property string label: ""
|
||||
property string secondaryLabel: ""
|
||||
property int charLimit: 0
|
||||
property string errorMessage: ""
|
||||
property real leftPadding: 16
|
||||
property real rightPadding: 16
|
||||
property list<StatusValidator> validators
|
||||
property list<StatusAsyncValidator> asyncValidators
|
||||
property int validationMode: StatusInput.ValidationMode.OnlyWhenDirty
|
||||
property string validatedValue
|
||||
|
||||
/*!
|
||||
\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
|
||||
Always, // validates input even before it has become dirty
|
||||
IgnoreInvalidInput // ignore the new content if it doesn't match
|
||||
}
|
||||
|
||||
/*!
|
||||
\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 = false
|
||||
statusBaseInput.valid = true
|
||||
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
|
||||
}
|
||||
@@ -85,11 +260,23 @@ 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) {
|
||||
@@ -106,7 +293,10 @@ Item {
|
||||
root.validatedValue = root.text
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod
|
||||
This function updates the text input async validation.
|
||||
*/
|
||||
function updateAsyncValidity(validatorName, value, result) {
|
||||
if (!asyncErrors) {
|
||||
asyncErrors = {}
|
||||
@@ -189,6 +379,13 @@ Item {
|
||||
onTextChanged: root.validate()
|
||||
|
||||
Keys.forwardTo: [root]
|
||||
onIconClicked: root.iconClicked()
|
||||
onKeyPressed: {
|
||||
root.keyPressed(event);
|
||||
}
|
||||
onEditChanged: {
|
||||
root.editClicked();
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
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,27 +4,110 @@ 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
|
||||
property string labelVeryWeak: "Very weak"
|
||||
property string labelWeak: "Weak"
|
||||
property string labelSoso: "So-so"
|
||||
property string labelGood: "Good"
|
||||
property string labelGreat: "Great"
|
||||
/*!
|
||||
\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")
|
||||
|
||||
enum Strength {
|
||||
VeryWeak, // 0
|
||||
Weak, // 1
|
||||
SoSo, // 2
|
||||
Good, // 3
|
||||
Great // 4
|
||||
None, // 0
|
||||
VeryWeak, // 1
|
||||
Weak, // 2
|
||||
SoSo, // 3
|
||||
Good, // 4
|
||||
Great // 5
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
@@ -11,7 +11,14 @@ Button {
|
||||
|
||||
property color bgColor: Theme.palette.baseColor2
|
||||
property color contentColor: Theme.palette.baseColor1
|
||||
signal clicked()
|
||||
property var type: StatusPickerButton.Type.Next
|
||||
property int lateralMargins: 16
|
||||
property int textPixelSize: 15
|
||||
|
||||
enum Type {
|
||||
Next,
|
||||
Down
|
||||
}
|
||||
|
||||
background: Item {
|
||||
anchors.fill: parent
|
||||
@@ -20,31 +27,57 @@ 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 {
|
||||
fill: parent
|
||||
leftMargin: 16
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - icon.width - anchors.rightMargin - anchors.leftMargin - icon.anchors.rightMargin - icon.anchors.leftMargin
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pixelSize: 15
|
||||
font.pixelSize: root.textPixelSize
|
||||
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 {
|
||||
@@ -52,8 +85,6 @@ Button {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.clicked();
|
||||
}
|
||||
onClicked: { root.clicked() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,24 @@ 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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
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 verticalAlignmet: edit.verticalAlignment
|
||||
property alias verticalAlignment: edit.verticalAlignment
|
||||
property alias horizontalAlignment: edit.horizontalAlignment
|
||||
|
||||
signal linkActivated(string link)
|
||||
|
||||
@@ -6,37 +6,35 @@ 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: 4
|
||||
implicitHeight: Math.max(handle.implicitHeight, background.implicitHeight)
|
||||
|
||||
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: {
|
||||
if (statusSlider.value === statusSlider.to) {
|
||||
return Theme.palette.primaryColor1
|
||||
}
|
||||
return Theme.palette.primaryColor3
|
||||
}
|
||||
color: 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
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ TabBar {
|
||||
|
||||
background: Rectangle {
|
||||
implicitHeight: 36
|
||||
color: Theme.palette.directColor7
|
||||
color: Theme.palette.statusSwitchTab.barBackgroundColor
|
||||
radius: 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ TabButton {
|
||||
StatusBaseText {
|
||||
id: label
|
||||
text: statusSwitchTabButton.text
|
||||
color: Theme.palette.primaryColor1
|
||||
color: statusSwitchTabButton.checked ?
|
||||
Theme.palette.statusSwitchTab.selectedTextColor :
|
||||
Theme.palette.statusSwitchTab.textColor
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 15
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@@ -36,7 +38,7 @@ TabButton {
|
||||
implicitHeight: 36
|
||||
implicitWidth: 148
|
||||
color: statusSwitchTabButton.checked ?
|
||||
Theme.palette.statusSwitchTab.backgroundColor :
|
||||
Theme.palette.statusSwitchTab.buttonBackgroundColor :
|
||||
"transparent"
|
||||
radius: 8
|
||||
layer.enabled: true
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
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 { }
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
/*!
|
||||
\qmltype StatusTabButton
|
||||
\inherits TabButton
|
||||
\inqmlmodule StatusQ.Controls
|
||||
\since StatusQ.Controls 0.1
|
||||
\brief StatusTabButton is used in conjunction with a StatusTabBar
|
||||
|
||||
It's customized from Qt's \l{https://doc.qt.io/qt-6/qml-qtquick-controls2-tabbutton.html}{TabButton}, adding:
|
||||
- transparent background
|
||||
- theme-styled text
|
||||
- styled underscore for active state
|
||||
- `StatusBadge` to the right from the text
|
||||
|
||||
An alias `badge` property is added to control the `StatusBadge` behaviour and content.
|
||||
*/
|
||||
|
||||
TabButton {
|
||||
id: root
|
||||
|
||||
property alias badge: statusBadge
|
||||
|
||||
leftPadding: 12
|
||||
rightPadding: 12
|
||||
|
||||
background: Item { }
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: contentItemGrid.implicitWidth
|
||||
implicitHeight: contentItemGrid.implicitHeight + 11
|
||||
|
||||
RowLayout {
|
||||
id: contentItemGrid
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
elide: Qt.ElideRight
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 15
|
||||
color: root.checked || root.hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||
text: root.text
|
||||
}
|
||||
|
||||
StatusBadge {
|
||||
id: statusBadge
|
||||
Layout.leftMargin: 10
|
||||
visible: value > 0
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: root.checked || root.hovered
|
||||
implicitWidth: 24
|
||||
implicitHeight: 2
|
||||
radius: 4
|
||||
color: root.checked ? Theme.palette.primaryColor1 : Theme.palette.primaryColor2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
/*!
|
||||
\qmltype StatusTokenInlineSelector
|
||||
\inherits RowLayout
|
||||
\inqmlmodule StatusQ.Controls
|
||||
\since StatusQ.Controls 0.1
|
||||
\brief It presents selectable tokens in inline form.
|
||||
|
||||
The \c StatusTokenInlineSelector is a graphical component that is meant to be used as inline text input.
|
||||
|
||||
Example of how the component looks like:
|
||||
\image status_token_inline_selector.png
|
||||
*/
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
/*!
|
||||
\qmlproperty var StatusTokenInlineSelector::tokens
|
||||
This is a REQUIRED property that contains an array of objects that describes each token.
|
||||
|
||||
\qml
|
||||
tokens: [{amount: 0.1, token: "ETH"}, {amount: 10, token: "SNT"}]
|
||||
\endqml
|
||||
*/
|
||||
property var tokens
|
||||
|
||||
/*!
|
||||
\qmlproperty var StatusTokenInlineSelector::tokenImageSourceGetter
|
||||
This is a property function used to acquire image source for given token.
|
||||
*/
|
||||
property var tokenImageSourceGetter: function (token) {
|
||||
return "../../assets/img/icons/%1.png".arg(token)
|
||||
}
|
||||
|
||||
signal triggered(real amount, string token)
|
||||
|
||||
StatusBaseText {
|
||||
text: qsTr("Hold")
|
||||
color: Theme.palette.directColor6
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Math.max(0, root.tokens.length - 1)
|
||||
delegate: Loader {
|
||||
sourceComponent: tokenComponent
|
||||
onLoaded: d.assignItemProperties(item, index, root.tokens[index])
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
text: qsTr("or")
|
||||
color: Theme.palette.directColor6
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.tokens.length > 1
|
||||
sourceComponent: tokenComponent
|
||||
onLoaded: {
|
||||
const index = root.tokens.length - 1
|
||||
d.assignItemProperties(item, index, root.tokens[index])
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
text: qsTr("to post")
|
||||
color: Theme.palette.directColor6
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
function assignItemProperties(item, index, modelData) {
|
||||
item.tokenImageSource = root.tokenImageSourceGetter(modelData.token)
|
||||
item.token = modelData.token
|
||||
item.amount = modelData.amount
|
||||
item.backgroundColor = Qt.binding(() => index % 2 ? Theme.palette.primaryColor3 : Theme.palette.dangerColor3)
|
||||
item.hoverColor = Qt.binding(() => index % 2 ? Theme.palette.primaryColor2 : Theme.palette.dangerColor2)
|
||||
item.textColor = Qt.binding(() => index % 2 ? Theme.palette.primaryColor1 : Theme.palette.dangerColor1)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: tokenComponent
|
||||
|
||||
Rectangle {
|
||||
property string amount
|
||||
property string token
|
||||
property string tokenImageSource
|
||||
property color backgroundColor
|
||||
property color hoverColor
|
||||
property color textColor
|
||||
|
||||
width: content.width + 4
|
||||
height: content.height + 4
|
||||
|
||||
radius: height/2
|
||||
|
||||
color: mouseArea.containsMouse ? hoverColor : backgroundColor
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
StatusRoundedImage {
|
||||
id: roundedImage
|
||||
Layout.maximumHeight: text.height + 2
|
||||
Layout.maximumWidth: text.height + 2
|
||||
image.source: tokenImageSource
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: text
|
||||
text: amount + " " + token
|
||||
color: textColor
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Item {
|
||||
implicitWidth: 2
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: pressIndicator
|
||||
|
||||
height: parent.height
|
||||
radius: height / 2
|
||||
color: textColor
|
||||
opacity: 0.1
|
||||
|
||||
NumberAnimation on width {
|
||||
from: 0
|
||||
to: parent.width
|
||||
duration: 800
|
||||
running: mouseArea.containsPress
|
||||
|
||||
onStopped: {
|
||||
if (pressIndicator.width == parent.width) {
|
||||
root.triggered(amount, token)
|
||||
}
|
||||
pressIndicator.width = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import StatusQ.Controls 0.1
|
||||
Item {
|
||||
id: control
|
||||
property string selectedColor
|
||||
property string label: "Account color"
|
||||
property string label: qsTr("Account color")
|
||||
property var model
|
||||
property bool enabled: true
|
||||
|
||||
@@ -45,7 +45,7 @@ Item {
|
||||
return true
|
||||
}
|
||||
// Check the colors in the other theme
|
||||
const currentColor = Utils.getThemeAccountColor(upperCaseColor, Theme.palette.accountColors)
|
||||
const currentColor = Utils.getThemeAccountColor(upperCaseColor, Theme.palette.userCustomizationColors)
|
||||
if (!currentColor) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import StatusQ.Core.Utils 0.1
|
||||
StatusValidator {
|
||||
name: "addressOrEns"
|
||||
|
||||
errorMessage: "Please enter a valid address or ENS name."
|
||||
errorMessage: qsTr("Please enter a valid address or ENS name.")
|
||||
|
||||
validate: function (t) {
|
||||
return Utils.isValidAddress(t) || Utils.isValidEns(t) ?
|
||||
|
||||
@@ -4,7 +4,7 @@ import StatusQ.Core.Utils 0.1
|
||||
StatusValidator {
|
||||
name: "address"
|
||||
|
||||
errorMessage: "Please enter a valid address."
|
||||
errorMessage: qsTr("Please enter a valid address.")
|
||||
|
||||
validate: function (t) {
|
||||
return Utils.isValidAddress(t) ? true : { actual: t }
|
||||
|
||||
@@ -6,7 +6,7 @@ StatusAsyncValidator {
|
||||
|
||||
name: "asyncEns"
|
||||
|
||||
errorMessage: "ENS name could not be resolved in to an address"
|
||||
errorMessage: qsTr("ENS name could not be resolved in to an address")
|
||||
|
||||
validate: function (asyncResult) {
|
||||
return Utils.isValidAddress(asyncResult)
|
||||
|
||||
@@ -5,7 +5,7 @@ Item {
|
||||
id: statusAsyncValidator
|
||||
|
||||
property string name: ""
|
||||
property string errorMessage: "invalid input"
|
||||
property string errorMessage: qsTr("invalid input")
|
||||
signal asyncComplete(var result)
|
||||
signal validationComplete(var value, bool valid)
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import QtQuick 2.14
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
/*!
|
||||
\qmltype StatusFloatValidator
|
||||
\inherits StatusValidator
|
||||
\inqmlmodule StatusQ.Controls.Validators
|
||||
\since StatusQ.Controls.Validators 0.1
|
||||
\brief The StatusFloatValidator.qml type provides a validator for float values.
|
||||
|
||||
The \c StatusFloatValidator type provides a validator for float values.
|
||||
|
||||
Example of how to use it:
|
||||
|
||||
\qml
|
||||
StatusFloatValidator {
|
||||
bottom: 0
|
||||
top: 1.25
|
||||
errorMessage: qsTr("This is an invalid numeric value")
|
||||
}
|
||||
\endqml
|
||||
|
||||
For a list of components available see StatusQ.
|
||||
*/
|
||||
StatusValidator {
|
||||
id: root
|
||||
|
||||
/*!
|
||||
\qmlproperty string StatusFloatValidator::bottom
|
||||
This property holds the validator's lowest acceptable value. By default, this property's value is derived from the lowest signed float available (typically -2147483647).
|
||||
*/
|
||||
property real bottom
|
||||
|
||||
/*!
|
||||
\qmlproperty string StatusFloatValidator::locale
|
||||
This property holds the name of the locale used to interpret the number.
|
||||
*/
|
||||
property string locale
|
||||
|
||||
/*!
|
||||
\qmlproperty string StatusFloatValidator::top
|
||||
This property holds the validator's highest acceptable value. By default, this property's value is derived from the highest float available (typically 2147483647).
|
||||
*/
|
||||
property real top
|
||||
|
||||
name: "floatValidator"
|
||||
errorMessage: qsTr("Please enter a valid numeric value.")
|
||||
|
||||
validate: function (t) {
|
||||
return !isNaN(t) && t >= bottom && t <= top ? true : {
|
||||
bottom: bottom,
|
||||
top: top,
|
||||
actual: t
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ StatusValidator {
|
||||
property int top
|
||||
|
||||
name: "intValidator"
|
||||
errorMessage: "Please enter a valid numeric value."
|
||||
errorMessage: qsTr("Please enter a valid numeric value.")
|
||||
validatorObj: IntValidator { bottom: root.bottom; locale: root.locale; top: root.top }
|
||||
|
||||
validate: function (t) {
|
||||
|
||||
@@ -50,11 +50,11 @@ StatusValidator {
|
||||
property var regularExpression
|
||||
|
||||
name: "regex"
|
||||
errorMessage: "Please enter a valid regular expression."
|
||||
errorMessage: `Must match regex(${validatorObj.regularExpression.toString()})`
|
||||
validatorObj: RegularExpressionValidator { regularExpression: root.regularExpression }
|
||||
|
||||
validate: function (t) {
|
||||
validate: function (value) {
|
||||
// Basic validation management
|
||||
return root.validatorObj.validate() === RegularExpressionValidator.Acceptable
|
||||
return root.validatorObj.regularExpression.test(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ StatusValidator {
|
||||
|
||||
name: "url"
|
||||
|
||||
errorMessage: "Please enter a valid URL"
|
||||
errorMessage: qsTr("Please enter a valid URL")
|
||||
|
||||
validate: function (value) {
|
||||
return Utils.isURL(value);
|
||||
|
||||
@@ -5,7 +5,7 @@ QtObject {
|
||||
id: statusValidator
|
||||
|
||||
property string name: ""
|
||||
property string errorMessage: "invalid input"
|
||||
property string errorMessage: qsTr("invalid input")
|
||||
property var validatorObj
|
||||
|
||||
property var validate: function (value) {
|
||||
|
||||
@@ -10,3 +10,4 @@ StatusMaxLengthValidator 0.1 StatusMaxLengthValidator.qml
|
||||
StatusRegularExpressionValidator 0.1 StatusRegularExpressionValidator.qml
|
||||
StatusUrlValidator 0.1 StatusUrlValidator.qml
|
||||
StatusValidator 0.1 StatusValidator.qml
|
||||
StatusFloatValidator 0.1 StatusFloatValidator.qml
|
||||
|
||||
@@ -9,6 +9,7 @@ StatusChatListCategoryItemButton 0.1 StatusChatListCategoryItemButton.qml
|
||||
StatusColorSelector 0.1 StatusColorSelector.qml
|
||||
StatusIconTabButton 0.1 StatusIconTabButton.qml
|
||||
StatusIdenticonRing 0.1 StatusIdenticonRing.qml
|
||||
StatusItemPicker 0.1 StatusItemPicker.qml
|
||||
StatusNavBarTabButton 0.1 StatusNavBarTabButton.qml
|
||||
StatusTabBarIconButton 0.1 StatusTabBarIconButton.qml
|
||||
StatusToolTip 0.1 StatusToolTip.qml
|
||||
@@ -31,5 +32,11 @@ StatusPasswordStrengthIndicator 0.1 StatusPasswordStrengthIndicator.qml
|
||||
StatusSwitchTabButton 0.1 StatusSwitchTabButton.qml
|
||||
StatusSwitchTabBar 0.1 StatusSwitchTabBar.qml
|
||||
StatusSelectableText 0.1 StatusSelectableText.qml
|
||||
StatusTabBar 0.1 StatusTabBar.qml
|
||||
StatusTabButton 0.1 StatusTabButton.qml
|
||||
StatusTokenInlineSelector 0.1 StatusTokenInlineSelector.qml
|
||||
StatusWalletColorButton 0.1 StatusWalletColorButton.qml
|
||||
StatusWalletColorSelect 0.1 StatusWalletColorSelect.qml
|
||||
StatusColorSelectorGrid 0.1 StatusColorSelectorGrid.qml
|
||||
StatusSeedPhraseInput 0.1 StatusSeedPhraseInput.qml
|
||||
StatusImageCrop 0.1 StatusImageCrop.qml
|
||||
|
||||
@@ -14,7 +14,7 @@ import StatusQ.Core.Theme 0.1
|
||||
\qml
|
||||
StatusBaseText {
|
||||
width: 240
|
||||
text: "Hello World!"
|
||||
text: qsTr("Hello World!")
|
||||
font.pixelSize: 24
|
||||
color: Theme.pallete.directColor1
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import QtQuick 2.13
|
||||
import StatusQ.Core 0.1
|
||||
|
||||
QtObject {
|
||||
id: statusIconSettings
|
||||
id: root
|
||||
|
||||
property string name
|
||||
property real width
|
||||
@@ -12,6 +12,14 @@ QtObject {
|
||||
property url source
|
||||
property int rotation
|
||||
property bool isLetterIdenticon
|
||||
property int letterSize
|
||||
property int letterSize: emoji ? 11 : (charactersLen == 1 ? _oneLetterSize : _twoLettersSize)
|
||||
property int charactersLen: 1
|
||||
property string emoji
|
||||
property string emojiSize: _emojiSize
|
||||
property StatusIconBackgroundSettings background: StatusIconBackgroundSettings {}
|
||||
|
||||
// can't have QtObject { id: d } inside QtObject, using '_' to indicate private
|
||||
readonly property int _oneLetterSize: Math.max(15, root.width / 2.0)
|
||||
readonly property int _twoLettersSize: Math.max(12, root.width / 2.2)
|
||||
readonly property string _emojiSize: "%1x%1".arg(Math.max(16, root.width * 0.6))
|
||||
}
|
||||
|
||||
@@ -14,29 +14,15 @@ import StatusQ.Core 0.1
|
||||
|
||||
\qml
|
||||
StatusIdenticonRingSettings {
|
||||
totalRingUnits: 25
|
||||
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}]
|
||||
ringSpecModel: [ {colorId: 0; segmentLength: 1},
|
||||
{colorId: 5; segmentLength: 1},
|
||||
{colorId: 1; segmentLength: 1},
|
||||
{colorId: 1; segmentLength: 4},
|
||||
{colorId: 2; segmentLength: 3} ]
|
||||
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"]
|
||||
"#E7E7E7", "#FFFFFF", "#00FF00" ]
|
||||
}
|
||||
\endqml
|
||||
|
||||
@@ -47,10 +33,18 @@ QtObject {
|
||||
|
||||
/*!
|
||||
\qmlproperty ListModel StatusIdenticonRingSettings::ringSpecModel
|
||||
This is a REQUIRED property that contains a ListModel object that describes each ring segment color and length.
|
||||
Example of use: [ListElement {colorId: 0; segmentLength: 1}, ...]
|
||||
This is a REQUIRED property that contains a ListModel or array of objects that describes each ring segment color and length.
|
||||
|
||||
Examples:
|
||||
\qml
|
||||
ringSpecModel: ListModel {ListElement{colorId: 0; segmentLength: 1} ListElement{colorId: 1; segmentLength: 2}}
|
||||
\endqml
|
||||
|
||||
\qml
|
||||
ringSpecModel: [{colorId: 0, segmentLength: 1}, {colorId: 1, segmentLength: 2}]
|
||||
\endqml
|
||||
*/
|
||||
property ListModel ringSpecModel
|
||||
property var ringSpecModel
|
||||
|
||||
/*!
|
||||
\qmlproperty var StatusIdenticonRingSettings::distinctiveColors
|
||||
@@ -58,12 +52,6 @@ QtObject {
|
||||
*/
|
||||
property var distinctiveColors
|
||||
|
||||
/*!
|
||||
\qmlproperty real StatusIdenticonRingSettings::totalRingUnits
|
||||
This property provides the total number of units the identicon ring is composed by.
|
||||
*/
|
||||
property real totalRingUnits
|
||||
|
||||
/*!
|
||||
\qmlproperty real StatusIdenticonRingSettings::initalAngleRad
|
||||
This property provides the initial angle, in radians, the identicon ring will start rendering the line.
|
||||
|
||||
@@ -147,15 +147,19 @@ ThemePalette {
|
||||
miscColor11: getColor('yellow2')
|
||||
miscColor12: getColor('green6')
|
||||
|
||||
accountColors: [
|
||||
getColor('blue5'),
|
||||
getColor('yellow2'),
|
||||
"#E6ABFC",
|
||||
getColor('moss2'),
|
||||
"#FB8383",
|
||||
getColor('green4'),
|
||||
"#ADA3FF",
|
||||
getColor('brown3')
|
||||
userCustomizationColors: [
|
||||
"#AAC6FF",
|
||||
"#887AF9",
|
||||
"#51D0F0",
|
||||
"#D37EF4",
|
||||
"#FA6565",
|
||||
"#FFCA0F",
|
||||
"#93DB33",
|
||||
"#10A88E",
|
||||
"#AD4343",
|
||||
"#EAD27B",
|
||||
"silver", // update me when figma is ready
|
||||
"darkgrey", // update me when figma is ready
|
||||
]
|
||||
|
||||
identiconRingColors: ["#000000", "#726F6F", "#C4C4C4", "#E7E7E7", "#FFFFFF", "#00FF00",
|
||||
@@ -226,7 +230,10 @@ ThemePalette {
|
||||
}
|
||||
|
||||
property QtObject statusSwitchTab: QtObject {
|
||||
property color backgroundColor: baseColor3
|
||||
property color buttonBackgroundColor: primaryColor1
|
||||
property color barBackgroundColor: primaryColor3
|
||||
property color selectedTextColor: white
|
||||
property color textColor: primaryColor1
|
||||
}
|
||||
|
||||
property QtObject statusSelect: QtObject {
|
||||
|
||||