HLabeledTextField → extensible HLabeledItem

This commit is contained in:
miruka 2020-06-02 20:14:55 -04:00
parent fdaf7089ab
commit e676473f82
11 changed files with 158 additions and 102 deletions

View File

@ -37,7 +37,7 @@ and this project adheres to
- Removed delay when multiple rooms are removed/hidden from the list. - Removed delay when multiple rooms are removed/hidden from the list.
This should provide a smoother experience when filtering rooms or collapsing This should provide a smoother experience when filtering rooms or collapsing
accounts. accounts, and prevent the account duplication bug.
If you encounter issues with these operations like the room list becoming If you encounter issues with these operations like the room list becoming
invisible, make sure first that your Qt installation is up-to-date invisible, make sure first that your Qt installation is up-to-date
(latest x.y.Z version, e.g. 5.14.2). (latest x.y.Z version, e.g. 5.14.2).

View File

@ -1,5 +1,12 @@
# TODO # TODO
- fix cursor over field
- update room highlight when creating new room
- unknownerror matrixnotfound when creatign directchat
- keyerror when forgetting room while loading members
- account order, and verify not adding to config fiel works
- Refetch profile after manual profile change, don't wait for a room event
## Refactoring ## Refactoring
- Rewrite account settings using `HTabbedContainer` - Rewrite account settings using `HTabbedContainer`
@ -155,7 +162,6 @@
## Backend ## Backend
- Saving the room settings - Saving the room settings
- Refetch profile after manual profile change, don't wait for a room event
- Better config file format - Better config file format

View File

@ -7,10 +7,12 @@ HColumnLayout {
spacing: theme.spacing / 2 spacing: theme.spacing / 2
property alias label: label default property alias insideData: itemHolder.data
property alias errorLabel: errorLabel
property alias field: field readonly property Item item: itemHolder.visibleChildren[0]
property alias toolTip: toolTip readonly property alias label: label
readonly property alias errorLabel: errorLabel
readonly property alias toolTip: toolTip
HRowLayout { HRowLayout {
@ -49,9 +51,10 @@ HColumnLayout {
} }
} }
HTextField { Item {
id: field id: itemHolder
radius: 2 // implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
Layout.fillWidth: true Layout.fillWidth: true
} }

View File

@ -12,7 +12,7 @@ HGridLayout {
saveButton.nameChangeRunning = true saveButton.nameChangeRunning = true
py.callClientCoro( py.callClientCoro(
userId, "set_displayname", [nameField.field.text], () => { userId, "set_displayname", [nameField.item.text], () => {
saveButton.nameChangeRunning = false saveButton.nameChangeRunning = false
accountSettings.headerName = accountSettings.headerName =
Qt.binding(() => accountInfo.display_name) Qt.binding(() => accountInfo.display_name)
@ -21,7 +21,7 @@ HGridLayout {
} }
if (aliasField.changed) { if (aliasField.changed) {
window.settings.writeAliases[userId] = aliasField.field.text window.settings.writeAliases[userId] = aliasField.item.text
window.settingsChanged() window.settingsChanged()
} }
@ -41,8 +41,8 @@ HGridLayout {
} }
function cancelChanges() { function cancelChanges() {
nameField.field.text = accountInfo.display_name nameField.item.text = accountInfo.display_name
aliasField.field.text = aliasField.currentAlias aliasField.item.text = aliasField.currentAlias
fileDialog.selectedFile = "" fileDialog.selectedFile = ""
fileDialog.file = "" fileDialog.file = ""
@ -53,14 +53,14 @@ HGridLayout {
flow: pageLoader.isWide ? GridLayout.LeftToRight : GridLayout.TopToBottom flow: pageLoader.isWide ? GridLayout.LeftToRight : GridLayout.TopToBottom
rowSpacing: currentSpacing rowSpacing: currentSpacing
Component.onCompleted: nameField.field.forceActiveFocus() Component.onCompleted: nameField.item.forceActiveFocus()
HUserAvatar { HUserAvatar {
property bool changed: Boolean(sourceOverride) property bool changed: Boolean(sourceOverride)
id: avatar id: avatar
userId: accountSettings.userId userId: accountSettings.userId
displayName: nameField.field.text displayName: nameField.item.text
mxc: accountInfo.avatar_url mxc: accountInfo.avatar_url
toolTipMxc: "" toolTipMxc: ""
sourceOverride: fileDialog.selectedFile || fileDialog.file sourceOverride: fileDialog.selectedFile || fileDialog.file
@ -155,36 +155,38 @@ HGridLayout {
Layout.fillWidth: true Layout.fillWidth: true
} }
HLabeledTextField { HLabeledItem {
property bool changed: field.text !== accountInfo.display_name property bool changed: item.text !== accountInfo.display_name
readonly property string fText: field.text
onFTextChanged: accountSettings.headerName = field.text
id: nameField id: nameField
label.text: qsTr("Display name:") label.text: qsTr("Display name:")
field.maximumLength: 255
field.onAccepted: applyChanges()
Component.onCompleted: field.text = accountInfo.display_name
Keys.onEscapePressed: cancelChanges()
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: 480 Layout.maximumWidth: 480
HTextField {
width: parent.width
maximumLength: 255
onAccepted: applyChanges()
onTextChanged: accountSettings.headerName = text
Component.onCompleted: text = accountInfo.display_name
Keys.onEscapePressed: cancelChanges()
}
} }
HLabeledTextField { HLabeledItem {
property string currentAlias: aliases[userId] || "" property string currentAlias: aliases[userId] || ""
property bool changed: field.text !== currentAlias property bool changed: item.text !== currentAlias
readonly property var aliases: window.settings.writeAliases readonly property var aliases: window.settings.writeAliases
readonly property string alreadyTakenBy: { readonly property string alreadyTakenBy: {
if (! field.text) return "" if (! item.text) return ""
for (const [id, idAlias] of Object.entries(aliases)) for (const [id, idAlias] of Object.entries(aliases))
if (id !== userId && idAlias === field.text) return id if (id !== userId && idAlias === item.text) return id
return "" return ""
} }
@ -199,14 +201,6 @@ HGridLayout {
qsTr("Taken by %1").arg(alreadyTakenBy) : qsTr("Taken by %1").arg(alreadyTakenBy) :
"" ""
field.error: alreadyTakenBy !== ""
field.onAccepted: applyChanges()
field.placeholderText: qsTr("e.g. %1").arg((
nameField.field.text ||
accountInfo.display_name ||
userId.substring(1)
)[0])
toolTip.text: qsTr( toolTip.text: qsTr(
"From any chat, start a message with specified alias " + "From any chat, start a message with specified alias " +
"followed by a space to type and send as this " + "followed by a space to type and send as this " +
@ -215,12 +209,23 @@ HGridLayout {
"To ignore the alias when typing, prepend it with a space." "To ignore the alias when typing, prepend it with a space."
) )
Component.onCompleted: field.text = currentAlias
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: 480 Layout.maximumWidth: 480
Keys.onEscapePressed: cancelChanges() HTextField {
width: parent.width
error: aliasField.alreadyTakenBy !== ""
onAccepted: applyChanges()
placeholderText: qsTr("e.g. %1").arg((
nameField.item.text ||
accountInfo.display_name ||
userId.substring(1)
)[0])
Component.onCompleted: text = aliasField.currentAlias
Keys.onEscapePressed: cancelChanges()
}
} }
} }

View File

@ -8,7 +8,7 @@ HBox {
id: signInBox id: signInBox
clickButtonOnEnter: "apply" clickButtonOnEnter: "apply"
onFocusChanged: idField.field.forceActiveFocus() onFocusChanged: idField.item.forceActiveFocus()
buttonModel: [ buttonModel: [
{ {
@ -31,8 +31,8 @@ HBox {
errorMessage.text = "" errorMessage.text = ""
const args = [ const args = [
idField.field.text.trim(), passwordField.field.text, idField.item.text.trim(), passwordField.item.text,
undefined, serverField.field.text.trim(), undefined, serverField.item.text.trim(),
] ]
loginFuture = py.callCoro("login_client", args, userId => { loginFuture = py.callCoro("login_client", args, userId => {
@ -86,8 +86,8 @@ HBox {
property string signInWith: "username" property string signInWith: "username"
readonly property bool canSignIn: readonly property bool canSignIn:
serverField.field.text.trim() && idField.field.text.trim() && serverField.item.text.trim() && idField.item.text.trim() &&
passwordField.field.text && ! serverField.field.error passwordField.item.text && ! serverField.item.error
Timer { Timer {
@ -128,7 +128,7 @@ HBox {
} }
} }
HLabeledTextField { HLabeledItem {
id: idField id: idField
label.text: qsTr( label.text: qsTr(
signInWith === "email" ? "Email:" : signInWith === "email" ? "Email:" :
@ -137,27 +137,30 @@ HBox {
) )
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
}
} }
HLabeledTextField { HLabeledItem {
id: passwordField id: passwordField
label.text: qsTr("Password:") label.text: qsTr("Password:")
field.echoMode: HTextField.Password
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
echoMode: HTextField.Password
}
} }
HLabeledTextField { HLabeledItem {
id: serverField id: serverField
label.text: qsTr("Homeserver:") label.text: qsTr("Homeserver:")
field.text: "https://matrix.org"
field.error: ! /.+:\/\/.+/.test(cleanText)
Layout.fillWidth: true Layout.fillWidth: true
readonly property string cleanText: field.text.toLowerCase().trim()
// 2019-11-11 https://www.hello-matrix.net/public_servers.php // 2019-11-11 https://www.hello-matrix.net/public_servers.php
readonly property var knownServers: [ readonly property var knownServers: [
"https://matrix.org", "https://matrix.org",
@ -177,7 +180,15 @@ HBox {
] ]
readonly property bool knownServerChosen: readonly property bool knownServerChosen:
knownServers.includes(cleanText) knownServers.includes(item.cleanText)
HTextField {
width: parent.width
text: "https://matrix.org"
error: ! /.+:\/\/.+/.test(cleanText)
readonly property string cleanText: text.toLowerCase().trim()
}
} }
HCheckBox { HCheckBox {

View File

@ -8,7 +8,7 @@ HBox {
id: addChatBox id: addChatBox
clickButtonOnEnter: "apply" clickButtonOnEnter: "apply"
onFocusChanged: nameField.field.forceActiveFocus() onFocusChanged: nameField.item.forceActiveFocus()
buttonModel: [ buttonModel: [
{ name: "apply", text: qsTr("Create"), iconName: "room-create" }, { name: "apply", text: qsTr("Create"), iconName: "room-create" },
@ -21,8 +21,8 @@ HBox {
errorMessage.text = "" errorMessage.text = ""
const args = [ const args = [
nameField.field.text, nameField.item.text,
topicField.field.text, topicField.item.text,
publicCheckBox.checked, publicCheckBox.checked,
encryptCheckBox.checked, encryptCheckBox.checked,
! blockOtherServersCheckBox.checked, ! blockOtherServersCheckBox.checked,
@ -40,8 +40,8 @@ HBox {
}, },
cancel: button => { cancel: button => {
nameField.field.text = "" nameField.item.text = ""
topicField.field.text = "" topicField.item.text = ""
publicCheckBox.checked = false publicCheckBox.checked = false
encryptCheckBox.checked = false encryptCheckBox.checked = false
blockOtherServersCheckBox.checked = false blockOtherServersCheckBox.checked = false
@ -57,7 +57,7 @@ HBox {
HRoomAvatar { HRoomAvatar {
id: avatar id: avatar
roomId: "" roomId: ""
displayName: nameField.field.text displayName: nameField.item.text
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: 128 Layout.preferredWidth: 128
@ -66,27 +66,35 @@ HBox {
CurrentUserAvatar { CurrentUserAvatar {
anchors.fill: parent anchors.fill: parent
z: 10 z: 10
opacity: nameField.field.text ? 0 : 1 opacity: nameField.item.text ? 0 : 1
visible: opacity > 0 visible: opacity > 0
Behavior on opacity { HNumberAnimation {} } Behavior on opacity { HNumberAnimation {} }
} }
} }
HLabeledTextField { HLabeledItem {
id: nameField id: nameField
label.text: qsTr("Name:") label.text: qsTr("Name:")
field.maximumLength: 255
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
maximumLength: 255
}
} }
HLabeledTextField { HLabeledItem {
id: topicField id: topicField
label.text: qsTr("Topic:") label.text: qsTr("Topic:")
field.placeholderText: qsTr("This room is about...")
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
placeholderText: qsTr("This room is about...")
}
} }
HCheckBox { HCheckBox {

View File

@ -8,14 +8,14 @@ HBox {
id: addChatBox id: addChatBox
clickButtonOnEnter: "apply" clickButtonOnEnter: "apply"
onFocusChanged: userField.field.forceActiveFocus() onFocusChanged: userField.item.forceActiveFocus()
buttonModel: [ buttonModel: [
{ {
name: "apply", name: "apply",
text: qsTr("Start chat"), text: qsTr("Start chat"),
iconName: "start-direct-chat", iconName: "start-direct-chat",
enabled: Boolean(userField.field.text.trim()) enabled: Boolean(userField.item.text.trim())
}, },
{ name: "cancel", text: qsTr("Cancel"), iconName: "cancel" }, { name: "cancel", text: qsTr("Cancel"), iconName: "cancel" },
] ]
@ -25,7 +25,7 @@ HBox {
button.loading = true button.loading = true
errorMessage.text = "" errorMessage.text = ""
const args = [userField.field.text.trim(), encryptCheckBox.checked] const args = [userField.item.text.trim(), encryptCheckBox.checked]
py.callClientCoro(userId, "new_direct_chat", args, roomId => { py.callClientCoro(userId, "new_direct_chat", args, roomId => {
button.loading = false button.loading = false
@ -52,8 +52,8 @@ HBox {
}, },
cancel: button => { cancel: button => {
userField.field.text = "" userField.item.text = ""
errorMessage.text = "" errorMessage.text = ""
pageLoader.showPrevious() pageLoader.showPrevious()
} }
}) })
@ -68,13 +68,17 @@ HBox {
Layout.preferredHeight: Layout.preferredWidth Layout.preferredHeight: Layout.preferredWidth
} }
HLabeledTextField { HLabeledItem {
id: userField id: userField
label.text: qsTr("Peer user ID:") label.text: qsTr("Peer user ID:")
field.placeholderText: qsTr("@example:matrix.org")
field.error: Boolean(errorMessage.text)
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
placeholderText: qsTr("@example:matrix.org")
error: Boolean(errorMessage.text)
}
} }
EncryptCheckBox { EncryptCheckBox {

View File

@ -8,14 +8,14 @@ HBox {
id: addChatBox id: addChatBox
clickButtonOnEnter: "apply" clickButtonOnEnter: "apply"
onFocusChanged: roomField.field.forceActiveFocus() onFocusChanged: roomField.item.forceActiveFocus()
buttonModel: [ buttonModel: [
{ {
name: "apply", name: "apply",
text: qsTr("Join"), text: qsTr("Join"),
iconName: "room-join", iconName: "room-join",
enabled: Boolean(roomField.field.text.trim()), enabled: Boolean(roomField.item.text.trim()),
}, },
{ name: "cancel", text: qsTr("Cancel"), iconName: "cancel" }, { name: "cancel", text: qsTr("Cancel"), iconName: "cancel" },
] ]
@ -25,7 +25,7 @@ HBox {
button.loading = true button.loading = true
errorMessage.text = "" errorMessage.text = ""
const args = [roomField.field.text.trim()] const args = [roomField.item.text.trim()]
py.callClientCoro(userId, "room_join", args, roomId => { py.callClientCoro(userId, "room_join", args, roomId => {
button.loading = false button.loading = false
@ -51,8 +51,8 @@ HBox {
}, },
cancel: button => { cancel: button => {
roomField.field.text = "" roomField.item.text = ""
errorMessage.text = "" errorMessage.text = ""
pageLoader.showPrevious() pageLoader.showPrevious()
} }
}) })
@ -67,13 +67,17 @@ HBox {
Layout.preferredHeight: Layout.preferredWidth Layout.preferredHeight: Layout.preferredWidth
} }
HLabeledTextField { HLabeledItem {
id: roomField id: roomField
label.text: qsTr("Alias, URL or room ID:") label.text: qsTr("Alias, URL or room ID:")
field.placeholderText: qsTr("#example:matrix.org")
field.error: Boolean(errorMessage.text)
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
placeholderText: qsTr("#example:matrix.org")
error: Boolean(errorMessage.text)
}
} }
HLabel { HLabel {

View File

@ -37,8 +37,8 @@ HBox {
saveFuture = null saveFuture = null
} }
nameField.field.reset() nameField.item.reset()
topicField.field.reset() topicField.item.reset()
encryptCheckBox.reset() encryptCheckBox.reset()
requireInviteCheckbox.reset() requireInviteCheckbox.reset()
forbidGuestsCheckBox.reset() forbidGuestsCheckBox.reset()
@ -49,11 +49,11 @@ HBox {
property var saveFuture: null property var saveFuture: null
readonly property bool anyChange: readonly property bool anyChange:
nameField.field.changed || topicField.field.changed || nameField.item.changed || topicField.item.changed ||
encryptCheckBox.changed || requireInviteCheckbox.changed || encryptCheckBox.changed || requireInviteCheckbox.changed ||
forbidGuestsCheckBox.changed forbidGuestsCheckBox.changed
readonly property Item keybindFocusItem: nameField.field readonly property Item keybindFocusItem: nameField.item
HRoomAvatar { HRoomAvatar {
@ -69,26 +69,33 @@ HBox {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
} }
HLabeledTextField { HLabeledItem {
id: nameField id: nameField
label.text: qsTr("Name:") label.text: qsTr("Name:")
field.maximumLength: 255
field.defaultText: chat.roomInfo.given_name
field.enabled: chat.roomInfo.can_set_name
Layout.fillWidth: true Layout.fillWidth: true
Component.onCompleted: field.forceActiveFocus() HTextField {
width: parent.width
maximumLength: 255
defaultText: chat.roomInfo.given_name
enabled: chat.roomInfo.can_set_name
Component.onCompleted: forceActiveFocus()
}
} }
HLabeledTextField { HLabeledItem {
id: topicField id: topicField
label.text: qsTr("Topic:") label.text: qsTr("Topic:")
field.placeholderText: qsTr("This room is about...")
field.defaultText: chat.roomInfo.plain_topic
field.enabled: chat.roomInfo.can_set_topic
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
placeholderText: qsTr("This room is about...")
defaultText: chat.roomInfo.plain_topic
enabled: chat.roomInfo.can_set_topic
}
} }
HCheckBox { HCheckBox {

View File

@ -23,7 +23,7 @@ BoxPopup {
okText: qsTr("Remove") okText: qsTr("Remove")
// box.focusButton: "ok" // box.focusButton: "ok"
onOpened: reasonField.field.forceActiveFocus() onOpened: reasonField.item.forceActiveFocus()
onOk: { onOk: {
const idsForSender = {} // {senderId: [event.id, ...]} const idsForSender = {} // {senderId: [event.id, ...]}
@ -38,7 +38,7 @@ BoxPopup {
py.callClientCoro( py.callClientCoro(
mainUI.accountIds.includes(senderId) ? senderId : preferUserId, mainUI.accountIds.includes(senderId) ? senderId : preferUserId,
"room_mass_redact", "room_mass_redact",
[roomId, reasonField.field.text, ...eventClientIds] [roomId, reasonField.item.text, ...eventClientIds]
) )
} }
@ -51,10 +51,14 @@ BoxPopup {
property bool isLast: false property bool isLast: false
HLabeledTextField { HLabeledItem {
id: reasonField id: reasonField
label.text: qsTr("Optional reason:") label.text: qsTr("Optional reason:")
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
}
} }
} }

View File

@ -24,12 +24,12 @@ BoxPopup {
qsTr("Ban") qsTr("Ban")
onOpened: reasonField.field.forceActiveFocus() onOpened: reasonField.item.forceActiveFocus()
onOk: py.callClientCoro( onOk: py.callClientCoro(
userId, userId,
operation === RemoveMemberPopup.Operation.Ban ? operation === RemoveMemberPopup.Operation.Ban ?
"room_ban" : "room_kick", "room_ban" : "room_kick",
[roomId, targetUserId, reasonField.field.text || null], [roomId, targetUserId, reasonField.item.text || null],
) )
@ -45,10 +45,14 @@ BoxPopup {
utils.coloredNameHtml(targetDisplayName, targetUserId) utils.coloredNameHtml(targetDisplayName, targetUserId)
HLabeledTextField { HLabeledItem {
id: reasonField id: reasonField
label.text: qsTr("Optional reason:") label.text: qsTr("Optional reason:")
Layout.fillWidth: true Layout.fillWidth: true
HTextField {
width: parent.width
}
} }
} }