mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-21 18:50:47 +03:00
Enable qmlformat in linting
This commit is contained in:
parent
ee6ee46e7c
commit
814d9c6a17
94 changed files with 4317 additions and 4984 deletions
|
@ -15,6 +15,14 @@ do
|
||||||
clang-format -i "$f"
|
clang-format -i "$f"
|
||||||
done;
|
done;
|
||||||
|
|
||||||
|
if command -v /usr/lib64/qt6/bin/qmlformat &> /dev/null; then
|
||||||
|
/usr/lib64/qt6/bin/qmlformat -i $QML_FILES
|
||||||
|
elif command -v /usr/lib/qt6/bin/qmlformat &> /dev/null; then
|
||||||
|
/usr/lib/qt6/bin/qmlformat -i $QML_FILES
|
||||||
|
else
|
||||||
|
echo "No qmlformat found, skipping check!"
|
||||||
|
fi
|
||||||
|
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|
||||||
if command -v /usr/lib64/qt6/bin/qmllint &> /dev/null; then
|
if command -v /usr/lib64/qt6/bin/qmllint &> /dev/null; then
|
||||||
|
|
|
@ -35,9 +35,9 @@ Page {
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
height: parent.height
|
height: parent.height
|
||||||
model: Communities.filtered()
|
model: Communities.filtered()
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
id: scrollbar
|
id: scrollbar
|
||||||
|
@ -138,10 +138,11 @@ Page {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredHeight: avatarSize
|
||||||
|
Layout.preferredWidth: avatarSize
|
||||||
color: communityItem.backgroundColor
|
color: communityItem.backgroundColor
|
||||||
displayName: model.displayName
|
displayName: model.displayName
|
||||||
enabled: false
|
enabled: false
|
||||||
Layout.preferredHeight: avatarSize
|
|
||||||
roomid: model.id
|
roomid: model.id
|
||||||
textColor: model.avatarUrl?.startsWith(":/") == true ? communityItem.unimportantText : communityItem.importantText
|
textColor: model.avatarUrl?.startsWith(":/") == true ? communityItem.unimportantText : communityItem.importantText
|
||||||
url: {
|
url: {
|
||||||
|
@ -152,7 +153,6 @@ Page {
|
||||||
else
|
else
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
Layout.preferredWidth: avatarSize
|
|
||||||
|
|
||||||
NotificationBubble {
|
NotificationBubble {
|
||||||
anchors.bottom: avatar.bottom
|
anchors.bottom: avatar.bottom
|
||||||
|
|
|
@ -149,10 +149,10 @@ Control {
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
displayName: model.displayName
|
|
||||||
enabled: false
|
|
||||||
Layout.preferredHeight: popup.avatarHeight
|
Layout.preferredHeight: popup.avatarHeight
|
||||||
Layout.preferredWidth: popup.avatarWidth
|
Layout.preferredWidth: popup.avatarWidth
|
||||||
|
displayName: model.displayName
|
||||||
|
enabled: false
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: model.userid
|
userid: model.userid
|
||||||
}
|
}
|
||||||
|
@ -180,14 +180,14 @@ Control {
|
||||||
visible: !!model.unicode
|
visible: !!model.unicode
|
||||||
}
|
}
|
||||||
Avatar {
|
Avatar {
|
||||||
|
Layout.preferredHeight: popup.avatarHeight
|
||||||
|
Layout.preferredWidth: popup.avatarWidth
|
||||||
crop: false
|
crop: false
|
||||||
displayName: model.shortcode
|
displayName: model.shortcode
|
||||||
enabled: false
|
enabled: false
|
||||||
Layout.preferredHeight: popup.avatarHeight
|
|
||||||
//userid: model.shortcode
|
//userid: model.shortcode
|
||||||
url: (model.url ? model.url : "").replace("mxc://", "image://MxcImage/")
|
url: (model.url ? model.url : "").replace("mxc://", "image://MxcImage/")
|
||||||
visible: !model.unicode
|
visible: !model.unicode
|
||||||
Layout.preferredWidth: popup.avatarWidth
|
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.leftMargin: Nheko.paddingSmall
|
Layout.leftMargin: Nheko.paddingSmall
|
||||||
|
@ -227,12 +227,12 @@ Control {
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
|
Layout.preferredHeight: popup.avatarHeight
|
||||||
|
Layout.preferredWidth: popup.avatarWidth
|
||||||
displayName: model.roomName
|
displayName: model.roomName
|
||||||
enabled: false
|
enabled: false
|
||||||
Layout.preferredHeight: popup.avatarHeight
|
|
||||||
roomid: model.roomid
|
roomid: model.roomid
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
Layout.preferredWidth: popup.avatarWidth
|
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
||||||
|
@ -251,12 +251,12 @@ Control {
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
|
Layout.preferredHeight: popup.avatarHeight
|
||||||
|
Layout.preferredWidth: popup.avatarWidth
|
||||||
displayName: model.roomName
|
displayName: model.roomName
|
||||||
enabled: false
|
enabled: false
|
||||||
Layout.preferredHeight: popup.avatarHeight
|
|
||||||
roomid: model.roomid
|
roomid: model.roomid
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
Layout.preferredWidth: popup.avatarWidth
|
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
||||||
|
|
|
@ -55,8 +55,8 @@ Popup {
|
||||||
id: replyPreview
|
id: replyPreview
|
||||||
|
|
||||||
eventId: mid
|
eventId: mid
|
||||||
userColor: TimelineManager.userColor(replyPreview.userId, palette.window)
|
|
||||||
maxWidth: parent.width
|
maxWidth: parent.width
|
||||||
|
userColor: TimelineManager.userColor(replyPreview.userId, palette.window)
|
||||||
}
|
}
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: roomTextInput
|
id: roomTextInput
|
||||||
|
@ -64,7 +64,7 @@ Popup {
|
||||||
color: palette.text
|
color: palette.text
|
||||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: event => {
|
||||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
completerPopup.up();
|
completerPopup.up();
|
||||||
|
|
|
@ -140,8 +140,8 @@ ColumnLayout {
|
||||||
id: blueBar
|
id: blueBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: palette.highlight
|
|
||||||
Layout.preferredHeight: 1
|
Layout.preferredHeight: 1
|
||||||
|
color: palette.highlight
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: blackBar
|
id: blackBar
|
||||||
|
|
|
@ -44,14 +44,14 @@ Rectangle {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : (CallManager.isOnCallOnOtherDevice ? qsTr("Already on a call") : qsTr("Place a call"))
|
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : (CallManager.isOnCallOnOtherDevice ? qsTr("Already on a call") : qsTr("Place a call"))
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.svg" : ":/icons/icons/ui/place-call.svg"
|
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.svg" : ":/icons/icons/ui/place-call.svg"
|
||||||
opacity: (CallManager.haveCallInvite || CallManager.isOnCallOnOtherDevice) ? 0.3 : 1
|
opacity: (CallManager.haveCallInvite || CallManager.isOnCallOnOtherDevice) ? 0.3 : 1
|
||||||
visible: CallManager.callsSupported && showAllButtons
|
visible: CallManager.callsSupported && showAllButtons
|
||||||
Layout.preferredWidth: 22
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (room) {
|
if (room) {
|
||||||
|
@ -72,13 +72,13 @@ Rectangle {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
ToolTip.text: qsTr("Send a file")
|
ToolTip.text: qsTr("Send a file")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/attach.svg"
|
image: ":/icons/icons/ui/attach.svg"
|
||||||
visible: showAllButtons
|
visible: showAllButtons
|
||||||
Layout.preferredWidth: 22
|
|
||||||
|
|
||||||
onClicked: room.input.openFileSelection()
|
onClicked: room.input.openFileSelection()
|
||||||
|
|
||||||
|
@ -393,13 +393,13 @@ Rectangle {
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
ToolTip.text: qsTr("Stickers")
|
ToolTip.text: qsTr("Stickers")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/sticky-note-solid.svg"
|
image: ":/icons/icons/ui/sticky-note-solid.svg"
|
||||||
visible: showAllButtons
|
visible: showAllButtons
|
||||||
Layout.preferredWidth: 22
|
|
||||||
|
|
||||||
onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function (row) {
|
onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function (row) {
|
||||||
room.input.sticker(row);
|
room.input.sticker(row);
|
||||||
|
@ -417,12 +417,12 @@ Rectangle {
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
ToolTip.text: qsTr("Emoji")
|
ToolTip.text: qsTr("Emoji")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/smile.svg"
|
image: ":/icons/icons/ui/smile.svg"
|
||||||
Layout.preferredWidth: 22
|
|
||||||
|
|
||||||
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, room.roomId, function (plaintext, markdown) {
|
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, room.roomId, function (plaintext, markdown) {
|
||||||
messageInput.insert(messageInput.cursorPosition, markdown);
|
messageInput.insert(messageInput.cursorPosition, markdown);
|
||||||
|
@ -438,13 +438,13 @@ Rectangle {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
ToolTip.text: qsTr("Send")
|
ToolTip.text: qsTr("Send")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/send.svg"
|
image: ":/icons/icons/ui/send.svg"
|
||||||
Layout.preferredWidth: 22
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
room.input.send();
|
room.input.send();
|
||||||
|
|
|
@ -16,8 +16,8 @@ Item {
|
||||||
|
|
||||||
property int availableWidth: width
|
property int availableWidth: width
|
||||||
property int padding: Nheko.paddingMedium
|
property int padding: Nheko.paddingMedium
|
||||||
property string searchString: ""
|
|
||||||
property Room roommodel: room
|
property Room roommodel: room
|
||||||
|
property string searchString: ""
|
||||||
|
|
||||||
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
||||||
Connections {
|
Connections {
|
||||||
|
@ -41,6 +41,7 @@ Item {
|
||||||
|
|
||||||
property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < chatRoot.availableWidth) ? Settings.timelineMaxWidth : chatRoot.availableWidth) - chatRoot.padding * 2 - (scrollbar.interactive ? scrollbar.width : 0)
|
property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < chatRoot.availableWidth) ? Settings.timelineMaxWidth : chatRoot.availableWidth) - chatRoot.padding * 2 - (scrollbar.interactive ? scrollbar.width : 0)
|
||||||
readonly property alias filteringInProgress: filteredTimeline.filteringInProgress
|
readonly property alias filteringInProgress: filteredTimeline.filteringInProgress
|
||||||
|
property int lastScrollPos: 0
|
||||||
|
|
||||||
ScrollBar.vertical: scrollbar
|
ScrollBar.vertical: scrollbar
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -49,6 +50,7 @@ Item {
|
||||||
//onModelChanged: if (room) room.sendReset()
|
//onModelChanged: if (room) room.sendReset()
|
||||||
//reuseItems: true
|
//reuseItems: true
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
delegate: Settings.bubbles ? bubbleMessageStyle : defaultMessageStyle
|
||||||
displayMarginBeginning: height / 4
|
displayMarginBeginning: height / 4
|
||||||
displayMarginEnd: height / 4
|
displayMarginEnd: height / 4
|
||||||
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
|
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
|
||||||
|
@ -56,35 +58,6 @@ Item {
|
||||||
spacing: 2
|
spacing: 2
|
||||||
verticalLayoutDirection: ListView.BottomToTop
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
|
||||||
property int lastScrollPos: 0
|
|
||||||
|
|
||||||
// Fixup the scroll position when the height changes. Without this, the view is kept around the center of the currently visible content, while we usually want to stick to the bottom.
|
|
||||||
onMovementEnded: lastScrollPos = (contentY+height)
|
|
||||||
onModelChanged: lastScrollPos = (contentY+height)
|
|
||||||
onHeightChanged: contentY = (lastScrollPos-height)
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: defaultMessageStyle
|
|
||||||
|
|
||||||
TimelineDefaultMessageStyle {
|
|
||||||
messageActions: messageActionsC
|
|
||||||
messageContextMenu: messageContextMenuC
|
|
||||||
replyContextMenu: replyContextMenuC
|
|
||||||
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Component {
|
|
||||||
id: bubbleMessageStyle
|
|
||||||
|
|
||||||
TimelineBubbleMessageStyle {
|
|
||||||
messageActions: messageActionsC
|
|
||||||
messageContextMenu: messageContextMenuC
|
|
||||||
replyContextMenu: replyContextMenuC
|
|
||||||
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Settings.bubbles ? bubbleMessageStyle : defaultMessageStyle
|
|
||||||
footer: Item {
|
footer: Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
|
@ -109,7 +82,32 @@ Item {
|
||||||
if (atYEnd && room)
|
if (atYEnd && room)
|
||||||
model.currentIndex = 0;
|
model.currentIndex = 0;
|
||||||
}
|
}
|
||||||
|
onHeightChanged: contentY = (lastScrollPos - height)
|
||||||
|
onModelChanged: lastScrollPos = (contentY + height)
|
||||||
|
|
||||||
|
// Fixup the scroll position when the height changes. Without this, the view is kept around the center of the currently visible content, while we usually want to stick to the bottom.
|
||||||
|
onMovementEnded: lastScrollPos = (contentY + height)
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: defaultMessageStyle
|
||||||
|
|
||||||
|
TimelineDefaultMessageStyle {
|
||||||
|
messageActions: messageActionsC
|
||||||
|
messageContextMenu: messageContextMenuC
|
||||||
|
replyContextMenu: replyContextMenuC
|
||||||
|
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component {
|
||||||
|
id: bubbleMessageStyle
|
||||||
|
|
||||||
|
TimelineBubbleMessageStyle {
|
||||||
|
messageActions: messageActionsC
|
||||||
|
messageContextMenu: messageContextMenuC
|
||||||
|
replyContextMenu: replyContextMenuC
|
||||||
|
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||||
|
}
|
||||||
|
}
|
||||||
TimelineFilter {
|
TimelineFilter {
|
||||||
id: filteredTimeline
|
id: filteredTimeline
|
||||||
|
|
||||||
|
@ -124,13 +122,13 @@ Item {
|
||||||
// use comma to update on scroll
|
// use comma to update on scroll
|
||||||
property alias model: row.model
|
property alias model: row.model
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
padding: Nheko.paddingSmall
|
|
||||||
visible: Settings.buttonsInTimeline && !!attached && (attached.hovered || hovered)
|
|
||||||
z: 10
|
|
||||||
parent: chat.contentItem
|
|
||||||
anchors.bottom: attached?.top
|
anchors.bottom: attached?.top
|
||||||
anchors.right: attached?.right
|
anchors.right: attached?.right
|
||||||
|
hoverEnabled: true
|
||||||
|
padding: Nheko.paddingSmall
|
||||||
|
parent: chat.contentItem
|
||||||
|
visible: Settings.buttonsInTimeline && !!attached && (attached.hovered || hovered)
|
||||||
|
z: 10
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
border.color: palette.buttonText
|
border.color: palette.buttonText
|
||||||
|
@ -200,6 +198,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
Layout.preferredWidth: 16
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Edit")
|
ToolTip.text: qsTr("Edit")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
@ -207,7 +206,6 @@ Item {
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/edit.svg"
|
image: ":/icons/icons/ui/edit.svg"
|
||||||
visible: !!row.model && row.model.isEditable
|
visible: !!row.model && row.model.isEditable
|
||||||
Layout.preferredWidth: 16
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (row.model.isEditable)
|
if (row.model.isEditable)
|
||||||
|
@ -217,13 +215,13 @@ Item {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: reactButton
|
id: reactButton
|
||||||
|
|
||||||
|
Layout.preferredWidth: 16
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("React")
|
ToolTip.text: qsTr("React")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/smile-add.svg"
|
image: ":/icons/icons/ui/smile-add.svg"
|
||||||
visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
|
visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
|
||||||
Layout.preferredWidth: 16
|
|
||||||
|
|
||||||
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(reactButton, room.roomId, function (plaintext, markdown) {
|
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(reactButton, room.roomId, function (plaintext, markdown) {
|
||||||
var event_id = row.model ? row.model.eventId : "";
|
var event_id = row.model ? row.model.eventId : "";
|
||||||
|
@ -232,28 +230,29 @@ Item {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
Layout.preferredWidth: 16
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: (row.model && row.model.threadId) ? qsTr("Reply in thread") : qsTr("New thread")
|
ToolTip.text: (row.model && row.model.threadId) ? qsTr("Reply in thread") : qsTr("New thread")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: (row.model && row.model.threadId) ? ":/icons/icons/ui/thread.svg" : ":/icons/icons/ui/new-thread.svg"
|
image: (row.model && row.model.threadId) ? ":/icons/icons/ui/thread.svg" : ":/icons/icons/ui/new-thread.svg"
|
||||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||||
Layout.preferredWidth: 16
|
|
||||||
|
|
||||||
onClicked: room.thread = (row.model.threadId || row.model.eventId)
|
onClicked: room.thread = (row.model.threadId || row.model.eventId)
|
||||||
}
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
Layout.preferredWidth: 16
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Reply")
|
ToolTip.text: qsTr("Reply")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/reply.svg"
|
image: ":/icons/icons/ui/reply.svg"
|
||||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||||
Layout.preferredWidth: 16
|
|
||||||
|
|
||||||
onClicked: room.reply = row.model.eventId
|
onClicked: room.reply = row.model.eventId
|
||||||
}
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
Layout.preferredWidth: 16
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Go to message")
|
ToolTip.text: qsTr("Go to message")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
@ -261,7 +260,6 @@ Item {
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/go-to.svg"
|
image: ":/icons/icons/ui/go-to.svg"
|
||||||
visible: !!row.model && filteredTimeline.filterByContent
|
visible: !!row.model && filteredTimeline.filterByContent
|
||||||
Layout.preferredWidth: 16
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
topBar.searchString = "";
|
topBar.searchString = "";
|
||||||
|
@ -271,12 +269,12 @@ Item {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: optionsButton
|
id: optionsButton
|
||||||
|
|
||||||
|
Layout.preferredWidth: 16
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Options")
|
ToolTip.text: qsTr("Options")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/options.svg"
|
image: ":/icons/icons/ui/options.svg"
|
||||||
Layout.preferredWidth: 16
|
|
||||||
|
|
||||||
onClicked: messageContextMenuC.show(row.model.eventId, row.model.threadId, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton)
|
onClicked: messageContextMenuC.show(row.model.eventId, row.model.threadId, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton)
|
||||||
}
|
}
|
||||||
|
@ -413,9 +411,9 @@ Item {
|
||||||
Component {
|
Component {
|
||||||
id: reportDialog
|
id: reportDialog
|
||||||
|
|
||||||
ReportMessage {}
|
ReportMessage {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("Go to &message")
|
text: qsTr("Go to &message")
|
||||||
|
@ -523,10 +521,13 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Report message")
|
|
||||||
enabled: visible
|
enabled: visible
|
||||||
|
text: qsTr("Report message")
|
||||||
|
|
||||||
onTriggered: function () {
|
onTriggered: function () {
|
||||||
var dialog = reportDialog.createObject(timelineRoot, {"eventId": messageContextMenu.eventId});
|
var dialog = reportDialog.createObject(timelineRoot, {
|
||||||
|
"eventId": messageContextMenu.eventId
|
||||||
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
dialog.forceActiveFocus();
|
dialog.forceActiveFocus();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
|
|
|
@ -50,8 +50,8 @@ Item {
|
||||||
name: "Visible"
|
name: "Visible"
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
screenSaver.visible: true
|
|
||||||
screenSaver.opacity: 1
|
screenSaver.opacity: 1
|
||||||
|
screenSaver.visible: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
|
|
|
@ -30,9 +30,9 @@ Rectangle {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: Nheko.paddingSmall
|
anchors.topMargin: Nheko.paddingSmall
|
||||||
eventId: room?.reply ?? ""
|
eventId: room?.reply ?? ""
|
||||||
|
maxWidth: parent.width - anchors.leftMargin - anchors.rightMargin
|
||||||
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
||||||
visible: room && room.reply
|
visible: room && room.reply
|
||||||
maxWidth: parent.width - anchors.leftMargin - anchors.rightMargin
|
|
||||||
}
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: closeReplyButton
|
id: closeReplyButton
|
||||||
|
|
|
@ -26,8 +26,8 @@ Page {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: Nheko.theme.separator
|
|
||||||
Layout.preferredHeight: 1
|
Layout.preferredHeight: 1
|
||||||
|
color: Nheko.theme.separator
|
||||||
}
|
}
|
||||||
Pane {
|
Pane {
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
@ -45,11 +45,11 @@ Page {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: Nheko.paddingMedium
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Start a new chat")
|
ToolTip.text: qsTr("Start a new chat")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
Layout.preferredWidth: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/add-square-button.svg"
|
image: ":/icons/icons/ui/add-square-button.svg"
|
||||||
|
|
||||||
|
@ -97,11 +97,11 @@ Page {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: Nheko.paddingMedium
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Room directory")
|
ToolTip.text: qsTr("Room directory")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
Layout.preferredWidth: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/room-directory.svg"
|
image: ":/icons/icons/ui/room-directory.svg"
|
||||||
visible: !collapsed
|
visible: !collapsed
|
||||||
|
@ -115,11 +115,11 @@ Page {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: Nheko.paddingMedium
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Search rooms (Ctrl+K)")
|
ToolTip.text: qsTr("Search rooms (Ctrl+K)")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
Layout.preferredWidth: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/search.svg"
|
image: ":/icons/icons/ui/search.svg"
|
||||||
ripple: false
|
ripple: false
|
||||||
|
@ -139,11 +139,11 @@ Page {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: Nheko.paddingMedium
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("User settings")
|
ToolTip.text: qsTr("User settings")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
Layout.preferredHeight: 22
|
|
||||||
Layout.preferredWidth: 22
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/settings.svg"
|
image: ":/icons/icons/ui/settings.svg"
|
||||||
ripple: false
|
ripple: false
|
||||||
|
@ -268,37 +268,45 @@ Page {
|
||||||
}
|
}
|
||||||
Platform.MenuSeparator {
|
Platform.MenuSeparator {
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItemGroup {
|
Platform.MenuItemGroup {
|
||||||
id: onlineStateGroup
|
id: onlineStateGroup
|
||||||
|
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Automatic online status")
|
|
||||||
group: onlineStateGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Settings.presence == Settings.AutomaticPresence
|
checked: Settings.presence == Settings.AutomaticPresence
|
||||||
onTriggered: if (checked) Settings.presence = Settings.AutomaticPresence
|
group: onlineStateGroup
|
||||||
|
text: qsTr("Automatic online status")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Settings.presence = Settings.AutomaticPresence
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Online")
|
|
||||||
group: onlineStateGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Settings.presence == Settings.Online
|
checked: Settings.presence == Settings.Online
|
||||||
onTriggered: if (checked) Settings.presence = Settings.Online
|
group: onlineStateGroup
|
||||||
|
text: qsTr("Online")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Settings.presence = Settings.Online
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Unavailable")
|
|
||||||
group: onlineStateGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Settings.presence == Settings.Unavailable
|
checked: Settings.presence == Settings.Unavailable
|
||||||
onTriggered: if (checked) Settings.presence = Settings.Unavailable
|
group: onlineStateGroup
|
||||||
|
text: qsTr("Unavailable")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Settings.presence = Settings.Unavailable
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Offline")
|
|
||||||
group: onlineStateGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Settings.presence == Settings.Offline
|
checked: Settings.presence == Settings.Offline
|
||||||
onTriggered: if (checked) Settings.presence = Settings.Offline
|
group: onlineStateGroup
|
||||||
|
text: qsTr("Offline")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Settings.presence = Settings.Offline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TapHandler {
|
TapHandler {
|
||||||
|
@ -319,8 +327,8 @@ Page {
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: Nheko.theme.separator
|
|
||||||
Layout.preferredHeight: 2
|
Layout.preferredHeight: 2
|
||||||
|
color: Nheko.theme.separator
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: unverifiedStuffBubble
|
id: unverifiedStuffBubble
|
||||||
|
@ -366,14 +374,14 @@ Page {
|
||||||
id: closeUnverifiedBubble
|
id: closeUnverifiedBubble
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
Layout.preferredHeight: fontMetrics.font.pixelSize
|
||||||
|
Layout.preferredWidth: fontMetrics.font.pixelSize
|
||||||
Layout.rightMargin: Nheko.paddingMedium
|
Layout.rightMargin: Nheko.paddingMedium
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Close")
|
ToolTip.text: qsTr("Close")
|
||||||
ToolTip.visible: closeUnverifiedBubble.hovered
|
ToolTip.visible: closeUnverifiedBubble.hovered
|
||||||
Layout.preferredHeight: fontMetrics.font.pixelSize
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
Layout.preferredWidth: fontMetrics.font.pixelSize
|
|
||||||
|
|
||||||
onClicked: unverifiedStuffBubble.visible = false
|
onClicked: unverifiedStuffBubble.visible = false
|
||||||
}
|
}
|
||||||
|
@ -398,8 +406,8 @@ Page {
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: Nheko.theme.separator
|
|
||||||
Layout.preferredHeight: 1
|
Layout.preferredHeight: 1
|
||||||
|
color: Nheko.theme.separator
|
||||||
visible: unverifiedStuffBubble.visible
|
visible: unverifiedStuffBubble.visible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,9 +444,9 @@ Page {
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
height: parent.height
|
height: parent.height
|
||||||
model: Rooms
|
model: Rooms
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
|
|
||||||
//reuseItems: true
|
//reuseItems: true
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
@ -550,13 +558,13 @@ Page {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredHeight: avatarSize
|
||||||
|
Layout.preferredWidth: avatarSize
|
||||||
displayName: roomName
|
displayName: roomName
|
||||||
enabled: false
|
enabled: false
|
||||||
roomid: roomId
|
roomid: roomId
|
||||||
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: isDirect ? directChatOtherUserId : ""
|
userid: isDirect ? directChatOtherUserId : ""
|
||||||
Layout.preferredWidth: avatarSize
|
|
||||||
Layout.preferredHeight: avatarSize
|
|
||||||
|
|
||||||
NotificationBubble {
|
NotificationBubble {
|
||||||
id: collapsedNotificationBubble
|
id: collapsedNotificationBubble
|
||||||
|
@ -576,8 +584,8 @@ Page {
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.minimumWidth: 100
|
Layout.minimumWidth: 100
|
||||||
Layout.preferredWidth: roomItem.width - avatar.width
|
|
||||||
Layout.preferredHeight: avatar.height
|
Layout.preferredHeight: avatar.height
|
||||||
|
Layout.preferredWidth: roomItem.width - avatar.width
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
visible: !collapsed
|
visible: !collapsed
|
||||||
|
|
||||||
|
|
|
@ -9,54 +9,53 @@ import im.nheko
|
||||||
|
|
||||||
TimelineEvent {
|
TimelineEvent {
|
||||||
id: wrapper
|
id: wrapper
|
||||||
ListView.delayRemove: true
|
|
||||||
width: chat.delegateMaxWidth
|
|
||||||
height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 10)
|
|
||||||
anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter
|
|
||||||
//room: chatRoot.roommodel
|
|
||||||
|
|
||||||
required property var day
|
|
||||||
required property bool isSender
|
|
||||||
required property int index
|
|
||||||
property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day)
|
|
||||||
property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent)
|
|
||||||
property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId)
|
|
||||||
|
|
||||||
required property date timestamp
|
|
||||||
required property string userId
|
|
||||||
required property string userName
|
|
||||||
required property string threadId
|
|
||||||
required property int userPowerlevel
|
|
||||||
required property bool isEdited
|
|
||||||
required property bool isEncrypted
|
|
||||||
required property var reactions
|
|
||||||
required property int status
|
|
||||||
required property int trustlevel
|
|
||||||
required property int notificationlevel
|
|
||||||
required property int type
|
|
||||||
required property bool isEditable
|
|
||||||
|
|
||||||
required property QtObject messageContextMenu
|
|
||||||
required property QtObject replyContextMenu
|
|
||||||
required property Item messageActions
|
|
||||||
|
|
||||||
property int avatarMargin: (wrapper.isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) // align bubble with section header
|
property int avatarMargin: (wrapper.isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) // align bubble with section header
|
||||||
|
|
||||||
property alias hovered: messageHover.hovered
|
|
||||||
property bool scrolledToThis: false
|
|
||||||
|
|
||||||
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0) + 4
|
|
||||||
replyInset: mainInset + 4 + Nheko.paddingSmall
|
|
||||||
|
|
||||||
property int bubbleMargin: 40
|
property int bubbleMargin: 40
|
||||||
|
//room: chatRoot.roommodel
|
||||||
|
|
||||||
|
required property var day
|
||||||
|
property alias hovered: messageHover.hovered
|
||||||
|
required property int index
|
||||||
|
required property bool isEditable
|
||||||
|
required property bool isEdited
|
||||||
|
required property bool isEncrypted
|
||||||
|
required property bool isSender
|
||||||
|
required property Item messageActions
|
||||||
|
required property QtObject messageContextMenu
|
||||||
|
required property int notificationlevel
|
||||||
|
property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day)
|
||||||
|
property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent)
|
||||||
|
property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId)
|
||||||
|
required property var reactions
|
||||||
|
required property QtObject replyContextMenu
|
||||||
|
property bool scrolledToThis: false
|
||||||
|
required property int status
|
||||||
|
required property string threadId
|
||||||
|
required property date timestamp
|
||||||
|
required property int trustlevel
|
||||||
|
required property int type
|
||||||
|
required property string userId
|
||||||
|
required property string userName
|
||||||
|
required property int userPowerlevel
|
||||||
|
|
||||||
|
ListView.delayRemove: true
|
||||||
|
anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter
|
||||||
|
height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 10)
|
||||||
|
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0) + 4
|
||||||
maxWidth: chat.delegateMaxWidth - avatarMargin - bubbleMargin
|
maxWidth: chat.delegateMaxWidth - avatarMargin - bubbleMargin
|
||||||
|
replyInset: mainInset + 4 + Nheko.paddingSmall
|
||||||
|
width: chat.delegateMaxWidth
|
||||||
|
|
||||||
data: [
|
data: [
|
||||||
Loader {
|
Loader {
|
||||||
id: section
|
id: section
|
||||||
|
|
||||||
active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.previousMessageDay !== wrapper.day || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent
|
active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.previousMessageDay !== wrapper.day || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent
|
||||||
|
visible: status == Loader.Ready
|
||||||
|
z: 4
|
||||||
|
|
||||||
//asynchronous: true
|
//asynchronous: true
|
||||||
sourceComponent: TimelineSectionHeader {
|
sourceComponent: TimelineSectionHeader {
|
||||||
day: wrapper.day
|
day: wrapper.day
|
||||||
|
@ -71,13 +70,12 @@ TimelineEvent {
|
||||||
userName: wrapper.userName
|
userName: wrapper.userName
|
||||||
userPowerlevel: wrapper.userPowerlevel
|
userPowerlevel: wrapper.userPowerlevel
|
||||||
}
|
}
|
||||||
visible: status == Loader.Ready
|
|
||||||
z: 4
|
|
||||||
},
|
},
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: gridContainer
|
|
||||||
property color threadColor: TimelineManager.userColor(wrapper.threadId, palette.base)
|
|
||||||
property color threadBackgroundColor: wrapper.threadId ? Qt.tint(palette.base, Qt.hsla(threadColor.hslHue, 0.7, threadColor.hslLightness, 0.1)) : "transparent"
|
property color threadBackgroundColor: wrapper.threadId ? Qt.tint(palette.base, Qt.hsla(threadColor.hslHue, 0.7, threadColor.hslLightness, 0.1)) : "transparent"
|
||||||
|
property color threadColor: TimelineManager.userColor(wrapper.threadId, palette.base)
|
||||||
|
|
||||||
|
anchors.fill: gridContainer
|
||||||
color: (Settings.messageHoverHighlight && messageHover.hovered) ? palette.alternateBase : threadBackgroundColor
|
color: (Settings.messageHoverHighlight && messageHover.hovered) ? palette.alternateBase : threadBackgroundColor
|
||||||
|
|
||||||
// this looks better without margins
|
// this looks better without margins
|
||||||
|
@ -91,8 +89,8 @@ TimelineEvent {
|
||||||
},
|
},
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: scrollHighlight
|
id: scrollHighlight
|
||||||
anchors.fill: gridContainer
|
|
||||||
|
|
||||||
|
anchors.fill: gridContainer
|
||||||
color: palette.highlight
|
color: palette.highlight
|
||||||
enabled: false
|
enabled: false
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
@ -133,37 +131,43 @@ TimelineEvent {
|
||||||
Item {
|
Item {
|
||||||
id: gridContainer
|
id: gridContainer
|
||||||
|
|
||||||
width: wrapper.width - wrapper.avatarMargin
|
|
||||||
implicitHeight: messageBubble.implicitHeight
|
implicitHeight: messageBubble.implicitHeight
|
||||||
|
width: wrapper.width - wrapper.avatarMargin
|
||||||
x: wrapper.avatarMargin
|
x: wrapper.avatarMargin
|
||||||
y: section.visible && section.active ? section.y + section.height : 0
|
y: section.visible && section.active ? section.y + section.height : 0
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: messageHover
|
id: messageHover
|
||||||
|
|
||||||
blocking: false
|
blocking: false
|
||||||
|
|
||||||
onHoveredChanged: () => {
|
onHoveredChanged: () => {
|
||||||
if (!Settings.mobileMode && hovered) {
|
if (!Settings.mobileMode && hovered) {
|
||||||
if (!messageActions.hovered) {
|
if (!messageActions.hovered) {
|
||||||
messageActions.model = wrapper;
|
messageActions.model = wrapper;
|
||||||
messageActions.attached = wrapper;
|
messageActions.attached = wrapper;
|
||||||
messageActions.anchors.bottomMargin = -gridContainer.y
|
messageActions.anchors.bottomMargin = -gridContainer.y;
|
||||||
//messageActions.anchors.rightMargin = metadata.width
|
//messageActions.anchors.rightMargin = metadata.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: messageBubble
|
id: messageBubble
|
||||||
|
|
||||||
anchors.left: (wrapper.isStateEvent || wrapper.isSender) ? undefined : parent.left // qmllint disable Quick.anchor-combinations
|
|
||||||
anchors.right: (wrapper.isStateEvent || !wrapper.isSender) ? undefined : parent.right
|
|
||||||
anchors.horizontalCenter: wrapper.isStateEvent ? parent.horizontalCenter : undefined
|
|
||||||
|
|
||||||
property color userColor: TimelineManager.userColor(wrapper.main?.userId ?? '', palette.base)
|
property color userColor: TimelineManager.userColor(wrapper.main?.userId ?? '', palette.base)
|
||||||
|
|
||||||
|
anchors.horizontalCenter: wrapper.isStateEvent ? parent.horizontalCenter : undefined
|
||||||
|
anchors.left: (wrapper.isStateEvent || wrapper.isSender) ? undefined : parent.left // qmllint disable Quick.anchor-combinations
|
||||||
|
anchors.right: (wrapper.isStateEvent || !wrapper.isSender) ? undefined : parent.right
|
||||||
|
padding: wrapper.isStateEvent ? 0 : 4
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: Nheko.theme.red
|
||||||
|
border.width: wrapper.notificationlevel == MtxEvent.Highlight ? 1 : 0
|
||||||
|
color: !wrapper.isStateEvent ? Qt.tint(palette.base, Qt.hsla(messageBubble.userColor.hslHue, wrapper.hovered ? 0.8 : 0.5, messageBubble.userColor.hslLightness, 0.2)) : "transparent"
|
||||||
|
radius: 4
|
||||||
|
}
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
id: contentPlacementContainer
|
id: contentPlacementContainer
|
||||||
|
|
||||||
|
@ -173,52 +177,47 @@ TimelineEvent {
|
||||||
// property bool fitsMetadataInside: wrapper.main?.positionAt ? (wrapper.main.positionAt(wrapper.main.width, wrapper.main.height - 4) == wrapper.main.positionAt(wrapper.main.width - metadata.width, wrapper.main.height - 4)) : false
|
// property bool fitsMetadataInside: wrapper.main?.positionAt ? (wrapper.main.positionAt(wrapper.main.width, wrapper.main.height - 4) == wrapper.main.positionAt(wrapper.main.width - metadata.width, wrapper.main.height - 4)) : false
|
||||||
property bool fitsMetadataInside: false
|
property bool fitsMetadataInside: false
|
||||||
|
|
||||||
implicitWidth: Math.max((wrapper.reply?.width ?? 0) + wrapper.replyInset, (wrapper.main?.width ?? 0) + wrapper.mainInset + ((fitsMetadata && !fitsMetadataInside) ? metadata.width : 0))
|
|
||||||
implicitHeight: contentColumn.implicitHeight + ((fitsMetadata || fitsMetadataInside) ? 0 : metadata.height)
|
implicitHeight: contentColumn.implicitHeight + ((fitsMetadata || fitsMetadataInside) ? 0 : metadata.height)
|
||||||
|
implicitWidth: Math.max((wrapper.reply?.width ?? 0) + wrapper.replyInset, (wrapper.main?.width ?? 0) + wrapper.mainInset + ((fitsMetadata && !fitsMetadataInside) ? metadata.width : 0))
|
||||||
|
|
||||||
TimelineMetadata {
|
TimelineMetadata {
|
||||||
id: metadata
|
id: metadata
|
||||||
|
|
||||||
scaling: 0.75
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
visible: !wrapper.isStateEvent
|
|
||||||
|
|
||||||
eventId: wrapper.eventId
|
eventId: wrapper.eventId
|
||||||
status: wrapper.status
|
|
||||||
trustlevel: wrapper.trustlevel
|
|
||||||
isEdited: wrapper.isEdited
|
isEdited: wrapper.isEdited
|
||||||
isEncrypted: wrapper.isEncrypted
|
isEncrypted: wrapper.isEncrypted
|
||||||
|
room: wrapper.room
|
||||||
|
scaling: 0.75
|
||||||
|
status: wrapper.status
|
||||||
threadId: wrapper.threadId
|
threadId: wrapper.threadId
|
||||||
timestamp: wrapper.timestamp
|
timestamp: wrapper.timestamp
|
||||||
room: wrapper.room
|
trustlevel: wrapper.trustlevel
|
||||||
|
visible: !wrapper.isStateEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: contentColumn
|
id: contentColumn
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
data: [replyRow, wrapper.main]
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: replyRow
|
id: replyRow
|
||||||
visible: wrapper.reply
|
|
||||||
|
|
||||||
height: replyLine.height
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base)
|
property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base)
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
clip: true
|
clip: true
|
||||||
|
height: replyLine.height
|
||||||
|
visible: wrapper.reply
|
||||||
|
|
||||||
NhekoCursorShape {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
//width: replyRow.implicitContentWidth
|
||||||
cursorShape: Qt.PointingHandCursor
|
color: Qt.tint(palette.base, Qt.hsla(replyRow.userColor.hslHue, 0.5, replyRow.userColor.hslLightness, 0.1))
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Row {
|
contentItem: Row {
|
||||||
id: replyRowLay
|
id: replyRowLay
|
||||||
|
|
||||||
|
@ -226,94 +225,81 @@ TimelineEvent {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: replyLine
|
id: replyLine
|
||||||
height: Math.min( wrapper.reply?.height, timelineView.height / 10) + Nheko.paddingSmall + replyUserButton.height
|
|
||||||
color: replyRow.userColor
|
color: replyRow.userColor
|
||||||
|
height: Math.min(wrapper.reply?.height, timelineView.height / 10) + Nheko.paddingSmall + replyUserButton.height
|
||||||
width: 4
|
width: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
id: replyCol
|
id: replyCol
|
||||||
|
|
||||||
|
data: [replyUserButton, wrapper.reply,]
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: replyUserButton
|
id: replyUserButton
|
||||||
|
|
||||||
contentItem: Label {
|
contentItem: Label {
|
||||||
id: userName_
|
id: userName_
|
||||||
text: wrapper.reply?.userName ?? ''
|
|
||||||
color: replyRow.userColor
|
color: replyRow.userColor
|
||||||
|
text: wrapper.reply?.userName ?? ''
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
width: wrapper.maxWidth
|
width: wrapper.maxWidth
|
||||||
//elideWidth: wrapper.maxWidth
|
//elideWidth: wrapper.maxWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: wrapper.room.openUserProfile(wrapper.reply?.userId)
|
onClicked: wrapper.room.openUserProfile(wrapper.reply?.userId)
|
||||||
}
|
}
|
||||||
data: [
|
|
||||||
replyUserButton,
|
|
||||||
wrapper.reply,
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
//width: replyRow.implicitContentWidth
|
|
||||||
color: Qt.tint(palette.base, Qt.hsla(replyRow.userColor.hslHue, 0.5, replyRow.userColor.hslLightness, 0.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let link = wrapper.reply.hoveredLink
|
let link = wrapper.reply.hoveredLink;
|
||||||
if (link) {
|
if (link) {
|
||||||
Nheko.openLink(link)
|
Nheko.openLink(link);
|
||||||
} else {
|
} else {
|
||||||
console.log("Scrolling to " + wrapper.replyTo);
|
console.log("Scrolling to " + wrapper.replyTo);
|
||||||
wrapper.room.showEvent(wrapper.replyTo)
|
wrapper.room.showEvent(wrapper.replyTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onPressAndHold: wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(pressX - replyLine.width - Nheko.paddingSmall, pressY - replyUserButton.implicitHeight) : "", wrapper.replyTo)
|
onPressAndHold: wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(pressX - replyLine.width - Nheko.paddingSmall, pressY - replyUserButton.implicitHeight) : "", wrapper.replyTo)
|
||||||
|
|
||||||
|
NhekoCursorShape {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onSingleTapped: (eventPoint) => wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(eventPoint.position.x-replyLine.width - Nheko.paddingSmall, eventPoint.position.y - replyUserButton.implicitHeight) : "", wrapper.replyTo)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
}
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
}
|
|
||||||
|
|
||||||
data: [replyRow, wrapper.main]
|
onSingleTapped: eventPoint => wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(eventPoint.position.x - replyLine.width - Nheko.paddingSmall, eventPoint.position.y - replyUserButton.implicitHeight) : "", wrapper.replyTo)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
padding: wrapper.isStateEvent ? 0 : 4
|
|
||||||
background: Rectangle {
|
|
||||||
color: !wrapper.isStateEvent ? Qt.tint(palette.base, Qt.hsla(messageBubble.userColor.hslHue, wrapper.hovered ? 0.8 : 0.5, messageBubble.userColor.hslLightness, 0.2)) : "transparent"
|
|
||||||
radius: 4
|
|
||||||
border.color: Nheko.theme.red
|
|
||||||
border.width: wrapper.notificationlevel == MtxEvent.Highlight ? 1 : 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DragHandler {
|
DragHandler {
|
||||||
id: replyDragHandler
|
id: replyDragHandler
|
||||||
yAxis.enabled: false
|
|
||||||
xAxis.enabled: true
|
xAxis.enabled: true
|
||||||
xAxis.minimum: wrapper.avatarMargin - 100
|
|
||||||
xAxis.maximum: wrapper.avatarMargin
|
xAxis.maximum: wrapper.avatarMargin
|
||||||
|
xAxis.minimum: wrapper.avatarMargin - 100
|
||||||
|
yAxis.enabled: false
|
||||||
|
|
||||||
onActiveChanged: {
|
onActiveChanged: {
|
||||||
if (!replyDragHandler.active) {
|
if (!replyDragHandler.active) {
|
||||||
if (replyDragHandler.xAxis.minimum <= replyDragHandler.xAxis.activeValue + 1) {
|
if (replyDragHandler.xAxis.minimum <= replyDragHandler.xAxis.activeValue + 1) {
|
||||||
wrapper.room.reply = wrapper.eventId
|
wrapper.room.reply = wrapper.eventId;
|
||||||
}
|
}
|
||||||
gridContainer.x = wrapper.avatarMargin;
|
gridContainer.x = wrapper.avatarMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onDoubleTapped: wrapper.room.reply = wrapper.eventId
|
onDoubleTapped: wrapper.room.reply = wrapper.eventId
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Reactions {
|
Reactions {
|
||||||
id: reactionRow
|
id: reactionRow
|
||||||
|
@ -347,4 +333,3 @@ TimelineEvent {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,52 +9,52 @@ import im.nheko
|
||||||
|
|
||||||
TimelineEvent {
|
TimelineEvent {
|
||||||
id: wrapper
|
id: wrapper
|
||||||
ListView.delayRemove: true
|
|
||||||
width: chat.delegateMaxWidth
|
|
||||||
height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 10)
|
|
||||||
anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter
|
|
||||||
//room: chatRoot.roommodel
|
|
||||||
|
|
||||||
required property var day
|
|
||||||
required property bool isSender
|
|
||||||
required property int index
|
|
||||||
property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day)
|
|
||||||
property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent)
|
|
||||||
property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId)
|
|
||||||
|
|
||||||
required property date timestamp
|
|
||||||
required property string userId
|
|
||||||
required property string userName
|
|
||||||
required property string threadId
|
|
||||||
required property int userPowerlevel
|
|
||||||
required property bool isEdited
|
|
||||||
required property bool isEncrypted
|
|
||||||
required property var reactions
|
|
||||||
required property int status
|
|
||||||
required property int trustlevel
|
|
||||||
required property int notificationlevel
|
|
||||||
required property int type
|
|
||||||
required property bool isEditable
|
|
||||||
|
|
||||||
required property QtObject messageContextMenu
|
|
||||||
required property QtObject replyContextMenu
|
|
||||||
required property Item messageActions
|
|
||||||
|
|
||||||
property int avatarMargin: (wrapper.isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) // align bubble with section header
|
property int avatarMargin: (wrapper.isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) // align bubble with section header
|
||||||
|
|
||||||
|
//room: chatRoot.roommodel
|
||||||
|
|
||||||
|
required property var day
|
||||||
property alias hovered: messageHover.hovered
|
property alias hovered: messageHover.hovered
|
||||||
|
required property int index
|
||||||
|
required property bool isEditable
|
||||||
|
required property bool isEdited
|
||||||
|
required property bool isEncrypted
|
||||||
|
required property bool isSender
|
||||||
|
required property Item messageActions
|
||||||
|
required property QtObject messageContextMenu
|
||||||
|
required property int notificationlevel
|
||||||
|
property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day)
|
||||||
|
property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent)
|
||||||
|
property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId)
|
||||||
|
required property var reactions
|
||||||
|
required property QtObject replyContextMenu
|
||||||
property bool scrolledToThis: false
|
property bool scrolledToThis: false
|
||||||
|
required property int status
|
||||||
|
required property string threadId
|
||||||
|
required property date timestamp
|
||||||
|
required property int trustlevel
|
||||||
|
required property int type
|
||||||
|
required property string userId
|
||||||
|
required property string userName
|
||||||
|
required property int userPowerlevel
|
||||||
|
|
||||||
|
ListView.delayRemove: true
|
||||||
|
anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter
|
||||||
|
height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 10)
|
||||||
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0)
|
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0)
|
||||||
replyInset: mainInset + 4 + Nheko.paddingSmall
|
|
||||||
|
|
||||||
maxWidth: chat.delegateMaxWidth - avatarMargin - metadata.width
|
maxWidth: chat.delegateMaxWidth - avatarMargin - metadata.width
|
||||||
|
replyInset: mainInset + 4 + Nheko.paddingSmall
|
||||||
|
width: chat.delegateMaxWidth
|
||||||
|
|
||||||
data: [
|
data: [
|
||||||
Loader {
|
Loader {
|
||||||
id: section
|
id: section
|
||||||
|
|
||||||
active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.previousMessageDay !== wrapper.day || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent
|
active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.previousMessageDay !== wrapper.day || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent
|
||||||
|
visible: status == Loader.Ready
|
||||||
|
z: 4
|
||||||
|
|
||||||
//asynchronous: true
|
//asynchronous: true
|
||||||
sourceComponent: TimelineSectionHeader {
|
sourceComponent: TimelineSectionHeader {
|
||||||
day: wrapper.day
|
day: wrapper.day
|
||||||
|
@ -69,8 +69,6 @@ TimelineEvent {
|
||||||
userName: wrapper.userName
|
userName: wrapper.userName
|
||||||
userPowerlevel: wrapper.userPowerlevel
|
userPowerlevel: wrapper.userPowerlevel
|
||||||
}
|
}
|
||||||
visible: status == Loader.Ready
|
|
||||||
z: 4
|
|
||||||
},
|
},
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: gridContainer
|
anchors.fill: gridContainer
|
||||||
|
@ -87,8 +85,8 @@ TimelineEvent {
|
||||||
},
|
},
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: scrollHighlight
|
id: scrollHighlight
|
||||||
anchors.fill: gridContainer
|
|
||||||
|
|
||||||
|
anchors.fill: gridContainer
|
||||||
color: palette.highlight
|
color: palette.highlight
|
||||||
enabled: false
|
enabled: false
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
@ -127,41 +125,41 @@ TimelineEvent {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.top: gridContainer.top
|
|
||||||
anchors.left: gridContainer.left
|
anchors.left: gridContainer.left
|
||||||
anchors.topMargin: -2
|
|
||||||
anchors.leftMargin: -2
|
anchors.leftMargin: -2
|
||||||
color: "transparent"
|
anchors.top: gridContainer.top
|
||||||
|
anchors.topMargin: -2
|
||||||
border.color: Nheko.theme.red
|
border.color: Nheko.theme.red
|
||||||
border.width: wrapper.notificationlevel == MtxEvent.Highlight ? 1 : 0
|
border.width: wrapper.notificationlevel == MtxEvent.Highlight ? 1 : 0
|
||||||
radius: 4
|
color: "transparent"
|
||||||
height: contentColumn.implicitHeight + 4
|
height: contentColumn.implicitHeight + 4
|
||||||
|
radius: 4
|
||||||
width: contentColumn.implicitWidth + 4
|
width: contentColumn.implicitWidth + 4
|
||||||
},
|
},
|
||||||
Row {
|
Row {
|
||||||
id: gridContainer
|
id: gridContainer
|
||||||
|
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
width: wrapper.width - wrapper.avatarMargin
|
width: wrapper.width - wrapper.avatarMargin
|
||||||
x: wrapper.avatarMargin
|
x: wrapper.avatarMargin
|
||||||
y: section.visible && section.active ? section.y + section.height : 0
|
y: section.visible && section.active ? section.y + section.height : 0
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: messageHover
|
id: messageHover
|
||||||
|
|
||||||
blocking: false
|
blocking: false
|
||||||
|
|
||||||
onHoveredChanged: () => {
|
onHoveredChanged: () => {
|
||||||
if (!Settings.mobileMode && hovered) {
|
if (!Settings.mobileMode && hovered) {
|
||||||
if (!messageActions.hovered) {
|
if (!messageActions.hovered) {
|
||||||
messageActions.model = wrapper;
|
messageActions.model = wrapper;
|
||||||
messageActions.attached = wrapper;
|
messageActions.attached = wrapper;
|
||||||
messageActions.anchors.bottomMargin = -gridContainer.y
|
messageActions.anchors.bottomMargin = -gridContainer.y;
|
||||||
messageActions.anchors.rightMargin = metadata.width
|
messageActions.anchors.rightMargin = metadata.width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Part of a thread")
|
ToolTip.text: qsTr("Part of a thread")
|
||||||
|
@ -179,31 +177,29 @@ TimelineEvent {
|
||||||
color: TimelineManager.userColor(wrapper.threadId, palette.base)
|
color: TimelineManager.userColor(wrapper.threadId, palette.base)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
height: 1
|
||||||
visible: wrapper.isStateEvent
|
visible: wrapper.isStateEvent
|
||||||
width: (wrapper.maxWidth - (wrapper.main?.width ?? 0)) / 2
|
width: (wrapper.maxWidth - (wrapper.main?.width ?? 0)) / 2
|
||||||
height: 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: contentColumn
|
id: contentColumn
|
||||||
|
|
||||||
|
data: [replyRow, wrapper.main,]
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: replyRow
|
id: replyRow
|
||||||
visible: wrapper.reply
|
|
||||||
|
|
||||||
height: replyLine.height
|
|
||||||
|
|
||||||
property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base)
|
property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base)
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
height: replyLine.height
|
||||||
|
visible: wrapper.reply
|
||||||
|
|
||||||
NhekoCursorShape {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
//width: replyRow.implicitContentWidth
|
||||||
cursorShape: Qt.PointingHandCursor
|
color: Qt.tint(palette.base, Qt.hsla(replyRow.userColor.hslHue, 0.5, replyRow.userColor.hslLightness, 0.1))
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Row {
|
contentItem: Row {
|
||||||
id: replyRowLay
|
id: replyRowLay
|
||||||
|
|
||||||
|
@ -211,80 +207,76 @@ TimelineEvent {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: replyLine
|
id: replyLine
|
||||||
height: Math.min( wrapper.reply?.height, timelineView.height / 10) + Nheko.paddingSmall + replyUserButton.height
|
|
||||||
color: replyRow.userColor
|
color: replyRow.userColor
|
||||||
|
height: Math.min(wrapper.reply?.height, timelineView.height / 10) + Nheko.paddingSmall + replyUserButton.height
|
||||||
width: 4
|
width: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
id: replyCol
|
id: replyCol
|
||||||
|
|
||||||
|
data: [replyUserButton, wrapper.reply,]
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: replyUserButton
|
id: replyUserButton
|
||||||
|
|
||||||
contentItem: Label {
|
contentItem: Label {
|
||||||
id: userName_
|
id: userName_
|
||||||
text: wrapper.reply?.userName ?? ''
|
|
||||||
color: replyRow.userColor
|
color: replyRow.userColor
|
||||||
|
text: wrapper.reply?.userName ?? ''
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
width: wrapper.maxWidth
|
width: wrapper.maxWidth
|
||||||
//elideWidth: wrapper.maxWidth
|
//elideWidth: wrapper.maxWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: wrapper.room.openUserProfile(wrapper.reply?.userId)
|
onClicked: wrapper.room.openUserProfile(wrapper.reply?.userId)
|
||||||
}
|
}
|
||||||
data: [
|
|
||||||
replyUserButton,
|
|
||||||
wrapper.reply,
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
//width: replyRow.implicitContentWidth
|
|
||||||
color: Qt.tint(palette.base, Qt.hsla(replyRow.userColor.hslHue, 0.5, replyRow.userColor.hslLightness, 0.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let link = wrapper.reply.hoveredLink
|
let link = wrapper.reply.hoveredLink;
|
||||||
if (link) {
|
if (link) {
|
||||||
Nheko.openLink(link)
|
Nheko.openLink(link);
|
||||||
} else {
|
} else {
|
||||||
console.log("Scrolling to " + wrapper.replyTo);
|
console.log("Scrolling to " + wrapper.replyTo);
|
||||||
wrapper.room.showEvent(wrapper.replyTo)
|
wrapper.room.showEvent(wrapper.replyTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onPressAndHold: wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(pressX - replyLine.width - Nheko.paddingSmall, pressY - replyUserButton.implicitHeight) : "", wrapper.replyTo)
|
onPressAndHold: wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(pressX - replyLine.width - Nheko.paddingSmall, pressY - replyUserButton.implicitHeight) : "", wrapper.replyTo)
|
||||||
|
|
||||||
|
NhekoCursorShape {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onSingleTapped: (eventPoint) => wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(eventPoint.position.x-replyLine.width - Nheko.paddingSmall, eventPoint.position.y - replyUserButton.implicitHeight) : "", wrapper.replyTo)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
}
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
}
|
|
||||||
|
|
||||||
data: [
|
onSingleTapped: eventPoint => wrapper.replyContextMenu.show(wrapper.reply.copyText ?? "", wrapper.reply.linkAt ? wrapper.reply.linkAt(eventPoint.position.x - replyLine.width - Nheko.paddingSmall, eventPoint.position.y - replyUserButton.implicitHeight) : "", wrapper.replyTo)
|
||||||
replyRow, wrapper.main,
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DragHandler {
|
DragHandler {
|
||||||
id: replyDragHandler
|
id: replyDragHandler
|
||||||
yAxis.enabled: false
|
|
||||||
xAxis.enabled: true
|
xAxis.enabled: true
|
||||||
xAxis.minimum: wrapper.avatarMargin - 100
|
|
||||||
xAxis.maximum: wrapper.avatarMargin
|
xAxis.maximum: wrapper.avatarMargin
|
||||||
|
xAxis.minimum: wrapper.avatarMargin - 100
|
||||||
|
yAxis.enabled: false
|
||||||
|
|
||||||
onActiveChanged: {
|
onActiveChanged: {
|
||||||
if (!replyDragHandler.active) {
|
if (!replyDragHandler.active) {
|
||||||
if (replyDragHandler.xAxis.minimum <= replyDragHandler.xAxis.activeValue + 1) {
|
if (replyDragHandler.xAxis.minimum <= replyDragHandler.xAxis.activeValue + 1) {
|
||||||
wrapper.room.reply = wrapper.eventId
|
wrapper.room.reply = wrapper.eventId;
|
||||||
}
|
}
|
||||||
gridContainer.x = wrapper.avatarMargin;
|
gridContainer.x = wrapper.avatarMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onDoubleTapped: wrapper.room.reply = wrapper.eventId
|
onDoubleTapped: wrapper.room.reply = wrapper.eventId
|
||||||
}
|
}
|
||||||
|
@ -292,21 +284,18 @@ TimelineEvent {
|
||||||
TimelineMetadata {
|
TimelineMetadata {
|
||||||
id: metadata
|
id: metadata
|
||||||
|
|
||||||
scaling: 1
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
y: section.visible && section.active ? section.y + section.height : 0
|
|
||||||
|
|
||||||
visible: !wrapper.isStateEvent
|
|
||||||
|
|
||||||
eventId: wrapper.eventId
|
eventId: wrapper.eventId
|
||||||
status: wrapper.status
|
|
||||||
trustlevel: wrapper.trustlevel
|
|
||||||
isEdited: wrapper.isEdited
|
isEdited: wrapper.isEdited
|
||||||
isEncrypted: wrapper.isEncrypted
|
isEncrypted: wrapper.isEncrypted
|
||||||
|
room: wrapper.room
|
||||||
|
scaling: 1
|
||||||
|
status: wrapper.status
|
||||||
threadId: wrapper.threadId
|
threadId: wrapper.threadId
|
||||||
timestamp: wrapper.timestamp
|
timestamp: wrapper.timestamp
|
||||||
room: wrapper.room
|
trustlevel: wrapper.trustlevel
|
||||||
|
visible: !wrapper.isStateEvent
|
||||||
|
y: section.visible && section.active ? section.y + section.height : 0
|
||||||
},
|
},
|
||||||
Reactions {
|
Reactions {
|
||||||
id: reactionRow
|
id: reactionRow
|
||||||
|
|
|
@ -11,17 +11,16 @@ import im.nheko
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: metadata
|
id: metadata
|
||||||
|
|
||||||
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
|
|
||||||
required property double scaling
|
|
||||||
|
|
||||||
required property string eventId
|
required property string eventId
|
||||||
required property int status
|
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
|
||||||
required property int trustlevel
|
|
||||||
required property bool isEdited
|
required property bool isEdited
|
||||||
required property bool isEncrypted
|
required property bool isEncrypted
|
||||||
|
required property Room room
|
||||||
|
required property double scaling
|
||||||
|
required property int status
|
||||||
required property string threadId
|
required property string threadId
|
||||||
required property date timestamp
|
required property date timestamp
|
||||||
required property Room room
|
required property int trustlevel
|
||||||
|
|
||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
|
@ -43,6 +42,7 @@ RowLayout {
|
||||||
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
||||||
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
||||||
visible: metadata.isEdited || metadata.eventId == metadata.room.edit
|
visible: metadata.isEdited || metadata.eventId == metadata.room.edit
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: editHovered
|
id: editHovered
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,9 @@ import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
import "./components"
|
import "./components"
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
|
||||||
required property var day
|
required property var day
|
||||||
required property bool isSender
|
required property bool isSender
|
||||||
required property bool isStateEvent
|
required property bool isStateEvent
|
||||||
|
@ -79,31 +77,14 @@ Column {
|
||||||
|
|
||||||
target: room
|
target: room
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: userNameButton
|
id: userNameButton
|
||||||
|
|
||||||
PowerlevelIndicator {
|
|
||||||
id: powerlevelIndicator
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
powerlevel: userPowerlevel
|
|
||||||
height: fontMetrics.ascent
|
|
||||||
width: height
|
|
||||||
|
|
||||||
sourceSize.width: fontMetrics.lineSpacing
|
|
||||||
sourceSize.height: fontMetrics.lineSpacing
|
|
||||||
|
|
||||||
permissions: room ? room.permissions : null
|
|
||||||
visible: isAdmin || isModerator
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: userId
|
ToolTip.text: userId
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
leftPadding: powerlevelIndicator.visible ? 16 : 0
|
|
||||||
leftInset: 0
|
leftInset: 0
|
||||||
|
leftPadding: powerlevelIndicator.visible ? 16 : 0
|
||||||
rightInset: 0
|
rightInset: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
|
|
||||||
|
@ -117,6 +98,19 @@ Column {
|
||||||
|
|
||||||
onClicked: room.openUserProfile(userId)
|
onClicked: room.openUserProfile(userId)
|
||||||
|
|
||||||
|
PowerlevelIndicator {
|
||||||
|
id: powerlevelIndicator
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
height: fontMetrics.ascent
|
||||||
|
permissions: room ? room.permissions : null
|
||||||
|
powerlevel: userPowerlevel
|
||||||
|
sourceSize.height: fontMetrics.lineSpacing
|
||||||
|
sourceSize.width: fontMetrics.lineSpacing
|
||||||
|
visible: isAdmin || isModerator
|
||||||
|
width: height
|
||||||
|
}
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: userNameTextMetrics
|
id: userNameTextMetrics
|
||||||
|
|
||||||
|
@ -161,4 +155,3 @@ Column {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -188,9 +188,9 @@ Item {
|
||||||
displayName: parent.roomName
|
displayName: parent.roomName
|
||||||
enabled: false
|
enabled: false
|
||||||
implicitHeight: 130
|
implicitHeight: 130
|
||||||
|
implicitWidth: 130
|
||||||
roomid: parent.roomId
|
roomid: parent.roomId
|
||||||
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
implicitWidth: 130
|
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
@ -293,10 +293,10 @@ Item {
|
||||||
displayName: roomPreview?.inviterDisplayName ?? ""
|
displayName: roomPreview?.inviterDisplayName ?? ""
|
||||||
enabled: true
|
enabled: true
|
||||||
implicitHeight: 48
|
implicitHeight: 48
|
||||||
|
implicitWidth: 48
|
||||||
roomid: preview.roomId
|
roomid: preview.roomId
|
||||||
url: (roomPreview?.inviterAvatarUrl ?? "").replace("mxc://", "image://MxcImage/")
|
url: (roomPreview?.inviterAvatarUrl ?? "").replace("mxc://", "image://MxcImage/")
|
||||||
userid: roomPreview?.inviterUserId ?? ""
|
userid: roomPreview?.inviterUserId ?? ""
|
||||||
implicitWidth: 48
|
|
||||||
|
|
||||||
onClicked: TimelineManager.openGlobalUserProfile(roomPreview.inviterUserId)
|
onClicked: TimelineManager.openGlobalUserProfile(roomPreview.inviterUserId)
|
||||||
}
|
}
|
||||||
|
@ -378,8 +378,8 @@ Item {
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
timelineEffects.removeParticles()
|
timelineEffects.removeParticles();
|
||||||
shouldEffectsRun = false
|
shouldEffectsRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
|
|
|
@ -124,9 +124,9 @@ Pane {
|
||||||
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
|
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
|
||||||
Layout.row: 2
|
Layout.row: 2
|
||||||
clip: true
|
clip: true
|
||||||
enabled: false
|
|
||||||
// don't use the disabled color
|
// don't use the disabled color
|
||||||
color: topBar.palette.text
|
color: topBar.palette.text
|
||||||
|
enabled: false
|
||||||
selectByMouse: false
|
selectByMouse: false
|
||||||
text: roomTopic
|
text: roomTopic
|
||||||
}
|
}
|
||||||
|
@ -287,9 +287,9 @@ Pane {
|
||||||
|
|
||||||
property var e: room ? room.getDump(modelData, "pins") : {}
|
property var e: room ? room.getDump(modelData, "pins") : {}
|
||||||
|
|
||||||
maxWidth: pinnedMessages.width
|
|
||||||
//Layout.preferredHeight: height
|
//Layout.preferredHeight: height
|
||||||
eventId: e.eventId ?? ""
|
eventId: e.eventId ?? ""
|
||||||
|
maxWidth: pinnedMessages.width
|
||||||
userColor: TimelineManager.userColor(e.userId, palette.window)
|
userColor: TimelineManager.userColor(e.userId, palette.window)
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
|
|
@ -13,13 +13,80 @@ Container {
|
||||||
|
|
||||||
id: container
|
id: container
|
||||||
|
|
||||||
|
property Component handle: Rectangle {
|
||||||
|
anchors.right: parent.right
|
||||||
|
color: Nheko.theme.separator
|
||||||
|
height: container.height
|
||||||
|
width: visible ? 1 : 0
|
||||||
|
z: 3
|
||||||
|
}
|
||||||
|
property Component handleToucharea: Item {
|
||||||
|
id: splitter
|
||||||
|
|
||||||
|
property int calculatedWidth: {
|
||||||
|
if (!visible)
|
||||||
|
return 0;
|
||||||
|
else if (container.singlePageMode)
|
||||||
|
return container.width;
|
||||||
|
else
|
||||||
|
return (collapsible && x < minimumWidth) ? collapsedWidth : x;
|
||||||
|
}
|
||||||
|
property int collapsedWidth: parent.collapsedWidth
|
||||||
|
property bool collapsible: parent.collapsible
|
||||||
|
property int maximumWidth: parent.maximumWidth
|
||||||
|
property int minimumWidth: parent.minimumWidth
|
||||||
|
|
||||||
|
enabled: !container.singlePageMode
|
||||||
|
height: container.height
|
||||||
|
width: 1
|
||||||
|
x: parent.preferredWidth
|
||||||
|
z: 3
|
||||||
|
|
||||||
|
NhekoCursorShape {
|
||||||
|
cursorShape: Qt.SizeHorCursor
|
||||||
|
height: parent.height
|
||||||
|
width: container.splitterGrabMargin * 2
|
||||||
|
x: -container.splitterGrabMargin
|
||||||
|
}
|
||||||
|
DragHandler {
|
||||||
|
id: dragHandler
|
||||||
|
|
||||||
|
enabled: !container.singlePageMode
|
||||||
|
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.ApprovesTakeOverByHandlersOfSameType
|
||||||
|
margin: container.splitterGrabMargin
|
||||||
|
xAxis.enabled: true
|
||||||
|
xAxis.maximum: splitter.maximumWidth
|
||||||
|
xAxis.minimum: splitter.minimumWidth - 1
|
||||||
|
yAxis.enabled: false
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (!active) {
|
||||||
|
splitter.x = splitter.calculatedWidth;
|
||||||
|
splitter.parent.preferredWidth = splitter.calculatedWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HoverHandler {
|
||||||
|
enabled: !container.singlePageMode
|
||||||
|
margin: container.splitterGrabMargin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
property alias pageIndex: view.currentIndex
|
||||||
property bool singlePageMode: width < 800
|
property bool singlePageMode: width < 800
|
||||||
property int splitterGrabMargin: Nheko.paddingSmall
|
property int splitterGrabMargin: Nheko.paddingSmall
|
||||||
property alias pageIndex: view.currentIndex
|
|
||||||
property Component handle
|
|
||||||
property Component handleToucharea
|
|
||||||
|
|
||||||
onSinglePageModeChanged: if (!singlePageMode) pageIndex = 0
|
contentItem: ListView {
|
||||||
|
id: view
|
||||||
|
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
currentIndex: container.singlePageMode ? container.pageIndex : 0
|
||||||
|
highlightMoveDuration: (container.singlePageMode && !Settings.reducedMotion) ? 200 : 0
|
||||||
|
highlightRangeMode: ListView.StrictlyEnforceRange
|
||||||
|
interactive: singlePageMode
|
||||||
|
model: container.contentModel
|
||||||
|
orientation: ListView.Horizontal
|
||||||
|
snapMode: ListView.SnapOneItem
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
for (var i = 0; i < count - 1; i++) {
|
for (var i = 0; i < count - 1; i++) {
|
||||||
|
@ -40,7 +107,6 @@ Container {
|
||||||
for (var i = 0; i < count - 1; i++) {
|
for (var i = 0; i < count - 1; i++) {
|
||||||
if (contentChildren[i].width)
|
if (contentChildren[i].width)
|
||||||
w = w - contentChildren[i].width;
|
w = w - contentChildren[i].width;
|
||||||
|
|
||||||
}
|
}
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
@ -55,81 +121,6 @@ Container {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onSinglePageModeChanged: if (!singlePageMode)
|
||||||
handle: Rectangle {
|
pageIndex = 0
|
||||||
z: 3
|
|
||||||
color: Nheko.theme.separator
|
|
||||||
height: container.height
|
|
||||||
width: visible ? 1 : 0
|
|
||||||
anchors.right: parent.right
|
|
||||||
}
|
|
||||||
|
|
||||||
handleToucharea: Item {
|
|
||||||
id: splitter
|
|
||||||
|
|
||||||
property int minimumWidth: parent.minimumWidth
|
|
||||||
property int maximumWidth: parent.maximumWidth
|
|
||||||
property int collapsedWidth: parent.collapsedWidth
|
|
||||||
property bool collapsible: parent.collapsible
|
|
||||||
property int calculatedWidth: {
|
|
||||||
if (!visible)
|
|
||||||
return 0;
|
|
||||||
else if (container.singlePageMode)
|
|
||||||
return container.width;
|
|
||||||
else
|
|
||||||
return (collapsible && x < minimumWidth) ? collapsedWidth : x;
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled: !container.singlePageMode
|
|
||||||
height: container.height
|
|
||||||
width: 1
|
|
||||||
x: parent.preferredWidth
|
|
||||||
z: 3
|
|
||||||
|
|
||||||
NhekoCursorShape {
|
|
||||||
height: parent.height
|
|
||||||
width: container.splitterGrabMargin * 2
|
|
||||||
x: -container.splitterGrabMargin
|
|
||||||
cursorShape: Qt.SizeHorCursor
|
|
||||||
}
|
|
||||||
|
|
||||||
DragHandler {
|
|
||||||
id: dragHandler
|
|
||||||
|
|
||||||
enabled: !container.singlePageMode
|
|
||||||
xAxis.enabled: true
|
|
||||||
yAxis.enabled: false
|
|
||||||
xAxis.minimum: splitter.minimumWidth - 1
|
|
||||||
xAxis.maximum: splitter.maximumWidth
|
|
||||||
margin: container.splitterGrabMargin
|
|
||||||
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.ApprovesTakeOverByHandlersOfSameType
|
|
||||||
onActiveChanged: {
|
|
||||||
if (!active) {
|
|
||||||
splitter.x = splitter.calculatedWidth;
|
|
||||||
splitter.parent.preferredWidth = splitter.calculatedWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
enabled: !container.singlePageMode
|
|
||||||
margin: container.splitterGrabMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: ListView {
|
|
||||||
id: view
|
|
||||||
|
|
||||||
model: container.contentModel
|
|
||||||
snapMode: ListView.SnapOneItem
|
|
||||||
orientation: ListView.Horizontal
|
|
||||||
highlightRangeMode: ListView.StrictlyEnforceRange
|
|
||||||
interactive: singlePageMode
|
|
||||||
highlightMoveDuration: (container.singlePageMode && !Settings.reducedMotion) ? 200 : 0
|
|
||||||
currentIndex: container.singlePageMode ? container.pageIndex : 0
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property int minimumWidth: 100
|
property bool collapsed: width < minimumWidth
|
||||||
property int maximumWidth: 400
|
|
||||||
property int collapsedWidth: 40
|
property int collapsedWidth: 40
|
||||||
property bool collapsible: true
|
property bool collapsible: true
|
||||||
property bool collapsed: width < minimumWidth
|
property int maximumWidth: 400
|
||||||
property int splitterWidth: 1
|
property int minimumWidth: 100
|
||||||
property int preferredWidth: 100
|
property int preferredWidth: 100
|
||||||
|
property int splitterWidth: 1
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
children[0].width = Qt.binding(() => {
|
children[0].width = Qt.binding(() => {
|
||||||
|
|
|
@ -10,25 +10,26 @@ import im.nheko
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: tile
|
id: tile
|
||||||
|
|
||||||
property color background: palette.window
|
|
||||||
property color importantText: palette.text
|
|
||||||
property color unimportantText: palette.buttonText
|
|
||||||
property color bubbleBackground: palette.highlight
|
|
||||||
property color bubbleText: palette.highlightedText
|
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||||
required property string avatarUrl
|
required property string avatarUrl
|
||||||
required property string title
|
property color background: palette.window
|
||||||
required property string subtitle
|
property color bubbleBackground: palette.highlight
|
||||||
required property int index
|
property color bubbleText: palette.highlightedText
|
||||||
required property int selectedIndex
|
|
||||||
property bool crop: true
|
property bool crop: true
|
||||||
|
property color importantText: palette.text
|
||||||
|
required property int index
|
||||||
property alias roomid: avatar.roomid
|
property alias roomid: avatar.roomid
|
||||||
|
required property int selectedIndex
|
||||||
|
required property string subtitle
|
||||||
|
required property string title
|
||||||
|
property color unimportantText: palette.buttonText
|
||||||
property alias userid: avatar.userid
|
property alias userid: avatar.userid
|
||||||
|
|
||||||
color: background
|
color: background
|
||||||
height: avatarSize + 2 * Nheko.paddingMedium
|
height: avatarSize + 2 * Nheko.paddingMedium
|
||||||
width: ListView.view.width
|
|
||||||
state: "normal"
|
state: "normal"
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "highlight"
|
name: "highlight"
|
||||||
|
@ -37,13 +38,12 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
tile {
|
tile {
|
||||||
background: palette.dark
|
background: palette.dark
|
||||||
importantText: palette.brightText
|
|
||||||
unimportantText: palette.brightText
|
|
||||||
bubbleBackground: palette.highlight
|
bubbleBackground: palette.highlight
|
||||||
bubbleText: palette.highlightedText
|
bubbleText: palette.highlightedText
|
||||||
|
importantText: palette.brightText
|
||||||
|
unimportantText: palette.brightText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "selected"
|
name: "selected"
|
||||||
|
@ -52,37 +52,35 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
tile {
|
tile {
|
||||||
background: palette.highlight
|
background: palette.highlight
|
||||||
importantText: palette.highlightedText
|
|
||||||
unimportantText: palette.highlightedText
|
|
||||||
bubbleBackground: palette.highlightedText
|
bubbleBackground: palette.highlightedText
|
||||||
bubbleText: palette.highlight
|
bubbleText: palette.highlight
|
||||||
|
importantText: palette.highlightedText
|
||||||
|
unimportantText: palette.highlightedText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hovered
|
id: hovered
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
enabled: false
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
crop: tile.crop
|
||||||
|
displayName: title
|
||||||
|
enabled: false
|
||||||
implicitHeight: avatarSize
|
implicitHeight: avatarSize
|
||||||
implicitWidth: avatarSize
|
implicitWidth: avatarSize
|
||||||
url: tile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: tile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
displayName: title
|
|
||||||
crop: tile.crop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: textContent
|
id: textContent
|
||||||
|
|
||||||
|
@ -103,33 +101,25 @@ Rectangle {
|
||||||
fullText: title
|
fullText: title
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
color: tile.unimportantText
|
color: tile.unimportantText
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 0.9
|
|
||||||
elideWidth: textContent.width - Nheko.paddingSmall
|
elideWidth: textContent.width - Nheko.paddingSmall
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 0.9
|
||||||
fullText: subtitle
|
fullText: subtitle
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,53 +12,51 @@ import im.nheko
|
||||||
Button {
|
Button {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.70)
|
|
||||||
implicitWidth: Math.ceil(control.contentItem.implicitWidth + control.contentItem.implicitHeight)
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
property string iconImage: ""
|
property string iconImage: ""
|
||||||
|
|
||||||
MultiEffect {
|
hoverEnabled: true
|
||||||
anchors.fill: control.background
|
implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.70)
|
||||||
shadowHorizontalOffset: 3
|
implicitWidth: Math.ceil(control.contentItem.implicitWidth + control.contentItem.implicitHeight)
|
||||||
shadowVerticalOffset: 3
|
|
||||||
shadowBlur: 8
|
|
||||||
shadowEnabled: true
|
|
||||||
shadowColor: "#80000000"
|
|
||||||
source: control.background
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
spacing: 0
|
|
||||||
anchors.centerIn: parent
|
|
||||||
Image {
|
|
||||||
Layout.leftMargin: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
|
||||||
Layout.preferredHeight: fontMetrics.font.pixelSize * 1.5
|
|
||||||
Layout.preferredWidth: fontMetrics.font.pixelSize * 1.5
|
|
||||||
visible: !!iconImage
|
|
||||||
source: iconImage
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: control.text
|
|
||||||
//font: control.font
|
|
||||||
font.capitalization: Font.AllUppercase
|
|
||||||
font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
|
|
||||||
//font.capitalization: Font.AllUppercase
|
|
||||||
color: palette.light
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
|
color: Qt.lighter(palette.dark, control.down ? 1.4 : (control.hovered ? 1.2 : 1))
|
||||||
//height: control.contentItem.implicitHeight * 2
|
//height: control.contentItem.implicitHeight * 2
|
||||||
//width: control.contentItem.implicitWidth * 2
|
//width: control.contentItem.implicitWidth * 2
|
||||||
radius: height / 8
|
radius: height / 8
|
||||||
color: Qt.lighter(palette.dark, control.down ? 1.4 : (control.hovered ? 1.2 : 1))
|
}
|
||||||
|
contentItem: RowLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
Layout.leftMargin: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: fontMetrics.font.pixelSize * 1.5
|
||||||
|
Layout.preferredWidth: fontMetrics.font.pixelSize * 1.5
|
||||||
|
source: iconImage
|
||||||
|
visible: !!iconImage
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
//font.capitalization: Font.AllUppercase
|
||||||
|
color: palette.light
|
||||||
|
elide: Text.ElideRight
|
||||||
|
//font: control.font
|
||||||
|
font.capitalization: Font.AllUppercase
|
||||||
|
font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: control.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: control.background
|
||||||
|
shadowBlur: 8
|
||||||
|
shadowColor: "#80000000"
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowHorizontalOffset: 3
|
||||||
|
shadowVerticalOffset: 3
|
||||||
|
source: control.background
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,30 +10,29 @@ Dialog {
|
||||||
default property alias inner: scroll.data
|
default property alias inner: scroll.data
|
||||||
property int useableWidth: scroll.width - scroll.ScrollBar.vertical.width
|
property int useableWidth: scroll.width - scroll.ScrollBar.vertical.width
|
||||||
|
|
||||||
parent: Overlay.overlay
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: (Math.floor(parent.height / 2) - Nheko.paddingLarge) * 2
|
|
||||||
width: (Math.floor(parent.width / 2) - Nheko.paddingLarge) * 2
|
|
||||||
padding: 0
|
|
||||||
modal: true
|
|
||||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
|
||||||
closePolicy: Popup.NoAutoClose
|
closePolicy: Popup.NoAutoClose
|
||||||
|
height: (Math.floor(parent.height / 2) - Nheko.paddingLarge) * 2
|
||||||
|
modal: true
|
||||||
|
padding: 0
|
||||||
|
parent: Overlay.overlay
|
||||||
|
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
|
width: (Math.floor(parent.width / 2) - Nheko.paddingLarge) * 2
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: Nheko.theme.separator
|
||||||
|
border.width: 1
|
||||||
|
color: palette.window
|
||||||
|
radius: Nheko.paddingSmall
|
||||||
|
}
|
||||||
contentChildren: [
|
contentChildren: [
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: scroll
|
id: scroll
|
||||||
|
|
||||||
clip: true
|
|
||||||
anchors.fill: parent
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
ScrollBar.vertical.visible: true
|
ScrollBar.vertical.visible: true
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: palette.window
|
|
||||||
border.color: Nheko.theme.separator
|
|
||||||
border.width: 1
|
|
||||||
radius: Nheko.paddingSmall
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,19 @@ import im.nheko 1.0
|
||||||
TabButton {
|
TabButton {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
contentItem: Text {
|
|
||||||
text: control.text
|
|
||||||
font: control.font
|
|
||||||
opacity: enabled ? 1.0 : 0.3
|
|
||||||
color: control.down ? palette.highlightedText : palette.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
border.color: control.down ? palette.highlight : Nheko.theme.separator
|
border.color: control.down ? palette.highlight : Nheko.theme.separator
|
||||||
color: control.checked ? palette.highlight : palette.base
|
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
color: control.checked ? palette.highlight : palette.base
|
||||||
radius: 2
|
radius: 2
|
||||||
}
|
}
|
||||||
|
contentItem: Text {
|
||||||
|
color: control.down ? palette.highlightedText : palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font: control.font
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
opacity: enabled ? 1.0 : 0.3
|
||||||
|
text: control.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,39 +9,38 @@ import im.nheko 1.0
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: bubbleRoot
|
id: bubbleRoot
|
||||||
|
|
||||||
required property int notificationCount
|
|
||||||
required property bool hasLoudNotification
|
|
||||||
required property color bubbleBackgroundColor
|
required property color bubbleBackgroundColor
|
||||||
required property color bubbleTextColor
|
required property color bubbleTextColor
|
||||||
property bool mayBeVisible: true
|
|
||||||
property alias font: notificationBubbleText.font
|
property alias font: notificationBubbleText.font
|
||||||
baselineOffset: notificationBubbleText.baseline - bubbleRoot.top
|
required property bool hasLoudNotification
|
||||||
|
property bool mayBeVisible: true
|
||||||
|
required property int notificationCount
|
||||||
|
|
||||||
visible: mayBeVisible && notificationCount > 0
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: notificationCount
|
||||||
|
ToolTip.visible: notificationBubbleHover.hovered && (notificationCount > 9999)
|
||||||
|
baselineOffset: notificationBubbleText.baseline - bubbleRoot.top
|
||||||
|
color: hasLoudNotification ? Nheko.theme.red : bubbleBackgroundColor
|
||||||
implicitHeight: notificationBubbleText.height + Nheko.paddingMedium
|
implicitHeight: notificationBubbleText.height + Nheko.paddingMedium
|
||||||
implicitWidth: Math.max(notificationBubbleText.width, height)
|
implicitWidth: Math.max(notificationBubbleText.width, height)
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
color: hasLoudNotification ? Nheko.theme.red : bubbleBackgroundColor
|
visible: mayBeVisible && notificationCount > 0
|
||||||
ToolTip.text: notificationCount
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
ToolTip.visible: notificationBubbleHover.hovered && (notificationCount > 9999)
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: notificationBubbleText
|
id: notificationBubbleText
|
||||||
|
|
||||||
anchors.centerIn: bubbleRoot
|
anchors.centerIn: bubbleRoot
|
||||||
horizontalAlignment: Text.AlignHCenter
|
color: bubbleRoot.hasLoudNotification ? "white" : bubbleRoot.bubbleTextColor
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
width: Math.max(implicitWidth + Nheko.paddingMedium, bubbleRoot.height)
|
|
||||||
font.bold: true
|
font.bold: true
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 0.8
|
font.pixelSize: fontMetrics.font.pixelSize * 0.8
|
||||||
color: bubbleRoot.hasLoudNotification ? "white" : bubbleRoot.bubbleTextColor
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: bubbleRoot.notificationCount > 9999 ? "9999+" : bubbleRoot.notificationCount
|
text: bubbleRoot.notificationCount > 9999 ? "9999+" : bubbleRoot.notificationCount
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
width: Math.max(implicitWidth + Nheko.paddingMedium, bubbleRoot.height)
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: notificationBubbleHover
|
id: notificationBubbleHover
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,11 @@ import QtQuick.Controls
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
required property int powerlevel
|
|
||||||
required property var permissions
|
|
||||||
|
|
||||||
readonly property bool isAdmin: permissions ? permissions.changeLevel(MtxEvent.PowerLevels) <= powerlevel : false
|
readonly property bool isAdmin: permissions ? permissions.changeLevel(MtxEvent.PowerLevels) <= powerlevel : false
|
||||||
readonly property bool isModerator: permissions ? permissions.redactLevel() <= powerlevel : false
|
|
||||||
readonly property bool isDefault: permissions ? permissions.defaultLevel() <= powerlevel : false
|
readonly property bool isDefault: permissions ? permissions.defaultLevel() <= powerlevel : false
|
||||||
|
readonly property bool isModerator: permissions ? permissions.redactLevel() <= powerlevel : false
|
||||||
|
required property var permissions
|
||||||
|
required property int powerlevel
|
||||||
readonly property string sourceUrl: {
|
readonly property string sourceUrl: {
|
||||||
if (isAdmin)
|
if (isAdmin)
|
||||||
return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?";
|
return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?";
|
||||||
|
@ -23,8 +21,6 @@ Image {
|
||||||
return "image://colorimage/:/icons/icons/ui/person.svg?";
|
return "image://colorimage/:/icons/icons/ui/person.svg?";
|
||||||
}
|
}
|
||||||
|
|
||||||
source: sourceUrl + (ma.hovered ? palette.highlight : palette.buttonText)
|
|
||||||
ToolTip.visible: ma.hovered
|
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
if (isAdmin)
|
if (isAdmin)
|
||||||
return qsTr("Administrator: %1").arg(powerlevel);
|
return qsTr("Administrator: %1").arg(powerlevel);
|
||||||
|
@ -33,8 +29,11 @@ Image {
|
||||||
else
|
else
|
||||||
return qsTr("User: %1").arg(powerlevel);
|
return qsTr("User: %1").arg(powerlevel);
|
||||||
}
|
}
|
||||||
|
ToolTip.visible: ma.hovered
|
||||||
|
source: sourceUrl + (ma.hovered ? palette.highlight : palette.buttonText)
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import QtQml.Models
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias model: visualModel.model
|
|
||||||
property Component delegate
|
property Component delegate
|
||||||
|
property alias model: visualModel.model
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: dragDelegate
|
id: dragDelegate
|
||||||
|
@ -17,104 +17,117 @@ Item {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: dragArea
|
id: dragArea
|
||||||
|
|
||||||
required property var model
|
|
||||||
required property int index
|
|
||||||
|
|
||||||
enabled: model.moveable == undefined || model.moveable
|
|
||||||
|
|
||||||
property bool held: false
|
property bool held: false
|
||||||
|
required property int index
|
||||||
|
required property var model
|
||||||
|
|
||||||
anchors { left: parent.left; right: parent.right }
|
drag.axis: Drag.YAxis
|
||||||
|
drag.target: held ? content : undefined
|
||||||
|
enabled: model.moveable == undefined || model.moveable
|
||||||
height: content.height
|
height: content.height
|
||||||
|
|
||||||
drag.target: held ? content : undefined
|
onHeldChanged: if (held)
|
||||||
drag.axis: Drag.YAxis
|
ListView.view.currentIndex = dragArea.index
|
||||||
|
else
|
||||||
|
ListView.view.currentIndex = -1
|
||||||
onPressAndHold: held = true
|
onPressAndHold: held = true
|
||||||
onPressed: if (mouse.source !== Qt.MouseEventNotSynthesized) { held = true }
|
onPressed: if (mouse.source !== Qt.MouseEventNotSynthesized) {
|
||||||
|
held = true;
|
||||||
|
}
|
||||||
onReleased: held = false
|
onReleased: held = false
|
||||||
onHeldChanged: if (held) ListView.view.currentIndex = dragArea.index; else ListView.view.currentIndex = -1
|
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
|
Drag.active: dragArea.held
|
||||||
|
Drag.hotSpot.x: width / 2
|
||||||
|
Drag.hotSpot.y: height / 2
|
||||||
|
Drag.source: dragArea
|
||||||
|
border.color: palette.highlight
|
||||||
|
border.width: dragArea.enabled ? 1 : 0
|
||||||
|
color: dragArea.held ? palette.highlight : palette.base
|
||||||
|
height: actualDelegate.implicitHeight + 4
|
||||||
|
radius: 2
|
||||||
|
width: dragArea.width
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
states: State {
|
||||||
|
when: dragArea.held
|
||||||
|
|
||||||
|
ParentChange {
|
||||||
|
parent: root
|
||||||
|
target: content
|
||||||
|
}
|
||||||
|
AnchorChanges {
|
||||||
|
target: content
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: undefined
|
||||||
|
verticalCenter: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
width: dragArea.width; height: actualDelegate.implicitHeight + 4
|
|
||||||
|
|
||||||
border.width: dragArea.enabled ? 1 : 0
|
|
||||||
border.color: palette.highlight
|
|
||||||
|
|
||||||
color: dragArea.held ? palette.highlight : palette.base
|
|
||||||
Behavior on color { ColorAnimation { duration: 100 } }
|
|
||||||
|
|
||||||
radius: 2
|
|
||||||
|
|
||||||
Drag.active: dragArea.held
|
|
||||||
Drag.source: dragArea
|
|
||||||
Drag.hotSpot.x: width / 2
|
|
||||||
Drag.hotSpot.y: height / 2
|
|
||||||
|
|
||||||
states: State {
|
|
||||||
when: dragArea.held
|
|
||||||
|
|
||||||
ParentChange { target: content; parent: root }
|
|
||||||
AnchorChanges {
|
|
||||||
target: content
|
|
||||||
anchors { horizontalCenter: undefined; verticalCenter: undefined }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: actualDelegate
|
id: actualDelegate
|
||||||
sourceComponent: root.delegate
|
|
||||||
property var model: dragArea.model
|
|
||||||
property int index: dragArea.index
|
property int index: dragArea.index
|
||||||
|
property var model: dragArea.model
|
||||||
property int offset: -view.contentY + dragArea.y
|
property int offset: -view.contentY + dragArea.y
|
||||||
anchors { fill: parent; margins: 2 }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
sourceComponent: root.delegate
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
DropArea {
|
DropArea {
|
||||||
enabled: index != 0 || model.moveable == undefined || model.moveable
|
enabled: index != 0 || model.moveable == undefined || model.moveable
|
||||||
anchors { fill: parent; margins: 8 }
|
|
||||||
|
|
||||||
onEntered: (drag)=> {
|
onEntered: drag => {
|
||||||
visualModel.model.move(drag.source.index, dragArea.index)
|
visualModel.model.move(drag.source.index, dragArea.index);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 8
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DelegateModel {
|
DelegateModel {
|
||||||
id: visualModel
|
id: visualModel
|
||||||
|
|
||||||
delegate: dragDelegate
|
delegate: dragDelegate
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: view
|
id: view
|
||||||
|
|
||||||
|
cacheBuffer: 50
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
anchors { fill: parent; margins: 2 }
|
|
||||||
|
|
||||||
model: visualModel
|
|
||||||
|
|
||||||
highlightRangeMode: ListView.ApplyRange
|
highlightRangeMode: ListView.ApplyRange
|
||||||
|
model: visualModel
|
||||||
preferredHighlightBegin: 0.2 * height
|
preferredHighlightBegin: 0.2 * height
|
||||||
preferredHighlightEnd: 0.8 * height
|
preferredHighlightEnd: 0.8 * height
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
cacheBuffer: 50
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,76 +9,87 @@ import im.nheko 1.0
|
||||||
Platform.Menu {
|
Platform.Menu {
|
||||||
id: spacesMenu
|
id: spacesMenu
|
||||||
|
|
||||||
property string roomid
|
|
||||||
property Component childMenu
|
property Component childMenu
|
||||||
|
|
||||||
property int position: modelData == undefined ? -2 : modelData.treeIndex
|
|
||||||
title: modelData != undefined ? modelData.name : qsTr("Add or remove from community")
|
|
||||||
property bool loadChildren: false
|
property bool loadChildren: false
|
||||||
|
property int position: modelData == undefined ? -2 : modelData.treeIndex
|
||||||
|
property string roomid
|
||||||
|
|
||||||
|
title: modelData != undefined ? modelData.name : qsTr("Add or remove from community")
|
||||||
|
|
||||||
onAboutToShow: loadChildren = true
|
onAboutToShow: loadChildren = true
|
||||||
|
|
||||||
//onAboutToHide: loadChildren = false
|
//onAboutToHide: loadChildren = false
|
||||||
|
|
||||||
Platform.MenuItemGroup {
|
Platform.MenuItemGroup {
|
||||||
id: modificationGroup
|
id: modificationGroup
|
||||||
|
|
||||||
visible: position != -1
|
visible: position != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Official community for this room")
|
|
||||||
group: modificationGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && modelData.canonical)
|
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && modelData.canonical)
|
||||||
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, true)
|
group: modificationGroup
|
||||||
|
text: qsTr("Official community for this room")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, true)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Affiliated community for this room")
|
|
||||||
group: modificationGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && !modelData.canonical)
|
checked: spacesMenu.position >= 0 && (modelData.childValid && modelData.parentValid && !modelData.canonical)
|
||||||
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
enabled: spacesMenu.position >= 0 && (modelData.canEditChild && modelData.canEditParent)
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, false)
|
group: modificationGroup
|
||||||
|
text: qsTr("Affiliated community for this room")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, true, false)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Listed only for community members")
|
|
||||||
group: modificationGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (modelData.childValid && !modelData.parentValid)
|
checked: spacesMenu.position >= 0 && (modelData.childValid && !modelData.parentValid)
|
||||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, true, false)
|
group: modificationGroup
|
||||||
|
text: qsTr("Listed only for community members")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, true, false)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Listed only for room members")
|
|
||||||
group: modificationGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (!modelData.childValid && modelData.parentValid)
|
checked: spacesMenu.position >= 0 && (!modelData.childValid && modelData.parentValid)
|
||||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild) && (modelData.parentValid || modelData.canEditParent))
|
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild) && (modelData.parentValid || modelData.canEditParent))
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, false, false)
|
group: modificationGroup
|
||||||
|
text: qsTr("Listed only for room members")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, true, false, false)
|
||||||
}
|
}
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Not related")
|
|
||||||
group: modificationGroup
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: spacesMenu.position >= 0 && (!modelData.childValid && !modelData.parentValid)
|
checked: spacesMenu.position >= 0 && (!modelData.childValid && !modelData.parentValid)
|
||||||
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || !modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
enabled: spacesMenu.position >= 0 && ((modelData.canEditChild || !modelData.childValid) && (!modelData.parentValid || modelData.canEditParent))
|
||||||
onTriggered: if (checked) Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuSeparator {
|
|
||||||
text: qsTr("Subcommunities")
|
|
||||||
group: modificationGroup
|
group: modificationGroup
|
||||||
|
text: qsTr("Not related")
|
||||||
|
|
||||||
|
onTriggered: if (checked)
|
||||||
|
Communities.updateSpaceStatus(modelData.roomid, spacesMenu.roomid, false, false, false)
|
||||||
|
}
|
||||||
|
Platform.MenuSeparator {
|
||||||
|
group: modificationGroup
|
||||||
|
text: qsTr("Subcommunities")
|
||||||
visible: modificationGroup.visible && inst.model != undefined
|
visible: modificationGroup.visible && inst.model != undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
Instantiator {
|
Instantiator {
|
||||||
id: inst
|
id: inst
|
||||||
model: spacesMenu.loadChildren ? Communities.spaceChildrenListFromIndex(spacesMenu.roomid, spacesMenu.position) : undefined
|
|
||||||
onObjectAdded: (idx, o) => {
|
|
||||||
spacesMenu.insertMenu(idx + (spacesMenu.position != -1 ? 6 : 0), o)
|
|
||||||
}
|
|
||||||
//onObjectRemoved: spacesMenu.removeMenu(object)
|
//onObjectRemoved: spacesMenu.removeMenu(object)
|
||||||
|
|
||||||
delegate: childMenu
|
delegate: childMenu
|
||||||
|
model: spacesMenu.loadChildren ? Communities.spaceChildrenListFromIndex(spacesMenu.roomid, spacesMenu.position) : undefined
|
||||||
|
|
||||||
|
onObjectAdded: (idx, o) => {
|
||||||
|
spacesMenu.insertMenu(idx + (spacesMenu.position != -1 ? 6 : 0), o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,37 +10,34 @@ import im.nheko 1.0 // for cursor shape
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
|
property color buttonTextColor: palette.buttonText
|
||||||
property alias cursor: mouseArea.cursorShape
|
property alias cursor: mouseArea.cursorShape
|
||||||
property color highlightColor: palette.highlight
|
property color highlightColor: palette.highlight
|
||||||
property color buttonTextColor: palette.buttonText
|
|
||||||
|
|
||||||
focusPolicy: Qt.NoFocus
|
focusPolicy: Qt.NoFocus
|
||||||
width: buttonText.implicitWidth
|
|
||||||
height: buttonText.implicitHeight
|
height: buttonText.implicitHeight
|
||||||
implicitWidth: buttonText.implicitWidth
|
|
||||||
implicitHeight: buttonText.implicitHeight
|
implicitHeight: buttonText.implicitHeight
|
||||||
|
implicitWidth: buttonText.implicitWidth
|
||||||
|
width: buttonText.implicitWidth
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: buttonText
|
id: buttonText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
padding: 0
|
|
||||||
text: button.text
|
|
||||||
color: button.hovered ? highlightColor : buttonTextColor
|
color: button.hovered ? highlightColor : buttonTextColor
|
||||||
font: button.font
|
font: button.font
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
padding: 0
|
||||||
|
text: button.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Ripple {
|
Ripple {
|
||||||
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,43 +9,50 @@ import QtQuick.Layouts 1.12
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
ItemDelegate {
|
ItemDelegate {
|
||||||
property alias bgColor: background.color
|
|
||||||
property alias userid: avatar.userid
|
|
||||||
property alias displayName: avatar.displayName
|
|
||||||
property string avatarUrl
|
property string avatarUrl
|
||||||
|
property alias bgColor: background.color
|
||||||
|
property alias displayName: avatar.displayName
|
||||||
|
property alias userid: avatar.userid
|
||||||
|
|
||||||
implicitHeight: layout.implicitHeight + Nheko.paddingSmall * 2
|
implicitHeight: layout.implicitHeight + Nheko.paddingSmall * 2
|
||||||
background: Rectangle {id: background}
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: background
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: layout
|
id: layout
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - Nheko.paddingSmall * 2
|
columnSpacing: Nheko.paddingMedium
|
||||||
rows: 2
|
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingSmall
|
rowSpacing: Nheko.paddingSmall
|
||||||
columnSpacing: Nheko.paddingMedium
|
rows: 2
|
||||||
|
width: parent.width - Nheko.paddingSmall * 2
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.preferredWidth: Nheko.avatarSize
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
Layout.preferredHeight: Nheko.avatarSize
|
||||||
|
Layout.preferredWidth: Nheko.avatarSize
|
||||||
|
Layout.rowSpan: 2
|
||||||
enabled: false
|
enabled: false
|
||||||
|
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: displayName
|
|
||||||
color: TimelineManager.userColor(userid, palette.window)
|
color: TimelineManager.userColor(userid, palette.window)
|
||||||
font.pointSize: fontMetrics.font.pointSize
|
font.pointSize: fontMetrics.font.pointSize
|
||||||
|
text: displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
text: userid
|
Layout.fillWidth: true
|
||||||
color: palette.buttonText
|
color: palette.buttonText
|
||||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
text: userid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,29 +13,37 @@ Control {
|
||||||
required property int encryptionError
|
required property int encryptionError
|
||||||
required property string eventId
|
required property string eventId
|
||||||
|
|
||||||
padding: Nheko.paddingMedium
|
|
||||||
implicitHeight: contents.implicitHeight + Nheko.paddingMedium * 2
|
|
||||||
Layout.maximumWidth: contents.Layout.maximumWidth + padding * 2
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: contents.Layout.maximumWidth + padding * 2
|
||||||
|
implicitHeight: contents.implicitHeight + Nheko.paddingMedium * 2
|
||||||
|
padding: Nheko.paddingMedium
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: palette.alternateBase
|
||||||
|
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingMedium
|
||||||
|
visible: !Settings.bubbles // the bubble in a bubble looks odd
|
||||||
|
}
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
id: contents
|
id: contents
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.preferredWidth: 24
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
|
Layout.preferredWidth: 24
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: encryptedText
|
id: encryptedText
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: implicitWidth + 1
|
||||||
|
color: palette.text
|
||||||
text: {
|
text: {
|
||||||
switch (r.encryptionError) {
|
switch (r.encryptionError) {
|
||||||
case Olm.MissingSession:
|
case Olm.MissingSession:
|
||||||
|
@ -56,24 +64,13 @@ Control {
|
||||||
}
|
}
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
wrapMode: Label.WordWrap
|
wrapMode: Label.WordWrap
|
||||||
color: palette.text
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: implicitWidth + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: r.encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
|
||||||
text: qsTr("Request key")
|
text: qsTr("Request key")
|
||||||
|
visible: r.encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
||||||
|
|
||||||
onClicked: room.requestKeyForEvent(eventId)
|
onClicked: room.requestKeyForEvent(eventId)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: palette.alternateBase
|
|
||||||
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingMedium
|
|
||||||
visible: !Settings.bubbles // the bubble in a bubble looks odd
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,53 +13,48 @@ Control {
|
||||||
|
|
||||||
required property string userName
|
required property string userName
|
||||||
|
|
||||||
padding: Nheko.paddingMedium
|
Layout.fillWidth: true
|
||||||
//implicitHeight: contents.implicitHeight + padd * 2
|
//implicitHeight: contents.implicitHeight + padd * 2
|
||||||
Layout.maximumWidth: contents.Layout.maximumWidth + padding * 2
|
Layout.maximumWidth: contents.Layout.maximumWidth + padding * 2
|
||||||
Layout.fillWidth: true
|
padding: Nheko.paddingMedium
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: Nheko.theme.green
|
||||||
|
border.width: 2
|
||||||
|
color: palette.alternateBase
|
||||||
|
height: contents.implicitHeight + Nheko.paddingMedium * 2
|
||||||
|
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
|
||||||
|
}
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
id: contents
|
id: contents
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
source: "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?" + Nheko.theme.green
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.preferredWidth: 24
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
|
Layout.preferredWidth: 24
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?" + Nheko.theme.green
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("%1 enabled end-to-end encryption").arg(r.userName)
|
|
||||||
font.bold: true
|
|
||||||
font.pointSize: 14
|
|
||||||
color: palette.text
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: implicitWidth + 1
|
Layout.maximumWidth: implicitWidth + 1
|
||||||
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
|
font.pointSize: 14
|
||||||
|
text: qsTr("%1 enabled end-to-end encryption").arg(r.userName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: implicitWidth + 1
|
||||||
text: qsTr("Encryption keeps your messages safe by only allowing the people you sent the message to to read it. For extra security, if you want to make sure you are talking to the right people, you can verify them in real life.")
|
text: qsTr("Encryption keeps your messages safe by only allowing the people you sent the message to to read it. For extra security, if you want to make sure you are talking to the right people, you can verify them in real life.")
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
wrapMode: Label.WordWrap
|
wrapMode: Label.WordWrap
|
||||||
Layout.fillWidth: true
|
}
|
||||||
Layout.maximumWidth: implicitWidth + 1
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
|
|
||||||
height: contents.implicitHeight + Nheko.paddingMedium * 2
|
|
||||||
color: palette.alternateBase
|
|
||||||
border.color: Nheko.theme.green
|
|
||||||
border.width: 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,19 @@ Control {
|
||||||
required property string eventId
|
required property string eventId
|
||||||
required property string filename
|
required property string filename
|
||||||
required property string filesize
|
required property string filesize
|
||||||
|
property bool fitsMetadata: false
|
||||||
padding: Settings.bubbles? 8 : 12
|
|
||||||
//Layout.preferredHeight: rowa.implicitHeight + padding
|
//Layout.preferredHeight: rowa.implicitHeight + padding
|
||||||
//Layout.maximumWidth: rowa.Layout.maximumWidth + metadataWidth + padding
|
//Layout.maximumWidth: rowa.Layout.maximumWidth + metadataWidth + padding
|
||||||
property int metadataWidth: 0
|
property int metadataWidth: 0
|
||||||
property bool fitsMetadata: false
|
|
||||||
|
|
||||||
Layout.maximumWidth: rowa.Layout.maximumWidth + padding * 2
|
Layout.maximumWidth: rowa.Layout.maximumWidth + padding * 2
|
||||||
|
padding: Settings.bubbles ? 8 : 12
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: palette.alternateBase
|
||||||
|
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
||||||
|
visible: !Settings.bubbles // the bubble in a bubble looks odd
|
||||||
|
}
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
id: rowa
|
id: rowa
|
||||||
|
|
||||||
|
@ -30,36 +34,32 @@ Control {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
color: palette.light
|
|
||||||
radius: 22
|
|
||||||
Layout.preferredHeight: 44
|
Layout.preferredHeight: 44
|
||||||
Layout.preferredWidth: 44
|
Layout.preferredWidth: 44
|
||||||
|
color: palette.light
|
||||||
|
radius: 22
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
fillMode: Image.Pad
|
||||||
height: 40
|
height: 40
|
||||||
width: 40
|
source: "qrc:/icons/icons/ui/download.svg"
|
||||||
sourceSize.height: 40
|
sourceSize.height: 40
|
||||||
sourceSize.width: 40
|
sourceSize.width: 40
|
||||||
|
width: 40
|
||||||
anchors.centerIn: parent
|
|
||||||
source: "qrc:/icons/icons/ui/download.svg"
|
|
||||||
fillMode: Image.Pad
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onSingleTapped: room.saveMedia(eventId)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
}
|
|
||||||
|
|
||||||
|
onSingleTapped: room.saveMedia(eventId)
|
||||||
|
}
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: col
|
id: col
|
||||||
|
|
||||||
|
@ -68,31 +68,21 @@ Control {
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: implicitWidth + 1
|
Layout.maximumWidth: implicitWidth + 1
|
||||||
|
color: palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
text: evRoot.filename
|
text: evRoot.filename
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
elide: Text.ElideRight
|
|
||||||
color: palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: filesize_
|
id: filesize_
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: implicitWidth + 1
|
Layout.maximumWidth: implicitWidth + 1
|
||||||
|
color: palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
text: evRoot.filesize
|
text: evRoot.filesize
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
elide: Text.ElideRight
|
|
||||||
color: palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: palette.alternateBase
|
|
||||||
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
|
||||||
visible: !Settings.bubbles // the bubble in a bubble looks odd
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,27 +8,28 @@ import QtQuick.Controls
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
required property int type
|
|
||||||
required property int originalWidth
|
|
||||||
required property int originalHeight
|
|
||||||
required property double proportionalHeight
|
|
||||||
required property string url
|
|
||||||
required property string blurhash
|
required property string blurhash
|
||||||
required property string body
|
required property string body
|
||||||
required property string filename
|
|
||||||
required property string eventId
|
|
||||||
required property int containerHeight
|
required property int containerHeight
|
||||||
property double divisor: EventDelegateChooser.isReply ? 10 : 4
|
property double divisor: EventDelegateChooser.isReply ? 10 : 4
|
||||||
|
required property string eventId
|
||||||
|
required property string filename
|
||||||
|
property bool fitsMetadata: parent != null ? (parent.width - width) > metadataWidth + 4 : false
|
||||||
|
property int metadataWidth
|
||||||
|
required property int originalHeight
|
||||||
|
required property int originalWidth
|
||||||
|
required property double proportionalHeight
|
||||||
|
required property int type
|
||||||
|
required property string url
|
||||||
|
|
||||||
EventDelegateChooser.keepAspectRatio: true
|
|
||||||
EventDelegateChooser.maxWidth: originalWidth
|
|
||||||
EventDelegateChooser.maxHeight: containerHeight / divisor
|
|
||||||
EventDelegateChooser.aspectRatio: proportionalHeight
|
EventDelegateChooser.aspectRatio: proportionalHeight
|
||||||
|
EventDelegateChooser.keepAspectRatio: true
|
||||||
hoverEnabled: true
|
EventDelegateChooser.maxHeight: containerHeight / divisor
|
||||||
|
EventDelegateChooser.maxWidth: originalWidth
|
||||||
enabled: !EventDelegateChooser.isReply
|
enabled: !EventDelegateChooser.isReply
|
||||||
|
hoverEnabled: true
|
||||||
state: (img.status != Image.Ready || timeline.privacyScreen.active) ? "BlurhashVisible" : "ImageVisible"
|
state: (img.status != Image.Ready || timeline.privacyScreen.active) ? "BlurhashVisible" : "ImageVisible"
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "BlurhashVisible"
|
name: "BlurhashVisible"
|
||||||
|
@ -39,11 +40,9 @@ AbstractButton {
|
||||||
visible: (img.status != Image.Ready) || (timeline.privacyScreen.active && blurhash)
|
visible: (img.status != Image.Ready) || (timeline.privacyScreen.active && blurhash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
img.opacity: 0
|
img.opacity: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
mxcimage.opacity: 0
|
mxcimage.opacity: 0
|
||||||
}
|
}
|
||||||
|
@ -57,11 +56,9 @@ AbstractButton {
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
img.opacity: 1
|
img.opacity: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
mxcimage.opacity: 1
|
mxcimage.opacity: 1
|
||||||
}
|
}
|
||||||
|
@ -70,114 +67,98 @@ AbstractButton {
|
||||||
transitions: [
|
transitions: [
|
||||||
Transition {
|
Transition {
|
||||||
from: "ImageVisible"
|
from: "ImageVisible"
|
||||||
to: "BlurhashVisible"
|
|
||||||
reversible: true
|
reversible: true
|
||||||
|
to: "BlurhashVisible"
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
PropertyAction {
|
PropertyAction {
|
||||||
target: blurhash_
|
|
||||||
property: "visible"
|
property: "visible"
|
||||||
|
target: blurhash_
|
||||||
}
|
}
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
property: "opacity"
|
||||||
target: blurhash_
|
target: blurhash_
|
||||||
property: "opacity"
|
}
|
||||||
|
NumberAnimation {
|
||||||
duration: 300
|
duration: 300
|
||||||
easing.type: Easing.Linear
|
easing.type: Easing.Linear
|
||||||
}
|
property: "opacity"
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
target: img
|
target: img
|
||||||
property: "opacity"
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.Linear
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: mxcimage
|
|
||||||
property: "opacity"
|
|
||||||
duration: 300
|
duration: 300
|
||||||
easing.type: Easing.Linear
|
easing.type: Easing.Linear
|
||||||
|
property: "opacity"
|
||||||
|
target: mxcimage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
property int metadataWidth
|
onClicked: Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId, originalWidth, proportionalHeight)
|
||||||
property bool fitsMetadata: parent != null ? (parent.width - width) > metadataWidth+4 : false
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
visible: !mxcimage.loaded
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: url != "" ? (url.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
horizontalAlignment: Image.AlignLeft
|
horizontalAlignment: Image.AlignLeft
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
mipmap: true
|
||||||
|
smooth: true
|
||||||
sourceSize.width: Math.min(Screen.desktopAvailableWidth, originalWidth < 1 ? Screen.desktopAvailableWidth : originalWidth) * Screen.devicePixelRatio
|
source: url != "" ? (url.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
||||||
sourceSize.height: Math.min(Screen.desktopAvailableHeight, (originalWidth < 1 ? Screen.desktopAvailableHeight : originalWidth * proportionalHeight)) * Screen.devicePixelRatio
|
sourceSize.height: Math.min(Screen.desktopAvailableHeight, (originalWidth < 1 ? Screen.desktopAvailableHeight : originalWidth * proportionalHeight)) * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: Math.min(Screen.desktopAvailableWidth, originalWidth < 1 ? Screen.desktopAvailableWidth : originalWidth) * Screen.devicePixelRatio
|
||||||
|
visible: !mxcimage.loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
MxcAnimatedImage {
|
MxcAnimatedImage {
|
||||||
id: mxcimage
|
id: mxcimage
|
||||||
|
|
||||||
visible: loaded
|
|
||||||
roomm: room
|
|
||||||
play: !Settings.animateImagesOnHover || parent.hovered
|
|
||||||
eventId: parent.eventId
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
eventId: parent.eventId
|
||||||
|
play: !Settings.animateImagesOnHover || parent.hovered
|
||||||
|
roomm: room
|
||||||
|
visible: loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: blurhash_
|
id: blurhash_
|
||||||
|
|
||||||
source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + palette.buttonText)
|
anchors.fill: parent
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
sourceSize.width: parent.width * Screen.devicePixelRatio
|
source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + palette.buttonText)
|
||||||
sourceSize.height: parent.height * Screen.devicePixelRatio
|
sourceSize.height: parent.height * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.width * Screen.devicePixelRatio
|
||||||
anchors.fill: parent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId, originalWidth, proportionalHeight);
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: overlay
|
id: overlay
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
visible: parent.hovered
|
visible: parent.hovered
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: container
|
id: container
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
implicitHeight: imgcaption.implicitHeight
|
|
||||||
anchors.bottom: overlay.bottom
|
anchors.bottom: overlay.bottom
|
||||||
color: palette.window
|
color: palette.window
|
||||||
|
implicitHeight: imgcaption.implicitHeight
|
||||||
opacity: 0.75
|
opacity: 0.75
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: imgcaption
|
id: imgcaption
|
||||||
|
|
||||||
anchors.fill: container
|
anchors.fill: container
|
||||||
|
color: palette.text
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
// See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530
|
// See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530
|
||||||
text: filename ? filename : body
|
text: filename ? filename : body
|
||||||
color: palette.text
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
|
|
||||||
TextMessage {
|
TextMessage {
|
||||||
property bool isStateEvent
|
property bool isStateEvent
|
||||||
font.italic: true
|
|
||||||
color: palette.buttonText
|
color: palette.buttonText
|
||||||
|
font.italic: true
|
||||||
font.pointSize: isStateEvent ? 0.8 * Settings.fontSize : Settings.fontSize
|
font.pointSize: isStateEvent ? 0.8 * Settings.fontSize : Settings.fontSize
|
||||||
horizontalAlignment: isStateEvent ? Text.AlignHCenter : undefined
|
horizontalAlignment: isStateEvent ? Text.AlignHCenter : undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,14 @@ import QtQuick.Controls 2.1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
property bool isStateEvent
|
property bool isStateEvent
|
||||||
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
height: Math.round(fontMetrics.height * 1.4)
|
height: Math.round(fontMetrics.height * 1.4)
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
width: contentWidth * 1.2
|
width: contentWidth * 1.2
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: parent.height / 2
|
|
||||||
color: palette.alternateBase
|
color: palette.alternateBase
|
||||||
|
radius: parent.height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import im.nheko 1.0
|
||||||
MatrixText {
|
MatrixText {
|
||||||
required property string typeString
|
required property string typeString
|
||||||
|
|
||||||
text: qsTr("unimplemented event: ") + typeString
|
|
||||||
// width: parent.width
|
// width: parent.width
|
||||||
color: palette.inactive.text
|
color: palette.inactive.text
|
||||||
|
text: qsTr("unimplemented event: ") + typeString
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,80 +11,79 @@ import im.nheko
|
||||||
Item {
|
Item {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
required property double proportionalHeight
|
|
||||||
required property int type
|
|
||||||
required property int originalWidth
|
|
||||||
required property int duration
|
|
||||||
required property string thumbnailUrl
|
|
||||||
required property string eventId
|
|
||||||
required property string url
|
|
||||||
required property string body
|
required property string body
|
||||||
required property string filesize
|
|
||||||
property double divisor: EventDelegateChooser.isReply ? 10 : 4
|
property double divisor: EventDelegateChooser.isReply ? 10 : 4
|
||||||
property int tempWidth: originalWidth < 1? 400: originalWidth
|
required property int duration
|
||||||
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500
|
required property string eventId
|
||||||
width: Math.min(parent?.width ?? implicitWidth, implicitWidth)
|
required property string filesize
|
||||||
height: (type == MtxEvent.VideoMessage ? width*proportionalHeight : 80) + fileInfoLabel.height
|
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth + 4
|
||||||
//implicitHeight: height
|
//implicitHeight: height
|
||||||
|
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth+4
|
required property int originalWidth
|
||||||
|
required property double proportionalHeight
|
||||||
|
property int tempWidth: originalWidth < 1 ? 400 : originalWidth
|
||||||
|
required property string thumbnailUrl
|
||||||
|
required property int type
|
||||||
|
required property string url
|
||||||
|
|
||||||
|
height: (type == MtxEvent.VideoMessage ? width * proportionalHeight : 80) + fileInfoLabel.height
|
||||||
|
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth * Math.min((timelineView.height / divisor) / (tempWidth * proportionalHeight), 1)) : 500
|
||||||
|
width: Math.min(parent?.width ?? implicitWidth, implicitWidth)
|
||||||
|
|
||||||
MxcMedia {
|
MxcMedia {
|
||||||
id: mxcmedia
|
id: mxcmedia
|
||||||
|
|
||||||
// TODO: Show error in overlay or so?
|
// TODO: Show error in overlay or so?
|
||||||
roomm: room
|
roomm: room
|
||||||
|
videoOutput: videoOutput
|
||||||
|
|
||||||
audioOutput: AudioOutput {
|
audioOutput: AudioOutput {
|
||||||
muted: mediaControls.muted
|
muted: mediaControls.muted
|
||||||
volume: mediaControls.desiredVolume
|
volume: mediaControls.desiredVolume
|
||||||
}
|
}
|
||||||
videoOutput: videoOutput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: videoContainer
|
id: videoContainer
|
||||||
|
|
||||||
color: content.type == MtxEvent.VideoMessage ? palette.window : "transparent"
|
color: content.type == MtxEvent.VideoMessage ? palette.window : "transparent"
|
||||||
width: parent.width
|
|
||||||
height: parent.height - fileInfoLabel.height
|
height: parent.height - fileInfoLabel.height
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onTapped: Settings.openVideoExternal ? room.openMedia(eventId) : mediaControls.showControls()
|
onTapped: Settings.openVideoExternal ? room.openMedia(eventId) : mediaControls.showControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: content.thumbnailUrl ? thumbnailUrl.replace("mxc://", "image://MxcImage/") + "?scale" : "image://colorimage/:/icons/icons/ui/video-file.svg?" + palette.windowText
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: content.thumbnailUrl ? thumbnailUrl.replace("mxc://", "image://MxcImage/") + "?scale" : "image://colorimage/:/icons/icons/ui/video-file.svg?" + palette.windowText
|
||||||
|
|
||||||
VideoOutput {
|
VideoOutput {
|
||||||
id: videoOutput
|
id: videoOutput
|
||||||
|
|
||||||
visible: content.type == MtxEvent.VideoMessage
|
|
||||||
clip: true
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
fillMode: VideoOutput.PreserveAspectFit
|
fillMode: VideoOutput.PreserveAspectFit
|
||||||
orientation: mxcmedia.orientation
|
orientation: mxcmedia.orientation
|
||||||
|
visible: content.type == MtxEvent.VideoMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaControls {
|
MediaControls {
|
||||||
id: mediaControls
|
id: mediaControls
|
||||||
|
|
||||||
|
anchors.bottom: videoContainer.bottom
|
||||||
anchors.left: videoContainer.left
|
anchors.left: videoContainer.left
|
||||||
anchors.right: videoContainer.right
|
anchors.right: videoContainer.right
|
||||||
anchors.bottom: videoContainer.bottom
|
|
||||||
playingVideo: content.type == MtxEvent.VideoMessage
|
|
||||||
positionValue: mxcmedia.position
|
|
||||||
duration: mediaLoaded ? mxcmedia.duration : content.duration
|
duration: mediaLoaded ? mxcmedia.duration : content.duration
|
||||||
mediaLoaded: mxcmedia.loaded
|
mediaLoaded: mxcmedia.loaded
|
||||||
mediaState: mxcmedia.playbackState
|
mediaState: mxcmedia.playbackState
|
||||||
onPositionChanged: mxcmedia.position = position
|
playingVideo: content.type == MtxEvent.VideoMessage
|
||||||
onPlayPauseActivated: mxcmedia.playbackState == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
|
positionValue: mxcmedia.position
|
||||||
|
|
||||||
onLoadActivated: mxcmedia.eventId = eventId
|
onLoadActivated: mxcmedia.eventId = eventId
|
||||||
|
onPlayPauseActivated: mxcmedia.playbackState == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
|
||||||
|
onPositionChanged: mxcmedia.position = position
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,15 +92,13 @@ Item {
|
||||||
id: fileInfoLabel
|
id: fileInfoLabel
|
||||||
|
|
||||||
anchors.top: videoContainer.bottom
|
anchors.top: videoContainer.bottom
|
||||||
|
color: palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
text: content.body + " [" + filesize + "]"
|
text: content.body + " [" + filesize + "]"
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
elide: Text.ElideRight
|
|
||||||
color: palette.text
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: palette.base
|
color: palette.base
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,47 +10,50 @@ import im.nheko
|
||||||
Control {
|
Control {
|
||||||
id: msgRoot
|
id: msgRoot
|
||||||
|
|
||||||
property int metadataWidth: 0
|
required property string eventId
|
||||||
property bool fitsMetadata: false //parent.width - redactedLayout.width > metadataWidth + 4
|
property bool fitsMetadata: false //parent.width - redactedLayout.width > metadataWidth + 4
|
||||||
|
|
||||||
required property string eventId
|
property int metadataWidth: 0
|
||||||
required property Room room
|
required property Room room
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
id: redactedLayout
|
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: trashImg
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
Layout.preferredWidth: fontMetrics.font.pixelSize
|
|
||||||
Layout.preferredHeight: fontMetrics.font.pixelSize
|
|
||||||
source: "image://colorimage/:/icons/icons/ui/delete.svg?" + palette.text
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: redactedLabel
|
|
||||||
Layout.margins: 0
|
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
|
||||||
Layout.maximumWidth: implicitWidth + 1
|
|
||||||
Layout.fillWidth: true
|
|
||||||
property var redactedPair: msgRoot.room.formatRedactedEvent(msgRoot.eventId)
|
|
||||||
text: redactedPair["first"]
|
|
||||||
wrapMode: Label.WordWrap
|
|
||||||
|
|
||||||
ToolTip.text: redactedPair["second"]
|
|
||||||
ToolTip.visible: hh.hovered
|
|
||||||
HoverHandler {
|
|
||||||
id: hh
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
padding: Nheko.paddingSmall
|
|
||||||
|
|
||||||
Layout.maximumWidth: redactedLayout.Layout.maximumWidth + padding * 2
|
Layout.maximumWidth: redactedLayout.Layout.maximumWidth + padding * 2
|
||||||
|
padding: Nheko.paddingSmall
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: palette.alternateBase
|
color: palette.alternateBase
|
||||||
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
||||||
}
|
}
|
||||||
|
contentItem: RowLayout {
|
||||||
|
id: redactedLayout
|
||||||
|
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: trashImg
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
Layout.preferredHeight: fontMetrics.font.pixelSize
|
||||||
|
Layout.preferredWidth: fontMetrics.font.pixelSize
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/delete.svg?" + palette.text
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: redactedLabel
|
||||||
|
|
||||||
|
property var redactedPair: msgRoot.room.formatRedactedEvent(msgRoot.eventId)
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 0
|
||||||
|
Layout.maximumWidth: implicitWidth + 1
|
||||||
|
ToolTip.text: redactedPair["second"]
|
||||||
|
ToolTip.visible: hh.hovered
|
||||||
|
text: redactedPair["first"]
|
||||||
|
wrapMode: Label.WordWrap
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hh
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,43 +11,35 @@ import "../"
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: r
|
id: r
|
||||||
|
|
||||||
property color userColor: "red"
|
|
||||||
property bool keepFullText: false
|
|
||||||
|
|
||||||
required property string eventId
|
required property string eventId
|
||||||
|
property bool keepFullText: false
|
||||||
|
required property int maxWidth
|
||||||
property var room_: room
|
property var room_: room
|
||||||
|
property color userColor: "red"
|
||||||
property string userId: eventId ? room.dataById(eventId, Room.UserId, "") : ""
|
property string userId: eventId ? room.dataById(eventId, Room.UserId, "") : ""
|
||||||
property string userName: eventId ? room.dataById(eventId, Room.UserName, "") : ""
|
property string userName: eventId ? room.dataById(eventId, Room.UserName, "") : ""
|
||||||
|
|
||||||
implicitHeight: replyContainer.implicitHeight
|
implicitHeight: replyContainer.implicitHeight
|
||||||
implicitWidth: replyContainer.implicitWidth
|
implicitWidth: replyContainer.implicitWidth
|
||||||
required property int maxWidth
|
|
||||||
|
|
||||||
NhekoCursorShape {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
id: backgroundItem
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
property color bgColor: palette.base
|
||||||
let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX-colorline.width, pressY - userName_.implicitHeight);
|
property color userColor: TimelineManager.userColor(r.userId, palette.base)
|
||||||
if (link) {
|
|
||||||
Nheko.openLink(link)
|
|
||||||
} else {
|
|
||||||
room.showEvent(r.eventId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX-colorline.width, pressY - userName_.implicitHeight), r.eventId)
|
|
||||||
|
|
||||||
|
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
contentItem: TimelineEvent {
|
contentItem: TimelineEvent {
|
||||||
id: timelineEvent
|
id: timelineEvent
|
||||||
|
|
||||||
isStateEvent: false
|
|
||||||
room: r.room_
|
|
||||||
eventId: r.eventId
|
eventId: r.eventId
|
||||||
replyTo: ""
|
isStateEvent: false
|
||||||
mainInset: 4 + Nheko.paddingMedium
|
mainInset: 4 + Nheko.paddingMedium
|
||||||
maxWidth: r.maxWidth
|
maxWidth: r.maxWidth
|
||||||
|
replyTo: ""
|
||||||
|
room: r.room_
|
||||||
|
|
||||||
//height: replyContainer.implicitHeight
|
//height: replyContainer.implicitHeight
|
||||||
data: Row {
|
data: Row {
|
||||||
|
@ -58,14 +50,14 @@ AbstractButton {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: colorline
|
id: colorline
|
||||||
|
|
||||||
width: 4
|
|
||||||
height: content.height
|
|
||||||
|
|
||||||
color: TimelineManager.userColor(r.userId, palette.base)
|
color: TimelineManager.userColor(r.userId, palette.base)
|
||||||
|
height: content.height
|
||||||
|
width: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
|
data: [usernameBtn, timelineEvent.main,]
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
|
@ -73,29 +65,31 @@ AbstractButton {
|
||||||
|
|
||||||
contentItem: Label {
|
contentItem: Label {
|
||||||
id: userName_
|
id: userName_
|
||||||
text: r.userName
|
|
||||||
color: r.userColor
|
color: r.userColor
|
||||||
|
text: r.userName
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
width: timelineEvent.main?.width
|
width: timelineEvent.main?.width
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: room.openUserProfile(r.userId)
|
onClicked: room.openUserProfile(r.userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
data: [
|
|
||||||
usernameBtn, timelineEvent.main,
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
onClicked: {
|
||||||
id: backgroundItem
|
let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX - colorline.width, pressY - userName_.implicitHeight);
|
||||||
|
if (link) {
|
||||||
z: -1
|
Nheko.openLink(link);
|
||||||
property color userColor: TimelineManager.userColor(r.userId, palette.base)
|
} else {
|
||||||
property color bgColor: palette.base
|
room.showEvent(r.eventId);
|
||||||
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
|
}
|
||||||
|
}
|
||||||
|
onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX - colorline.width, pressY - userName_.implicitHeight), r.eventId)
|
||||||
|
|
||||||
|
NhekoCursorShape {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,17 @@ import im.nheko
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
required property string body
|
required property string body
|
||||||
|
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
|
||||||
|
property bool fitsMetadata: false //positionAt(width,height-4) == positionAt(width-metadataWidth-10, height-4)
|
||||||
|
|
||||||
|
required property string formatted
|
||||||
required property bool isOnlyEmoji
|
required property bool isOnlyEmoji
|
||||||
property bool isReply: EventDelegateChooser.isReply
|
property bool isReply: EventDelegateChooser.isReply
|
||||||
required property bool keepFullText
|
required property bool keepFullText
|
||||||
required property string formatted
|
|
||||||
|
|
||||||
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
|
|
||||||
property int metadataWidth: 100
|
property int metadataWidth: 100
|
||||||
property bool fitsMetadata: false //positionAt(width,height-4) == positionAt(width-metadataWidth-10, height-4)
|
|
||||||
|
enabled: !isReply
|
||||||
|
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
||||||
|
|
||||||
// table border-collapse doesn't seem to work
|
// table border-collapse doesn't seem to work
|
||||||
text: `
|
text: `
|
||||||
|
@ -40,13 +43,9 @@ MatrixText {
|
||||||
`</style>
|
`</style>
|
||||||
` + formatted.replace(/<del>/g, "<s>").replace(/<\/del>/g, "</s>").replace(/<strike>/g, "<s>").replace(/<\/strike>/g, "</s>")
|
` + formatted.replace(/<del>/g, "<s>").replace(/<\/del>/g, "</s>").replace(/<strike>/g, "<s>").replace(/<\/strike>/g, "</s>")
|
||||||
|
|
||||||
enabled: !isReply
|
|
||||||
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
enabled: isReply
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: isReply
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,82 +12,69 @@ ApplicationWindow {
|
||||||
|
|
||||||
property var flow
|
property var flow
|
||||||
|
|
||||||
onClosing: VerificationManager.removeVerificationFlow(flow)
|
|
||||||
title: stack.currentItem ? (stack.currentItem.title_ || "") : ""
|
|
||||||
modality: Qt.NonModal
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: stack.currentItem.implicitHeight + 2 * Nheko.paddingMedium
|
||||||
//height: stack.currentItem.implicitHeight
|
//height: stack.currentItem.implicitHeight
|
||||||
minimumHeight: stack.currentItem.implicitHeight + 2 * Nheko.paddingLarge
|
minimumHeight: stack.currentItem.implicitHeight + 2 * Nheko.paddingLarge
|
||||||
height: stack.currentItem.implicitHeight + 2 * Nheko.paddingMedium
|
|
||||||
minimumWidth: 400
|
minimumWidth: 400
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: stack.currentItem ? (stack.currentItem.title_ || "") : ""
|
||||||
width: 400
|
width: 400
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: palette.window
|
color: palette.window
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClosing: VerificationManager.removeVerificationFlow(flow)
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: stack
|
id: stack
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
initialItem: newVerificationRequest
|
|
||||||
implicitWidth: dialog.width - 2* Nheko.paddingMedium
|
|
||||||
implicitHeight: dialog.height - 2 * Nheko.paddingMedium
|
implicitHeight: dialog.height - 2 * Nheko.paddingMedium
|
||||||
|
implicitWidth: dialog.width - 2 * Nheko.paddingMedium
|
||||||
|
initialItem: newVerificationRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: newVerificationRequest
|
id: newVerificationRequest
|
||||||
|
|
||||||
NewVerificationRequest {
|
NewVerificationRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: waiting
|
id: waiting
|
||||||
|
|
||||||
Waiting {
|
Waiting {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: success
|
id: success
|
||||||
|
|
||||||
Success {
|
Success {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: failed
|
id: failed
|
||||||
|
|
||||||
Failed {
|
Failed {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: digitVerification
|
id: digitVerification
|
||||||
|
|
||||||
DigitVerification {
|
DigitVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: emojiVerification
|
id: emojiVerification
|
||||||
|
|
||||||
EmojiVerification {
|
EmojiVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
state: flow.state
|
state: flow.state
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "PromptStartVerification"
|
name: "PromptStartVerification"
|
||||||
|
@ -95,7 +82,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(null, newVerificationRequest)
|
script: stack.replace(null, newVerificationRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "CompareEmoji"
|
name: "CompareEmoji"
|
||||||
|
@ -103,7 +89,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(null, emojiVerification)
|
script: stack.replace(null, emojiVerification)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "CompareNumber"
|
name: "CompareNumber"
|
||||||
|
@ -111,7 +96,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(null, digitVerification)
|
script: stack.replace(null, digitVerification)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "WaitingForKeys"
|
name: "WaitingForKeys"
|
||||||
|
@ -119,7 +103,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(null, waiting)
|
script: stack.replace(null, waiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "WaitingForOtherToAccept"
|
name: "WaitingForOtherToAccept"
|
||||||
|
@ -127,7 +110,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(null, waiting)
|
script: stack.replace(null, waiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "WaitingForMac"
|
name: "WaitingForMac"
|
||||||
|
@ -135,7 +117,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(null, waiting)
|
script: stack.replace(null, waiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "Success"
|
name: "Success"
|
||||||
|
@ -143,7 +124,6 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(null, success)
|
script: stack.replace(null, success)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "Failed"
|
name: "Failed"
|
||||||
|
@ -151,9 +131,7 @@ ApplicationWindow {
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
script: stack.replace(null, failed)
|
script: stack.replace(null, failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,59 +12,56 @@ ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: palette.text
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[0]
|
text: flow.sasList[0]
|
||||||
color: palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: palette.text
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[1]
|
text: flow.sasList[1]
|
||||||
color: palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: palette.text
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[2]
|
text: flow.sasList[2]
|
||||||
color: palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Item { Layout.fillHeight: true; }
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("They do not match!")
|
text: qsTr("They do not match!")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
flow.cancel();
|
flow.cancel();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("They match!")
|
text: qsTr("They match!")
|
||||||
|
|
||||||
onClicked: flow.next()
|
onClicked: flow.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import QtQuick.Layouts
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: "red"
|
color: "red"
|
||||||
|
height: Qt.application.font.pixelSize * 4
|
||||||
implicitHeight: Qt.application.font.pixelSize * 4
|
implicitHeight: Qt.application.font.pixelSize * 4
|
||||||
implicitWidth: col.width
|
implicitWidth: col.width
|
||||||
height: Qt.application.font.pixelSize * 4
|
|
||||||
width: col.width
|
width: col.width
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
@ -21,17 +21,14 @@ Rectangle {
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredHeight: font.pixelSize * 2
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: col.emoji.emoji
|
Layout.preferredHeight: font.pixelSize * 2
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
|
text: col.emoji.emoji
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
text: col.emoji.description
|
text: col.emoji.description
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,16 @@ ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: emojis
|
id: emojis
|
||||||
|
|
||||||
|
@ -370,58 +371,52 @@ ColumnLayout {
|
||||||
Label {
|
Label {
|
||||||
//height: font.pixelSize * 2
|
//height: font.pixelSize * 2
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: col.emoji.emoji
|
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
|
||||||
font.family: Settings.emojiFont
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
font.family: Settings.emojiFont
|
||||||
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
|
text: col.emoji.emoji
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
|
color: palette.text
|
||||||
text: col.emoji.description
|
text: col.emoji.description
|
||||||
color: palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Item { Layout.fillHeight: true; }
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
text: qsTr("The displayed emoji might look different in different clients if a different font is used. Similarly they might be translated into different languages. Nonetheless they should depict one of 64 different objects or animals. For example a lion and a cat are different, but a cat is the same even if one client just shows a cat face, while another client shows a full cat body.")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("The displayed emoji might look different in different clients if a different font is used. Similarly they might be translated into different languages. Nonetheless they should depict one of 64 different objects or animals. For example a lion and a cat are different, but a cat is the same even if one client just shows a cat face, while another client shows a full cat body.")
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("They do not match!")
|
text: qsTr("They do not match!")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
flow.cancel();
|
flow.cancel();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("They match!")
|
text: qsTr("They match!")
|
||||||
|
|
||||||
onClicked: flow.next()
|
onClicked: flow.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,15 @@ import im.nheko 1.0
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
property string title: qsTr("Verification failed")
|
property string title: qsTr("Verification failed")
|
||||||
|
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
|
color: palette.text
|
||||||
text: {
|
text: {
|
||||||
switch (flow.error) {
|
switch (flow.error) {
|
||||||
case DeviceVerificationFlow.UnknownMethod:
|
case DeviceVerificationFlow.UnknownMethod:
|
||||||
|
@ -35,23 +36,21 @@ ColumnLayout {
|
||||||
return qsTr("Unknown verification error.");
|
return qsTr("Unknown verification error.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: palette.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("Close")
|
text: qsTr("Close")
|
||||||
|
|
||||||
onClicked: dialog.close()
|
onClicked: dialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,11 @@ ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
// Self verification
|
// Self verification
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
Layout.preferredWidth: 400
|
||||||
Layout.fillWidth: true
|
color: palette.text
|
||||||
wrapMode: Text.Wrap
|
|
||||||
text: {
|
text: {
|
||||||
if (flow.sender) {
|
if (flow.sender) {
|
||||||
if (flow.isSelfVerification)
|
if (flow.isSelfVerification)
|
||||||
|
@ -35,32 +35,30 @@ ColumnLayout {
|
||||||
return qsTr("Your device (%1) has requested to be verified.").arg(flow.deviceId);
|
return qsTr("Your device (%1) has requested to be verified.").arg(flow.deviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: palette.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
|
text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
flow.cancel();
|
flow.cancel();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
|
text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
|
||||||
|
|
||||||
onClicked: flow.next()
|
onClicked: flow.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,27 +14,25 @@ ColumnLayout {
|
||||||
Label {
|
Label {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
text: qsTr("Verification successful! Both sides verified their devices!")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Verification successful! Both sides verified their devices!")
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("Close")
|
text: qsTr("Close")
|
||||||
|
|
||||||
onClicked: dialog.close()
|
onClicked: dialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,15 @@ import im.nheko 1.0
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
property string title: qsTr("Waiting for other party…")
|
property string title: qsTr("Waiting for other party…")
|
||||||
|
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
Layout.preferredWidth: 400
|
||||||
|
color: palette.text
|
||||||
text: {
|
text: {
|
||||||
switch (flow.state) {
|
switch (flow.state) {
|
||||||
case "WaitingForOtherToAccept":
|
case "WaitingForOtherToAccept":
|
||||||
|
@ -30,32 +31,31 @@ ColumnLayout {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: palette.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
foreground: palette.mid
|
foreground: palette.mid
|
||||||
}
|
}
|
||||||
Item { Layout.fillHeight: true; }
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Cancel")
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
flow.cancel();
|
flow.cancel();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,21 +8,31 @@ import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: aliasEditorW
|
id: aliasEditorW
|
||||||
|
|
||||||
property var roomSettings
|
|
||||||
property var editingModel: Nheko.editAliases(roomSettings.roomId)
|
property var editingModel: Nheko.editAliases(roomSettings.roomId)
|
||||||
|
property var roomSettings
|
||||||
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
minimumWidth: 300
|
|
||||||
minimumHeight: 400
|
|
||||||
height: 600
|
height: 600
|
||||||
|
minimumHeight: 400
|
||||||
|
minimumWidth: 300
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: qsTr("Aliases to %1").arg(roomSettings.roomName)
|
||||||
width: 500
|
width: 500
|
||||||
|
|
||||||
title: qsTr("Aliases to %1").arg(roomSettings.roomName);
|
footer: DialogButtonBox {
|
||||||
|
id: dbb
|
||||||
|
|
||||||
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
editingModel.commit();
|
||||||
|
aliasEditorW.close();
|
||||||
|
}
|
||||||
|
onRejected: aliasEditorW.close()
|
||||||
|
}
|
||||||
|
|
||||||
// Shortcut {
|
// Shortcut {
|
||||||
// sequence: StandardKey.Cancel
|
// sequence: StandardKey.Cancel
|
||||||
|
@ -30,32 +40,27 @@ ApplicationWindow {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("List of aliases to this room. Usually you can only add aliases on your server. You can have one canonical alias and many alternate aliases.")
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
color: palette.text
|
|
||||||
Layout.bottomMargin: Nheko.paddingMedium
|
Layout.bottomMargin: Nheko.paddingMedium
|
||||||
}
|
Layout.fillHeight: false
|
||||||
|
|
||||||
ListView {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
color: palette.text
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
||||||
|
text: qsTr("List of aliases to this room. Usually you can only add aliases on your server. You can have one canonical alias and many alternate aliases.")
|
||||||
|
}
|
||||||
|
ListView {
|
||||||
id: view
|
id: view
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
cacheBuffer: 50
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
|
||||||
model: editingModel
|
model: editingModel
|
||||||
spacing: 4
|
spacing: 4
|
||||||
cacheBuffer: 50
|
|
||||||
|
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -63,79 +68,70 @@ ApplicationWindow {
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: model.name
|
|
||||||
color: model.isPublished ? palette.text : Nheko.theme.error
|
color: model.isPublished ? palette.text : Nheko.theme.error
|
||||||
|
text: model.name
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.margins: 2
|
Layout.margins: 2
|
||||||
image: ":/icons/icons/ui/star.svg"
|
ToolTip.text: model.isCanonical ? qsTr("Primary alias") : qsTr("Make primary alias")
|
||||||
hoverEnabled: true
|
ToolTip.visible: hovered
|
||||||
buttonTextColor: model.isCanonical ? palette.highlight : palette.text
|
buttonTextColor: model.isCanonical ? palette.highlight : palette.text
|
||||||
highlightColor: editingModel.canAdvertize ? palette.highlight : buttonTextColor
|
highlightColor: editingModel.canAdvertize ? palette.highlight : buttonTextColor
|
||||||
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
image: ":/icons/icons/ui/star.svg"
|
||||||
ToolTip.text: model.isCanonical ? qsTr("Primary alias") : qsTr("Make primary alias")
|
|
||||||
|
|
||||||
onClicked: editingModel.makeCanonical(model.index)
|
onClicked: editingModel.makeCanonical(model.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.margins: 2
|
Layout.margins: 2
|
||||||
image: ":/icons/icons/ui/building-shop.svg"
|
ToolTip.text: qsTr("Advertise as an alias in this room")
|
||||||
hoverEnabled: true
|
ToolTip.visible: hovered
|
||||||
buttonTextColor: model.isAdvertized ? palette.highlight : palette.text
|
buttonTextColor: model.isAdvertized ? palette.highlight : palette.text
|
||||||
highlightColor: editingModel.canAdvertize ? palette.highlight : buttonTextColor
|
highlightColor: editingModel.canAdvertize ? palette.highlight : buttonTextColor
|
||||||
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
image: ":/icons/icons/ui/building-shop.svg"
|
||||||
ToolTip.text: qsTr("Advertise as an alias in this room")
|
|
||||||
|
|
||||||
onClicked: editingModel.toggleAdvertize(model.index)
|
onClicked: editingModel.toggleAdvertize(model.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.margins: 2
|
Layout.margins: 2
|
||||||
image: ":/icons/icons/ui/room-directory.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
buttonTextColor: model.isPublished ? palette.highlight : palette.text
|
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Publish in room directory")
|
ToolTip.text: qsTr("Publish in room directory")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
buttonTextColor: model.isPublished ? palette.highlight : palette.text
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/room-directory.svg"
|
||||||
|
|
||||||
onClicked: editingModel.togglePublish(model.index)
|
onClicked: editingModel.togglePublish(model.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.margins: 2
|
Layout.margins: 2
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Remove this alias")
|
ToolTip.text: qsTr("Remove this alias")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
|
|
||||||
onClicked: editingModel.deleteAlias(model.index)
|
onClicked: editingModel.deleteAlias(model.index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: newAliasVal
|
id: newAliasVal
|
||||||
|
|
||||||
focus: true
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
selectByMouse: true
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
focus: true
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize
|
||||||
placeholderText: qsTr("#new-alias:server.tld")
|
placeholderText: qsTr("#new-alias:server.tld")
|
||||||
|
selectByMouse: true
|
||||||
|
|
||||||
Component.onCompleted: forceActiveFocus()
|
Component.onCompleted: forceActiveFocus()
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
|
@ -145,10 +141,10 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Add")
|
|
||||||
Layout.preferredWidth: 100
|
Layout.preferredWidth: 100
|
||||||
|
text: qsTr("Add")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
editingModel.addAlias(newAliasVal.text);
|
editingModel.addAlias(newAliasVal.text);
|
||||||
newAliasVal.clear();
|
newAliasVal.clear();
|
||||||
|
@ -156,16 +152,4 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: dbb
|
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
|
||||||
onAccepted: {
|
|
||||||
editingModel.commit();
|
|
||||||
aliasEditorW.close();
|
|
||||||
}
|
|
||||||
onRejected: aliasEditorW.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,47 +14,54 @@ ApplicationWindow {
|
||||||
|
|
||||||
property var roomSettings
|
property var roomSettings
|
||||||
|
|
||||||
minimumWidth: 340
|
|
||||||
minimumHeight: 450
|
|
||||||
width: 450
|
|
||||||
height: 680
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 680
|
||||||
|
minimumHeight: 450
|
||||||
|
minimumWidth: 340
|
||||||
|
modality: Qt.NonModal
|
||||||
title: qsTr("Allowed rooms settings")
|
title: qsTr("Allowed rooms settings")
|
||||||
|
width: 450
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
id: dbb
|
||||||
|
|
||||||
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
roomSettings.applyAllowedFromModel();
|
||||||
|
allowedDialog.close();
|
||||||
|
}
|
||||||
|
onRejected: allowedDialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: roomSettingsDialog.close()
|
onActivated: roomSettingsDialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("List of rooms that allow access to this room. Anyone who is in any of those rooms can join this room.")
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
color: palette.text
|
|
||||||
Layout.bottomMargin: Nheko.paddingMedium
|
Layout.bottomMargin: Nheko.paddingMedium
|
||||||
}
|
Layout.fillHeight: false
|
||||||
|
|
||||||
ListView {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
color: palette.text
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
||||||
|
text: qsTr("List of rooms that allow access to this room. Anyone who is in any of those rooms can join this room.")
|
||||||
|
}
|
||||||
|
ListView {
|
||||||
id: view
|
id: view
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
cacheBuffer: 50
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
|
||||||
model: roomSettings.allowedRoomsModel
|
model: roomSettings.allowedRoomsModel
|
||||||
spacing: 4
|
spacing: 4
|
||||||
cacheBuffer: 50
|
|
||||||
|
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -62,63 +69,57 @@ ApplicationWindow {
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: model.name
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: model.name
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: model.isParent ? qsTr("Parent community") : qsTr("Other room")
|
|
||||||
color: palette.buttonText
|
color: palette.buttonText
|
||||||
|
text: model.isParent ? qsTr("Parent community") : qsTr("Other room")
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
checked: model.allowed
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: model.allowed
|
||||||
|
|
||||||
onCheckedChanged: model.allowed = checked
|
onCheckedChanged: model.allowed = checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: roomEntryCompleter
|
id: roomEntryCompleter
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: 1
|
spacing: 1
|
||||||
z: 5
|
z: 5
|
||||||
|
|
||||||
Completer {
|
Completer {
|
||||||
id: roomCompleter
|
id: roomCompleter
|
||||||
|
|
||||||
visible: roomEntry.text.length > 0
|
|
||||||
width: parent.width
|
|
||||||
roomId: allowedDialog.roomSettings.roomId
|
|
||||||
completerName: "room"
|
|
||||||
bottomToTop: true
|
|
||||||
fullWidth: true
|
|
||||||
avatarHeight: Nheko.avatarSize / 2
|
avatarHeight: Nheko.avatarSize / 2
|
||||||
avatarWidth: Nheko.avatarSize / 2
|
avatarWidth: Nheko.avatarSize / 2
|
||||||
|
bottomToTop: true
|
||||||
centerRowContent: false
|
centerRowContent: false
|
||||||
|
completerName: "room"
|
||||||
|
fullWidth: true
|
||||||
|
roomId: allowedDialog.roomSettings.roomId
|
||||||
rowMargin: 2
|
rowMargin: 2
|
||||||
rowSpacing: 2
|
rowSpacing: 2
|
||||||
|
visible: roomEntry.text.length > 0
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: roomEntry
|
id: roomEntry
|
||||||
|
|
||||||
|
color: palette.text
|
||||||
|
placeholderText: qsTr("Enter additional rooms not in the list yet...")
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
placeholderText: qsTr("Enter additional rooms not in the list yet...")
|
|
||||||
|
|
||||||
color: palette.text
|
|
||||||
onTextEdited: {
|
|
||||||
roomCompleter.completer.searchString = text;
|
|
||||||
}
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
@ -134,37 +135,23 @@ ApplicationWindow {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
roomCompleter.completer.searchString = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCompletionSelected(id) {
|
function onCompletionSelected(id) {
|
||||||
console.log("selected: " + id);
|
console.log("selected: " + id);
|
||||||
roomSettings.allowedRoomsModel.addRoom(id);
|
roomSettings.allowedRoomsModel.addRoom(id);
|
||||||
roomEntry.clear();
|
roomEntry.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCountChanged() {
|
function onCountChanged() {
|
||||||
if (roomCompleter.count > 0 && (roomCompleter.currentIndex < 0 || roomCompleter.currentIndex >= roomCompleter.count))
|
if (roomCompleter.count > 0 && (roomCompleter.currentIndex < 0 || roomCompleter.currentIndex >= roomCompleter.count))
|
||||||
roomCompleter.currentIndex = 0;
|
roomCompleter.currentIndex = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: roomCompleter
|
target: roomCompleter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: dbb
|
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
|
||||||
onAccepted: {
|
|
||||||
roomSettings.applyAllowedFromModel();
|
|
||||||
allowedDialog.close();
|
|
||||||
}
|
|
||||||
onRejected: allowedDialog.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,119 +15,18 @@ ApplicationWindow {
|
||||||
|
|
||||||
required property RoomSummary summary
|
required property RoomSummary summary
|
||||||
|
|
||||||
title: summary.isSpace ? qsTr("Confirm community join") : qsTr("Confirm room join")
|
|
||||||
modality: Qt.WindowModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
width: 350
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
height: content.implicitHeight + Nheko.paddingLarge + footer.implicitHeight
|
height: content.implicitHeight + Nheko.paddingLarge + footer.implicitHeight
|
||||||
|
modality: Qt.WindowModal
|
||||||
Shortcut {
|
title: summary.isSpace ? qsTr("Confirm community join") : qsTr("Confirm room join")
|
||||||
sequence: StandardKey.Cancel
|
width: 350
|
||||||
onActivated: dbb.rejected()
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Avatar {
|
|
||||||
Layout.topMargin: Nheko.paddingMedium
|
|
||||||
url: summary.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
roomid: summary.roomid
|
|
||||||
displayName: summary.roomName
|
|
||||||
Layout.preferredHeight: 130
|
|
||||||
Layout.preferredWidth: 130
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Spinner {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
visible: !summary.isLoaded
|
|
||||||
foreground: palette.mid
|
|
||||||
running: !summary.isLoaded
|
|
||||||
}
|
|
||||||
|
|
||||||
TextEdit {
|
|
||||||
readOnly: true
|
|
||||||
textFormat: TextEdit.RichText
|
|
||||||
text: summary.roomName
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 2
|
|
||||||
color: palette.text
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
selectByMouse: true
|
|
||||||
}
|
|
||||||
TextEdit {
|
|
||||||
readOnly: true
|
|
||||||
textFormat: TextEdit.RichText
|
|
||||||
text: summary.roomid
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 0.8
|
|
||||||
color: palette.text
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
selectByMouse: true
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
text: qsTr("%n member(s)", "", summary.memberCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
image: ":/icons/icons/ui/people.svg"
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
TextEdit {
|
|
||||||
readOnly: true
|
|
||||||
textFormat: TextEdit.RichText
|
|
||||||
text: summary.roomTopic
|
|
||||||
color: palette.text
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
selectByMouse: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: promptLabel
|
|
||||||
|
|
||||||
text: summary.isKnockOnly ? qsTr("This room can't be joined directly. You can, however, knock on the room and room members can accept or decline this join request. You can additionally provide a reason for them to let you in below:") : qsTr("Do you want to join this room? You can optionally add a reason below:")
|
|
||||||
color: palette.text
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
font.bold: true
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: reason
|
|
||||||
|
|
||||||
focus: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: joinRoomRoot.summary.reason
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: dbb
|
id: dbb
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
summary.reason = reason.text;
|
summary.reason = reason.text;
|
||||||
summary.join();
|
summary.join();
|
||||||
|
@ -138,11 +37,102 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: summary.isKnockOnly ? qsTr("Knock") : qsTr("Join")
|
|
||||||
enabled: input.text.match("#.+?:.{3,}")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
enabled: input.text.match("#.+?:.{3,}")
|
||||||
|
text: summary.isKnockOnly ? qsTr("Knock") : qsTr("Join")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: dbb.rejected()
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
Avatar {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredHeight: 130
|
||||||
|
Layout.preferredWidth: 130
|
||||||
|
Layout.topMargin: Nheko.paddingMedium
|
||||||
|
displayName: summary.roomName
|
||||||
|
roomid: summary.roomid
|
||||||
|
url: summary.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
}
|
||||||
|
Spinner {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
foreground: palette.mid
|
||||||
|
running: !summary.isLoaded
|
||||||
|
visible: !summary.isLoaded
|
||||||
|
}
|
||||||
|
TextEdit {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 2
|
||||||
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
text: summary.roomName
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
}
|
||||||
|
TextEdit {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 0.8
|
||||||
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
text: summary.roomid
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
MatrixText {
|
||||||
|
text: qsTr("%n member(s)", "", summary.memberCount)
|
||||||
|
}
|
||||||
|
ImageButton {
|
||||||
|
enabled: false
|
||||||
|
image: ":/icons/icons/ui/people.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextEdit {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
text: summary.roomTopic
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: promptLabel
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: summary.isKnockOnly ? qsTr("This room can't be joined directly. You can, however, knock on the room and room members can accept or decline this join request. You can additionally provide a reason for them to let you in below:") : qsTr("Do you want to join this room? You can optionally add a reason below:")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: reason
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
focus: true
|
||||||
|
text: joinRoomRoot.summary.reason
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,31 @@ import im.nheko
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: createDirectRoot
|
id: createDirectRoot
|
||||||
title: qsTr("Create Direct Chat")
|
|
||||||
property var profile
|
|
||||||
property bool otherUserHasE2ee: profile ? profile.deviceList.rowCount() > 0 : true
|
property bool otherUserHasE2ee: profile ? profile.deviceList.rowCount() > 0 : true
|
||||||
|
property var profile
|
||||||
|
|
||||||
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
minimumHeight: layout.implicitHeight + footer.implicitHeight + Nheko.paddingLarge * 2
|
minimumHeight: layout.implicitHeight + footer.implicitHeight + Nheko.paddingLarge * 2
|
||||||
minimumWidth: Math.max(footer.implicitWidth, layout.implicitWidth)
|
minimumWidth: Math.max(footer.implicitWidth, layout.implicitWidth)
|
||||||
modality: Qt.NonModal
|
modality: Qt.NonModal
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
title: qsTr("Create Direct Chat")
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
profile.startChat(encryption.checked);
|
||||||
|
createDirectRoot.close();
|
||||||
|
}
|
||||||
|
onRejected: createDirectRoot.close()
|
||||||
|
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
enabled: userID.isValidMxid && profile
|
||||||
|
text: "Start Direct Chat"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onVisibilityChanged: {
|
onVisibilityChanged: {
|
||||||
userID.forceActiveFocus();
|
userID.forceActiveFocus();
|
||||||
|
@ -25,54 +43,56 @@ ApplicationWindow {
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: createDirectRoot.close()
|
onActivated: createDirectRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: layout
|
id: layout
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
spacing: userID.height / 4
|
spacing: userID.height / 4
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
rows: 2
|
columnSpacing: Nheko.paddingMedium
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingSmall
|
rowSpacing: Nheko.paddingSmall
|
||||||
columnSpacing: Nheko.paddingMedium
|
rows: 2
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.preferredWidth: Nheko.avatarSize
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
userid: profile? profile.userid : ""
|
Layout.preferredHeight: Nheko.avatarSize
|
||||||
url: profile? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : null
|
Layout.preferredWidth: Nheko.avatarSize
|
||||||
|
Layout.rowSpan: 2
|
||||||
displayName: profile ? profile.displayName : ""
|
displayName: profile ? profile.displayName : ""
|
||||||
enabled: false
|
enabled: false
|
||||||
|
url: profile ? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : null
|
||||||
|
userid: profile ? profile.userid : ""
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: profile? profile.displayName : ""
|
|
||||||
color: TimelineManager.userColor(userID.text, palette.window)
|
color: TimelineManager.userColor(userID.text, palette.window)
|
||||||
font.pointSize: fontMetrics.font.pointSize
|
font.pointSize: fontMetrics.font.pointSize
|
||||||
|
text: profile ? profile.displayName : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: userID.text
|
|
||||||
color: palette.buttonText
|
color: palette.buttonText
|
||||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
text: userID.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: userID
|
id: userID
|
||||||
|
|
||||||
property bool isValidMxid: text.match("@.+?:.{3,}")
|
property bool isValidMxid: text.match("@.+?:.{3,}")
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
focus: true
|
focus: true
|
||||||
label: qsTr("User to invite")
|
label: qsTr("User to invite")
|
||||||
placeholderText: qsTr("@user:server.tld")
|
placeholderText: qsTr("@user:server.tld")
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
// we can't use "isValidMxid" here, since the property might only be reevaluated after this change handler.
|
// we can't use "isValidMxid" here, since the property might only be reevaluated after this change handler.
|
||||||
if (text.match("@.+?:.{3,}")) {
|
if (text.match("@.+?:.{3,}")) {
|
||||||
|
@ -81,35 +101,24 @@ ApplicationWindow {
|
||||||
profile = null;
|
profile = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Encryption")
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Encryption")
|
||||||
}
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
id: encryption
|
id: encryption
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: otherUserHasE2ee
|
checked: otherUserHasE2ee
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
Item {Layout.fillHeight: true}
|
Layout.fillHeight: true
|
||||||
}
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
|
||||||
Button {
|
|
||||||
text: "Start Direct Chat"
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
enabled: userID.isValidMxid && profile
|
|
||||||
}
|
|
||||||
onRejected: createDirectRoot.close();
|
|
||||||
onAccepted: {
|
|
||||||
profile.startChat(encryption.checked)
|
|
||||||
createDirectRoot.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,32 @@ ApplicationWindow {
|
||||||
|
|
||||||
property bool space: false
|
property bool space: false
|
||||||
|
|
||||||
title: space ? qsTr("New community") : qsTr("New Room")
|
|
||||||
minimumWidth: Math.max(rootLayout.implicitWidth+2*rootLayout.anchors.margins, footer.implicitWidth + Nheko.paddingLarge)
|
|
||||||
minimumHeight: rootLayout.implicitHeight+footer.implicitHeight+2*rootLayout.anchors.margins
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
minimumHeight: rootLayout.implicitHeight + footer.implicitHeight + 2 * rootLayout.anchors.margins
|
||||||
|
minimumWidth: Math.max(rootLayout.implicitWidth + 2 * rootLayout.anchors.margins, footer.implicitWidth + Nheko.paddingLarge)
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: space ? qsTr("New community") : qsTr("New Room")
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
var preset = 0;
|
||||||
|
if (isPublic.checked) {
|
||||||
|
preset = 1;
|
||||||
|
} else {
|
||||||
|
preset = isTrusted.checked ? 2 : 0;
|
||||||
|
}
|
||||||
|
Nheko.createRoom(space, newRoomName.text, newRoomTopic.text, newRoomAlias.text, isEncrypted.checked, preset);
|
||||||
|
createRoomRoot.close();
|
||||||
|
}
|
||||||
|
onRejected: createRoomRoot.close()
|
||||||
|
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
text: qsTr("Create Room")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onVisibilityChanged: {
|
onVisibilityChanged: {
|
||||||
newRoomName.forceActiveFocus();
|
newRoomName.forceActiveFocus();
|
||||||
|
@ -26,10 +47,12 @@ ApplicationWindow {
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: createRoomRoot.close()
|
onActivated: createRoomRoot.close()
|
||||||
}
|
}
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: rootLayout
|
id: rootLayout
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
columns: 2
|
columns: 2
|
||||||
|
@ -37,127 +60,118 @@ ApplicationWindow {
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: newRoomName
|
id: newRoomName
|
||||||
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
focus: true
|
focus: true
|
||||||
label: qsTr("Name")
|
label: qsTr("Name")
|
||||||
placeholderText: qsTr("No name")
|
placeholderText: qsTr("No name")
|
||||||
}
|
}
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: newRoomTopic
|
id: newRoomTopic
|
||||||
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
focus: true
|
focus: true
|
||||||
label: qsTr("Topic")
|
label: qsTr("Topic")
|
||||||
placeholderText: qsTr("No topic")
|
placeholderText: qsTr("No topic")
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.preferredHeight: newRoomName.height / 2
|
Layout.preferredHeight: newRoomName.height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
text: "#"
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: "#"
|
||||||
}
|
}
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: newRoomAlias
|
id: newRoomAlias
|
||||||
|
|
||||||
focus: true
|
focus: true
|
||||||
placeholderText: qsTr("Alias")
|
placeholderText: qsTr("Alias")
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
property string userName: userInfoGrid.profile.userid
|
property string userName: userInfoGrid.profile.userid
|
||||||
text: userName.substring(userName.indexOf(":"))
|
|
||||||
|
Layout.preferredWidth: implicitWidth
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: userName.substring(userName.indexOf(":"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Public")
|
Layout.preferredWidth: implicitWidth
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Public rooms can be joined by anyone; private rooms need explicit invites.")
|
||||||
|
ToolTip.visible: privateHover.hovered
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Public")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: privateHover
|
id: privateHover
|
||||||
|
|
||||||
}
|
}
|
||||||
ToolTip.visible: privateHover.hovered
|
|
||||||
ToolTip.text: qsTr("Public rooms can be joined by anyone; private rooms need explicit invites.")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
}
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
|
id: isPublic
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
id: isPublic
|
|
||||||
checked: false
|
checked: false
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
visible: !space
|
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Trusted")
|
Layout.preferredWidth: implicitWidth
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("All invitees are given the same power level as the creator")
|
||||||
|
ToolTip.visible: trustedHover.hovered
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Trusted")
|
||||||
|
visible: !space
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: trustedHover
|
id: trustedHover
|
||||||
|
|
||||||
}
|
}
|
||||||
ToolTip.visible: trustedHover.hovered
|
|
||||||
ToolTip.text: qsTr("All invitees are given the same power level as the creator")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
}
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
visible: !space
|
id: isTrusted
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
id: isTrusted
|
|
||||||
checked: false
|
checked: false
|
||||||
enabled: !isPublic.checked
|
enabled: !isPublic.checked
|
||||||
|
visible: !space
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
visible: !space
|
|
||||||
Layout.preferredWidth: implicitWidth
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Encryption")
|
Layout.preferredWidth: implicitWidth
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Caution: Encryption cannot be disabled")
|
||||||
|
ToolTip.visible: encryptionHover.hovered
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Encryption")
|
||||||
|
visible: !space
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: encryptionHover
|
id: encryptionHover
|
||||||
|
|
||||||
}
|
}
|
||||||
ToolTip.visible: encryptionHover.hovered
|
|
||||||
ToolTip.text: qsTr("Caution: Encryption cannot be disabled")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
}
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
visible: !space
|
id: isEncrypted
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
id: isEncrypted
|
|
||||||
checked: false
|
checked: false
|
||||||
|
visible: !space
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
Item {Layout.fillHeight: true}
|
Layout.fillHeight: true
|
||||||
}
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
|
||||||
Button {
|
|
||||||
text: qsTr("Create Room")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
}
|
|
||||||
onRejected: createRoomRoot.close();
|
|
||||||
onAccepted: {
|
|
||||||
var preset = 0;
|
|
||||||
|
|
||||||
if (isPublic.checked) {
|
|
||||||
preset = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
preset = isTrusted.checked ? 2 : 0;
|
|
||||||
}
|
|
||||||
Nheko.createRoom(space, newRoomName.text, newRoomTopic.text, newRoomAlias.text, isEncrypted.checked, preset)
|
|
||||||
createRoomRoot.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,157 +11,151 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: dialog
|
id: dialog
|
||||||
|
|
||||||
property string roomid: ""
|
|
||||||
property string roomName: ""
|
|
||||||
property var onAccepted: undefined
|
property var onAccepted: undefined
|
||||||
|
property string roomName: ""
|
||||||
|
property string roomid: ""
|
||||||
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowTitleHint
|
||||||
width: 275
|
|
||||||
height: 330
|
height: 330
|
||||||
minimumWidth: 250
|
|
||||||
minimumHeight: 220
|
minimumHeight: 220
|
||||||
|
minimumWidth: 250
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: {
|
||||||
|
if (roomid) {
|
||||||
|
return qsTr("Event expiration for %1").arg(roomName);
|
||||||
|
} else {
|
||||||
|
return qsTr("Event expiration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width: 275
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
id: dbb
|
||||||
|
|
||||||
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
eventExpiry.save();
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
onRejected: dialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
EventExpiry {
|
EventExpiry {
|
||||||
id: eventExpiry
|
id: eventExpiry
|
||||||
|
|
||||||
roomid: dialog.roomid
|
roomid: dialog.roomid
|
||||||
}
|
}
|
||||||
|
|
||||||
title: {
|
|
||||||
if (roomid) {
|
|
||||||
return qsTr("Event expiration for %1").arg(roomName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return qsTr("Event expiration");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: dbb.rejected()
|
onActivated: dbb.rejected()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
id: promptLabel
|
id: promptLabel
|
||||||
|
|
||||||
|
Layout.fillHeight: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.2)
|
||||||
text: {
|
text: {
|
||||||
if (roomid) {
|
if (roomid) {
|
||||||
return qsTr("You can configure when your messages will be deleted in %1. This only happens when Nheko is open and has permissions to delete messages until Matrix servers support this feature natively. In general 0 means disable.").arg(roomName);
|
return qsTr("You can configure when your messages will be deleted in %1. This only happens when Nheko is open and has permissions to delete messages until Matrix servers support this feature natively. In general 0 means disable.").arg(roomName);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return qsTr("You can configure when your messages will be deleted in all rooms unless configured otherwise. This only happens when Nheko is open and has permissions to delete messages until Matrix servers support this feature natively. In general 0 means disable.");
|
return qsTr("You can configure when your messages will be deleted in all rooms unless configured otherwise. This only happens when Nheko is open and has permissions to delete messages until Matrix servers support this feature natively. In general 0 means disable.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.2)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingMedium
|
rowSpacing: Nheko.paddingMedium
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Expire events after X days")
|
Layout.fillWidth: true
|
||||||
ToolTip.text: qsTr("Automatically redacts messages after X days, unless otherwise protected. Set to 0 to disable.")
|
ToolTip.text: qsTr("Automatically redacts messages after X days, unless otherwise protected. Set to 0 to disable.")
|
||||||
ToolTip.visible: hh1.hovered
|
ToolTip.visible: hh1.hovered
|
||||||
Layout.fillWidth: true
|
text: qsTr("Expire events after X days")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hh1
|
id: hh1
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
SpinBox {
|
SpinBox {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
from: 0
|
|
||||||
to: 1000
|
|
||||||
stepSize: 1
|
|
||||||
value: eventExpiry.expireEventsAfterDays
|
|
||||||
onValueChanged: eventExpiry.expireEventsAfterDays = value
|
|
||||||
editable: true
|
editable: true
|
||||||
}
|
from: 0
|
||||||
|
stepSize: 1
|
||||||
|
to: 1000
|
||||||
|
value: eventExpiry.expireEventsAfterDays
|
||||||
|
|
||||||
|
onValueChanged: eventExpiry.expireEventsAfterDays = value
|
||||||
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Only keep latest X events")
|
Layout.fillWidth: true
|
||||||
ToolTip.text: qsTr("Deletes your events in this room if there are more than X newer messages unless otherwise protected. Set to 0 to disable.")
|
ToolTip.text: qsTr("Deletes your events in this room if there are more than X newer messages unless otherwise protected. Set to 0 to disable.")
|
||||||
ToolTip.visible: hh2.hovered
|
ToolTip.visible: hh2.hovered
|
||||||
Layout.fillWidth: true
|
text: qsTr("Only keep latest X events")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hh2
|
id: hh2
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SpinBox {
|
SpinBox {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
from: 0
|
|
||||||
to: 1000000
|
|
||||||
stepSize: 1
|
|
||||||
value: eventExpiry.expireEventsAfterCount
|
|
||||||
onValueChanged: eventExpiry.expireEventsAfterCount = value
|
|
||||||
editable: true
|
editable: true
|
||||||
}
|
from: 0
|
||||||
|
stepSize: 1
|
||||||
|
to: 1000000
|
||||||
|
value: eventExpiry.expireEventsAfterCount
|
||||||
|
|
||||||
|
onValueChanged: eventExpiry.expireEventsAfterCount = value
|
||||||
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Always keep latest X events")
|
Layout.fillWidth: true
|
||||||
ToolTip.text: qsTr("This prevents events to be deleted by the above 2 settings if they are the latest X messages from you in the room.")
|
ToolTip.text: qsTr("This prevents events to be deleted by the above 2 settings if they are the latest X messages from you in the room.")
|
||||||
ToolTip.visible: hh3.hovered
|
ToolTip.visible: hh3.hovered
|
||||||
Layout.fillWidth: true
|
text: qsTr("Always keep latest X events")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hh3
|
id: hh3
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SpinBox {
|
SpinBox {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
from: 0
|
|
||||||
to: 1000000
|
|
||||||
stepSize: 1
|
|
||||||
value: eventExpiry.protectLatestEvents
|
|
||||||
onValueChanged: eventExpiry.protectLatestEvents = value
|
|
||||||
editable: true
|
editable: true
|
||||||
}
|
from: 0
|
||||||
|
stepSize: 1
|
||||||
|
to: 1000000
|
||||||
|
value: eventExpiry.protectLatestEvents
|
||||||
|
|
||||||
|
onValueChanged: eventExpiry.protectLatestEvents = value
|
||||||
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Include state events")
|
Layout.fillWidth: true
|
||||||
ToolTip.text: qsTr("If this is turned on, old state events also get redacted. The latest state event of any type+key combination is excluded from redaction to not remove the room name and similar state by accident.")
|
ToolTip.text: qsTr("If this is turned on, old state events also get redacted. The latest state event of any type+key combination is excluded from redaction to not remove the room name and similar state by accident.")
|
||||||
ToolTip.visible: hh4.hovered
|
ToolTip.visible: hh4.hovered
|
||||||
Layout.fillWidth: true
|
text: qsTr("Include state events")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hh4
|
id: hh4
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: eventExpiry.expireStateEvents
|
checked: eventExpiry.expireStateEvents
|
||||||
|
|
||||||
onToggled: eventExpiry.expireStateEvents = checked
|
onToggled: eventExpiry.expireStateEvents = checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: dbb
|
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
|
||||||
onAccepted: {
|
|
||||||
eventExpiry.save();
|
|
||||||
dialog.close();
|
|
||||||
}
|
}
|
||||||
onRejected: dialog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -15,49 +15,46 @@ ApplicationWindow {
|
||||||
fallback.confirm();
|
fallback.confirm();
|
||||||
fallbackRoot.close();
|
fallbackRoot.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function reject() {
|
function reject() {
|
||||||
fallback.cancel();
|
fallback.cancel();
|
||||||
fallbackRoot.close();
|
fallbackRoot.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
title: qsTr("Fallback authentication")
|
|
||||||
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
height: msg.implicitHeight + footer.implicitHeight
|
height: msg.implicitHeight + footer.implicitHeight
|
||||||
|
title: qsTr("Fallback authentication")
|
||||||
width: Math.max(msg.implicitWidth, footer.implicitWidth)
|
width: Math.max(msg.implicitWidth, footer.implicitWidth)
|
||||||
|
|
||||||
Shortcut {
|
|
||||||
sequence: StandardKey.Cancel
|
|
||||||
onActivated: fallbackRoot.reject()
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: msg
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
padding: 8
|
|
||||||
text: qsTr("Open the fallback, follow the steps, and confirm after completing them.")
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
onAccepted: fallbackRoot.accept()
|
onAccepted: fallbackRoot.accept()
|
||||||
onRejected: fallbackRoot.reject()
|
onRejected: fallbackRoot.reject()
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Open Fallback in Browser")
|
text: qsTr("Open Fallback in Browser")
|
||||||
|
|
||||||
onClicked: fallback.openFallbackAuth()
|
onClicked: fallback.openFallbackAuth()
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Cancel")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||||
|
text: qsTr("Cancel")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Confirm")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
text: qsTr("Confirm")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: fallbackRoot.reject()
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: msg
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
padding: 8
|
||||||
|
text: qsTr("Open the fallback, follow the steps, and confirm after completing them.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,119 +11,115 @@ import im.nheko 1.0
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: hiddenEventsDialog
|
id: hiddenEventsDialog
|
||||||
|
|
||||||
property string roomid: ""
|
|
||||||
property string roomName: ""
|
|
||||||
property var onAccepted: undefined
|
property var onAccepted: undefined
|
||||||
|
property string roomName: ""
|
||||||
|
property string roomid: ""
|
||||||
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowTitleHint
|
||||||
width: 275
|
|
||||||
height: 220
|
height: 220
|
||||||
minimumWidth: 250
|
|
||||||
minimumHeight: 220
|
minimumHeight: 220
|
||||||
|
minimumWidth: 250
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: {
|
||||||
|
if (roomid) {
|
||||||
|
return qsTr("Hidden events for %1").arg(roomName);
|
||||||
|
} else {
|
||||||
|
return qsTr("Hidden events");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width: 275
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
id: dbb
|
||||||
|
|
||||||
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
hiddenEvents.save();
|
||||||
|
hiddenEventsDialog.close();
|
||||||
|
}
|
||||||
|
onRejected: hiddenEventsDialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
HiddenEvents {
|
HiddenEvents {
|
||||||
id: hiddenEvents
|
id: hiddenEvents
|
||||||
|
|
||||||
roomid: hiddenEventsDialog.roomid
|
roomid: hiddenEventsDialog.roomid
|
||||||
}
|
}
|
||||||
|
|
||||||
title: {
|
|
||||||
if (roomid) {
|
|
||||||
return qsTr("Hidden events for %1").arg(roomName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return qsTr("Hidden events");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: dbb.rejected()
|
onActivated: dbb.rejected()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
id: promptLabel
|
id: promptLabel
|
||||||
|
|
||||||
|
Layout.fillHeight: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.2)
|
||||||
text: {
|
text: {
|
||||||
if (roomid) {
|
if (roomid) {
|
||||||
return qsTr("These events will be <b>shown</b> in %1:").arg(roomName);
|
return qsTr("These events will be <b>shown</b> in %1:").arg(roomName);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return qsTr("These events will be <b>shown</b> in all rooms:");
|
return qsTr("These events will be <b>shown</b> in all rooms:");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.2)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingMedium
|
rowSpacing: Nheko.paddingMedium
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("User events")
|
Layout.fillWidth: true
|
||||||
ToolTip.text: qsTr("Joins, leaves, avatar and name changes, bans, …")
|
ToolTip.text: qsTr("Joins, leaves, avatar and name changes, bans, …")
|
||||||
ToolTip.visible: hh1.hovered
|
ToolTip.visible: hh1.hovered
|
||||||
Layout.fillWidth: true
|
text: qsTr("User events")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hh1
|
id: hh1
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.Member)
|
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.Member)
|
||||||
|
|
||||||
onToggled: hiddenEvents.toggle(MtxEvent.Member)
|
onToggled: hiddenEvents.toggle(MtxEvent.Member)
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Power level changes")
|
Layout.fillWidth: true
|
||||||
ToolTip.text: qsTr("Sent when a moderator is added/removed or the permissions of a room are changed.")
|
ToolTip.text: qsTr("Sent when a moderator is added/removed or the permissions of a room are changed.")
|
||||||
ToolTip.visible: hh2.hovered
|
ToolTip.visible: hh2.hovered
|
||||||
Layout.fillWidth: true
|
text: qsTr("Power level changes")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hh2
|
id: hh2
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.PowerLevels)
|
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.PowerLevels)
|
||||||
|
|
||||||
onToggled: hiddenEvents.toggle(MtxEvent.PowerLevels)
|
onToggled: hiddenEvents.toggle(MtxEvent.PowerLevels)
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Stickers")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Stickers")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.Sticker)
|
checked: !hiddenEvents.hiddenEvents.includes(MtxEvent.Sticker)
|
||||||
|
|
||||||
onToggled: hiddenEvents.toggle(MtxEvent.Sticker)
|
onToggled: hiddenEvents.toggle(MtxEvent.Sticker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: dbb
|
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
|
||||||
onAccepted: {
|
|
||||||
hiddenEvents.save();
|
|
||||||
hiddenEventsDialog.close();
|
|
||||||
}
|
|
||||||
onRejected: hiddenEventsDialog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,72 +13,74 @@ import "../"
|
||||||
Window {
|
Window {
|
||||||
id: ignoredUsers
|
id: ignoredUsers
|
||||||
|
|
||||||
title: qsTr("Ignored users")
|
color: palette.window
|
||||||
flags: Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
height: 650
|
height: 650
|
||||||
width: 420
|
|
||||||
minimumHeight: 420
|
minimumHeight: 420
|
||||||
color: palette.window
|
title: qsTr("Ignored users")
|
||||||
|
width: 420
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: view
|
id: view
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
footerPositioning: ListView.OverlayFooter
|
footerPositioning: ListView.OverlayFooter
|
||||||
|
|
||||||
model: TimelineManager.ignoredUsers
|
model: TimelineManager.ignoredUsers
|
||||||
header: ColumnLayout {
|
spacing: Nheko.paddingMedium
|
||||||
Text {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: view.width
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
color: palette.text
|
|
||||||
text: qsTr("Ignoring a user hides their messages (they can still see yours!).")
|
|
||||||
}
|
|
||||||
|
|
||||||
Item { Layout.preferredHeight: Nheko.paddingLarge }
|
|
||||||
}
|
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
property var profile: TimelineManager.getGlobalUserProfile(modelData)
|
property var profile: TimelineManager.getGlobalUserProfile(modelData)
|
||||||
|
|
||||||
width: view.width
|
width: view.width
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
enabled: false
|
|
||||||
displayName: profile.displayName
|
displayName: profile.displayName
|
||||||
userid: profile.userid
|
enabled: false
|
||||||
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: profile.userid
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
elide: Text.ElideRight
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
text: modelData
|
text: modelData
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Stop Ignoring.")
|
ToolTip.text: qsTr("Stop Ignoring.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
|
|
||||||
onClicked: profile.ignored = false
|
onClicked: profile.ignored = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
z: 2
|
|
||||||
width: view.width
|
|
||||||
alignment: Qt.AlignRight
|
alignment: Qt.AlignRight
|
||||||
standardButtons: DialogButtonBox.Ok
|
standardButtons: DialogButtonBox.Ok
|
||||||
onAccepted: ignoredUsers.close()
|
width: view.width
|
||||||
|
z: 2
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: palette.window
|
color: palette.window
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAccepted: ignoredUsers.close()
|
||||||
|
}
|
||||||
|
header: ColumnLayout {
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: view.width
|
||||||
|
color: palette.text
|
||||||
|
text: qsTr("Ignoring a user hides their messages (they can still see yours!).")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.preferredHeight: Nheko.paddingLarge
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,33 +4,32 @@
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
|
|
||||||
import ".."
|
import ".."
|
||||||
|
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: imageOverlay
|
id: imageOverlay
|
||||||
|
|
||||||
required property string url
|
|
||||||
required property string eventId
|
required property string eventId
|
||||||
required property Room room
|
|
||||||
required property int originalWidth
|
required property int originalWidth
|
||||||
required property double proportionalHeight
|
required property double proportionalHeight
|
||||||
|
required property Room room
|
||||||
flags: Qt.FramelessWindowHint
|
required property string url
|
||||||
|
|
||||||
//visibility: Window.FullScreen
|
//visibility: Window.FullScreen
|
||||||
color: Qt.rgba(0.2, 0.2, 0.2, 0.66)
|
color: Qt.rgba(0.2, 0.2, 0.2, 0.66)
|
||||||
|
flags: Qt.FramelessWindowHint
|
||||||
|
|
||||||
Component.onCompleted: Nheko.setWindowRole(imageOverlay, "imageoverlay")
|
Component.onCompleted: Nheko.setWindowRole(imageOverlay, "imageoverlay")
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequences: [StandardKey.Cancel]
|
sequences: [StandardKey.Cancel]
|
||||||
|
|
||||||
onActivated: imageOverlay.close()
|
onActivated: imageOverlay.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequences: [StandardKey.Copy]
|
sequences: [StandardKey.Copy]
|
||||||
|
|
||||||
onActivated: {
|
onActivated: {
|
||||||
if (room) {
|
if (room) {
|
||||||
room.copyMedia(eventId);
|
room.copyMedia(eventId);
|
||||||
|
@ -39,94 +38,85 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onSingleTapped: imageOverlay.close();
|
onSingleTapped: imageOverlay.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: imgContainer
|
id: imgContainer
|
||||||
|
|
||||||
property int imgSrcWidth: (imageOverlay.originalWidth && imageOverlay.originalWidth > 100) ? imageOverlay.originalWidth : Screen.width
|
|
||||||
property int imgSrcHeight: imageOverlay.proportionalHeight ? imgSrcWidth * imageOverlay.proportionalHeight : Screen.height
|
property int imgSrcHeight: imageOverlay.proportionalHeight ? imgSrcWidth * imageOverlay.proportionalHeight : Screen.height
|
||||||
|
property int imgSrcWidth: (imageOverlay.originalWidth && imageOverlay.originalWidth > 100) ? imageOverlay.originalWidth : Screen.width
|
||||||
|
|
||||||
height: Math.min(parent.height || Screen.height, imgSrcHeight)
|
height: Math.min(parent.height || Screen.height, imgSrcHeight)
|
||||||
width: Math.min(parent.width || Screen.width, imgSrcWidth)
|
width: Math.min(parent.width || Screen.width, imgSrcWidth)
|
||||||
|
|
||||||
x: (parent.width - width) / 2
|
x: (parent.width - width) / 2
|
||||||
y: (parent.height - height) / 2
|
y: (parent.height - height) / 2
|
||||||
|
|
||||||
|
onScaleChanged: {
|
||||||
|
if (scale > 10)
|
||||||
|
scale = 10;
|
||||||
|
if (scale < 0.1)
|
||||||
|
scale = 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
visible: !mxcimage.loaded
|
property bool loaded: status == Image.Ready
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: url.replace("mxc://", "image://MxcImage/")
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
mipmap: true
|
||||||
property bool loaded: status == Image.Ready
|
smooth: true
|
||||||
|
source: url.replace("mxc://", "image://MxcImage/")
|
||||||
|
visible: !mxcimage.loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
MxcAnimatedImage {
|
MxcAnimatedImage {
|
||||||
id: mxcimage
|
id: mxcimage
|
||||||
|
|
||||||
visible: loaded
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
roomm: imageOverlay.room
|
|
||||||
play: !Settings.animateImagesOnHover || mouseArea.hovered
|
|
||||||
eventId: imageOverlay.eventId
|
eventId: imageOverlay.eventId
|
||||||
}
|
play: !Settings.animateImagesOnHover || mouseArea.hovered
|
||||||
|
roomm: imageOverlay.room
|
||||||
onScaleChanged: {
|
visible: loaded
|
||||||
if (scale > 10) scale = 10;
|
|
||||||
if (scale < 0.1) scale = 0.1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
|
||||||
PinchHandler {
|
PinchHandler {
|
||||||
target: imgContainer
|
|
||||||
maximumScale: 10
|
maximumScale: 10
|
||||||
minimumScale: 0.1
|
minimumScale: 0.1
|
||||||
|
target: imgContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
WheelHandler {
|
WheelHandler {
|
||||||
property: "scale"
|
|
||||||
// workaround for QTBUG-87646 / QTBUG-112394 / QTBUG-112432:
|
// workaround for QTBUG-87646 / QTBUG-112394 / QTBUG-112432:
|
||||||
// Magic Mouse pretends to be a trackpad but doesn't work with PinchHandler
|
// Magic Mouse pretends to be a trackpad but doesn't work with PinchHandler
|
||||||
// and we don't yet distinguish mice and trackpads on Wayland either
|
// and we don't yet distinguish mice and trackpads on Wayland either
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
property: "scale"
|
||||||
target: imgContainer
|
target: imgContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
DragHandler {
|
DragHandler {
|
||||||
target: imgContainer
|
target: imgContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
height: 48
|
height: 48
|
||||||
width: 48
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/copy.svg"
|
image: ":/icons/icons/ui/copy.svg"
|
||||||
|
width: 48
|
||||||
|
|
||||||
//ToolTip.visible: hovered
|
//ToolTip.visible: hovered
|
||||||
//ToolTip.delay: Nheko.tooltipDelay
|
//ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
@ -142,12 +132,11 @@ Window {
|
||||||
imageOverlay.close();
|
imageOverlay.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
height: 48
|
height: 48
|
||||||
width: 48
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/download.svg"
|
image: ":/icons/icons/ui/download.svg"
|
||||||
|
width: 48
|
||||||
|
|
||||||
//ToolTip.visible: hovered
|
//ToolTip.visible: hovered
|
||||||
//ToolTip.delay: Nheko.tooltipDelay
|
//ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
@ -165,9 +154,9 @@ Window {
|
||||||
}
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
height: 48
|
height: 48
|
||||||
width: 48
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
|
width: 48
|
||||||
|
|
||||||
//ToolTip.visible: hovered
|
//ToolTip.visible: hovered
|
||||||
//ToolTip.delay: Nheko.tooltipDelay
|
//ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
@ -176,5 +165,4 @@ Window {
|
||||||
onClicked: imageOverlay.close()
|
onClicked: imageOverlay.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,34 +14,46 @@ ApplicationWindow {
|
||||||
id: win
|
id: win
|
||||||
|
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||||
property SingleImagePackModel imagePack
|
|
||||||
property int currentImageIndex: -1
|
property int currentImageIndex: -1
|
||||||
|
property SingleImagePackModel imagePack
|
||||||
readonly property int stickerDim: 128
|
readonly property int stickerDim: 128
|
||||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
||||||
|
|
||||||
title: qsTr("Editing image pack")
|
|
||||||
height: 600
|
|
||||||
width: 600
|
|
||||||
color: palette.base
|
color: palette.base
|
||||||
modality: Qt.WindowModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 600
|
||||||
|
modality: Qt.WindowModal
|
||||||
|
title: qsTr("Editing image pack")
|
||||||
|
width: 600
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
id: buttons
|
||||||
|
|
||||||
|
standardButtons: DialogButtonBox.Save | DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
imagePack.save();
|
||||||
|
win.close();
|
||||||
|
}
|
||||||
|
onRejected: win.close()
|
||||||
|
}
|
||||||
|
|
||||||
AdaptiveLayout {
|
AdaptiveLayout {
|
||||||
id: adaptiveView
|
id: adaptiveView
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
singlePageMode: false
|
|
||||||
pageIndex: 0
|
pageIndex: 0
|
||||||
|
singlePageMode: false
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: packlistC
|
id: packlistC
|
||||||
|
|
||||||
visible: Settings.groupView
|
|
||||||
minimumWidth: 200
|
|
||||||
collapsedWidth: 200
|
|
||||||
preferredWidth: 300
|
|
||||||
maximumWidth: 300
|
|
||||||
clip: true
|
clip: true
|
||||||
|
collapsedWidth: 200
|
||||||
|
maximumWidth: 300
|
||||||
|
minimumWidth: 200
|
||||||
|
preferredWidth: 300
|
||||||
|
visible: Settings.groupView
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
//required property bool isEmote
|
//required property bool isEmote
|
||||||
|
@ -49,75 +61,67 @@ ApplicationWindow {
|
||||||
|
|
||||||
model: imagePack
|
model: imagePack
|
||||||
|
|
||||||
|
|
||||||
header: AvatarListTile {
|
|
||||||
title: imagePack.packname
|
|
||||||
avatarUrl: imagePack.avatarUrl
|
|
||||||
roomid: imagePack.statekey
|
|
||||||
subtitle: imagePack.statekey
|
|
||||||
index: -1
|
|
||||||
selectedIndex: currentImageIndex
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
onSingleTapped: currentImageIndex = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
height: parent.height - Nheko.paddingSmall * 2
|
|
||||||
width: 3
|
|
||||||
color: palette.highlight
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: Button {
|
|
||||||
onClicked: addFilesDialog.open()
|
|
||||||
width: ListView.view.width
|
|
||||||
text: qsTr("Add images")
|
|
||||||
|
|
||||||
FileDialog {
|
|
||||||
id: addFilesDialog
|
|
||||||
|
|
||||||
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
|
||||||
fileMode: FileDialog.OpenFiles
|
|
||||||
nameFilters: [qsTr("Images (*.png *.webp *.gif *.jpg *.jpeg)")]
|
|
||||||
title: qsTr("Select images for pack")
|
|
||||||
acceptLabel: qsTr("Add to pack")
|
|
||||||
onAccepted: imagePack.addStickers(files)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: AvatarListTile {
|
delegate: AvatarListTile {
|
||||||
id: packItem
|
id: packItem
|
||||||
|
|
||||||
property color background: palette.window
|
property color background: palette.window
|
||||||
property color importantText: palette.text
|
required property string body
|
||||||
property color unimportantText: palette.buttonText
|
|
||||||
property color bubbleBackground: palette.highlight
|
property color bubbleBackground: palette.highlight
|
||||||
property color bubbleText: palette.highlightedText
|
property color bubbleText: palette.highlightedText
|
||||||
|
property color importantText: palette.text
|
||||||
required property string shortCode
|
required property string shortCode
|
||||||
|
property color unimportantText: palette.buttonText
|
||||||
required property string url
|
required property string url
|
||||||
required property string body
|
|
||||||
|
|
||||||
title: shortCode
|
|
||||||
subtitle: body
|
|
||||||
avatarUrl: url
|
avatarUrl: url
|
||||||
selectedIndex: currentImageIndex
|
|
||||||
crop: false
|
crop: false
|
||||||
|
selectedIndex: currentImageIndex
|
||||||
|
subtitle: body
|
||||||
|
title: shortCode
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onSingleTapped: currentImageIndex = index
|
onSingleTapped: currentImageIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
footer: Button {
|
||||||
|
text: qsTr("Add images")
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
onClicked: addFilesDialog.open()
|
||||||
|
|
||||||
|
FileDialog {
|
||||||
|
id: addFilesDialog
|
||||||
|
|
||||||
|
acceptLabel: qsTr("Add to pack")
|
||||||
|
fileMode: FileDialog.OpenFiles
|
||||||
|
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
||||||
|
nameFilters: [qsTr("Images (*.png *.webp *.gif *.jpg *.jpeg)")]
|
||||||
|
title: qsTr("Select images for pack")
|
||||||
|
|
||||||
|
onAccepted: imagePack.addStickers(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
header: AvatarListTile {
|
||||||
|
avatarUrl: imagePack.avatarUrl
|
||||||
|
index: -1
|
||||||
|
roomid: imagePack.statekey
|
||||||
|
selectedIndex: currentImageIndex
|
||||||
|
subtitle: imagePack.statekey
|
||||||
|
title: imagePack.packname
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
onSingleTapped: currentImageIndex = -1
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: palette.highlight
|
||||||
|
height: parent.height - Nheko.paddingSmall * 2
|
||||||
|
width: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: packinfoC
|
id: packinfoC
|
||||||
|
|
||||||
|
@ -127,211 +131,189 @@ ApplicationWindow {
|
||||||
GridLayout {
|
GridLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
visible: currentImageIndex == -1
|
|
||||||
enabled: visible
|
|
||||||
columns: 2
|
columns: 2
|
||||||
|
enabled: visible
|
||||||
rowSpacing: Nheko.paddingLarge
|
rowSpacing: Nheko.paddingLarge
|
||||||
|
visible: currentImageIndex == -1
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: imagePack.packname
|
|
||||||
roomid: imagePack.statekey
|
|
||||||
Layout.preferredHeight: 130
|
Layout.preferredHeight: 130
|
||||||
Layout.preferredWidth: 130
|
Layout.preferredWidth: 130
|
||||||
crop: false
|
crop: false
|
||||||
Layout.alignment: Qt.AlignHCenter
|
displayName: imagePack.packname
|
||||||
|
roomid: imagePack.statekey
|
||||||
|
url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change the overview image for this pack")
|
ToolTip.text: qsTr("Change the overview image for this pack")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.leftMargin: Nheko.paddingMedium
|
anchors.leftMargin: Nheko.paddingMedium
|
||||||
|
anchors.top: parent.top
|
||||||
anchors.topMargin: Nheko.paddingMedium
|
anchors.topMargin: Nheko.paddingMedium
|
||||||
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/edit.svg"
|
image: ":/icons/icons/ui/edit.svg"
|
||||||
|
|
||||||
onClicked: addAvatarDialog.open()
|
onClicked: addAvatarDialog.open()
|
||||||
|
|
||||||
FileDialog {
|
FileDialog {
|
||||||
id: addAvatarDialog
|
id: addAvatarDialog
|
||||||
|
|
||||||
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
|
||||||
fileMode: FileDialog.OpenFile
|
fileMode: FileDialog.OpenFile
|
||||||
|
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
||||||
nameFilters: [qsTr("Overview Image (*.png *.webp *.jpg *.jpeg)")]
|
nameFilters: [qsTr("Overview Image (*.png *.webp *.jpg *.jpeg)")]
|
||||||
title: qsTr("Select overview image for pack")
|
title: qsTr("Select overview image for pack")
|
||||||
|
|
||||||
onAccepted: imagePack.setAvatar(file)
|
onAccepted: imagePack.setAvatar(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: statekeyField
|
id: statekeyField
|
||||||
|
|
||||||
visible: imagePack.roomid
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
label: qsTr("State key")
|
label: qsTr("State key")
|
||||||
text: imagePack.statekey
|
text: imagePack.statekey
|
||||||
|
visible: imagePack.roomid
|
||||||
|
|
||||||
onTextEdited: imagePack.statekey = text
|
onTextEdited: imagePack.statekey = text
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
label: qsTr("Packname")
|
label: qsTr("Packname")
|
||||||
text: imagePack.packname
|
text: imagePack.packname
|
||||||
|
|
||||||
onTextEdited: imagePack.packname = text
|
onTextEdited: imagePack.packname = text
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
label: qsTr("Attribution")
|
label: qsTr("Attribution")
|
||||||
text: imagePack.attribution
|
text: imagePack.attribution
|
||||||
|
|
||||||
onTextEdited: imagePack.attribution = text
|
onTextEdited: imagePack.attribution = text
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.margins: statekeyField.textPadding
|
Layout.margins: statekeyField.textPadding
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
text: qsTr("Use as Emoji")
|
text: qsTr("Use as Emoji")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
checked: imagePack.isEmotePack
|
|
||||||
onCheckedChanged: imagePack.isEmotePack = checked
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
checked: imagePack.isEmotePack
|
||||||
|
|
||||||
|
onCheckedChanged: imagePack.isEmotePack = checked
|
||||||
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.margins: statekeyField.textPadding
|
Layout.margins: statekeyField.textPadding
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
text: qsTr("Use as Sticker")
|
text: qsTr("Use as Sticker")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
checked: imagePack.isStickerPack
|
|
||||||
onCheckedChanged: imagePack.isStickerPack = checked
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
checked: imagePack.isStickerPack
|
||||||
|
|
||||||
|
onCheckedChanged: imagePack.isStickerPack = checked
|
||||||
|
}
|
||||||
Item {
|
Item {
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
visible: currentImageIndex >= 0
|
|
||||||
enabled: visible
|
|
||||||
columns: 2
|
columns: 2
|
||||||
|
enabled: visible
|
||||||
rowSpacing: Nheko.paddingLarge
|
rowSpacing: Nheko.paddingLarge
|
||||||
|
visible: currentImageIndex >= 0
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/") + "?scale"
|
|
||||||
displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
|
|
||||||
roomid: displayName
|
|
||||||
Layout.preferredHeight: 130
|
Layout.preferredHeight: 130
|
||||||
Layout.preferredWidth: 130
|
Layout.preferredWidth: 130
|
||||||
crop: false
|
crop: false
|
||||||
Layout.alignment: Qt.AlignHCenter
|
displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
|
||||||
|
roomid: displayName
|
||||||
|
url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/") + "?scale"
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
|
||||||
label: qsTr("Shortcode")
|
|
||||||
property int bindingCounter: 0
|
property int bindingCounter: 0
|
||||||
|
|
||||||
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: qsTr("Shortcode")
|
||||||
text: bindingCounter, imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
|
text: bindingCounter, imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
|
||||||
|
|
||||||
onTextEdited: {
|
onTextEdited: {
|
||||||
imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.ShortCode);
|
imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.ShortCode);
|
||||||
// force text field to update in case the model disagreed with the new value.
|
// force text field to update in case the model disagreed with the new value.
|
||||||
bindingCounter++;
|
bindingCounter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: bodyField
|
id: bodyField
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
label: qsTr("Body")
|
label: qsTr("Body")
|
||||||
text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Body)
|
text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Body)
|
||||||
|
|
||||||
onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.Body)
|
onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.margins: bodyField.textPadding
|
Layout.margins: bodyField.textPadding
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
text: qsTr("Use as Emoji")
|
text: qsTr("Use as Emoji")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote)
|
|
||||||
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote)
|
||||||
|
|
||||||
|
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
|
||||||
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.margins: bodyField.textPadding
|
Layout.margins: bodyField.textPadding
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
text: qsTr("Use as Sticker")
|
text: qsTr("Use as Sticker")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker)
|
|
||||||
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker)
|
||||||
|
|
||||||
|
onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
|
||||||
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.margins: bodyField.textPadding
|
Layout.margins: bodyField.textPadding
|
||||||
font.weight: Font.DemiBold
|
|
||||||
font.letterSpacing: font.pixelSize * 0.02
|
font.letterSpacing: font.pixelSize * 0.02
|
||||||
|
font.weight: Font.DemiBold
|
||||||
text: qsTr("Remove from pack")
|
text: qsTr("Remove from pack")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
text: qsTr("Remove")
|
text: qsTr("Remove")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let temp = currentImageIndex;
|
let temp = currentImageIndex;
|
||||||
currentImageIndex = -1;
|
currentImageIndex = -1;
|
||||||
imagePack.remove(temp);
|
imagePack.remove(temp);
|
||||||
}
|
}
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: buttons
|
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Save | DialogButtonBox.Cancel
|
|
||||||
onAccepted: {
|
|
||||||
imagePack.save();
|
|
||||||
win.close();
|
|
||||||
}
|
|
||||||
onRejected: win.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,95 +12,74 @@ import im.nheko 1.0
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: win
|
id: win
|
||||||
|
|
||||||
property Room room
|
|
||||||
property ImagePackListModel packlist
|
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||||
property SingleImagePackModel currentPack: packlist.packAt(currentPackIndex)
|
property SingleImagePackModel currentPack: packlist.packAt(currentPackIndex)
|
||||||
property int currentPackIndex: 0
|
property int currentPackIndex: 0
|
||||||
|
property ImagePackListModel packlist
|
||||||
|
property Room room
|
||||||
readonly property int stickerDim: 128
|
readonly property int stickerDim: 128
|
||||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
||||||
|
|
||||||
title: qsTr("Image pack settings")
|
|
||||||
height: 600
|
|
||||||
width: 800
|
|
||||||
color: palette.base
|
color: palette.base
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 600
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: qsTr("Image pack settings")
|
||||||
|
width: 800
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
id: buttons
|
||||||
|
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
text: qsTr("Close")
|
||||||
|
|
||||||
|
onClicked: win.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: packEditor
|
id: packEditor
|
||||||
|
|
||||||
ImagePackEditorDialog {
|
ImagePackEditorDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayout {
|
AdaptiveLayout {
|
||||||
id: adaptiveView
|
id: adaptiveView
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
singlePageMode: false
|
|
||||||
pageIndex: 0
|
pageIndex: 0
|
||||||
|
singlePageMode: false
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: packlistC
|
id: packlistC
|
||||||
|
|
||||||
visible: Settings.groupView
|
|
||||||
minimumWidth: 200
|
|
||||||
collapsedWidth: 200
|
collapsedWidth: 200
|
||||||
preferredWidth: 300
|
|
||||||
maximumWidth: 300
|
maximumWidth: 300
|
||||||
|
minimumWidth: 200
|
||||||
|
preferredWidth: 300
|
||||||
|
visible: Settings.groupView
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
model: packlist
|
|
||||||
clip: true
|
clip: true
|
||||||
|
model: packlist
|
||||||
|
|
||||||
|
|
||||||
footer: ColumnLayout {
|
|
||||||
Button {
|
|
||||||
onClicked: {
|
|
||||||
var dialog = packEditor.createObject(timelineRoot, {
|
|
||||||
"imagePack": packlist.newPack(false)
|
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
timelineRoot.destroyOnClose(dialog);
|
|
||||||
}
|
|
||||||
Layout.preferredWidth: packlistC.width
|
|
||||||
visible: !packlist.containsAccountPack
|
|
||||||
text: qsTr("Create account pack")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
onClicked: {
|
|
||||||
var dialog = packEditor.createObject(timelineRoot, {
|
|
||||||
"imagePack": packlist.newPack(true)
|
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
timelineRoot.destroyOnClose(dialog);
|
|
||||||
}
|
|
||||||
Layout.preferredWidth: packlistC.width
|
|
||||||
visible: room.permissions.canChange(MtxEvent.ImagePackInRoom)
|
|
||||||
text: qsTr("New room pack")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: AvatarListTile {
|
delegate: AvatarListTile {
|
||||||
id: packItem
|
id: packItem
|
||||||
|
|
||||||
property color background: palette.window
|
property color background: palette.window
|
||||||
property color importantText: palette.text
|
|
||||||
property color unimportantText: palette.buttonText
|
|
||||||
property color bubbleBackground: palette.highlight
|
property color bubbleBackground: palette.highlight
|
||||||
property color bubbleText: palette.highlightedText
|
property color bubbleText: palette.highlightedText
|
||||||
required property string displayName
|
required property string displayName
|
||||||
required property bool fromAccountData
|
required property bool fromAccountData
|
||||||
required property bool fromCurrentRoom
|
required property bool fromCurrentRoom
|
||||||
required property bool fromSpace
|
required property bool fromSpace
|
||||||
|
property color importantText: palette.text
|
||||||
required property string statekey
|
required property string statekey
|
||||||
|
property color unimportantText: palette.buttonText
|
||||||
|
|
||||||
title: displayName
|
roomid: statekey
|
||||||
|
selectedIndex: currentPackIndex
|
||||||
subtitle: {
|
subtitle: {
|
||||||
if (fromAccountData)
|
if (fromAccountData)
|
||||||
return qsTr("Private pack");
|
return qsTr("Private pack");
|
||||||
|
@ -111,19 +90,42 @@ ApplicationWindow {
|
||||||
else
|
else
|
||||||
return qsTr("Globally enabled pack");
|
return qsTr("Globally enabled pack");
|
||||||
}
|
}
|
||||||
selectedIndex: currentPackIndex
|
title: displayName
|
||||||
roomid: statekey
|
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onSingleTapped: currentPackIndex = index
|
onSingleTapped: currentPackIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
footer: ColumnLayout {
|
||||||
|
Button {
|
||||||
|
Layout.preferredWidth: packlistC.width
|
||||||
|
text: qsTr("Create account pack")
|
||||||
|
visible: !packlist.containsAccountPack
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var dialog = packEditor.createObject(timelineRoot, {
|
||||||
|
"imagePack": packlist.newPack(false)
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Button {
|
||||||
|
Layout.preferredWidth: packlistC.width
|
||||||
|
text: qsTr("New room pack")
|
||||||
|
visible: room.permissions.canChange(MtxEvent.ImagePackInRoom)
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var dialog = packEditor.createObject(timelineRoot, {
|
||||||
|
"imagePack": packlist.newPack(true)
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
timelineRoot.destroyOnClose(dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: packinfoC
|
id: packinfoC
|
||||||
|
|
||||||
|
@ -133,9 +135,9 @@ ApplicationWindow {
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: packinfo
|
id: packinfo
|
||||||
|
|
||||||
property string packName: currentPack ? currentPack.packname : ""
|
|
||||||
property string attribution: currentPack ? currentPack.attribution : ""
|
property string attribution: currentPack ? currentPack.attribution : ""
|
||||||
property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
|
property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
|
||||||
|
property string packName: currentPack ? currentPack.packname : ""
|
||||||
property string statekey: currentPack ? currentPack.statekey : ""
|
property string statekey: currentPack ? currentPack.statekey : ""
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -143,56 +145,52 @@ ApplicationWindow {
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
|
Layout.alignment: Qt.AlignHCenter
|
||||||
displayName: packinfo.packName
|
|
||||||
roomid: packinfo.statekey
|
|
||||||
Layout.preferredHeight: 100
|
Layout.preferredHeight: 100
|
||||||
Layout.preferredWidth: 100
|
Layout.preferredWidth: 100
|
||||||
Layout.alignment: Qt.AlignHCenter
|
displayName: packinfo.packName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
roomid: packinfo.statekey
|
||||||
|
url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: packinfo.packName
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
||||||
font.pixelSize: Math.ceil(fontMetrics.pixelSize * 1.1)
|
font.pixelSize: Math.ceil(fontMetrics.pixelSize * 1.1)
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
Layout.alignment: Qt.AlignHCenter
|
text: packinfo.packName
|
||||||
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
|
||||||
textFormat: TextEdit.PlainText
|
textFormat: TextEdit.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: packinfo.attribution
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
|
||||||
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
text: packinfo.attribution
|
||||||
textFormat: TextEdit.PlainText
|
textFormat: TextEdit.PlainText
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
visible: currentPack && currentPack.roomid != ""
|
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingMedium
|
rowSpacing: Nheko.paddingMedium
|
||||||
|
visible: currentPack && currentPack.roomid != ""
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Enable globally")
|
text: qsTr("Enable globally")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
ToolTip.text: qsTr("Enables this pack to be used in all rooms")
|
ToolTip.text: qsTr("Enables this pack to be used in all rooms")
|
||||||
checked: currentPack ? currentPack.isGloballyEnabled : false
|
checked: currentPack ? currentPack.isGloballyEnabled : false
|
||||||
|
|
||||||
onCheckedChanged: currentPack.isGloballyEnabled = checked
|
onCheckedChanged: currentPack.isGloballyEnabled = checked
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("Edit")
|
|
||||||
enabled: currentPack.canEdit
|
enabled: currentPack.canEdit
|
||||||
|
text: qsTr("Edit")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var dialog = packEditor.createObject(timelineRoot, {
|
var dialog = packEditor.createObject(timelineRoot, {
|
||||||
"imagePack": currentPack
|
"imagePack": currentPack
|
||||||
|
@ -201,61 +199,40 @@ ApplicationWindow {
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridView {
|
GridView {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: currentPack
|
|
||||||
cellWidth: stickerDimPad
|
|
||||||
cellHeight: stickerDimPad
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
cacheBuffer: 500
|
||||||
|
cellHeight: stickerDimPad
|
||||||
|
cellWidth: stickerDimPad
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: -1 // prevent sorting from stealing focus
|
currentIndex: -1 // prevent sorting from stealing focus
|
||||||
cacheBuffer: 500
|
model: currentPack
|
||||||
|
|
||||||
|
|
||||||
// Individual emoji
|
// Individual emoji
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
width: stickerDim
|
|
||||||
height: stickerDim
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.text: ":" + model.shortCode + ": - " + model.body
|
ToolTip.text: ":" + model.shortCode + ": - " + model.body
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
|
||||||
contentItem: Image {
|
|
||||||
height: stickerDim
|
height: stickerDim
|
||||||
|
hoverEnabled: true
|
||||||
width: stickerDim
|
width: stickerDim
|
||||||
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: hovered ? palette.highlight : 'transparent'
|
color: hovered ? palette.highlight : 'transparent'
|
||||||
radius: 5
|
radius: 5
|
||||||
}
|
}
|
||||||
|
contentItem: Image {
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
height: stickerDim
|
||||||
|
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||||
|
width: stickerDim
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: buttons
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Close")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
onClicked: win.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,52 +11,27 @@ import im.nheko 1.0
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: inputDialog
|
id: inputDialog
|
||||||
|
|
||||||
property alias prompt: promptLabel.text
|
|
||||||
property alias echoMode: statusInput.echoMode
|
property alias echoMode: statusInput.echoMode
|
||||||
signal accepted(text: string)
|
property alias prompt: promptLabel.text
|
||||||
|
|
||||||
modality: Qt.NonModal
|
signal accepted(string text)
|
||||||
flags: Qt.Dialog
|
|
||||||
width: 350
|
|
||||||
height: fontMetrics.lineSpacing * 7
|
|
||||||
|
|
||||||
function forceActiveFocus() {
|
function forceActiveFocus() {
|
||||||
statusInput.forceActiveFocus();
|
statusInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
flags: Qt.Dialog
|
||||||
sequence: StandardKey.Cancel
|
height: fontMetrics.lineSpacing * 7
|
||||||
onActivated: dbb.rejected()
|
modality: Qt.NonModal
|
||||||
}
|
width: 350
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: promptLabel
|
|
||||||
|
|
||||||
color: palette.text
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: statusInput
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
onAccepted: dbb.accepted()
|
|
||||||
focus: true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: dbb
|
id: dbb
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
inputDialog.accepted(statusInput.text);
|
inputDialog.accepted(statusInput.text);
|
||||||
|
|
||||||
inputDialog.close();
|
inputDialog.close();
|
||||||
}
|
}
|
||||||
onRejected: {
|
onRejected: {
|
||||||
|
@ -64,4 +39,28 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: dbb.rejected()
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: promptLabel
|
||||||
|
|
||||||
|
color: palette.text
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: statusInput
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
onAccepted: dbb.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,84 +12,106 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: inviteDialogRoot
|
id: inviteDialogRoot
|
||||||
|
|
||||||
property InviteesModel invitees
|
|
||||||
property var friendsCompleter
|
property var friendsCompleter
|
||||||
|
property InviteesModel invitees
|
||||||
property var profile
|
property var profile
|
||||||
minimumWidth: 300
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
friendsCompleter = TimelineManager.completerFor("user", "friends")
|
|
||||||
width = 600
|
|
||||||
}
|
|
||||||
|
|
||||||
function addInvite(mxid, displayName, avatarUrl) {
|
function addInvite(mxid, displayName, avatarUrl) {
|
||||||
if (mxid.match("@.+?:.{3,}")) {
|
if (mxid.match("@.+?:.{3,}")) {
|
||||||
invitees.addUser(mxid, displayName, avatarUrl);
|
invitees.addUser(mxid, displayName, avatarUrl);
|
||||||
} else
|
} else
|
||||||
console.log("invalid mxid: " + mxid)
|
console.log("invalid mxid: " + mxid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanUpAndClose() {
|
function cleanUpAndClose() {
|
||||||
if (inviteeEntry.isValidMxid)
|
if (inviteeEntry.isValidMxid)
|
||||||
addInvite(inviteeEntry.text, "", "");
|
addInvite(inviteeEntry.text, "", "");
|
||||||
|
|
||||||
invitees.accept();
|
invitees.accept();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
title: qsTr("Invite users to %1").arg(invitees.room.plainRoomName)
|
|
||||||
height: 380
|
|
||||||
width: 340
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 380
|
||||||
|
minimumWidth: 300
|
||||||
|
title: qsTr("Invite users to %1").arg(invitees.room.plainRoomName)
|
||||||
|
width: 340
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
id: buttons
|
||||||
|
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
enabled: invitees.count > 0
|
||||||
|
text: qsTr("Invite")
|
||||||
|
|
||||||
|
onClicked: cleanUpAndClose()
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: inviteDialogRoot.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
friendsCompleter = TimelineManager.completerFor("user", "friends");
|
||||||
|
width = 600;
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+Enter"
|
sequence: "Ctrl+Enter"
|
||||||
|
|
||||||
onActivated: cleanUpAndClose()
|
onActivated: cleanUpAndClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: inviteDialogRoot.close()
|
onActivated: inviteDialogRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Flow {
|
Flow {
|
||||||
layoutDirection: Qt.LeftToRight
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: implicitHeight
|
Layout.preferredHeight: implicitHeight
|
||||||
|
layoutDirection: Qt.LeftToRight
|
||||||
spacing: 4
|
spacing: 4
|
||||||
visible: !inviteesList.visible
|
visible: !inviteesList.visible
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: inviteesRepeater
|
id: inviteesRepeater
|
||||||
|
|
||||||
model: invitees
|
model: invitees
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
onClicked: invitees.removeUser(model.mxid)
|
|
||||||
id: inviteeButton
|
id: inviteeButton
|
||||||
contentItem: Label {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
id: inviteeUserid
|
|
||||||
text: model.displayName != "" ? model.displayName : model.userid
|
|
||||||
color: inviteeButton.hovered ? palette.highlightedText: palette.text
|
|
||||||
maximumLineCount: 1
|
|
||||||
}
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
border.color: palette.text
|
border.color: palette.text
|
||||||
color: inviteeButton.hovered ? palette.highlight : palette.window
|
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
color: inviteeButton.hovered ? palette.highlight : palette.window
|
||||||
radius: inviteeButton.height / 2
|
radius: inviteeButton.height / 2
|
||||||
}
|
}
|
||||||
}
|
contentItem: Label {
|
||||||
}
|
id: inviteeUserid
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: inviteeButton.hovered ? palette.highlightedText : palette.text
|
||||||
|
maximumLineCount: 1
|
||||||
|
text: model.displayName != "" ? model.displayName : model.userid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: invitees.removeUser(model.mxid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Search user")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Search user")
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
@ -99,147 +121,136 @@ ApplicationWindow {
|
||||||
|
|
||||||
property bool isValidMxid: text.match("@.+?:.{3,}")
|
property bool isValidMxid: text.match("@.+?:.{3,}")
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
backgroundColor: palette.window
|
backgroundColor: palette.window
|
||||||
placeholderText: qsTr("@user:yourserver.example.com", "Example user id. The name 'user' can be localized however you want.")
|
placeholderText: qsTr("@user:yourserver.example.com", "Example user id. The name 'user' can be localized however you want.")
|
||||||
Layout.fillWidth: true
|
|
||||||
onAccepted: {
|
|
||||||
if (isValidMxid) {
|
|
||||||
addInvite(text, "", "");
|
|
||||||
clear()
|
|
||||||
}
|
|
||||||
else if (userSearch.count > 0) {
|
|
||||||
addInvite(userSearch.itemAtIndex(0).userid, userSearch.itemAtIndex(0).displayName, userSearch.itemAtIndex(0).avatarUrl)
|
|
||||||
clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Component.onCompleted: forceActiveFocus()
|
Component.onCompleted: forceActiveFocus()
|
||||||
Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier))
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier))
|
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier))
|
||||||
cleanUpAndClose();
|
cleanUpAndClose();
|
||||||
|
}
|
||||||
|
Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier))
|
||||||
|
onAccepted: {
|
||||||
|
if (isValidMxid) {
|
||||||
|
addInvite(text, "", "");
|
||||||
|
clear();
|
||||||
|
} else if (userSearch.count > 0) {
|
||||||
|
addInvite(userSearch.itemAtIndex(0).userid, userSearch.itemAtIndex(0).displayName, userSearch.itemAtIndex(0).avatarUrl);
|
||||||
|
clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
searchTimer.restart()
|
searchTimer.restart();
|
||||||
if (isValidMxid) {
|
if (isValidMxid) {
|
||||||
profile = TimelineManager.getGlobalUserProfile(text);
|
profile = TimelineManager.getGlobalUserProfile(text);
|
||||||
} else
|
} else
|
||||||
profile = null;
|
profile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: searchTimer
|
id: searchTimer
|
||||||
|
|
||||||
interval: 350
|
interval: 350
|
||||||
onTriggered: {
|
|
||||||
userSearch.model.setSearchString(parent.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
userSearch.model.setSearchString(parent.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: searchOnServer
|
id: searchOnServer
|
||||||
|
|
||||||
checked: false
|
checked: false
|
||||||
|
|
||||||
onClicked: userSearch.model.setSearchString(inviteeEntry.text)
|
onClicked: userSearch.model.setSearchString(inviteeEntry.text)
|
||||||
}
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Search on Server")
|
text: qsTr("Search on Server")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
UserListRow {
|
UserListRow {
|
||||||
visible: inviteeEntry.isValidMxid
|
|
||||||
id: del3
|
id: del3
|
||||||
Layout.preferredWidth: inviteDialogRoot.width/2
|
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
Layout.preferredHeight: implicitHeight
|
Layout.preferredHeight: implicitHeight
|
||||||
displayName: profile? profile.displayName : ""
|
Layout.preferredWidth: inviteDialogRoot.width / 2
|
||||||
avatarUrl: profile ? profile.avatarUrl : ""
|
avatarUrl: profile ? profile.avatarUrl : ""
|
||||||
userid: inviteeEntry.text
|
|
||||||
onClicked: addInvite(inviteeEntry.text, displayName, avatarUrl)
|
|
||||||
bgColor: del3.hovered ? palette.dark : inviteDialogRoot.color
|
bgColor: del3.hovered ? palette.dark : inviteDialogRoot.color
|
||||||
|
displayName: profile ? profile.displayName : ""
|
||||||
|
userid: inviteeEntry.text
|
||||||
|
visible: inviteeEntry.isValidMxid
|
||||||
|
|
||||||
|
onClicked: addInvite(inviteeEntry.text, displayName, avatarUrl)
|
||||||
}
|
}
|
||||||
ListView {
|
ListView {
|
||||||
visible: !inviteeEntry.isValidMxid
|
|
||||||
id: userSearch
|
id: userSearch
|
||||||
model: searchOnServer.checked? userDirectory : friendsCompleter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
clip: true
|
clip: true
|
||||||
|
model: searchOnServer.checked ? userDirectory : friendsCompleter
|
||||||
|
visible: !inviteeEntry.isValidMxid
|
||||||
|
|
||||||
delegate: UserListRow {
|
delegate: UserListRow {
|
||||||
id: del2
|
id: del2
|
||||||
width: ListView.view.width
|
|
||||||
height: implicitHeight
|
|
||||||
displayName: model.displayName
|
|
||||||
userid: model.userid
|
|
||||||
avatarUrl: model.avatarUrl
|
avatarUrl: model.avatarUrl
|
||||||
onClicked: addInvite(userid, displayName, avatarUrl)
|
|
||||||
bgColor: del2.hovered ? palette.dark : inviteDialogRoot.color
|
bgColor: del2.hovered ? palette.dark : inviteDialogRoot.color
|
||||||
|
displayName: model.displayName
|
||||||
|
height: implicitHeight
|
||||||
|
userid: model.userid
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
onClicked: addInvite(userid, displayName, avatarUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
visible: inviteesList.visible
|
|
||||||
Layout.preferredWidth: 1
|
Layout.preferredWidth: 1
|
||||||
color: Nheko.theme.separator
|
color: Nheko.theme.separator
|
||||||
|
visible: inviteesList.visible
|
||||||
}
|
}
|
||||||
ListView {
|
ListView {
|
||||||
id: inviteesList
|
id: inviteesList
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
model: invitees
|
Layout.fillWidth: true
|
||||||
clip: true
|
clip: true
|
||||||
|
model: invitees
|
||||||
visible: inviteDialogRoot.width >= 500
|
visible: inviteDialogRoot.width >= 500
|
||||||
|
|
||||||
delegate: UserListRow {
|
delegate: UserListRow {
|
||||||
id: del
|
id: del
|
||||||
hoverEnabled: true
|
|
||||||
width: ListView.view.width
|
|
||||||
height: implicitHeight
|
|
||||||
onClicked: TimelineManager.openGlobalUserProfile(model.mxid)
|
|
||||||
userid: model.mxid
|
|
||||||
avatarUrl: model.avatarUrl
|
avatarUrl: model.avatarUrl
|
||||||
displayName: model.displayName
|
|
||||||
bgColor: del.hovered ? palette.dark : inviteDialogRoot.color
|
bgColor: del.hovered ? palette.dark : inviteDialogRoot.color
|
||||||
|
displayName: model.displayName
|
||||||
|
height: implicitHeight
|
||||||
|
hoverEnabled: true
|
||||||
|
userid: model.mxid
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
onClicked: TimelineManager.openGlobalUserProfile(model.mxid)
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
id: removeButton
|
||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Nheko.paddingSmall
|
anchors.rightMargin: Nheko.paddingSmall
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: Nheko.paddingSmall
|
anchors.topMargin: Nheko.paddingSmall
|
||||||
id: removeButton
|
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
|
|
||||||
onClicked: invitees.removeUser(model.mxid)
|
onClicked: invitees.removeUser(model.mxid)
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: buttons
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Invite")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
enabled: invitees.count > 0
|
|
||||||
onClicked: cleanUpAndClose()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
|
|
||||||
onClicked: inviteDialogRoot.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,48 +11,18 @@ import im.nheko 1.0
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: joinRoomRoot
|
id: joinRoomRoot
|
||||||
|
|
||||||
title: qsTr("Join room")
|
|
||||||
modality: Qt.WindowModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
width: 350
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
height: fontMetrics.lineSpacing * 7
|
height: fontMetrics.lineSpacing * 7
|
||||||
|
modality: Qt.WindowModal
|
||||||
Shortcut {
|
title: qsTr("Join room")
|
||||||
sequence: StandardKey.Cancel
|
width: 350
|
||||||
onActivated: dbb.rejected()
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: promptLabel
|
|
||||||
|
|
||||||
text: qsTr("Room ID or alias")
|
|
||||||
color: palette.text
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: input
|
|
||||||
|
|
||||||
focus: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
onAccepted: {
|
|
||||||
if (input.text.match("#.+?:.{3,}"))
|
|
||||||
dbb.accepted();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: dbb
|
id: dbb
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
Nheko.joinRoom(input.text);
|
Nheko.joinRoom(input.text);
|
||||||
joinRoomRoot.close();
|
joinRoomRoot.close();
|
||||||
|
@ -62,11 +32,38 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Join")
|
|
||||||
enabled: input.text.match("#.+?:.{3,}")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
enabled: input.text.match("#.+?:.{3,}")
|
||||||
|
text: qsTr("Join")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: dbb.rejected()
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: promptLabel
|
||||||
|
|
||||||
|
color: palette.text
|
||||||
|
text: qsTr("Room ID or alias")
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: input
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (input.text.match("#.+?:.{3,}"))
|
||||||
|
dbb.accepted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,20 @@ import im.nheko
|
||||||
P.MessageDialog {
|
P.MessageDialog {
|
||||||
id: leaveRoomRoot
|
id: leaveRoomRoot
|
||||||
|
|
||||||
required property string roomId
|
|
||||||
property string reason: ""
|
property string reason: ""
|
||||||
|
required property string roomId
|
||||||
|
|
||||||
title: qsTr("Leave room")
|
|
||||||
text: qsTr("Are you sure you want to leave?")
|
|
||||||
modality: Qt.ApplicationModal
|
|
||||||
buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel
|
buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel
|
||||||
onAccepted: {
|
modality: Qt.ApplicationModal
|
||||||
|
text: qsTr("Are you sure you want to leave?")
|
||||||
|
title: qsTr("Leave room")
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
if (CallManager.haveCallInvite) {
|
if (CallManager.haveCallInvite) {
|
||||||
callManager.rejectInvite();
|
callManager.rejectInvite();
|
||||||
} else if (CallManager.isOnCall) {
|
} else if (CallManager.isOnCall) {
|
||||||
CallManager.hangUp();
|
CallManager.hangUp();
|
||||||
}
|
}
|
||||||
Rooms.leave(roomId, reason)
|
Rooms.leave(roomId, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,11 @@ import im.nheko
|
||||||
P.MessageDialog {
|
P.MessageDialog {
|
||||||
id: logoutRoot
|
id: logoutRoot
|
||||||
|
|
||||||
title: qsTr("Log out")
|
|
||||||
text: CallManager.isOnCall ? qsTr("A call is in progress. Log out?") : qsTr("Are you sure you want to log out?")
|
|
||||||
modality: Qt.WindowModal
|
|
||||||
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
|
||||||
buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel
|
buttons: P.MessageDialog.Ok | P.MessageDialog.Cancel
|
||||||
|
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
modality: Qt.WindowModal
|
||||||
|
text: CallManager.isOnCall ? qsTr("A call is in progress. Log out?") : qsTr("Are you sure you want to log out?")
|
||||||
|
title: qsTr("Log out")
|
||||||
|
|
||||||
onAccepted: Nheko.logout()
|
onAccepted: Nheko.logout()
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,21 +9,38 @@ import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: plEditorW
|
id: plEditorW
|
||||||
|
|
||||||
property var roomSettings
|
|
||||||
property var editingModel: Nheko.editPowerlevels(roomSettings.roomId)
|
property var editingModel: Nheko.editPowerlevels(roomSettings.roomId)
|
||||||
|
property var roomSettings
|
||||||
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
minimumWidth: 300
|
|
||||||
minimumHeight: 400
|
|
||||||
height: 600
|
height: 600
|
||||||
|
minimumHeight: 400
|
||||||
|
minimumWidth: 300
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: qsTr("Permissions in %1").arg(roomSettings.roomName)
|
||||||
width: 300
|
width: 300
|
||||||
|
|
||||||
title: qsTr("Permissions in %1").arg(roomSettings.roomName);
|
footer: DialogButtonBox {
|
||||||
|
id: dbb
|
||||||
|
|
||||||
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (editingModel.isSpace) {
|
||||||
|
// TODO(Nico): Replace with showing a list of spaces to apply to
|
||||||
|
editingModel.updateSpacesModel();
|
||||||
|
plEditorW.close();
|
||||||
|
timelineRoot.showSpacePLApplyPrompt(roomSettings, editingModel);
|
||||||
|
} else {
|
||||||
|
editingModel.commit();
|
||||||
|
plEditorW.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onRejected: plEditorW.close()
|
||||||
|
}
|
||||||
|
|
||||||
// Shortcut {
|
// Shortcut {
|
||||||
// sequence: StandardKey.Cancel
|
// sequence: StandardKey.Cancel
|
||||||
|
@ -31,22 +48,21 @@ ApplicationWindow {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Be careful when editing permissions. You can't lower the permissions of people with a same or higher level than you. Be careful when promoting others.")
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
color: palette.text
|
|
||||||
Layout.bottomMargin: Nheko.paddingMedium
|
Layout.bottomMargin: Nheko.paddingMedium
|
||||||
|
Layout.fillHeight: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
||||||
|
text: qsTr("Be careful when editing permissions. You can't lower the permissions of people with a same or higher level than you. Be careful when promoting others.")
|
||||||
}
|
}
|
||||||
|
|
||||||
TabBar {
|
TabBar {
|
||||||
id: bar
|
id: bar
|
||||||
|
|
||||||
Layout.preferredWidth: parent.width
|
Layout.preferredWidth: parent.width
|
||||||
|
|
||||||
NhekoTabButton {
|
NhekoTabButton {
|
||||||
|
@ -57,95 +73,95 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
color: palette.alternateBase
|
Layout.fillWidth: true
|
||||||
border.width: 1
|
|
||||||
border.color: Nheko.theme.separator
|
border.color: Nheko.theme.separator
|
||||||
|
border.width: 1
|
||||||
|
color: palette.alternateBase
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
currentIndex: bar.currentIndex
|
currentIndex: bar.currentIndex
|
||||||
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Move permissions between roles to change them")
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
Layout.fillHeight: false
|
||||||
color: palette.text
|
|
||||||
}
|
|
||||||
|
|
||||||
ReorderableListview {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
||||||
|
text: qsTr("Move permissions between roles to change them")
|
||||||
|
}
|
||||||
|
ReorderableListview {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
model: editingModel.types
|
model: editingModel.types
|
||||||
|
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
Column {
|
Column {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Text { visible: model.isType; text: model.displayName; color: palette.text}
|
|
||||||
Text {
|
Text {
|
||||||
visible: !model.isType;
|
color: palette.text
|
||||||
|
text: model.displayName
|
||||||
|
visible: model.isType
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
color: palette.text
|
||||||
text: {
|
text: {
|
||||||
if (editingModel.adminLevel == model.powerlevel)
|
if (editingModel.adminLevel == model.powerlevel)
|
||||||
return qsTr("Administrator (%1)").arg(model.powerlevel)
|
return qsTr("Administrator (%1)").arg(model.powerlevel);
|
||||||
else if (editingModel.moderatorLevel == model.powerlevel)
|
else if (editingModel.moderatorLevel == model.powerlevel)
|
||||||
return qsTr("Moderator (%1)").arg(model.powerlevel)
|
return qsTr("Moderator (%1)").arg(model.powerlevel);
|
||||||
else if (editingModel.defaultUserLevel == model.powerlevel)
|
else if (editingModel.defaultUserLevel == model.powerlevel)
|
||||||
return qsTr("User (%1)").arg(model.powerlevel)
|
return qsTr("User (%1)").arg(model.powerlevel);
|
||||||
else
|
else
|
||||||
return qsTr("Custom (%1)").arg(model.powerlevel)
|
return qsTr("Custom (%1)").arg(model.powerlevel);
|
||||||
}
|
}
|
||||||
color: palette.text
|
visible: !model.isType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.rightMargin: 2
|
Layout.rightMargin: 2
|
||||||
|
ToolTip.text: model.isType ? qsTr("Remove event type") : qsTr("Add event type")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
image: model.isType ? ":/icons/icons/ui/dismiss.svg" : ":/icons/icons/ui/add-square-button.svg"
|
image: model.isType ? ":/icons/icons/ui/dismiss.svg" : ":/icons/icons/ui/add-square-button.svg"
|
||||||
visible: !model.isType || model.removeable
|
visible: !model.isType || model.removeable
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: model.isType ? qsTr("Remove event type") : qsTr("Add event type")
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model.isType) {
|
if (model.isType) {
|
||||||
editingModel.types.remove(index);
|
editingModel.types.remove(index);
|
||||||
} else {
|
} else {
|
||||||
typeEntry.y = offset
|
typeEntry.y = offset;
|
||||||
typeEntry.visible = true
|
typeEntry.visible = true;
|
||||||
typeEntry.index = index;
|
typeEntry.index = index;
|
||||||
typeEntry.forceActiveFocus()
|
typeEntry.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: typeEntry
|
id: typeEntry
|
||||||
|
|
||||||
property int index
|
property int index
|
||||||
|
|
||||||
|
color: palette.text
|
||||||
|
visible: false
|
||||||
width: parent.width
|
width: parent.width
|
||||||
z: 5
|
z: 5
|
||||||
visible: false
|
|
||||||
|
|
||||||
color: palette.text
|
|
||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (typeEntry.text.includes('.') && event.matches(StandardKey.InsertParagraphSeparator)) {
|
if (typeEntry.text.includes('.') && event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||||
editingModel.types.add(typeEntry.index, typeEntry.text)
|
editingModel.types.add(typeEntry.index, typeEntry.text);
|
||||||
typeEntry.visible = false;
|
typeEntry.visible = false;
|
||||||
typeEntry.clear();
|
typeEntry.clear();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
} else if (event.matches(StandardKey.Cancel)) {
|
||||||
else if (event.matches(StandardKey.Cancel)) {
|
|
||||||
typeEntry.visible = false;
|
typeEntry.visible = false;
|
||||||
typeEntry.clear();
|
typeEntry.clear();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
@ -153,7 +169,6 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: qsTr("Add new role")
|
text: qsTr("Add new role")
|
||||||
|
@ -164,19 +179,18 @@ ApplicationWindow {
|
||||||
id: newPLLay
|
id: newPLLay
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: false
|
|
||||||
color: palette.alternateBase
|
color: palette.alternateBase
|
||||||
|
visible: false
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
SpinBox {
|
SpinBox {
|
||||||
id: newPLVal
|
id: newPLVal
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
editable: true
|
editable: true
|
||||||
//from: -9007199254740991
|
//from: -9007199254740991
|
||||||
//to: 9007199254740991
|
//to: 9007199254740991
|
||||||
|
@ -192,10 +206,10 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Add")
|
|
||||||
Layout.preferredWidth: 100
|
Layout.preferredWidth: 100
|
||||||
|
text: qsTr("Add")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
editingModel.addRole(newPLVal.value);
|
editingModel.addRole(newPLVal.value);
|
||||||
newPLLay.visible = false;
|
newPLLay.visible = false;
|
||||||
|
@ -205,42 +219,109 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Move users up or down to change their permissions")
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
Layout.fillHeight: false
|
||||||
}
|
|
||||||
|
|
||||||
ReorderableListview {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
||||||
|
text: qsTr("Move users up or down to change their permissions")
|
||||||
|
}
|
||||||
|
ReorderableListview {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
model: editingModel.users
|
model: editingModel.users
|
||||||
|
|
||||||
|
delegate: RowLayout {
|
||||||
|
//anchors { fill: parent; margins: 2 }
|
||||||
|
id: row
|
||||||
|
|
||||||
|
Avatar {
|
||||||
|
id: avatar
|
||||||
|
|
||||||
|
Layout.leftMargin: 2
|
||||||
|
Layout.preferredHeight: Nheko.avatarSize / 2
|
||||||
|
Layout.preferredWidth: Nheko.avatarSize / 2
|
||||||
|
displayName: model.displayName
|
||||||
|
enabled: false
|
||||||
|
url: {
|
||||||
|
if (model.isUser)
|
||||||
|
return model.avatarUrl.replace("mxc://", "image://MxcImage/");
|
||||||
|
else if (editingModel.adminLevel >= model.powerlevel)
|
||||||
|
return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?" + palette.buttonText;
|
||||||
|
else if (editingModel.moderatorLevel >= model.powerlevel)
|
||||||
|
return "image://colorimage/:/icons/icons/ui/ribbon.svg?" + palette.buttonText;
|
||||||
|
else
|
||||||
|
return "image://colorimage/:/icons/icons/ui/person.svg?" + palette.buttonText;
|
||||||
|
}
|
||||||
|
userid: model.mxid
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: palette.text
|
||||||
|
text: model.displayName
|
||||||
|
visible: model.isUser
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
color: palette.text
|
||||||
|
text: model.mxid
|
||||||
|
visible: model.isUser
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
color: palette.text
|
||||||
|
text: {
|
||||||
|
if (editingModel.adminLevel == model.powerlevel)
|
||||||
|
return qsTr("Administrator (%1)").arg(model.powerlevel);
|
||||||
|
else if (editingModel.moderatorLevel == model.powerlevel)
|
||||||
|
return qsTr("Moderator (%1)").arg(model.powerlevel);
|
||||||
|
else
|
||||||
|
return qsTr("Custom (%1)").arg(model.powerlevel);
|
||||||
|
}
|
||||||
|
visible: !model.isUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImageButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
Layout.rightMargin: 2
|
||||||
|
ToolTip.text: model.isUser ? qsTr("Remove user") : qsTr("Add user")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: model.isUser ? ":/icons/icons/ui/dismiss.svg" : ":/icons/icons/ui/add-square-button.svg"
|
||||||
|
visible: !model.isUser || model.removeable
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (model.isUser) {
|
||||||
|
editingModel.users.remove(index);
|
||||||
|
} else {
|
||||||
|
userEntryCompleter.y = offset;
|
||||||
|
userEntryCompleter.visible = true;
|
||||||
|
userEntryCompleter.index = index;
|
||||||
|
userEntry.forceActiveFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: userEntryCompleter
|
id: userEntryCompleter
|
||||||
|
|
||||||
property int index: 0
|
property int index: 0
|
||||||
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
spacing: 1
|
spacing: 1
|
||||||
|
visible: false
|
||||||
|
width: parent.width
|
||||||
z: 5
|
z: 5
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: userEntry
|
id: userEntry
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
//font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
|
//font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
|
||||||
color: palette.text
|
color: palette.text
|
||||||
onTextEdited: {
|
width: parent.width
|
||||||
userCompleter.completer.searchString = text;
|
|
||||||
}
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
@ -264,130 +345,45 @@ ApplicationWindow {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
userCompleter.completer.searchString = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Completer {
|
Completer {
|
||||||
id: userCompleter
|
id: userCompleter
|
||||||
|
|
||||||
visible: userEntry.text.length > 0
|
|
||||||
width: parent.width
|
|
||||||
roomId: plEditorW.roomSettings.roomId
|
|
||||||
completerName: "user"
|
|
||||||
bottomToTop: false
|
|
||||||
fullWidth: true
|
|
||||||
avatarHeight: Nheko.avatarSize / 2
|
avatarHeight: Nheko.avatarSize / 2
|
||||||
avatarWidth: Nheko.avatarSize / 2
|
avatarWidth: Nheko.avatarSize / 2
|
||||||
|
bottomToTop: false
|
||||||
centerRowContent: false
|
centerRowContent: false
|
||||||
|
completerName: "user"
|
||||||
|
fullWidth: true
|
||||||
|
roomId: plEditorW.roomSettings.roomId
|
||||||
rowMargin: 2
|
rowMargin: 2
|
||||||
rowSpacing: 2
|
rowSpacing: 2
|
||||||
|
visible: userEntry.text.length > 0
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
id: userCompletionConnections
|
||||||
|
|
||||||
function onCompletionSelected(id) {
|
function onCompletionSelected(id) {
|
||||||
console.log("selected: " + id);
|
console.log("selected: " + id);
|
||||||
editingModel.users.add(userEntryCompleter.index, id);
|
editingModel.users.add(userEntryCompleter.index, id);
|
||||||
userEntry.clear();
|
userEntry.clear();
|
||||||
userEntryCompleter.visible = false;
|
userEntryCompleter.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCountChanged() {
|
function onCountChanged() {
|
||||||
if (userCompleter.count > 0 && (userCompleter.currentIndex < 0 || userCompleter.currentIndex >= userCompleter.count))
|
if (userCompleter.count > 0 && (userCompleter.currentIndex < 0 || userCompleter.currentIndex >= userCompleter.count))
|
||||||
userCompleter.currentIndex = 0;
|
userCompleter.currentIndex = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: userCompleter
|
target: userCompleter
|
||||||
id: userCompletionConnections
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: RowLayout {
|
|
||||||
//anchors { fill: parent; margins: 2 }
|
|
||||||
id: row
|
|
||||||
|
|
||||||
Avatar {
|
|
||||||
id: avatar
|
|
||||||
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize / 2
|
|
||||||
Layout.preferredWidth: Nheko.avatarSize / 2
|
|
||||||
Layout.leftMargin: 2
|
|
||||||
userid: model.mxid
|
|
||||||
url: {
|
|
||||||
if (model.isUser)
|
|
||||||
return model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
else if (editingModel.adminLevel >= model.powerlevel)
|
|
||||||
return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?" + palette.buttonText;
|
|
||||||
else if (editingModel.moderatorLevel >= model.powerlevel)
|
|
||||||
return "image://colorimage/:/icons/icons/ui/ribbon.svg?" + palette.buttonText;
|
|
||||||
else
|
|
||||||
return "image://colorimage/:/icons/icons/ui/person.svg?" + palette.buttonText;
|
|
||||||
}
|
|
||||||
displayName: model.displayName
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
Column {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Text { visible: model.isUser; text: model.displayName; color: palette.text}
|
|
||||||
Text { visible: model.isUser; text: model.mxid; color: palette.text}
|
|
||||||
Text {
|
|
||||||
visible: !model.isUser;
|
|
||||||
text: {
|
|
||||||
if (editingModel.adminLevel == model.powerlevel)
|
|
||||||
return qsTr("Administrator (%1)").arg(model.powerlevel)
|
|
||||||
else if (editingModel.moderatorLevel == model.powerlevel)
|
|
||||||
return qsTr("Moderator (%1)").arg(model.powerlevel)
|
|
||||||
else
|
|
||||||
return qsTr("Custom (%1)").arg(model.powerlevel)
|
|
||||||
}
|
|
||||||
color: palette.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
Layout.rightMargin: 2
|
|
||||||
image: model.isUser ? ":/icons/icons/ui/dismiss.svg" : ":/icons/icons/ui/add-square-button.svg"
|
|
||||||
visible: !model.isUser || model.removeable
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: model.isUser ? qsTr("Remove user") : qsTr("Add user")
|
|
||||||
onClicked: {
|
|
||||||
if (model.isUser) {
|
|
||||||
editingModel.users.remove(index);
|
|
||||||
} else {
|
|
||||||
userEntryCompleter.y = offset
|
|
||||||
userEntryCompleter.visible = true
|
|
||||||
userEntryCompleter.index = index;
|
|
||||||
userEntry.forceActiveFocus()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
id: dbb
|
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
|
||||||
onAccepted: {
|
|
||||||
if (editingModel.isSpace) {
|
|
||||||
// TODO(Nico): Replace with showing a list of spaces to apply to
|
|
||||||
editingModel.updateSpacesModel();
|
|
||||||
plEditorW.close();
|
|
||||||
timelineRoot.showSpacePLApplyPrompt(roomSettings, editingModel)
|
|
||||||
} else {
|
|
||||||
editingModel.commit();
|
|
||||||
plEditorW.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onRejected: plEditorW.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,124 +12,23 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: applyDialog
|
id: applyDialog
|
||||||
|
|
||||||
property RoomSettings roomSettings
|
|
||||||
property PowerlevelEditingModels editingModel
|
property PowerlevelEditingModels editingModel
|
||||||
|
property RoomSettings roomSettings
|
||||||
|
|
||||||
minimumWidth: 340
|
|
||||||
minimumHeight: 450
|
|
||||||
width: 450
|
|
||||||
height: 680
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 680
|
||||||
|
minimumHeight: 450
|
||||||
|
minimumWidth: 340
|
||||||
|
modality: Qt.NonModal
|
||||||
title: qsTr("Apply permission changes")
|
title: qsTr("Apply permission changes")
|
||||||
|
width: 450
|
||||||
Shortcut {
|
|
||||||
sequence: StandardKey.Cancel
|
|
||||||
onActivated: roomSettingsDialog.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: Nheko.paddingLarge
|
|
||||||
|
|
||||||
|
|
||||||
MatrixText {
|
|
||||||
text: qsTr("Which of the subcommunities and rooms should these permissions be applied to?")
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
color: palette.text
|
|
||||||
Layout.bottomMargin: Nheko.paddingMedium
|
|
||||||
}
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
columns: 2
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: qsTr("Apply permissions recursively")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: palette.text
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
checked: editingModel.spaces.applyToChildren
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
onCheckedChanged: editingModel.spaces.applyToChildren = checked
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: qsTr("Overwrite exisiting modifications in rooms")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: palette.text
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
checked: editingModel.spaces.overwriteDiverged
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
onCheckedChanged: editingModel.spaces.overwriteDiverged = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
id: view
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
model: editingModel.spaces
|
|
||||||
spacing: 4
|
|
||||||
cacheBuffer: 50
|
|
||||||
|
|
||||||
delegate: RowLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Text {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: model.displayName
|
|
||||||
color: palette.text
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: {
|
|
||||||
if (!model.isEditable) return qsTr("No permissions to apply the new permissions here");
|
|
||||||
if (model.isAlreadyUpToDate) return qsTr("No changes needed");
|
|
||||||
if (model.isDifferentFromBase) return qsTr("Existing modifications to the permissions in this room will be overwritten");
|
|
||||||
return qsTr("Permissions synchronized with community")
|
|
||||||
}
|
|
||||||
elide: Text.ElideRight
|
|
||||||
color: palette.buttonText
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleButton {
|
|
||||||
checked: model.applyPermissions
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
onCheckedChanged: model.applyPermissions = checked
|
|
||||||
enabled: model.isEditable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: dbb
|
id: dbb
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
editingModel.spaces.commit();
|
editingModel.spaces.commit();
|
||||||
applyDialog.close();
|
applyDialog.close();
|
||||||
|
@ -137,4 +36,100 @@ ApplicationWindow {
|
||||||
onRejected: applyDialog.close()
|
onRejected: applyDialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: roomSettingsDialog.close()
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
|
MatrixText {
|
||||||
|
Layout.bottomMargin: Nheko.paddingMedium
|
||||||
|
Layout.fillHeight: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
||||||
|
text: qsTr("Which of the subcommunities and rooms should these permissions be applied to?")
|
||||||
|
}
|
||||||
|
GridLayout {
|
||||||
|
Layout.fillHeight: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
columns: 2
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
text: qsTr("Apply permissions recursively")
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: editingModel.spaces.applyToChildren
|
||||||
|
|
||||||
|
onCheckedChanged: editingModel.spaces.applyToChildren = checked
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
text: qsTr("Overwrite exisiting modifications in rooms")
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: editingModel.spaces.overwriteDiverged
|
||||||
|
|
||||||
|
onCheckedChanged: editingModel.spaces.overwriteDiverged = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListView {
|
||||||
|
id: view
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
cacheBuffer: 50
|
||||||
|
clip: true
|
||||||
|
model: editingModel.spaces
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
delegate: RowLayout {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: model.displayName
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.buttonText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: {
|
||||||
|
if (!model.isEditable)
|
||||||
|
return qsTr("No permissions to apply the new permissions here");
|
||||||
|
if (model.isAlreadyUpToDate)
|
||||||
|
return qsTr("No changes needed");
|
||||||
|
if (model.isDifferentFromBase)
|
||||||
|
return qsTr("Existing modifications to the permissions in this room will be overwritten");
|
||||||
|
return qsTr("Permissions synchronized with community");
|
||||||
|
}
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToggleButton {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: model.applyPermissions
|
||||||
|
enabled: model.isEditable
|
||||||
|
|
||||||
|
onCheckedChanged: model.applyPermissions = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,42 +11,39 @@ ApplicationWindow {
|
||||||
|
|
||||||
property alias rawMessage: rawMessageView.text
|
property alias rawMessage: rawMessageView.text
|
||||||
|
|
||||||
height: 420
|
|
||||||
width: 420
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 420
|
||||||
|
width: 420
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
|
||||||
|
onAccepted: rawMessageRoot.close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: rawMessageRoot.close()
|
onActivated: rawMessageRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
padding: Nheko.paddingMedium
|
padding: Nheko.paddingMedium
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
id: rawMessageView
|
id: rawMessageView
|
||||||
|
|
||||||
font: Nheko.monospaceFont()
|
anchors.fill: parent
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
font: Nheko.monospaceFont()
|
||||||
readOnly: true
|
readOnly: true
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: palette.base
|
color: palette.base
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: rawMessageRoot.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,49 +15,46 @@ ApplicationWindow {
|
||||||
recaptcha.confirm();
|
recaptcha.confirm();
|
||||||
recaptchaRoot.close();
|
recaptchaRoot.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function reject() {
|
function reject() {
|
||||||
recaptcha.cancel();
|
recaptcha.cancel();
|
||||||
recaptchaRoot.close();
|
recaptchaRoot.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
title: recaptcha.context
|
|
||||||
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
height: msg.implicitHeight + footer.implicitHeight
|
height: msg.implicitHeight + footer.implicitHeight
|
||||||
|
title: recaptcha.context
|
||||||
width: Math.max(msg.implicitWidth, footer.implicitWidth)
|
width: Math.max(msg.implicitWidth, footer.implicitWidth)
|
||||||
|
|
||||||
Shortcut {
|
|
||||||
sequence: StandardKey.Cancel
|
|
||||||
onActivated: recaptchaRoot.reject()
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: msg
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
padding: 8
|
|
||||||
text: qsTr("Solve the reCAPTCHA and press the confirm button")
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
onAccepted: recaptchaRoot.accept()
|
onAccepted: recaptchaRoot.accept()
|
||||||
onRejected: recaptchaRoot.reject()
|
onRejected: recaptchaRoot.reject()
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Open reCAPTCHA")
|
text: qsTr("Open reCAPTCHA")
|
||||||
|
|
||||||
onClicked: recaptcha.openReCaptcha()
|
onClicked: recaptcha.openReCaptcha()
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Cancel")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||||
|
text: qsTr("Cancel")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Confirm")
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
text: qsTr("Confirm")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
|
onActivated: recaptchaRoot.reject()
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: msg
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
padding: 8
|
||||||
|
text: qsTr("Solve the reCAPTCHA and press the confirm button")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,18 +14,24 @@ ApplicationWindow {
|
||||||
property ReadReceiptsProxy readReceipts
|
property ReadReceiptsProxy readReceipts
|
||||||
property Room room
|
property Room room
|
||||||
|
|
||||||
height: 380
|
|
||||||
width: 340
|
|
||||||
minimumHeight: 380
|
|
||||||
minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 380
|
||||||
|
minimumHeight: 380
|
||||||
|
minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
|
||||||
|
width: 340
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
|
||||||
|
onAccepted: readReceiptsRoot.close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: readReceiptsRoot.close()
|
onActivated: readReceiptsRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
@ -34,98 +40,84 @@ ApplicationWindow {
|
||||||
Label {
|
Label {
|
||||||
id: headerTitle
|
id: headerTitle
|
||||||
|
|
||||||
color: palette.text
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
text: qsTr("Read receipts")
|
color: palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.5
|
font.pointSize: fontMetrics.font.pointSize * 1.5
|
||||||
|
text: qsTr("Read receipts")
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
padding: Nheko.paddingMedium
|
|
||||||
ScrollBar.horizontal.visible: false
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.minimumHeight: 200
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 200
|
||||||
|
ScrollBar.horizontal.visible: false
|
||||||
|
padding: Nheko.paddingMedium
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: readReceiptsList
|
id: readReceiptsList
|
||||||
|
|
||||||
clip: true
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
model: readReceipts
|
model: readReceipts
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
id: del
|
id: del
|
||||||
|
|
||||||
onClicked: room.openUserProfile(model.mxid)
|
ToolTip.text: model.mxid
|
||||||
padding: Nheko.paddingMedium
|
ToolTip.visible: hovered
|
||||||
width: ListView.view.width
|
|
||||||
height: receiptLayout.implicitHeight + Nheko.paddingSmall * 2
|
height: receiptLayout.implicitHeight + Nheko.paddingSmall * 2
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
padding: Nheko.paddingMedium
|
||||||
ToolTip.text: model.mxid
|
width: ListView.view.width
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: del.hovered ? palette.dark : readReceiptsRoot.color
|
color: del.hovered ? palette.dark : readReceiptsRoot.color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: room.openUserProfile(model.mxid)
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: receiptLayout
|
id: receiptLayout
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingSmall
|
anchors.margins: Nheko.paddingSmall
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
Layout.preferredWidth: Nheko.avatarSize
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize
|
Layout.preferredHeight: Nheko.avatarSize
|
||||||
userid: model.mxid
|
Layout.preferredWidth: Nheko.avatarSize
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: model.displayName
|
displayName: model.displayName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: model.mxid
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
fullText: model.displayName
|
Layout.fillWidth: true
|
||||||
color: TimelineManager.userColor(model ? model.mxid : "", palette.window)
|
color: TimelineManager.userColor(model ? model.mxid : "", palette.window)
|
||||||
|
elideWidth: del.width - Nheko.paddingMedium - avatar.width
|
||||||
font.pointSize: fontMetrics.font.pointSize
|
font.pointSize: fontMetrics.font.pointSize
|
||||||
elideWidth: del.width - Nheko.paddingMedium - avatar.width
|
fullText: model.displayName
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
fullText: model.timestamp
|
|
||||||
color: palette.buttonText
|
|
||||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
|
||||||
elideWidth: del.width - Nheko.paddingMedium - avatar.width
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: palette.buttonText
|
||||||
|
elideWidth: del.width - Nheko.paddingMedium - avatar.width
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
fullText: model.timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: readReceiptsRoot.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,71 +10,66 @@ import im.nheko
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
required property string eventId
|
required property string eventId
|
||||||
|
|
||||||
width: 400
|
|
||||||
height: gl.implicitHeight + 2 * Nheko.paddingMedium
|
height: gl.implicitHeight + 2 * Nheko.paddingMedium
|
||||||
title: qsTr("Report message")
|
title: qsTr("Report message")
|
||||||
|
width: 400
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: gl
|
id: gl
|
||||||
|
|
||||||
columnSpacing: Nheko.paddingMedium
|
|
||||||
rowSpacing: Nheko.paddingMedium
|
|
||||||
columns: 2
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
columnSpacing: Nheko.paddingMedium
|
||||||
|
columns: 2
|
||||||
|
rowSpacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Label.WordWrap
|
|
||||||
text: qsTr("This message you are reporting will be sent to your server administrator for review. Please note that not all server administrators review reported content. You should also ask a room moderator to remove the content if necessary.")
|
text: qsTr("This message you are reporting will be sent to your server administrator for review. Please note that not all server administrators review reported content. You should also ask a room moderator to remove the content if necessary.")
|
||||||
|
wrapMode: Label.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Enter your reason for reporting:")
|
text: qsTr("Enter your reason for reporting:")
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: reason
|
id: reason
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("How bad is the message?")
|
text: qsTr("How bad is the message?")
|
||||||
}
|
}
|
||||||
|
|
||||||
Slider {
|
Slider {
|
||||||
id: score
|
id: score
|
||||||
|
|
||||||
from: 0
|
|
||||||
to: -100
|
|
||||||
stepSize: 25
|
|
||||||
snapMode: Slider.SnapAlways
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
from: 0
|
||||||
|
snapMode: Slider.SnapAlways
|
||||||
|
stepSize: 25
|
||||||
|
to: -100
|
||||||
|
}
|
||||||
|
Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {}
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: {
|
text: {
|
||||||
if (score.value === 0)
|
if (score.value === 0)
|
||||||
return qsTr("Not bad")
|
return qsTr("Not bad");
|
||||||
else if (score.value === -25)
|
else if (score.value === -25)
|
||||||
return qsTr("Mild")
|
return qsTr("Mild");
|
||||||
else if (score.value === -50)
|
else if (score.value === -50)
|
||||||
return qsTr("Bad")
|
return qsTr("Bad");
|
||||||
else if (score.value === -75)
|
else if (score.value === -75)
|
||||||
return qsTr("Serious")
|
return qsTr("Serious");
|
||||||
else if (score.value === -100)
|
else if (score.value === -100)
|
||||||
return qsTr("Extremely serious")
|
return qsTr("Extremely serious");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogButtonBox {
|
DialogButtonBox {
|
||||||
Layout.columnSpan: 2
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
Layout.columnSpan: 2
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
room.reportEvent(eventId, reason.text, score.value);
|
room.reportEvent(eventId, reason.text, score.value);
|
||||||
close();
|
close();
|
||||||
|
|
|
@ -13,21 +13,72 @@ import im.nheko 1.0
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: roomDirectoryWindow
|
id: roomDirectoryWindow
|
||||||
|
|
||||||
visible: true
|
|
||||||
minimumWidth: 340
|
|
||||||
minimumHeight: 340
|
|
||||||
height: 420
|
|
||||||
width: 650
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 420
|
||||||
|
minimumHeight: 340
|
||||||
|
minimumWidth: 340
|
||||||
|
modality: Qt.NonModal
|
||||||
title: qsTr("Explore Public Rooms")
|
title: qsTr("Explore Public Rooms")
|
||||||
|
visible: true
|
||||||
|
width: 650
|
||||||
|
|
||||||
|
footer: RowLayout {
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
text: qsTr("Close")
|
||||||
|
|
||||||
|
onClicked: roomDirectoryWindow.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header: RowLayout {
|
||||||
|
id: searchBarLayout
|
||||||
|
|
||||||
|
implicitHeight: roomSearch.height
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
MatrixTextField {
|
||||||
|
id: roomSearch
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
focus: true
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize
|
||||||
|
placeholderText: qsTr("Search for public rooms")
|
||||||
|
selectByMouse: true
|
||||||
|
|
||||||
|
Component.onCompleted: forceActiveFocus()
|
||||||
|
onTextChanged: searchTimer.restart()
|
||||||
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: chooseServer
|
||||||
|
|
||||||
|
Layout.maximumWidth: 0.3 * header.width
|
||||||
|
Layout.minimumWidth: 0.3 * header.width
|
||||||
|
color: palette.text
|
||||||
|
placeholderText: qsTr("Choose custom homeserver")
|
||||||
|
|
||||||
|
onTextChanged: publicRooms.setMatrixServer(text)
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
id: searchTimer
|
||||||
|
|
||||||
|
interval: 350
|
||||||
|
|
||||||
|
onTriggered: roomDirView.model.setSearchTerm(roomSearch.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: roomDirectoryWindow.close()
|
onActivated: roomDirectoryWindow.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: roomDirView
|
id: roomDirView
|
||||||
|
|
||||||
|
@ -37,171 +88,108 @@ ApplicationWindow {
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
id: roomDirDelegate
|
id: roomDirDelegate
|
||||||
|
|
||||||
|
property int avatarSize: fontMetrics.height * 3.2
|
||||||
property color background: palette.window
|
property color background: palette.window
|
||||||
property color importantText: palette.text
|
property color importantText: palette.text
|
||||||
property color unimportantText: palette.buttonText
|
property color unimportantText: palette.buttonText
|
||||||
property int avatarSize: fontMetrics.height * 3.2
|
|
||||||
|
|
||||||
color: background
|
color: background
|
||||||
height: avatarSize + Nheko.paddingLarge
|
height: avatarSize + Nheko.paddingLarge
|
||||||
width: ListView.view.width
|
width: ListView.view.width
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
implicitHeight: textContent.implicitHeight
|
implicitHeight: textContent.implicitHeight
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: roomAvatar
|
id: roomAvatar
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.rightMargin: Nheko.paddingMedium
|
|
||||||
Layout.preferredWidth: roomDirDelegate.avatarSize
|
|
||||||
Layout.preferredHeight: roomDirDelegate.avatarSize
|
Layout.preferredHeight: roomDirDelegate.avatarSize
|
||||||
|
Layout.preferredWidth: roomDirDelegate.avatarSize
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
Layout.rightMargin: Nheko.paddingMedium
|
||||||
roomid: model.roomid
|
|
||||||
displayName: model.name
|
displayName: model.name
|
||||||
|
roomid: model.roomid
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: textContent
|
id: textContent
|
||||||
rows: 2
|
|
||||||
columns: 2
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.preferredWidth: parent.width - roomAvatar.width
|
Layout.preferredWidth: parent.width - roomAvatar.width
|
||||||
|
columns: 2
|
||||||
|
rows: 2
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
Layout.row: 0
|
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.row: 0
|
||||||
color: roomDirDelegate.importantText
|
color: roomDirDelegate.importantText
|
||||||
elideWidth: width
|
elideWidth: width
|
||||||
fullText: model.name
|
fullText: model.name
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: roomTopic
|
id: roomTopic
|
||||||
|
|
||||||
color: roomDirDelegate.unimportantText
|
|
||||||
Layout.row: 1
|
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
font.pointSize: fontMetrics.font.pointSize*0.9
|
|
||||||
elide: Text.ElideRight
|
|
||||||
maximumLineCount: 2
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.row: 1
|
||||||
|
color: roomDirDelegate.unimportantText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
maximumLineCount: 2
|
||||||
text: model.topic
|
text: model.topic
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.row: 0
|
|
||||||
Layout.column: 1
|
|
||||||
id: roomCount
|
id: roomCount
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.row: 0
|
||||||
color: roomDirDelegate.unimportantText
|
color: roomDirDelegate.unimportantText
|
||||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
text: model.numMembers.toString()
|
text: model.numMembers.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.row: 1
|
|
||||||
Layout.column: 1
|
|
||||||
id: joinRoomButton
|
id: joinRoomButton
|
||||||
|
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.row: 1
|
||||||
enabled: model.roomid !== ""
|
enabled: model.roomid !== ""
|
||||||
text: model.canJoin ? qsTr("Join") : qsTr("Open")
|
text: model.canJoin ? qsTr("Join") : qsTr("Open")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model.canJoin)
|
if (model.canJoin)
|
||||||
publicRooms.joinRoom(model.index);
|
publicRooms.joinRoom(model.index);
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
Rooms.setCurrentRoom(model.roomid);
|
Rooms.setCurrentRoom(model.roomid);
|
||||||
roomDirectoryWindow.close();
|
roomDirectoryWindow.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: Item {
|
footer: Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: parent.width
|
anchors.margins: Nheko.paddingLarge
|
||||||
visible: !publicRooms.reachedEndOfPagination && publicRooms.loadingMoreRooms
|
|
||||||
// hacky but works
|
// hacky but works
|
||||||
height: loadingSpinner.height + 2 * Nheko.paddingLarge
|
height: loadingSpinner.height + 2 * Nheko.paddingLarge
|
||||||
anchors.margins: Nheko.paddingLarge
|
visible: !publicRooms.reachedEndOfPagination && publicRooms.loadingMoreRooms
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
id: loadingSpinner
|
id: loadingSpinner
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
running: visible
|
|
||||||
foreground: palette.mid
|
foreground: palette.mid
|
||||||
}
|
running: visible
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
header: RowLayout {
|
|
||||||
id: searchBarLayout
|
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
width: parent.width
|
|
||||||
implicitHeight: roomSearch.height
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: roomSearch
|
|
||||||
|
|
||||||
focus: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
selectByMouse: true
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize
|
|
||||||
color: palette.text
|
|
||||||
placeholderText: qsTr("Search for public rooms")
|
|
||||||
onTextChanged: searchTimer.restart()
|
|
||||||
|
|
||||||
Component.onCompleted: forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
id: chooseServer
|
|
||||||
|
|
||||||
Layout.minimumWidth: 0.3 * header.width
|
|
||||||
Layout.maximumWidth: 0.3 * header.width
|
|
||||||
color: palette.text
|
|
||||||
placeholderText: qsTr("Choose custom homeserver")
|
|
||||||
onTextChanged: publicRooms.setMatrixServer(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: searchTimer
|
|
||||||
|
|
||||||
interval: 350
|
|
||||||
onTriggered: roomDirView.model.setSearchTerm(roomSearch.text)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
footer: RowLayout {
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Close")
|
|
||||||
onClicked: roomDirectoryWindow.close()
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,18 +17,24 @@ ApplicationWindow {
|
||||||
property MemberList members
|
property MemberList members
|
||||||
property Room room
|
property Room room
|
||||||
|
|
||||||
title: qsTr("Members of %1").arg(members.roomName)
|
|
||||||
height: 650
|
|
||||||
width: 420
|
|
||||||
minimumHeight: 420
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 650
|
||||||
|
minimumHeight: 420
|
||||||
|
title: qsTr("Members of %1").arg(members.roomName)
|
||||||
|
width: 420
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
|
||||||
|
onAccepted: roomMembersRoot.close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: roomMembersRoot.close()
|
onActivated: roomMembersRoot.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
@ -37,148 +43,146 @@ ApplicationWindow {
|
||||||
Avatar {
|
Avatar {
|
||||||
id: roomAvatar
|
id: roomAvatar
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredHeight: 130
|
Layout.preferredHeight: 130
|
||||||
Layout.preferredWidth: 130
|
Layout.preferredWidth: 130
|
||||||
|
|
||||||
roomid: members.roomId
|
|
||||||
displayName: members.roomName
|
displayName: members.roomName
|
||||||
Layout.alignment: Qt.AlignHCenter
|
roomid: members.roomId
|
||||||
url: members.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: members.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
|
||||||
onClicked: TimelineManager.openRoomSettings(members.roomId)
|
onClicked: TimelineManager.openRoomSettings(members.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 2
|
|
||||||
fullText: qsTr("%n people in %1", "Summary above list of members", members.memberCount).arg(members.roomName)
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
elideWidth: parent.width - Nheko.paddingMedium
|
elideWidth: parent.width - Nheko.paddingMedium
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 2
|
||||||
|
fullText: qsTr("%n people in %1", "Summary above list of members", members.memberCount).arg(members.roomName)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
image: ":/icons/icons/ui/add-square-button.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Invite more people")
|
ToolTip.text: qsTr("Invite more people")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/add-square-button.svg"
|
||||||
|
|
||||||
onClicked: TimelineManager.openInviteUsers(members.roomId)
|
onClicked: TimelineManager.openInviteUsers(members.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: searchBar
|
id: searchBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
placeholderText: qsTr("Search...")
|
placeholderText: qsTr("Search...")
|
||||||
onTextChanged: members.setFilterString(text)
|
|
||||||
|
|
||||||
Component.onCompleted: forceActiveFocus()
|
Component.onCompleted: forceActiveFocus()
|
||||||
|
onTextChanged: members.setFilterString(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Sort by: ")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Sort by: ")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
model: ListModel {
|
Layout.fillWidth: true
|
||||||
ListElement { data: MemberList.Mxid; text: qsTr("User ID") }
|
|
||||||
ListElement { data: MemberList.DisplayName; text: qsTr("Display name") }
|
|
||||||
ListElement { data: MemberList.Powerlevel; text: qsTr("Power level") }
|
|
||||||
}
|
|
||||||
textRole: "text"
|
textRole: "text"
|
||||||
valueRole: "data"
|
valueRole: "data"
|
||||||
onCurrentValueChanged: members.sortBy(currentValue)
|
|
||||||
Layout.fillWidth: true
|
model: ListModel {
|
||||||
|
ListElement {
|
||||||
|
data: MemberList.Mxid
|
||||||
|
text: qsTr("User ID")
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
data: MemberList.DisplayName
|
||||||
|
text: qsTr("Display name")
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
data: MemberList.Powerlevel
|
||||||
|
text: qsTr("Power level")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCurrentValueChanged: members.sortBy(currentValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
ScrollView {
|
ScrollView {
|
||||||
padding: Nheko.paddingMedium
|
|
||||||
ScrollBar.horizontal.visible: false
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.minimumHeight: 200
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 200
|
||||||
|
ScrollBar.horizontal.visible: false
|
||||||
|
padding: Nheko.paddingMedium
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: memberList
|
id: memberList
|
||||||
|
|
||||||
clip: true
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
model: members
|
model: members
|
||||||
|
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
id: del
|
id: del
|
||||||
|
|
||||||
onClicked: room.openUserProfile(model.mxid)
|
|
||||||
padding: Nheko.paddingMedium
|
|
||||||
width: ListView.view.width
|
|
||||||
height: memberLayout.implicitHeight + Nheko.paddingSmall * 2
|
height: memberLayout.implicitHeight + Nheko.paddingSmall * 2
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
padding: Nheko.paddingMedium
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: del.hovered ? palette.dark : roomMembersRoot.color
|
color: del.hovered ? palette.dark : roomMembersRoot.color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: room.openUserProfile(model.mxid)
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: memberLayout
|
id: memberLayout
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
width: parent.width - Nheko.paddingSmall * 2
|
width: parent.width - Nheko.paddingSmall * 2
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
Layout.preferredWidth: Nheko.avatarSize
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize
|
Layout.preferredHeight: Nheko.avatarSize
|
||||||
userid: model.mxid
|
Layout.preferredWidth: Nheko.avatarSize
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
displayName: model.displayName
|
displayName: model.displayName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: model.mxid
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
fullText: model.displayName
|
Layout.fillWidth: true
|
||||||
color: TimelineManager.userColor(model ? model.mxid : "", del.background.color)
|
color: TimelineManager.userColor(model ? model.mxid : "", del.background.color)
|
||||||
|
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
||||||
font.pixelSize: fontMetrics.font.pixelSize
|
font.pixelSize: fontMetrics.font.pixelSize
|
||||||
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
fullText: model.displayName
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
fullText: model.mxid
|
|
||||||
color: del.hovered ? palette.brightText : palette.buttonText
|
|
||||||
font.pixelSize: Math.ceil(fontMetrics.font.pixelSize * 0.9)
|
|
||||||
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: del.hovered ? palette.brightText : palette.buttonText
|
||||||
|
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
|
||||||
|
font.pixelSize: Math.ceil(fontMetrics.font.pixelSize * 0.9)
|
||||||
|
fullText: model.mxid
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerlevelIndicator {
|
PowerlevelIndicator {
|
||||||
powerlevel: model.powerlevel
|
|
||||||
permissions: room.permissions
|
permissions: room.permissions
|
||||||
|
powerlevel: model.powerlevel
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionIndicator {
|
EncryptionIndicator {
|
||||||
id: encryptInd
|
id: encryptInd
|
||||||
|
|
||||||
Layout.preferredWidth: 16
|
|
||||||
Layout.preferredHeight: 16
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
visible: room.isEncrypted
|
Layout.preferredHeight: 16
|
||||||
encrypted: room.isEncrypted
|
Layout.preferredWidth: 16
|
||||||
trust: encrypted ? model.trustlevel : Crypto.Unverified
|
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
if (!encrypted)
|
if (!encrypted)
|
||||||
return qsTr("This room is not encrypted!");
|
return qsTr("This room is not encrypted!");
|
||||||
|
|
||||||
switch (trust) {
|
switch (trust) {
|
||||||
case Crypto.Verified:
|
case Crypto.Verified:
|
||||||
return qsTr("This user is verified.");
|
return qsTr("This user is verified.");
|
||||||
|
@ -188,23 +192,22 @@ ApplicationWindow {
|
||||||
return qsTr("This user has unverified devices!");
|
return qsTr("This user has unverified devices!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
encrypted: room.isEncrypted
|
||||||
|
trust: encrypted ? model.trustlevel : Crypto.Unverified
|
||||||
|
visible: room.isEncrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: Item {
|
footer: Item {
|
||||||
width: parent.width
|
anchors.margins: Nheko.paddingMedium
|
||||||
visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
|
|
||||||
// use the default height if it's visible, otherwise no height at all
|
// use the default height if it's visible, otherwise no height at all
|
||||||
height: membersLoadingSpinner.implicitHeight
|
height: membersLoadingSpinner.implicitHeight
|
||||||
anchors.margins: Nheko.paddingMedium
|
visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
id: membersLoadingSpinner
|
id: membersLoadingSpinner
|
||||||
|
@ -212,18 +215,8 @@ ApplicationWindow {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
implicitHeight: parent.visible ? 35 : 0
|
implicitHeight: parent.visible ? 35 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: roomMembersRoot.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,80 +16,87 @@ ApplicationWindow {
|
||||||
|
|
||||||
property var roomSettings
|
property var roomSettings
|
||||||
|
|
||||||
minimumWidth: 340
|
|
||||||
minimumHeight: 450
|
|
||||||
width: 450
|
|
||||||
height: 680
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 680
|
||||||
|
minimumHeight: 450
|
||||||
|
minimumWidth: 340
|
||||||
|
modality: Qt.NonModal
|
||||||
title: qsTr("Room Settings")
|
title: qsTr("Room Settings")
|
||||||
|
width: 450
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
|
||||||
|
onAccepted: close()
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: roomSettingsDialog.close()
|
onActivated: roomSettingsDialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
id: flickable
|
id: flickable
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
clip: true
|
clip: true
|
||||||
flickableDirection: Flickable.VerticalFlick
|
|
||||||
contentWidth: roomSettingsDialog.width
|
|
||||||
contentHeight: contentLayout1.height
|
contentHeight: contentLayout1.height
|
||||||
|
contentWidth: roomSettingsDialog.width
|
||||||
|
flickableDirection: Flickable.VerticalFlick
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: contentLayout1
|
id: contentLayout1
|
||||||
width: parent.width
|
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: displayAvatar
|
id: displayAvatar
|
||||||
|
|
||||||
Layout.topMargin: Nheko.paddingMedium
|
Layout.alignment: Qt.AlignHCenter
|
||||||
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
roomid: roomSettings.roomId
|
|
||||||
displayName: roomSettings.roomName
|
|
||||||
Layout.preferredHeight: 130
|
Layout.preferredHeight: 130
|
||||||
Layout.preferredWidth: 130
|
Layout.preferredWidth: 130
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.topMargin: Nheko.paddingMedium
|
||||||
|
displayName: roomSettings.roomName
|
||||||
|
roomid: roomSettings.roomId
|
||||||
|
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
|
||||||
onClicked: TimelineManager.openImageOverlay(null, roomSettings.roomAvatarUrl, "", 0, 0)
|
onClicked: TimelineManager.openImageOverlay(null, roomSettings.roomAvatarUrl, "", 0, 0)
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change room avatar.")
|
ToolTip.text: qsTr("Change room avatar.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: displayAvatar.left
|
anchors.left: displayAvatar.left
|
||||||
anchors.top: displayAvatar.top
|
|
||||||
anchors.leftMargin: Nheko.paddingMedium
|
anchors.leftMargin: Nheko.paddingMedium
|
||||||
|
anchors.top: displayAvatar.top
|
||||||
anchors.topMargin: Nheko.paddingMedium
|
anchors.topMargin: Nheko.paddingMedium
|
||||||
visible: roomSettings.canChangeAvatar
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/edit.svg"
|
image: ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: roomSettings.canChangeAvatar
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
roomSettings.updateAvatar();
|
roomSettings.updateAvatar();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
visible: roomSettings.isLoading
|
|
||||||
foreground: palette.mid
|
foreground: palette.mid
|
||||||
running: roomSettings.isLoading
|
running: roomSettings.isLoading
|
||||||
|
visible: roomSettings.isLoading
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: errorText
|
id: errorText
|
||||||
|
|
||||||
color: "red"
|
|
||||||
visible: opacity > 0
|
|
||||||
opacity: 0
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
wrapMode: Text.Wrap // somehow still doesn't wrap
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: "red"
|
||||||
|
opacity: 0
|
||||||
|
visible: opacity > 0
|
||||||
|
wrapMode: Text.Wrap // somehow still doesn't wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
id: hideErrorAnimation
|
id: hideErrorAnimation
|
||||||
|
|
||||||
|
@ -98,43 +105,38 @@ ApplicationWindow {
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 4000
|
duration: 4000
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: errorText
|
|
||||||
property: 'opacity'
|
|
||||||
to: 0
|
|
||||||
duration: 1000
|
duration: 1000
|
||||||
|
property: 'opacity'
|
||||||
|
target: errorText
|
||||||
|
to: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: roomSettings
|
|
||||||
function onDisplayError(errorMessage) {
|
function onDisplayError(errorMessage) {
|
||||||
errorText.text = errorMessage;
|
errorText.text = errorMessage;
|
||||||
errorText.opacity = 1;
|
errorText.opacity = 1;
|
||||||
hideErrorAnimation.restart();
|
hideErrorAnimation.restart();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
target: roomSettings
|
||||||
|
}
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: roomName
|
id: roomName
|
||||||
|
|
||||||
property bool isNameEditingAllowed: false
|
property bool isNameEditingAllowed: false
|
||||||
|
|
||||||
readOnly: !isNameEditingAllowed
|
|
||||||
textFormat: isNameEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
|
|
||||||
text: isNameEditingAllowed ? roomSettings.plainRoomName : roomSettings.roomName
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 2
|
|
||||||
color: palette.text
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - nameChangeButton.anchors.leftMargin - (nameChangeButton.width * 2)
|
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - nameChangeButton.anchors.leftMargin - (nameChangeButton.width * 2)
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 2
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
wrapMode: TextEdit.Wrap
|
readOnly: !isNameEditingAllowed
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
text: isNameEditingAllowed ? roomSettings.plainRoomName : roomSettings.roomName
|
||||||
|
textFormat: isNameEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
|
||||||
Keys.onShortcutOverride: event.key === Qt.Key_Enter
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.matches(StandardKey.InsertLineSeparator) || event.matches(StandardKey.InsertParagraphSeparator)) {
|
if (event.matches(StandardKey.InsertLineSeparator) || event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||||
roomSettings.changeName(roomName.text);
|
roomSettings.changeName(roomName.text);
|
||||||
|
@ -142,18 +144,21 @@ ApplicationWindow {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Keys.onShortcutOverride: event.key === Qt.Key_Enter
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: nameChangeButton
|
id: nameChangeButton
|
||||||
visible: roomSettings.canChangeName
|
|
||||||
anchors.leftMargin: Nheko.paddingSmall
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Change name of this room")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: roomName.right
|
anchors.left: roomName.right
|
||||||
|
anchors.leftMargin: Nheko.paddingSmall
|
||||||
anchors.verticalCenter: roomName.verticalCenter
|
anchors.verticalCenter: roomName.verticalCenter
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change name of this room")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
image: roomName.isNameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
image: roomName.isNameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: roomSettings.canChangeName
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (roomName.isNameEditingAllowed) {
|
if (roomName.isNameEditingAllowed) {
|
||||||
roomSettings.changeName(roomName.text);
|
roomSettings.changeName(roomName.text);
|
||||||
|
@ -165,69 +170,64 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("%n member(s)", "", roomSettings.memberCount)
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("%n member(s)", "", roomSettings.memberCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/people.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("View members of %1").arg(roomSettings.roomName)
|
ToolTip.text: qsTr("View members of %1").arg(roomSettings.roomName)
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/people.svg"
|
||||||
|
|
||||||
onClicked: TimelineManager.openRoomMembers(Rooms.getRoomById(roomSettings.roomId))
|
onClicked: TimelineManager.openRoomMembers(Rooms.getRoomById(roomSettings.roomId))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
id: roomTopic
|
id: roomTopic
|
||||||
|
|
||||||
property bool cut: implicitHeight > 100
|
property bool cut: implicitHeight > 100
|
||||||
|
property bool isTopicEditingAllowed: false
|
||||||
property bool showMore: false
|
property bool showMore: false
|
||||||
clip: true
|
|
||||||
Layout.maximumHeight: showMore? Number.POSITIVE_INFINITY : 100
|
|
||||||
Layout.preferredHeight: implicitHeight
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: Nheko.paddingLarge
|
Layout.leftMargin: Nheko.paddingLarge
|
||||||
|
Layout.maximumHeight: showMore ? Number.POSITIVE_INFINITY : 100
|
||||||
|
Layout.preferredHeight: implicitHeight
|
||||||
Layout.rightMargin: Nheko.paddingLarge
|
Layout.rightMargin: Nheko.paddingLarge
|
||||||
|
|
||||||
property bool isTopicEditingAllowed: false
|
|
||||||
|
|
||||||
readOnly: !isTopicEditingAllowed
|
|
||||||
textFormat: isTopicEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
|
|
||||||
text: isTopicEditingAllowed
|
|
||||||
? roomSettings.plainRoomTopic
|
|
||||||
: (roomSettings.plainRoomTopic === "" ? ("<i>" + qsTr("No topic set") + "</i>") : roomSettings.roomTopic)
|
|
||||||
wrapMode: TextEdit.WordWrap
|
|
||||||
background: null
|
background: null
|
||||||
|
clip: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: !isTopicEditingAllowed
|
||||||
|
text: isTopicEditingAllowed ? roomSettings.plainRoomTopic : (roomSettings.plainRoomTopic === "" ? ("<i>" + qsTr("No topic set") + "</i>") : roomSettings.roomTopic)
|
||||||
|
textFormat: isTopicEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.WordWrap
|
||||||
|
|
||||||
onLinkActivated: Nheko.openLink(link)
|
onLinkActivated: Nheko.openLink(link)
|
||||||
|
|
||||||
NhekoCursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: topicChangeButton
|
id: topicChangeButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
visible: roomSettings.canChangeTopic
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change topic of this room")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Change topic of this room")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
image: roomTopic.isTopicEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
image: roomTopic.isTopicEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: roomSettings.canChangeTopic
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (roomTopic.isTopicEditingAllowed) {
|
if (roomTopic.isTopicEditingAllowed) {
|
||||||
roomSettings.changeTopic(roomTopic.text);
|
roomSettings.changeTopic(roomTopic.text);
|
||||||
|
@ -240,234 +240,219 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
id: showMorePlaceholder
|
id: showMorePlaceholder
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredHeight: showMoreButton.height
|
Layout.preferredHeight: showMoreButton.height
|
||||||
Layout.preferredWidth: showMoreButton.width
|
Layout.preferredWidth: showMoreButton.width
|
||||||
visible: roomTopic.cut
|
visible: roomTopic.cut
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: Nheko.paddingMedium
|
rowSpacing: Nheko.paddingMedium
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("NOTIFICATIONS")
|
|
||||||
font.bold: true
|
|
||||||
color: palette.text
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: Nheko.paddingLarge
|
Layout.topMargin: Nheko.paddingLarge
|
||||||
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("NOTIFICATIONS")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Notifications")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Notifications")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")]
|
Layout.fillWidth: true
|
||||||
currentIndex: roomSettings.notifications
|
currentIndex: roomSettings.notifications
|
||||||
|
model: [qsTr("Muted"), qsTr("Mentions only"), qsTr("All messages")]
|
||||||
|
|
||||||
onActivated: {
|
onActivated: {
|
||||||
roomSettings.changeNotifications(index);
|
roomSettings.changeNotifications(index);
|
||||||
}
|
}
|
||||||
Layout.fillWidth: true
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
|
||||||
}
|
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
} // suppress scrolling changing values
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("ENTRY PERMISSIONS")
|
|
||||||
font.bold: true
|
|
||||||
color: palette.text
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: Nheko.paddingLarge
|
Layout.topMargin: Nheko.paddingLarge
|
||||||
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("ENTRY PERMISSIONS")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Anyone can join")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Anyone can join")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: publicRoomButton
|
id: publicRoomButton
|
||||||
|
|
||||||
enabled: roomSettings.canChangeJoinRules
|
|
||||||
checked: !roomSettings.privateAccess
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: !roomSettings.privateAccess
|
||||||
|
enabled: roomSettings.canChangeJoinRules
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Allow knocking")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Allow knocking")
|
||||||
visible: knockingButton.visible
|
visible: knockingButton.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: knockingButton
|
id: knockingButton
|
||||||
|
|
||||||
visible: !publicRoomButton.checked
|
|
||||||
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsKnocking
|
|
||||||
checked: roomSettings.knockingEnabled
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (checked && !roomSettings.supportsKnockRestricted) restrictedButton.checked = false;
|
|
||||||
}
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
checked: roomSettings.knockingEnabled
|
||||||
|
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsKnocking
|
||||||
|
visible: !publicRoomButton.checked
|
||||||
|
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (checked && !roomSettings.supportsKnockRestricted)
|
||||||
|
restrictedButton.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Allow joining via other rooms")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Allow joining via other rooms")
|
||||||
visible: restrictedButton.visible
|
visible: restrictedButton.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: restrictedButton
|
id: restrictedButton
|
||||||
|
|
||||||
visible: !publicRoomButton.checked
|
|
||||||
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsRestricted
|
|
||||||
checked: roomSettings.restrictedEnabled
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (checked && !roomSettings.supportsKnockRestricted) knockingButton.checked = false;
|
|
||||||
}
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
checked: roomSettings.restrictedEnabled
|
||||||
|
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsRestricted
|
||||||
|
visible: !publicRoomButton.checked
|
||||||
|
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (checked && !roomSettings.supportsKnockRestricted)
|
||||||
|
knockingButton.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Rooms to join via")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Rooms to join via")
|
||||||
visible: allowedRoomsButton.visible
|
visible: allowedRoomsButton.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: allowedRoomsButton
|
id: allowedRoomsButton
|
||||||
|
|
||||||
visible: restrictedButton.checked && restrictedButton.visible
|
|
||||||
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsRestricted
|
|
||||||
|
|
||||||
text: qsTr("Change")
|
|
||||||
ToolTip.text: qsTr("Change the list of rooms users can join this room via. Usually this is the official community of this room.")
|
|
||||||
onClicked: timelineRoot.showAllowedRoomsEditor(roomSettings)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
ToolTip.text: qsTr("Change the list of rooms users can join this room via. Usually this is the official community of this room.")
|
||||||
|
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsRestricted
|
||||||
|
text: qsTr("Change")
|
||||||
|
visible: restrictedButton.checked && restrictedButton.visible
|
||||||
|
|
||||||
|
onClicked: timelineRoot.showAllowedRoomsEditor(roomSettings)
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Allow guests to join")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Allow guests to join")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: guestAccessButton
|
id: guestAccessButton
|
||||||
|
|
||||||
enabled: roomSettings.canChangeJoinRules
|
|
||||||
checked: roomSettings.guestAccess
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
checked: roomSettings.guestAccess
|
||||||
|
|
||||||
Button {
|
|
||||||
visible: publicRoomButton.checked == roomSettings.privateAccess || knockingButton.checked != roomSettings.knockingEnabled || restrictedButton.checked != roomSettings.restrictedEnabled || guestAccessButton.checked != roomSettings.guestAccess || roomSettings.allowedRoomsModified
|
|
||||||
enabled: roomSettings.canChangeJoinRules
|
enabled: roomSettings.canChangeJoinRules
|
||||||
|
}
|
||||||
text: qsTr("Apply access rules")
|
Button {
|
||||||
onClicked: roomSettings.changeAccessRules(!publicRoomButton.checked, guestAccessButton.checked, knockingButton.checked, restrictedButton.checked)
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
enabled: roomSettings.canChangeJoinRules
|
||||||
|
text: qsTr("Apply access rules")
|
||||||
|
visible: publicRoomButton.checked == roomSettings.privateAccess || knockingButton.checked != roomSettings.knockingEnabled || restrictedButton.checked != roomSettings.restrictedEnabled || guestAccessButton.checked != roomSettings.guestAccess || roomSettings.allowedRoomsModified
|
||||||
|
|
||||||
|
onClicked: roomSettings.changeAccessRules(!publicRoomButton.checked, guestAccessButton.checked, knockingButton.checked, restrictedButton.checked)
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("MESSAGE VISIBILITY")
|
|
||||||
font.bold: true
|
|
||||||
color: palette.text
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: Nheko.paddingLarge
|
Layout.topMargin: Nheko.paddingLarge
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: qsTr("Allow viewing history without joining")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("MESSAGE VISIBILITY")
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("This is useful to see previews of the room or view it on public websites.")
|
ToolTip.text: qsTr("This is useful to see previews of the room or view it on public websites.")
|
||||||
ToolTip.visible: publicHistoryHover.hovered
|
ToolTip.visible: publicHistoryHover.hovered
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
color: palette.text
|
||||||
|
text: qsTr("Allow viewing history without joining")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: publicHistoryHover
|
id: publicHistoryHover
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: publicHistoryButton
|
id: publicHistoryButton
|
||||||
|
|
||||||
enabled: roomSettings.canChangeHistoryVisibility
|
|
||||||
checked: roomSettings.historyVisibility == RoomSettings.WorldReadable
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: roomSettings.historyVisibility == RoomSettings.WorldReadable
|
||||||
|
enabled: roomSettings.canChangeHistoryVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
visible: !publicHistoryButton.checked
|
|
||||||
text: qsTr("Members can see messages since")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: palette.text
|
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("How much of the history is visible to joined members. Changing this won't affect the visibility of already sent messages. It only applies to new messages.")
|
ToolTip.text: qsTr("How much of the history is visible to joined members. Changing this won't affect the visibility of already sent messages. It only applies to new messages.")
|
||||||
ToolTip.visible: privateHistoryHover.hovered
|
ToolTip.visible: privateHistoryHover.hovered
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
color: palette.text
|
||||||
|
text: qsTr("Members can see messages since")
|
||||||
|
visible: !publicHistoryButton.checked
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: privateHistoryHover
|
id: privateHistoryHover
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
|
||||||
visible: !publicHistoryButton.checked
|
|
||||||
enabled: roomSettings.canChangeHistoryVisibility
|
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
enabled: roomSettings.canChangeHistoryVisibility
|
||||||
|
visible: !publicHistoryButton.checked
|
||||||
|
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: sharedHistory
|
id: sharedHistory
|
||||||
checked: roomSettings.historyVisibility == RoomSettings.Shared
|
|
||||||
text: qsTr("Everything")
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("As long as the user joined, they can see all previous messages.")
|
ToolTip.text: qsTr("As long as the user joined, they can see all previous messages.")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
checked: roomSettings.historyVisibility == RoomSettings.Shared
|
||||||
|
text: qsTr("Everything")
|
||||||
}
|
}
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: invitedHistory
|
id: invitedHistory
|
||||||
checked: roomSettings.historyVisibility == RoomSettings.Invited
|
|
||||||
text: qsTr("They got invited")
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Members can only see messages from when they got invited going forward.")
|
ToolTip.text: qsTr("Members can only see messages from when they got invited going forward.")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
checked: roomSettings.historyVisibility == RoomSettings.Invited
|
||||||
|
text: qsTr("They got invited")
|
||||||
}
|
}
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: joinedHistory
|
id: joinedHistory
|
||||||
checked: roomSettings.historyVisibility == RoomSettings.Joined || roomSettings.historyVisibility == RoomSettings.WorldReadable
|
|
||||||
text: qsTr("They joined")
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Members can only see messages since after they joined.")
|
ToolTip.text: qsTr("Members can only see messages since after they joined.")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
checked: roomSettings.historyVisibility == RoomSettings.Joined || roomSettings.historyVisibility == RoomSettings.WorldReadable
|
||||||
|
text: qsTr("They joined")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: roomSettings.historyVisibility != selectedVisibility
|
|
||||||
enabled: roomSettings.canChangeHistoryVisibility
|
|
||||||
|
|
||||||
text: qsTr("Apply visibility changes")
|
|
||||||
property int selectedVisibility: {
|
property int selectedVisibility: {
|
||||||
if (publicHistoryButton.checked)
|
if (publicHistoryButton.checked)
|
||||||
return RoomSettings.WorldReadable;
|
return RoomSettings.WorldReadable;
|
||||||
|
@ -477,65 +462,67 @@ ApplicationWindow {
|
||||||
return RoomSettings.Invited;
|
return RoomSettings.Invited;
|
||||||
return RoomSettings.Joined;
|
return RoomSettings.Joined;
|
||||||
}
|
}
|
||||||
onClicked: roomSettings.changeHistoryVisibility(selectedVisibility)
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
enabled: roomSettings.canChangeHistoryVisibility
|
||||||
|
text: qsTr("Apply visibility changes")
|
||||||
|
visible: roomSettings.historyVisibility != selectedVisibility
|
||||||
|
|
||||||
|
onClicked: roomSettings.changeHistoryVisibility(selectedVisibility)
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Locally hidden events")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Locally hidden events")
|
||||||
}
|
}
|
||||||
|
|
||||||
HiddenEventsDialog {
|
HiddenEventsDialog {
|
||||||
id: hiddenEventsDialog
|
id: hiddenEventsDialog
|
||||||
roomid: roomSettings.roomId
|
|
||||||
roomName: roomSettings.roomName
|
roomName: roomSettings.roomName
|
||||||
|
roomid: roomSettings.roomId
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Configure")
|
|
||||||
ToolTip.text: qsTr("Select events to hide in this room")
|
|
||||||
onClicked: hiddenEventsDialog.show()
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
ToolTip.text: qsTr("Select events to hide in this room")
|
||||||
|
text: qsTr("Configure")
|
||||||
|
|
||||||
|
onClicked: hiddenEventsDialog.show()
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Automatic event deletion")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Automatic event deletion")
|
||||||
}
|
}
|
||||||
|
|
||||||
EventExpirationDialog {
|
EventExpirationDialog {
|
||||||
id: eventExpirationDialog
|
id: eventExpirationDialog
|
||||||
roomid: roomSettings.roomId
|
|
||||||
roomName: roomSettings.roomName
|
roomName: roomSettings.roomName
|
||||||
|
roomid: roomSettings.roomId
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Configure")
|
|
||||||
ToolTip.text: qsTr("Select if your events get automatically deleted in this room.")
|
|
||||||
onClicked: eventExpirationDialog.show()
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
ToolTip.text: qsTr("Select if your events get automatically deleted in this room.")
|
||||||
|
text: qsTr("Configure")
|
||||||
|
|
||||||
|
onClicked: eventExpirationDialog.show()
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("GENERAL SETTINGS")
|
|
||||||
font.bold: true
|
|
||||||
color: palette.text
|
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: Nheko.paddingLarge
|
Layout.topMargin: Nheko.paddingLarge
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: qsTr("Encryption")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("GENERAL SETTINGS")
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
color: palette.text
|
||||||
|
text: qsTr("Encryption")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: encryptionToggle
|
id: encryptionToggle
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: roomSettings.isEncryptionEnabled
|
checked: roomSettings.isEncryptionEnabled
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if (roomSettings.isEncryptionEnabled) {
|
if (roomSettings.isEncryptionEnabled) {
|
||||||
checked = true;
|
checked = true;
|
||||||
|
@ -544,135 +531,131 @@ ApplicationWindow {
|
||||||
if (checked === true)
|
if (checked === true)
|
||||||
confirmEncryptionDialog.open();
|
confirmEncryptionDialog.open();
|
||||||
}
|
}
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MessageDialog {
|
Platform.MessageDialog {
|
||||||
id: confirmEncryptionDialog
|
id: confirmEncryptionDialog
|
||||||
|
|
||||||
title: qsTr("End-to-End Encryption")
|
buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel
|
||||||
|
modality: Qt.NonModal
|
||||||
text: qsTr(`Encryption is currently experimental and things might break unexpectedly. <br>
|
text: qsTr(`Encryption is currently experimental and things might break unexpectedly. <br>
|
||||||
Please take note that it can't be disabled afterwards.`)
|
Please take note that it can't be disabled afterwards.`)
|
||||||
modality: Qt.NonModal
|
title: qsTr("End-to-End Encryption")
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (roomSettings.isEncryptionEnabled)
|
if (roomSettings.isEncryptionEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
roomSettings.enableEncryption();
|
roomSettings.enableEncryption();
|
||||||
}
|
}
|
||||||
onRejected: {
|
onRejected: {
|
||||||
encryptionToggle.checked = false;
|
encryptionToggle.checked = false;
|
||||||
}
|
}
|
||||||
buttons: Platform.MessageDialog.Ok | Platform.MessageDialog.Cancel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: palette.text
|
||||||
text: qsTr("Permission")
|
text: qsTr("Permission")
|
||||||
color: palette.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Configure")
|
Layout.alignment: Qt.AlignRight
|
||||||
ToolTip.text: qsTr("View and change the permissions in this room")
|
ToolTip.text: qsTr("View and change the permissions in this room")
|
||||||
onClicked: timelineRoot.showPLEditor(roomSettings)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: qsTr("Aliases")
|
|
||||||
color: palette.text
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Configure")
|
text: qsTr("Configure")
|
||||||
ToolTip.text: qsTr("View and change the addresses/aliases of this room")
|
|
||||||
onClicked: timelineRoot.showAliasEditor(roomSettings)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
|
||||||
|
|
||||||
|
onClicked: timelineRoot.showPLEditor(roomSettings)
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Sticker & Emote Settings")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Aliases")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Change")
|
|
||||||
ToolTip.text: qsTr("Change what packs are enabled, remove packs, or create new ones")
|
|
||||||
onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId)
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
ToolTip.text: qsTr("View and change the addresses/aliases of this room")
|
||||||
|
text: qsTr("Configure")
|
||||||
|
|
||||||
|
onClicked: timelineRoot.showAliasEditor(roomSettings)
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("INFO")
|
|
||||||
font.bold: true
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Sticker & Emote Settings")
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
ToolTip.text: qsTr("Change what packs are enabled, remove packs, or create new ones")
|
||||||
|
text: qsTr("Change")
|
||||||
|
|
||||||
|
onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId)
|
||||||
|
}
|
||||||
|
Label {
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
Layout.topMargin: Nheko.paddingLarge
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
Layout.topMargin: Nheko.paddingLarge
|
||||||
|
|
||||||
Label {
|
|
||||||
text: qsTr("Internal ID")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("INFO")
|
||||||
}
|
}
|
||||||
|
Label {
|
||||||
AbstractButton { // AbstractButton does not allow setting text color
|
color: palette.text
|
||||||
|
text: qsTr("Internal ID")
|
||||||
|
}
|
||||||
|
AbstractButton {
|
||||||
|
// AbstractButton does not allow setting text color
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: idLabel.height
|
Layout.preferredHeight: idLabel.height
|
||||||
Label { // TextEdit does not trigger onClicked
|
|
||||||
|
onClicked: {
|
||||||
|
textEdit.selectAll();
|
||||||
|
textEdit.copy();
|
||||||
|
toolTipTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
// TextEdit does not trigger onClicked
|
||||||
id: idLabel
|
id: idLabel
|
||||||
text: roomSettings.roomId
|
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 0.8)
|
|
||||||
color: palette.text
|
|
||||||
width: parent.width
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
wrapMode: Text.WrapAnywhere
|
|
||||||
ToolTip.text: qsTr("Copied to clipboard")
|
ToolTip.text: qsTr("Copied to clipboard")
|
||||||
ToolTip.visible: toolTipTimer.running
|
ToolTip.visible: toolTipTimer.running
|
||||||
}
|
color: palette.text
|
||||||
TextEdit{ // label does not allow selection
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 0.8)
|
||||||
id: textEdit
|
horizontalAlignment: Text.AlignRight
|
||||||
visible: false
|
|
||||||
text: roomSettings.roomId
|
text: roomSettings.roomId
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.WrapAnywhere
|
||||||
}
|
}
|
||||||
onClicked: {
|
TextEdit {
|
||||||
textEdit.selectAll()
|
// label does not allow selection
|
||||||
textEdit.copy()
|
id: textEdit
|
||||||
toolTipTimer.start()
|
|
||||||
|
text: roomSettings.roomId
|
||||||
|
visible: false
|
||||||
}
|
}
|
||||||
Timer {
|
Timer {
|
||||||
id: toolTipTimer
|
id: toolTipTimer
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Room Version")
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Room Version")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: roomSettings.roomVersion
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize
|
||||||
|
text: roomSettings.roomVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
id: showMoreButton
|
id: showMoreButton
|
||||||
|
|
||||||
anchors.horizontalCenter: flickable.horizontalCenter
|
anchors.horizontalCenter: flickable.horizontalCenter
|
||||||
y: Math.min(showMorePlaceholder.y+contentLayout1.y-flickable.contentY,flickable.height-height)
|
|
||||||
visible: roomTopic.cut
|
|
||||||
text: roomTopic.showMore ? qsTr("show less") : qsTr("show more")
|
text: roomTopic.showMore ? qsTr("show less") : qsTr("show more")
|
||||||
onClicked: {roomTopic.showMore = !roomTopic.showMore
|
visible: roomTopic.cut
|
||||||
console.log(flickable.visibleArea)
|
y: Math.min(showMorePlaceholder.y + contentLayout1.y - flickable.contentY, flickable.height - height)
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
roomTopic.showMore = !roomTopic.showMore;
|
||||||
|
console.log(flickable.visibleArea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
footer: DialogButtonBox {
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,20 +17,20 @@ ApplicationWindow {
|
||||||
|
|
||||||
property var profile
|
property var profile
|
||||||
|
|
||||||
height: 650
|
|
||||||
width: 420
|
|
||||||
minimumWidth: 150
|
|
||||||
minimumHeight: 150
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
|
|
||||||
modality: Qt.NonModal
|
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
height: 650
|
||||||
|
minimumHeight: 150
|
||||||
|
minimumWidth: 150
|
||||||
|
modality: Qt.NonModal
|
||||||
|
title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
|
||||||
|
width: 420
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
|
|
||||||
onActivated: userProfileDialog.close()
|
onActivated: userProfileDialog.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: devicelist
|
id: devicelist
|
||||||
|
|
||||||
|
@ -38,61 +38,73 @@ ApplicationWindow {
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
clip: true
|
|
||||||
spacing: 8
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 10
|
anchors.margins: 10
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
footerPositioning: ListView.OverlayFooter
|
footerPositioning: ListView.OverlayFooter
|
||||||
|
model: (selectedTab == 0) ? devicesModel : sharedRoomsModel
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
alignment: Qt.AlignRight
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
width: devicelist.width
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: palette.window
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: userProfileDialog.close()
|
||||||
|
}
|
||||||
header: ColumnLayout {
|
header: ColumnLayout {
|
||||||
id: contentL
|
id: contentL
|
||||||
|
|
||||||
width: devicelist.width
|
|
||||||
spacing: Nheko.paddingMedium
|
spacing: Nheko.paddingMedium
|
||||||
|
width: devicelist.width
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: displayAvatar
|
id: displayAvatar
|
||||||
|
|
||||||
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredHeight: 130
|
Layout.preferredHeight: 130
|
||||||
Layout.preferredWidth: 130
|
Layout.preferredWidth: 130
|
||||||
displayName: profile.displayName
|
displayName: profile.displayName
|
||||||
|
url: profile.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: profile.userid
|
userid: profile.userid
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
onClicked: TimelineManager.openImageOverlay(null, profile.avatarUrl, "", 0, 0)
|
onClicked: TimelineManager.openImageOverlay(null, profile.avatarUrl, "", 0, 0)
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change avatar globally.") : qsTr("Change avatar. Will only apply to this room.")
|
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change avatar globally.") : qsTr("Change avatar. Will only apply to this room.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: displayAvatar.left
|
anchors.left: displayAvatar.left
|
||||||
anchors.top: displayAvatar.top
|
|
||||||
anchors.leftMargin: Nheko.paddingMedium
|
anchors.leftMargin: Nheko.paddingMedium
|
||||||
|
anchors.top: displayAvatar.top
|
||||||
anchors.topMargin: Nheko.paddingMedium
|
anchors.topMargin: Nheko.paddingMedium
|
||||||
visible: profile.isSelf
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/edit.svg"
|
image: ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: profile.isSelf
|
||||||
|
|
||||||
onClicked: profile.changeAvatar()
|
onClicked: profile.changeAvatar()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
foreground: palette.mid
|
||||||
running: profile.isLoading
|
running: profile.isLoading
|
||||||
visible: profile.isLoading
|
visible: profile.isLoading
|
||||||
foreground: palette.mid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: errorText
|
id: errorText
|
||||||
|
|
||||||
color: "red"
|
|
||||||
visible: opacity > 0
|
|
||||||
opacity: 0
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
color: "red"
|
||||||
|
opacity: 0
|
||||||
|
visible: opacity > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
id: hideErrorAnimation
|
id: hideErrorAnimation
|
||||||
|
|
||||||
|
@ -101,16 +113,13 @@ ApplicationWindow {
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 4000
|
duration: 4000
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: errorText
|
|
||||||
property: 'opacity'
|
|
||||||
to: 0
|
|
||||||
duration: 1000
|
duration: 1000
|
||||||
|
property: 'opacity'
|
||||||
|
target: errorText
|
||||||
|
to: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onDisplayError(errorMessage) {
|
function onDisplayError(errorMessage) {
|
||||||
errorText.text = errorMessage;
|
errorText.text = errorMessage;
|
||||||
|
@ -120,22 +129,22 @@ ApplicationWindow {
|
||||||
|
|
||||||
target: profile
|
target: profile
|
||||||
}
|
}
|
||||||
|
|
||||||
TextInput {
|
TextInput {
|
||||||
id: displayUsername
|
id: displayUsername
|
||||||
|
|
||||||
property bool isUsernameEditingAllowed
|
property bool isUsernameEditingAllowed
|
||||||
|
|
||||||
readOnly: !isUsernameEditingAllowed
|
|
||||||
text: profile.displayName
|
|
||||||
font.pixelSize: 20
|
|
||||||
color: TimelineManager.userColor(profile.userid, palette.window)
|
|
||||||
font.bold: true
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - usernameChangeButton.anchors.leftMargin - (usernameChangeButton.width * 2)
|
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - usernameChangeButton.anchors.leftMargin - (usernameChangeButton.width * 2)
|
||||||
|
color: TimelineManager.userColor(profile.userid, palette.window)
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: 20
|
||||||
horizontalAlignment: TextInput.AlignHCenter
|
horizontalAlignment: TextInput.AlignHCenter
|
||||||
wrapMode: TextInput.Wrap
|
readOnly: !isUsernameEditingAllowed
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
text: profile.displayName
|
||||||
|
wrapMode: TextInput.Wrap
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
profile.changeUsername(displayUsername.text);
|
profile.changeUsername(displayUsername.text);
|
||||||
displayUsername.isUsernameEditingAllowed = false;
|
displayUsername.isUsernameEditingAllowed = false;
|
||||||
|
@ -143,14 +152,16 @@ ApplicationWindow {
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: usernameChangeButton
|
id: usernameChangeButton
|
||||||
visible: profile.isSelf
|
|
||||||
anchors.leftMargin: Nheko.paddingSmall
|
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: displayUsername.right
|
anchors.left: displayUsername.right
|
||||||
|
anchors.leftMargin: Nheko.paddingSmall
|
||||||
anchors.verticalCenter: displayUsername.verticalCenter
|
anchors.verticalCenter: displayUsername.verticalCenter
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.")
|
|
||||||
image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: profile.isSelf
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (displayUsername.isUsernameEditingAllowed) {
|
if (displayUsername.isUsernameEditingAllowed) {
|
||||||
profile.changeUsername(displayUsername.text);
|
profile.changeUsername(displayUsername.text);
|
||||||
|
@ -162,82 +173,79 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: profile.userid
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
text: profile.userid
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
id: statusMsg
|
id: statusMsg
|
||||||
text: qsTr("<i><b>Status:</b> %1</i>").arg(userStatus)
|
|
||||||
visible: userStatus != ""
|
property string userStatus: Presence.userStatus(profile.userid)
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
|
||||||
Layout.leftMargin: Nheko.paddingMedium
|
Layout.leftMargin: Nheko.paddingMedium
|
||||||
Layout.rightMargin: Nheko.paddingMedium
|
Layout.rightMargin: Nheko.paddingMedium
|
||||||
font.pointSize: Math.floor(fontMetrics.font.pointSize * 0.9)
|
font.pointSize: Math.floor(fontMetrics.font.pointSize * 0.9)
|
||||||
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
text: qsTr("<i><b>Status:</b> %1</i>").arg(userStatus)
|
||||||
|
visible: userStatus != ""
|
||||||
|
|
||||||
property string userStatus: Presence.userStatus(profile.userid)
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Presence
|
|
||||||
function onPresenceChanged(id) {
|
function onPresenceChanged(id) {
|
||||||
if (id == profile.userid) statusMsg.userStatus = Presence.userStatus(profile.userid);
|
if (id == profile.userid)
|
||||||
}
|
statusMsg.userStatus = Presence.userStatus(profile.userid);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target: Presence
|
||||||
|
}
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: !profile.isGlobalUserProfile
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
visible: !profile.isGlobalUserProfile
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
id: displayRoomname
|
id: displayRoomname
|
||||||
|
|
||||||
text: qsTr("Room: %1").arg(profile.room ? profile.room.roomName : "")
|
Layout.maximumWidth: parent.parent.width - (parent.spacing * 3) - 16
|
||||||
ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.")
|
ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.")
|
||||||
ToolTip.visible: ma.hovered
|
ToolTip.visible: ma.hovered
|
||||||
Layout.maximumWidth: parent.parent.width - (parent.spacing * 3) - 16
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
text: qsTr("Room: %1").arg(profile.room ? profile.room.roomName : "")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/world.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Open the global profile for this user.")
|
ToolTip.text: qsTr("Open the global profile for this user.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/world.svg"
|
||||||
|
|
||||||
onClicked: profile.openGlobalProfile()
|
onClicked: profile.openGlobalProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: verifyUserButton
|
id: verifyUserButton
|
||||||
|
|
||||||
text: qsTr("Verify")
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
enabled: profile.userVerified != Crypto.Verified
|
enabled: profile.userVerified != Crypto.Verified
|
||||||
|
text: qsTr("Verify")
|
||||||
visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled
|
visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled
|
||||||
|
|
||||||
onClicked: profile.verify()
|
onClicked: profile.verify()
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionIndicator {
|
EncryptionIndicator {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.preferredHeight: 32
|
Layout.preferredHeight: 32
|
||||||
Layout.preferredWidth: 32
|
Layout.preferredWidth: 32
|
||||||
|
ToolTip.visible: false
|
||||||
encrypted: profile.userVerificationEnabled
|
encrypted: profile.userVerificationEnabled
|
||||||
trust: profile.userVerified
|
trust: profile.userVerified
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
ToolTip.visible: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
// ImageButton{
|
// ImageButton{
|
||||||
// image:":/icons/icons/ui/volume-off-indicator.svg"
|
// image:":/icons/icons/ui/volume-off-indicator.svg"
|
||||||
|
@ -259,113 +267,108 @@ ApplicationWindow {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
image: ":/icons/icons/ui/chat.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Start a private chat.")
|
ToolTip.text: qsTr("Start a private chat.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/chat.svg"
|
||||||
|
|
||||||
onClicked: profile.startChat()
|
onClicked: profile.startChat()
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
image: ":/icons/icons/ui/round-remove-button.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Kick the user.")
|
ToolTip.text: qsTr("Kick the user.")
|
||||||
onClicked: profile.kickUser()
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/round-remove-button.svg"
|
||||||
visible: !profile.isGlobalUserProfile && profile.room.permissions.canKick()
|
visible: !profile.isGlobalUserProfile && profile.room.permissions.canKick()
|
||||||
}
|
|
||||||
|
|
||||||
|
onClicked: profile.kickUser()
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
image: ":/icons/icons/ui/ban.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Ban the user.")
|
ToolTip.text: qsTr("Ban the user.")
|
||||||
onClicked: profile.banUser()
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/ban.svg"
|
||||||
visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan()
|
visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan()
|
||||||
}
|
|
||||||
|
|
||||||
|
onClicked: profile.banUser()
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
image: ":/icons/icons/ui/volume-off-indicator.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: profile.ignored ? qsTr("Unignore the user.") : qsTr("Ignore the user.")
|
ToolTip.text: profile.ignored ? qsTr("Unignore the user.") : qsTr("Ignore the user.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
buttonTextColor: profile.ignored ? Nheko.theme.red : palette.buttonText
|
buttonTextColor: profile.ignored ? Nheko.theme.red : palette.buttonText
|
||||||
onClicked: profile.ignored = !profile.ignored
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/volume-off-indicator.svg"
|
||||||
visible: !profile.isSelf
|
visible: !profile.isSelf
|
||||||
}
|
|
||||||
|
|
||||||
|
onClicked: profile.ignored = !profile.ignored
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
image: ":/icons/icons/ui/refresh.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Refresh device list.")
|
ToolTip.text: qsTr("Refresh device list.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/refresh.svg"
|
||||||
|
|
||||||
onClicked: profile.refreshDevices()
|
onClicked: profile.refreshDevices()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TabBar {
|
TabBar {
|
||||||
id: tabbar
|
id: tabbar
|
||||||
visible: !profile.isSelf
|
|
||||||
|
Layout.bottomMargin: Nheko.paddingMedium
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
visible: !profile.isSelf
|
||||||
|
|
||||||
onCurrentIndexChanged: devicelist.selectedTab = currentIndex
|
onCurrentIndexChanged: devicelist.selectedTab = currentIndex
|
||||||
|
|
||||||
|
|
||||||
NhekoTabButton {
|
NhekoTabButton {
|
||||||
text: qsTr("Devices")
|
text: qsTr("Devices")
|
||||||
}
|
}
|
||||||
NhekoTabButton {
|
NhekoTabButton {
|
||||||
text: qsTr("Shared Rooms")
|
text: qsTr("Shared Rooms")
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.bottomMargin: Nheko.paddingMedium
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
model: (selectedTab == 0) ? devicesModel : sharedRoomsModel
|
|
||||||
|
|
||||||
DelegateModel {
|
DelegateModel {
|
||||||
id: devicesModel
|
id: devicesModel
|
||||||
|
|
||||||
model: profile.deviceList
|
model: profile.deviceList
|
||||||
|
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
required property int verificationStatus
|
|
||||||
required property string deviceId
|
required property string deviceId
|
||||||
required property string deviceName
|
required property string deviceName
|
||||||
required property string lastIp
|
required property string lastIp
|
||||||
required property var lastTs
|
required property var lastTs
|
||||||
|
required property int verificationStatus
|
||||||
|
|
||||||
width: devicelist.width
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
width: devicelist.width
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Layout.leftMargin: Nheko.paddingMedium
|
Layout.leftMargin: Nheko.paddingMedium
|
||||||
Layout.rightMargin: Nheko.paddingMedium
|
Layout.rightMargin: Nheko.paddingMedium
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
font.bold: true
|
font.bold: true
|
||||||
color: palette.text
|
|
||||||
text: deviceId
|
text: deviceId
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredHeight: 16
|
Layout.preferredHeight: 16
|
||||||
Layout.preferredWidth: 16
|
Layout.preferredWidth: 16
|
||||||
visible: profile.isSelf && verificationStatus != VerificationStatus.NOT_APPLICABLE
|
|
||||||
sourceSize.height: 16 * Screen.devicePixelRatio
|
|
||||||
sourceSize.width: 16 * Screen.devicePixelRatio
|
|
||||||
source: {
|
source: {
|
||||||
switch (verificationStatus) {
|
switch (verificationStatus) {
|
||||||
case VerificationStatus.VERIFIED:
|
case VerificationStatus.VERIFIED:
|
||||||
|
@ -378,20 +381,21 @@ ApplicationWindow {
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.orange;
|
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.orange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sourceSize.height: 16 * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: 16 * Screen.devicePixelRatio
|
||||||
|
visible: profile.isSelf && verificationStatus != VerificationStatus.NOT_APPLICABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
image: ":/icons/icons/ui/power-off.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Sign out this device.")
|
ToolTip.text: qsTr("Sign out this device.")
|
||||||
onClicked: profile.signOutDevice(deviceId)
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/power-off.svg"
|
||||||
visible: profile.isSelf
|
visible: profile.isSelf
|
||||||
}
|
|
||||||
|
|
||||||
|
onClicked: profile.signOutDevice(deviceId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: deviceNameRow
|
id: deviceNameRow
|
||||||
|
|
||||||
|
@ -400,24 +404,25 @@ ApplicationWindow {
|
||||||
TextInput {
|
TextInput {
|
||||||
id: deviceNameField
|
id: deviceNameField
|
||||||
|
|
||||||
readOnly: !deviceNameRow.isEditingAllowed
|
|
||||||
text: deviceName
|
|
||||||
color: palette.text
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: palette.text
|
||||||
|
readOnly: !deviceNameRow.isEditingAllowed
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
text: deviceName
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
profile.changeDeviceName(deviceId, deviceNameField.text);
|
profile.changeDeviceName(deviceId, deviceNameField.text);
|
||||||
deviceNameRow.isEditingAllowed = false;
|
deviceNameRow.isEditingAllowed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
visible: profile.isSelf
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Change device name.")
|
ToolTip.text: qsTr("Change device name.")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
image: deviceNameRow.isEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
image: deviceNameRow.isEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg"
|
||||||
|
visible: profile.isSelf
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (deviceNameRow.isEditingAllowed) {
|
if (deviceNameRow.isEditingAllowed) {
|
||||||
profile.changeDeviceName(deviceId, deviceNameField.text);
|
profile.changeDeviceName(deviceId, deviceNameField.text);
|
||||||
|
@ -429,24 +434,19 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
visible: profile.isSelf
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
elide: Text.ElideRight
|
Layout.fillWidth: true
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
elide: Text.ElideRight
|
||||||
text: qsTr("Last seen %1 from %2").arg(new Date(lastTs).toLocaleString(Locale.ShortFormat)).arg(lastIp ? lastIp : "???")
|
text: qsTr("Last seen %1 from %2").arg(new Date(lastTs).toLocaleString(Locale.ShortFormat)).arg(lastIp ? lastIp : "???")
|
||||||
|
visible: profile.isSelf
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredHeight: 16
|
Layout.preferredHeight: 16
|
||||||
Layout.preferredWidth: 16
|
Layout.preferredWidth: 16
|
||||||
visible: !profile.isSelf && verificationStatus != VerificationStatus.NOT_APPLICABLE
|
|
||||||
source: {
|
source: {
|
||||||
switch (verificationStatus) {
|
switch (verificationStatus) {
|
||||||
case VerificationStatus.VERIFIED:
|
case VerificationStatus.VERIFIED:
|
||||||
|
@ -459,13 +459,14 @@ ApplicationWindow {
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?" + Nheko.theme.red;
|
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?" + Nheko.theme.red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
visible: !profile.isSelf && verificationStatus != VerificationStatus.NOT_APPLICABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: verifyButton
|
id: verifyButton
|
||||||
|
|
||||||
visible: verificationStatus == VerificationStatus.UNVERIFIED && (profile.isSelf || !profile.userVerificationEnabled)
|
|
||||||
text: (verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify")
|
text: (verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify")
|
||||||
|
visible: verificationStatus == VerificationStatus.UNVERIFIED && (profile.isSelf || !profile.userVerificationEnabled)
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (verificationStatus == VerificationStatus.VERIFIED)
|
if (verificationStatus == VerificationStatus.VERIFIED)
|
||||||
profile.unverify(deviceId);
|
profile.unverify(deviceId);
|
||||||
|
@ -473,67 +474,48 @@ ApplicationWindow {
|
||||||
profile.verify(deviceId);
|
profile.verify(deviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateModel {
|
DelegateModel {
|
||||||
id: sharedRoomsModel
|
id: sharedRoomsModel
|
||||||
|
|
||||||
model: profile.sharedRooms
|
model: profile.sharedRooms
|
||||||
|
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
|
required property string avatarUrl
|
||||||
required property string roomId
|
required property string roomId
|
||||||
required property string roomName
|
required property string roomName
|
||||||
required property string avatarUrl
|
|
||||||
|
|
||||||
width: devicelist.width
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
width: devicelist.width
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
enabled: false
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.leftMargin: Nheko.paddingMedium
|
Layout.leftMargin: Nheko.paddingMedium
|
||||||
|
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
|
|
||||||
Layout.preferredHeight: avatarSize
|
Layout.preferredHeight: avatarSize
|
||||||
Layout.preferredWidth: avatarSize
|
Layout.preferredWidth: avatarSize
|
||||||
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
roomid: roomId
|
|
||||||
displayName: roomName
|
displayName: roomName
|
||||||
|
enabled: false
|
||||||
|
roomid: roomId
|
||||||
|
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
}
|
}
|
||||||
|
|
||||||
ElidedLabel {
|
ElidedLabel {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
color: palette.text
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: Nheko.paddingMedium
|
||||||
|
color: palette.text
|
||||||
elideWidth: width
|
elideWidth: width
|
||||||
fullText: roomName
|
fullText: roomName
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
Layout.rightMargin: Nheko.paddingMedium
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
|
||||||
z: 2
|
|
||||||
width: devicelist.width
|
|
||||||
alignment: Qt.AlignRight
|
|
||||||
standardButtons: DialogButtonBox.Ok
|
|
||||||
onAccepted: userProfileDialog.close()
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: palette.window
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,17 +12,17 @@ Menu {
|
||||||
id: stickerPopup
|
id: stickerPopup
|
||||||
|
|
||||||
property var callback
|
property var callback
|
||||||
property string roomid
|
|
||||||
property alias model: gridView.model
|
|
||||||
required property bool emoji
|
required property bool emoji
|
||||||
property var textArea
|
|
||||||
property real highlightHue: palette.highlight.hslHue
|
property real highlightHue: palette.highlight.hslHue
|
||||||
property real highlightSat: palette.highlight.hslSaturation
|
|
||||||
property real highlightLight: palette.highlight.hslLightness
|
property real highlightLight: palette.highlight.hslLightness
|
||||||
|
property real highlightSat: palette.highlight.hslSaturation
|
||||||
|
property alias model: gridView.model
|
||||||
|
property string roomid
|
||||||
|
readonly property int sidebarAvatarSize: 24
|
||||||
readonly property int stickerDim: emoji ? 48 : 128
|
readonly property int stickerDim: emoji ? 48 : 128
|
||||||
readonly property int stickerDimPad: stickerDim + Nheko.paddingSmall
|
readonly property int stickerDimPad: stickerDim + Nheko.paddingSmall
|
||||||
readonly property int stickersPerRow: emoji ? 7 : 3
|
readonly property int stickersPerRow: emoji ? 7 : 3
|
||||||
readonly property int sidebarAvatarSize: 24
|
property var textArea
|
||||||
|
|
||||||
function show(showAt, roomid_, callback) {
|
function show(showAt, roomid_, callback) {
|
||||||
console.debug("Showing sticker picker");
|
console.debug("Showing sticker picker");
|
||||||
|
@ -31,14 +31,14 @@ Menu {
|
||||||
popup(showAt ? showAt : null);
|
popup(showAt ? showAt : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
margins: 2
|
|
||||||
bottomPadding: 0
|
bottomPadding: 0
|
||||||
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
focus: true
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
|
margins: 2
|
||||||
|
modal: true
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
topPadding: 0
|
topPadding: 0
|
||||||
modal: true
|
|
||||||
focus: true
|
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
|
||||||
width: sidebarAvatarSize + Nheko.paddingSmall + stickersPerRow * stickerDimPad + 20
|
width: sidebarAvatarSize + Nheko.paddingSmall + stickersPerRow * stickerDimPad + 20
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -49,11 +49,11 @@ Menu {
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: columnView
|
id: columnView
|
||||||
|
|
||||||
anchors.leftMargin: Nheko.paddingSmall
|
|
||||||
anchors.rightMargin: Nheko.paddingSmall
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Nheko.paddingSmall
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Nheko.paddingSmall
|
||||||
columns: 2
|
columns: 2
|
||||||
rows: 2
|
rows: 2
|
||||||
|
|
||||||
|
@ -61,14 +61,15 @@ Menu {
|
||||||
TextField {
|
TextField {
|
||||||
id: emojiSearch
|
id: emojiSearch
|
||||||
|
|
||||||
|
Layout.column: 1
|
||||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
||||||
Layout.row: 0
|
Layout.row: 0
|
||||||
Layout.column: 1
|
|
||||||
background: null
|
background: null
|
||||||
placeholderTextColor: palette.buttonText
|
|
||||||
placeholderText: qsTr("Search")
|
placeholderText: qsTr("Search")
|
||||||
selectByMouse: true
|
placeholderTextColor: palette.buttonText
|
||||||
rightPadding: clearSearch.width
|
rightPadding: clearSearch.width
|
||||||
|
selectByMouse: true
|
||||||
|
|
||||||
onTextChanged: searchTimer.restart()
|
onTextChanged: searchTimer.restart()
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible)
|
if (visible)
|
||||||
|
@ -81,23 +82,24 @@ Menu {
|
||||||
id: searchTimer
|
id: searchTimer
|
||||||
|
|
||||||
interval: 350 // tweak as needed?
|
interval: 350 // tweak as needed?
|
||||||
|
|
||||||
onTriggered: stickerPopup.model.searchString = emojiSearch.text
|
onTriggered: stickerPopup.model.searchString = emojiSearch.text
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: clearSearch
|
id: clearSearch
|
||||||
|
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/round-remove-button.svg"
|
||||||
visible: emojiSearch.text !== ''
|
visible: emojiSearch.text !== ''
|
||||||
|
|
||||||
image: ":/icons/icons/ui/round-remove-button.svg"
|
|
||||||
focusPolicy: Qt.NoFocus
|
|
||||||
onClicked: emojiSearch.clear()
|
onClicked: emojiSearch.clear()
|
||||||
hoverEnabled: true
|
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
right: parent.right
|
right: parent.right
|
||||||
rightMargin: Nheko.paddingSmall
|
rightMargin: Nheko.paddingSmall
|
||||||
|
top: parent.top
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,39 +108,30 @@ Menu {
|
||||||
ListView {
|
ListView {
|
||||||
id: gridView
|
id: gridView
|
||||||
|
|
||||||
model: roomid ? TimelineManager.completerFor(stickerPopup.emoji ? "emojigrid" : "stickergrid", roomid) : null
|
property int cellHeight: stickerDimPad
|
||||||
Layout.row: 1
|
|
||||||
Layout.column: 1
|
Layout.column: 1
|
||||||
Layout.preferredHeight: cellHeight * (stickersPerRow + 0.5)
|
Layout.preferredHeight: cellHeight * (stickersPerRow + 0.5)
|
||||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
||||||
property int cellHeight: stickerDimPad
|
Layout.row: 1
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: -1 // prevent sorting from stealing focus
|
currentIndex: -1 // prevent sorting from stealing focus
|
||||||
|
|
||||||
section.property: "packname"
|
model: roomid ? TimelineManager.completerFor(stickerPopup.emoji ? "emojigrid" : "stickergrid", roomid) : null
|
||||||
section.criteria: ViewSection.FullString
|
section.criteria: ViewSection.FullString
|
||||||
section.delegate: Rectangle {
|
|
||||||
width: gridView.width
|
|
||||||
height: childrenRect.height
|
|
||||||
color: palette.alternateBase
|
|
||||||
|
|
||||||
required property string section
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
text: parent.section
|
|
||||||
font.bold: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
|
section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
|
||||||
|
section.property: "packname"
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
id: emojiScroll
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Individual emoji
|
// Individual emoji
|
||||||
delegate: Row {
|
delegate: Row {
|
||||||
required property var row;
|
required property var row
|
||||||
|
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
|
@ -150,11 +143,45 @@ Menu {
|
||||||
|
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
width: stickerDim
|
|
||||||
height: stickerDim
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.text: ":" + modelData.shortcode + ": - " + (modelData.unicode ? modelData.unicodeName : modelData.body)
|
ToolTip.text: ":" + modelData.shortcode + ": - " + (modelData.unicode ? modelData.unicodeName : modelData.body)
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
height: stickerDim
|
||||||
|
hoverEnabled: true
|
||||||
|
width: stickerDim
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: hovered ? palette.highlight : 'transparent'
|
||||||
|
radius: 5
|
||||||
|
}
|
||||||
|
contentItem: DelegateChooser {
|
||||||
|
roleValue: del.modelData.unicode != undefined
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
font.family: Settings.emojiFont
|
||||||
|
font.pixelSize: 36
|
||||||
|
height: stickerDim
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: del.modelData.unicode.replace('\ufe0f', '')
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
width: stickerDim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: false
|
||||||
|
|
||||||
|
Image {
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
height: stickerDim
|
||||||
|
source: del.modelData.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||||
|
width: stickerDim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: maybe add favorites at some point?
|
// TODO: maybe add favorites at some point?
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.debug("Picked " + modelData);
|
console.debug("Picked " + modelData);
|
||||||
|
@ -170,95 +197,63 @@ Menu {
|
||||||
callback(modelData.url, modelData.markdown);
|
callback(modelData.url, modelData.markdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
section.delegate: Rectangle {
|
||||||
|
required property string section
|
||||||
|
|
||||||
contentItem: DelegateChooser {
|
color: palette.alternateBase
|
||||||
roleValue: del.modelData.unicode != undefined
|
height: childrenRect.height
|
||||||
|
width: gridView.width
|
||||||
DelegateChoice {
|
|
||||||
roleValue: true
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
width: stickerDim
|
anchors.left: parent.left
|
||||||
height: stickerDim
|
anchors.right: parent.right
|
||||||
horizontalAlignment: Text.AlignHCenter
|
font.bold: true
|
||||||
verticalAlignment: Text.AlignVCenter
|
text: parent.section
|
||||||
font.family: Settings.emojiFont
|
|
||||||
font.pixelSize: 36
|
|
||||||
text: del.modelData.unicode.replace('\ufe0f', '')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: false
|
|
||||||
Image {
|
|
||||||
height: stickerDim
|
|
||||||
width: stickerDim
|
|
||||||
source: del.modelData.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: hovered ? palette.highlight : 'transparent'
|
|
||||||
radius: 5
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
id: emojiScroll
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
Layout.row: 1
|
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
Layout.preferredWidth: sidebarAvatarSize
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: sidebarAvatarSize
|
||||||
Layout.rightMargin: Nheko.paddingSmall
|
Layout.rightMargin: Nheko.paddingSmall
|
||||||
|
Layout.row: 1
|
||||||
|
clip: true
|
||||||
model: gridView.model ? gridView.model.sections : null
|
model: gridView.model ? gridView.model.sections : null
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
clip: true
|
|
||||||
|
|
||||||
delegate: Avatar {
|
delegate: Avatar {
|
||||||
height: sidebarAvatarSize
|
|
||||||
width: sidebarAvatarSize
|
|
||||||
url: modelData.url.replace("mxc://", "image://MxcImage/")
|
|
||||||
textColor: modelData.url.startsWith("mxc://") ? palette.text : palette.buttonText
|
|
||||||
displayName: modelData.name
|
|
||||||
roomid: modelData.name
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: modelData.name
|
ToolTip.text: modelData.name
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
displayName: modelData.name
|
||||||
|
height: sidebarAvatarSize
|
||||||
|
hoverEnabled: true
|
||||||
|
roomid: modelData.name
|
||||||
|
textColor: modelData.url.startsWith("mxc://") ? palette.text : palette.buttonText
|
||||||
|
url: modelData.url.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: sidebarAvatarSize
|
||||||
|
|
||||||
onClicked: gridView.positionViewAtIndex(modelData.firstRowWith, ListView.Beginning)
|
onClicked: gridView.positionViewAtIndex(modelData.firstRowWith, ListView.Beginning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.row: 0
|
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
Layout.preferredWidth: sidebarAvatarSize
|
|
||||||
Layout.preferredHeight: sidebarAvatarSize
|
Layout.preferredHeight: sidebarAvatarSize
|
||||||
|
Layout.preferredWidth: sidebarAvatarSize
|
||||||
Layout.rightMargin: Nheko.paddingSmall
|
Layout.rightMargin: Nheko.paddingSmall
|
||||||
|
Layout.row: 0
|
||||||
image: ":/icons/icons/ui/settings.svg"
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Change what packs are enabled, remove packs, or create new ones")
|
ToolTip.text: qsTr("Change what packs are enabled, remove packs, or create new ones")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/settings.svg"
|
||||||
|
|
||||||
onClicked: TimelineManager.openImagePackSettings(stickerPopup.roomid)
|
onClicked: TimelineManager.openImagePackSettings(stickerPopup.roomid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,147 +13,139 @@ import "../"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: loginPage
|
id: loginPage
|
||||||
property int maxExpansion: 400
|
|
||||||
|
|
||||||
property string error: login.error
|
property string error: login.error
|
||||||
|
property int maxExpansion: 400
|
||||||
|
|
||||||
Login {
|
Login {
|
||||||
id: login
|
id: login
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: scroll
|
id: scroll
|
||||||
|
|
||||||
clip: false
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Nheko.paddingLarge
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: Math.min(loginPage.height, col.implicitHeight)
|
clip: false
|
||||||
anchors.margins: Nheko.paddingLarge
|
|
||||||
|
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
height: Math.min(loginPage.height, col.implicitHeight)
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: col
|
id: col
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
width: Math.min(loginPage.maxExpansion, scroll.width - Nheko.paddingLarge * 2)
|
width: Math.min(loginPage.maxExpansion, scroll.width - Nheko.paddingLarge * 2)
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
source: "qrc:/logos/login.png"
|
|
||||||
Layout.preferredHeight: 128
|
Layout.preferredHeight: 128
|
||||||
Layout.preferredWidth: 128
|
Layout.preferredWidth: 128
|
||||||
|
source: "qrc:/logos/login.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: matrixIdLabel
|
id: matrixIdLabel
|
||||||
|
|
||||||
|
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||||
|
ToolTip.text: qsTr("Your login name. A mxid should start with @ followed by the user ID. After the user ID you need to include your server name after a :.\nYou can also put your homeserver address there if your server doesn't support .well-known lookup.\nExample: @user:yourserver.example.com\nIf Nheko fails to discover your homeserver, it will show you a field to enter the server manually.")
|
||||||
label: qsTr("Matrix ID")
|
label: qsTr("Matrix ID")
|
||||||
placeholderText: qsTr("e.g @user:yourserver.example.com")
|
placeholderText: qsTr("e.g @user:yourserver.example.com")
|
||||||
|
|
||||||
onEditingFinished: login.mxid = text
|
onEditingFinished: login.mxid = text
|
||||||
|
|
||||||
ToolTip.text: qsTr("Your login name. A mxid should start with @ followed by the user ID. After the user ID you need to include your server name after a :.\nYou can also put your homeserver address there if your server doesn't support .well-known lookup.\nExample: @user:yourserver.example.com\nIf Nheko fails to discover your homeserver, it will show you a field to enter the server manually.")
|
|
||||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.preferredHeight: matrixIdLabel.height/2
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
Layout.preferredHeight: matrixIdLabel.height / 2
|
||||||
visible: running
|
|
||||||
running: login.lookingUpHs
|
|
||||||
foreground: palette.mid
|
foreground: palette.mid
|
||||||
|
running: login.lookingUpHs
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: login.mxidError
|
text: login.mxidError
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: passwordLabel
|
id: passwordLabel
|
||||||
Layout.fillWidth: true
|
|
||||||
label: qsTr("Password")
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
ToolTip.text: qsTr("Your password.")
|
|
||||||
visible: login.passwordSupported
|
|
||||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||||
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("Your password.")
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Password")
|
||||||
|
visible: login.passwordSupported
|
||||||
|
}
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: deviceNameLabel
|
id: deviceNameLabel
|
||||||
|
|
||||||
|
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("A name for this device which will be shown to others when verifying your devices. If nothing is provided, a default is used.")
|
||||||
label: qsTr("Device name")
|
label: qsTr("Device name")
|
||||||
placeholderText: login.initialDeviceName()
|
placeholderText: login.initialDeviceName()
|
||||||
ToolTip.text: qsTr("A name for this device which will be shown to others when verifying your devices. If nothing is provided, a default is used.")
|
|
||||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: hsLabel
|
id: hsLabel
|
||||||
enabled: visible
|
|
||||||
visible: login.homeserverNeeded
|
|
||||||
|
|
||||||
|
Keys.forwardTo: [pwBtn, ssoRepeater]
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("The address that can be used to contact your homeserver's client API.\nExample: https://yourserver.example.com:8787")
|
||||||
|
enabled: visible
|
||||||
label: qsTr("Homeserver address")
|
label: qsTr("Homeserver address")
|
||||||
placeholderText: qsTr("yourserver.example.com:8787")
|
placeholderText: qsTr("yourserver.example.com:8787")
|
||||||
text: login.homeserver
|
text: login.homeserver
|
||||||
onEditingFinished: login.homeserver = text
|
visible: login.homeserverNeeded
|
||||||
ToolTip.text: qsTr("The address that can be used to contact your homeserver's client API.\nExample: https://yourserver.example.com:8787")
|
|
||||||
Keys.forwardTo: [pwBtn, ssoRepeater]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
onEditingFinished: login.homeserver = text
|
||||||
|
}
|
||||||
Item {
|
Item {
|
||||||
Layout.preferredHeight: Nheko.avatarSize
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Nheko.avatarSize
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
height: parent.height
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
visible: running
|
|
||||||
running: login.loggingIn
|
|
||||||
foreground: palette.mid
|
foreground: palette.mid
|
||||||
|
height: parent.height
|
||||||
|
running: login.loggingIn
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: loginPage.error
|
text: loginPage.error
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
id: pwBtn
|
id: pwBtn
|
||||||
visible: login.passwordSupported
|
|
||||||
enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: qsTr("LOGIN")
|
|
||||||
function pwLogin() {
|
function pwLogin() {
|
||||||
login.onLoginButtonClicked(Login.Password, matrixIdLabel.text, passwordLabel.text, deviceNameLabel.text)
|
login.onLoginButtonClicked(Login.Password, matrixIdLabel.text, passwordLabel.text, deviceNameLabel.text);
|
||||||
}
|
|
||||||
onClicked: pwBtn.pwLogin()
|
|
||||||
Keys.onEnterPressed: pwBtn.pwLogin()
|
|
||||||
Keys.onReturnPressed: pwBtn.pwLogin()
|
|
||||||
Keys.enabled: pwBtn.enabled && login.passwordSupported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.enabled: pwBtn.enabled && login.passwordSupported
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
|
||||||
|
text: qsTr("LOGIN")
|
||||||
|
visible: login.passwordSupported
|
||||||
|
|
||||||
|
Keys.onEnterPressed: pwBtn.pwLogin()
|
||||||
|
Keys.onReturnPressed: pwBtn.pwLogin()
|
||||||
|
onClicked: pwBtn.pwLogin()
|
||||||
|
}
|
||||||
Repeater {
|
Repeater {
|
||||||
id: ssoRepeater
|
id: ssoRepeater
|
||||||
|
|
||||||
|
@ -161,32 +153,35 @@ Item {
|
||||||
|
|
||||||
delegate: FlatButton {
|
delegate: FlatButton {
|
||||||
id: ssoBtn
|
id: ssoBtn
|
||||||
visible: login.ssoSupported
|
|
||||||
enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: modelData.name
|
|
||||||
iconImage: modelData.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
function ssoLogin() {
|
function ssoLogin() {
|
||||||
login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, modelData.id, deviceNameLabel.text)
|
login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, modelData.id, deviceNameLabel.text);
|
||||||
}
|
|
||||||
onClicked: ssoBtn.ssoLogin()
|
|
||||||
Keys.onEnterPressed: ssoBtn.ssoLogin()
|
|
||||||
Keys.onReturnPressed: ssoBtn.ssoLogin()
|
|
||||||
Keys.enabled: ssoBtn.enabled && !login.passwordSupported
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.enabled: ssoBtn.enabled && !login.passwordSupported
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
|
||||||
|
iconImage: modelData.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
text: modelData.name
|
||||||
|
visible: login.ssoSupported
|
||||||
|
|
||||||
|
Keys.onEnterPressed: ssoBtn.ssoLogin()
|
||||||
|
Keys.onReturnPressed: ssoBtn.ssoLogin()
|
||||||
|
onClicked: ssoBtn.ssoLogin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
anchors.top: parent.top
|
ToolTip.text: qsTr("Back")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
width: Nheko.avatarSize
|
anchors.top: parent.top
|
||||||
height: Nheko.avatarSize
|
height: Nheko.avatarSize
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
ToolTip.visible: hovered
|
width: Nheko.avatarSize
|
||||||
ToolTip.text: qsTr("Back")
|
|
||||||
onClicked: mainWindow.pop()
|
onClicked: mainWindow.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,208 +13,197 @@ import "../"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: registrationPage
|
id: registrationPage
|
||||||
property int maxExpansion: 400
|
|
||||||
|
|
||||||
property string error: regis.error
|
property string error: regis.error
|
||||||
|
property int maxExpansion: 400
|
||||||
|
|
||||||
Registration {
|
Registration {
|
||||||
id: regis
|
id: regis
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: scroll
|
id: scroll
|
||||||
|
|
||||||
clip: false
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Nheko.paddingLarge
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: Math.min(registrationPage.height, col.implicitHeight)
|
clip: false
|
||||||
anchors.margins: Nheko.paddingLarge
|
|
||||||
|
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
height: Math.min(registrationPage.height, col.implicitHeight)
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: col
|
id: col
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
width: Math.min(registrationPage.maxExpansion, scroll.width - Nheko.paddingLarge * 2)
|
width: Math.min(registrationPage.maxExpansion, scroll.width - Nheko.paddingLarge * 2)
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
source: "qrc:/logos/login.png"
|
|
||||||
Layout.preferredHeight: 128
|
Layout.preferredHeight: 128
|
||||||
Layout.preferredWidth: 128
|
Layout.preferredWidth: 128
|
||||||
|
source: "qrc:/logos/login.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: hsLabel
|
id: hsLabel
|
||||||
label: qsTr("Homeserver")
|
|
||||||
placeholderText: qsTr("your.server")
|
|
||||||
onEditingFinished: regis.setServer(text)
|
|
||||||
|
|
||||||
ToolTip.text: qsTr("A server that allows registration. Since matrix is decentralized, you need to first find a server you can register on or host your own.")
|
ToolTip.text: qsTr("A server that allows registration. Since matrix is decentralized, you need to first find a server you can register on or host your own.")
|
||||||
|
label: qsTr("Homeserver")
|
||||||
|
placeholderText: qsTr("your.server")
|
||||||
|
|
||||||
|
onEditingFinished: regis.setServer(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.preferredHeight: hsLabel.height/2
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
Layout.preferredHeight: hsLabel.height / 2
|
||||||
visible: running
|
|
||||||
running: regis.lookingUpHs
|
|
||||||
foreground: palette.mid
|
foreground: palette.mid
|
||||||
|
running: regis.lookingUpHs
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: regis.hsError
|
text: regis.hsError
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
|
||||||
visible: regis.supported
|
visible: regis.supported
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: usernameLabel
|
id: usernameLabel
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: qsTr("Username")
|
|
||||||
ToolTip.text: qsTr("The username must not be empty, and must contain only the characters a-z, 0-9, ., _, =, -, and /.")
|
ToolTip.text: qsTr("The username must not be empty, and must contain only the characters a-z, 0-9, ., _, =, -, and /.")
|
||||||
|
label: qsTr("Username")
|
||||||
|
|
||||||
onEditingFinished: regis.checkUsername(text)
|
onEditingFinished: regis.checkUsername(text)
|
||||||
}
|
}
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.preferredHeight: usernameLabel.height/2
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
Layout.preferredHeight: usernameLabel.height / 2
|
||||||
visible: running
|
|
||||||
running: regis.lookingUpUsername
|
|
||||||
foreground: palette.mid
|
foreground: palette.mid
|
||||||
|
running: regis.lookingUpUsername
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
Layout.preferredHeight: usernameLabel.height / 2
|
Layout.preferredHeight: usernameLabel.height / 2
|
||||||
Layout.preferredWidth: usernameLabel.height / 2
|
Layout.preferredWidth: usernameLabel.height / 2
|
||||||
Layout.alignment: Qt.AlignBottom
|
|
||||||
source: regis.usernameAvailable ? ("image://colorimage/:/icons/icons/ui/checkmark.svg?green") : ("image://colorimage/:/icons/icons/ui/dismiss.svg?"+Nheko.theme.error)
|
|
||||||
visible: regis.usernameAvailable || regis.usernameUnavailable
|
|
||||||
ToolTip.visible: ma.hovered
|
|
||||||
ToolTip.text: qsTr("Back")
|
ToolTip.text: qsTr("Back")
|
||||||
|
ToolTip.visible: ma.hovered
|
||||||
|
source: regis.usernameAvailable ? ("image://colorimage/:/icons/icons/ui/checkmark.svg?green") : ("image://colorimage/:/icons/icons/ui/dismiss.svg?" + Nheko.theme.error)
|
||||||
sourceSize.height: height * Screen.devicePixelRatio
|
sourceSize.height: height * Screen.devicePixelRatio
|
||||||
sourceSize.width: width * Screen.devicePixelRatio
|
sourceSize.width: width * Screen.devicePixelRatio
|
||||||
|
visible: regis.usernameAvailable || regis.usernameUnavailable
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: regis.usernameError
|
text: regis.usernameError
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text && regis.supported
|
visible: text && regis.supported
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
visible: regis.supported
|
|
||||||
id: passwordLabel
|
id: passwordLabel
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: qsTr("Password")
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
ToolTip.text: qsTr("Please choose a secure password. The exact requirements for password strength may depend on your server.")
|
ToolTip.text: qsTr("Please choose a secure password. The exact requirements for password strength may depend on your server.")
|
||||||
}
|
|
||||||
|
|
||||||
MatrixTextField {
|
|
||||||
visible: regis.supported
|
|
||||||
id: passwordConfirmationLabel
|
|
||||||
Layout.fillWidth: true
|
|
||||||
label: qsTr("Password confirmation")
|
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Password")
|
||||||
|
visible: regis.supported
|
||||||
}
|
}
|
||||||
|
MatrixTextField {
|
||||||
|
id: passwordConfirmationLabel
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Password confirmation")
|
||||||
|
visible: regis.supported
|
||||||
|
}
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: regis.supported
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: passwordLabel.text != passwordConfirmationLabel.text ? qsTr("Your passwords do not match!") : ""
|
text: passwordLabel.text != passwordConfirmationLabel.text ? qsTr("Your passwords do not match!") : ""
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
visible: regis.supported
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
visible: regis.supported
|
|
||||||
id: deviceNameLabel
|
id: deviceNameLabel
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
ToolTip.text: qsTr("A name for this device which will be shown to others when verifying your devices. If nothing is provided a default is used.")
|
||||||
label: qsTr("Device name")
|
label: qsTr("Device name")
|
||||||
placeholderText: regis.initialDeviceName()
|
placeholderText: regis.initialDeviceName()
|
||||||
ToolTip.text: qsTr("A name for this device which will be shown to others when verifying your devices. If nothing is provided a default is used.")
|
visible: regis.supported
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.preferredHeight: Nheko.avatarSize
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Nheko.avatarSize
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
height: parent.height
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
visible: running
|
|
||||||
running: regis.registering
|
|
||||||
foreground: palette.mid
|
foreground: palette.mid
|
||||||
|
height: parent.height
|
||||||
|
running: regis.registering
|
||||||
|
visible: running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
textFormat: Text.PlainText
|
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
text: registrationPage.error
|
text: registrationPage.error
|
||||||
|
textFormat: Text.PlainText
|
||||||
visible: text
|
visible: text
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
id: regisBtn
|
id: regisBtn
|
||||||
visible: regis.supported
|
|
||||||
enabled: usernameLabel.text && passwordLabel.text && passwordLabel.text == passwordConfirmationLabel.text
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: qsTr("REGISTER")
|
|
||||||
function register() {
|
function register() {
|
||||||
regis.startRegistration(usernameLabel.text, passwordLabel.text, deviceNameLabel.text)
|
regis.startRegistration(usernameLabel.text, passwordLabel.text, deviceNameLabel.text);
|
||||||
}
|
|
||||||
onClicked: regisBtn.register()
|
|
||||||
Keys.onEnterPressed: regisBtn.register()
|
|
||||||
Keys.onReturnPressed: regisBtn.register()
|
|
||||||
Keys.enabled: regisBtn.enabled && regis.supported
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.enabled: regisBtn.enabled && regis.supported
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
enabled: usernameLabel.text && passwordLabel.text && passwordLabel.text == passwordConfirmationLabel.text
|
||||||
|
text: qsTr("REGISTER")
|
||||||
|
visible: regis.supported
|
||||||
|
|
||||||
|
Keys.onEnterPressed: regisBtn.register()
|
||||||
|
Keys.onReturnPressed: regisBtn.register()
|
||||||
|
onClicked: regisBtn.register()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
anchors.top: parent.top
|
ToolTip.text: qsTr("Back")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
width: Nheko.avatarSize
|
anchors.top: parent.top
|
||||||
height: Nheko.avatarSize
|
height: Nheko.avatarSize
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
ToolTip.visible: hovered
|
width: Nheko.avatarSize
|
||||||
ToolTip.text: qsTr("Back")
|
|
||||||
onClicked: mainWindow.pop()
|
onClicked: mainWindow.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ Rectangle {
|
||||||
|
|
||||||
property int collapsePoint: 600
|
property int collapsePoint: 600
|
||||||
property bool collapsed: width < collapsePoint
|
property bool collapsed: width < collapsePoint
|
||||||
|
|
||||||
color: palette.window
|
color: palette.window
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
@ -24,86 +25,90 @@ Rectangle {
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.topMargin: (collapsed ? backButton.height : 0) + Nheko.paddingLarge
|
anchors.topMargin: (collapsed ? backButton.height : 0) + Nheko.paddingLarge
|
||||||
leftPadding: collapsed? Nheko.paddingMedium : Nheko.paddingLarge
|
|
||||||
bottomPadding: Nheko.paddingLarge
|
bottomPadding: Nheko.paddingLarge
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
leftPadding: collapsed ? Nheko.paddingMedium : Nheko.paddingLarge
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: grid
|
id: grid
|
||||||
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
|
|
||||||
width: scroll.availableWidth
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: userSettingsDialog.collapsed ? 0 : (userSettingsDialog.width - userSettingsDialog.collapsePoint) * 0.4 + Nheko.paddingLarge
|
anchors.leftMargin: userSettingsDialog.collapsed ? 0 : (userSettingsDialog.width - userSettingsDialog.collapsePoint) * 0.4 + Nheko.paddingLarge
|
||||||
anchors.rightMargin: anchors.leftMargin
|
anchors.rightMargin: anchors.leftMargin
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
width: scroll.availableWidth
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: UserSettingsModel
|
model: UserSettingsModel
|
||||||
|
|
||||||
delegate: GridLayout {
|
delegate: GridLayout {
|
||||||
width: scroll.availableWidth
|
id: r
|
||||||
|
|
||||||
|
required property var model
|
||||||
|
|
||||||
columns: collapsed ? 1 : 2
|
columns: collapsed ? 1 : 2
|
||||||
rows: collapsed ? 2 : 1
|
rows: collapsed ? 2 : 1
|
||||||
required property var model
|
width: scroll.availableWidth
|
||||||
id: r
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.fillWidth: true
|
|
||||||
color: palette.text
|
|
||||||
text: model.name
|
|
||||||
//Layout.column: 0
|
//Layout.column: 0
|
||||||
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
||||||
|
Layout.fillWidth: true
|
||||||
//Layout.row: model.index
|
//Layout.row: model.index
|
||||||
//Layout.minimumWidth: implicitWidth
|
//Layout.minimumWidth: implicitWidth
|
||||||
Layout.leftMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
Layout.leftMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
||||||
Layout.topMargin: model.type == UserSettingsModel.SectionTitle ? Nheko.paddingLarge : 0
|
Layout.topMargin: model.type == UserSettingsModel.SectionTitle ? Nheko.paddingLarge : 0
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: model.description ?? ""
|
||||||
|
ToolTip.visible: hovered.hovered && model.description
|
||||||
|
color: palette.text
|
||||||
font.pointSize: 1.1 * fontMetrics.font.pointSize
|
font.pointSize: 1.1 * fontMetrics.font.pointSize
|
||||||
|
text: model.name
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hovered
|
id: hovered
|
||||||
|
|
||||||
enabled: model.description ?? false
|
enabled: model.description ?? false
|
||||||
}
|
}
|
||||||
ToolTip.visible: hovered.hovered && model.description
|
|
||||||
ToolTip.text: model.description ?? ""
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
id: chooser
|
id: chooser
|
||||||
|
|
||||||
roleValue: model.type
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
|
||||||
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
|
||||||
|
Layout.fillWidth: model.type == UserSettingsModel.SectionTitle || model.type == UserSettingsModel.Options || model.type == UserSettingsModel.Number
|
||||||
|
Layout.maximumWidth: model.type == UserSettingsModel.SectionTitle ? Number.POSITIVE_INFINITY : 400
|
||||||
Layout.preferredHeight: child.height
|
Layout.preferredHeight: child.height
|
||||||
Layout.preferredWidth: child.implicitWidth
|
Layout.preferredWidth: child.implicitWidth
|
||||||
Layout.maximumWidth: model.type == UserSettingsModel.SectionTitle ? Number.POSITIVE_INFINITY : 400
|
|
||||||
Layout.fillWidth: model.type == UserSettingsModel.SectionTitle || model.type == UserSettingsModel.Options || model.type == UserSettingsModel.Number
|
|
||||||
Layout.rightMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
Layout.rightMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
|
||||||
|
roleValue: model.type
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.Toggle
|
roleValue: UserSettingsModel.Toggle
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
checked: model.value
|
checked: model.value
|
||||||
onCheckedChanged: model.value = checked
|
|
||||||
enabled: model.enabled
|
enabled: model.enabled
|
||||||
|
|
||||||
|
onCheckedChanged: model.value = checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.Options
|
roleValue: UserSettingsModel.Options
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
model: r.model.values
|
|
||||||
currentIndex: r.model.value
|
currentIndex: r.model.value
|
||||||
width: Math.min(implicitWidth, scroll.availableWidth - Nheko.paddingMedium)
|
|
||||||
onCurrentIndexChanged: r.model.value = currentIndex
|
|
||||||
implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted
|
implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted
|
||||||
|
model: r.model.values
|
||||||
|
width: Math.min(implicitWidth, scroll.availableWidth - Nheko.paddingMedium)
|
||||||
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
onCurrentIndexChanged: r.model.value = currentIndex
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
} // suppress scrolling changing values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
|
@ -111,14 +116,16 @@ Rectangle {
|
||||||
|
|
||||||
SpinBox {
|
SpinBox {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
from: model.valueLowerBound
|
|
||||||
to: model.valueUpperBound
|
|
||||||
stepSize: model.valueStep
|
|
||||||
value: model.value
|
|
||||||
onValueChanged: model.value = value
|
|
||||||
editable: true
|
editable: true
|
||||||
|
from: model.valueLowerBound
|
||||||
|
stepSize: model.valueStep
|
||||||
|
to: model.valueUpperBound
|
||||||
|
value: model.value
|
||||||
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
onValueChanged: model.value = value
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
} // suppress scrolling changing values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
|
@ -127,54 +134,56 @@ Rectangle {
|
||||||
SpinBox {
|
SpinBox {
|
||||||
id: spinbox
|
id: spinbox
|
||||||
|
|
||||||
readonly property double div: 100
|
|
||||||
readonly property int decimals: 2
|
readonly property int decimals: 2
|
||||||
|
readonly property double div: 100
|
||||||
|
property real realValue: value / div
|
||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
from: model.valueLowerBound * div
|
|
||||||
to: model.valueUpperBound * div
|
|
||||||
stepSize: model.valueStep * div
|
|
||||||
value: model.value * div
|
|
||||||
onValueChanged: model.value = value/div
|
|
||||||
editable: true
|
editable: true
|
||||||
|
from: model.valueLowerBound * div
|
||||||
property real realValue: value / div
|
stepSize: model.valueStep * div
|
||||||
|
textFromValue: function (value, locale) {
|
||||||
|
return Number(value / spinbox.div).toLocaleString(locale, 'f', spinbox.decimals);
|
||||||
|
}
|
||||||
|
to: model.valueUpperBound * div
|
||||||
|
value: model.value * div
|
||||||
|
valueFromText: function (text, locale) {
|
||||||
|
return Number.fromLocaleString(locale, text) * spinbox.div;
|
||||||
|
}
|
||||||
|
|
||||||
validator: DoubleValidator {
|
validator: DoubleValidator {
|
||||||
bottom: Math.min(spinbox.from / spinbox.div, spinbox.to / spinbox.div)
|
bottom: Math.min(spinbox.from / spinbox.div, spinbox.to / spinbox.div)
|
||||||
top: Math.max(spinbox.from / spinbox.div, spinbox.to / spinbox.div)
|
top: Math.max(spinbox.from / spinbox.div, spinbox.to / spinbox.div)
|
||||||
}
|
}
|
||||||
|
|
||||||
textFromValue: function(value, locale) {
|
onValueChanged: model.value = value / div
|
||||||
return Number(value / spinbox.div).toLocaleString(locale, 'f', spinbox.decimals)
|
|
||||||
}
|
|
||||||
|
|
||||||
valueFromText: function(text, locale) {
|
WheelHandler {
|
||||||
return Number.fromLocaleString(locale, text) * spinbox.div
|
} // suppress scrolling changing values
|
||||||
}
|
|
||||||
|
|
||||||
WheelHandler{} // suppress scrolling changing values
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.ReadOnlyText
|
roleValue: UserSettingsModel.ReadOnlyText
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
color: palette.text
|
color: palette.text
|
||||||
text: model.value
|
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
text: model.value
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.SectionTitle
|
roleValue: UserSettingsModel.SectionTitle
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: grid.width
|
|
||||||
height: fontMetrics.lineSpacing
|
height: fontMetrics.lineSpacing
|
||||||
|
width: grid.width
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.topMargin: Nheko.paddingSmall
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Nheko.paddingSmall
|
||||||
color: palette.buttonText
|
color: palette.buttonText
|
||||||
height: 1
|
height: 1
|
||||||
}
|
}
|
||||||
|
@ -182,6 +191,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.KeyStatus
|
roleValue: UserSettingsModel.KeyStatus
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
color: model.good ? "green" : Nheko.theme.error
|
color: model.good ? "green" : Nheko.theme.error
|
||||||
text: model.value ? qsTr("CACHED") : qsTr("NOT CACHED")
|
text: model.value ? qsTr("CACHED") : qsTr("NOT CACHED")
|
||||||
|
@ -189,34 +199,42 @@ Rectangle {
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.SessionKeyImportExport
|
roleValue: UserSettingsModel.SessionKeyImportExport
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("IMPORT")
|
text: qsTr("IMPORT")
|
||||||
|
|
||||||
onClicked: UserSettingsModel.importSessionKeys()
|
onClicked: UserSettingsModel.importSessionKeys()
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("EXPORT")
|
text: qsTr("EXPORT")
|
||||||
|
|
||||||
onClicked: UserSettingsModel.exportSessionKeys()
|
onClicked: UserSettingsModel.exportSessionKeys()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.XSignKeysRequestDownload
|
roleValue: UserSettingsModel.XSignKeysRequestDownload
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("DOWNLOAD")
|
text: qsTr("DOWNLOAD")
|
||||||
|
|
||||||
onClicked: UserSettingsModel.downloadCrossSigningSecrets()
|
onClicked: UserSettingsModel.downloadCrossSigningSecrets()
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("REQUEST")
|
text: qsTr("REQUEST")
|
||||||
|
|
||||||
onClicked: UserSettingsModel.requestCrossSigningSecrets()
|
onClicked: UserSettingsModel.requestCrossSigningSecrets()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.ConfigureHiddenEvents
|
roleValue: UserSettingsModel.ConfigureHiddenEvents
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("CONFIGURE")
|
text: qsTr("CONFIGURE")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var dialog = hiddenEventsDialog.createObject();
|
var dialog = hiddenEventsDialog.createObject();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
@ -226,15 +244,17 @@ Rectangle {
|
||||||
Component {
|
Component {
|
||||||
id: hiddenEventsDialog
|
id: hiddenEventsDialog
|
||||||
|
|
||||||
HiddenEventsDialog {}
|
HiddenEventsDialog {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: UserSettingsModel.ManageIgnoredUsers
|
roleValue: UserSettingsModel.ManageIgnoredUsers
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("MANAGE")
|
text: qsTr("MANAGE")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var dialog = ignoredUsersDialog.createObject();
|
var dialog = ignoredUsersDialog.createObject();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
@ -244,11 +264,11 @@ Rectangle {
|
||||||
Component {
|
Component {
|
||||||
id: ignoredUsersDialog
|
id: ignoredUsersDialog
|
||||||
|
|
||||||
IgnoredUsers {}
|
IgnoredUsers {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
Text {
|
Text {
|
||||||
text: model.value
|
text: model.value
|
||||||
|
@ -259,19 +279,18 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: backButton
|
id: backButton
|
||||||
anchors.top: parent.top
|
|
||||||
|
ToolTip.text: qsTr("Back")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
width: Nheko.avatarSize
|
anchors.top: parent.top
|
||||||
height: Nheko.avatarSize
|
height: Nheko.avatarSize
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
ToolTip.visible: hovered
|
width: Nheko.avatarSize
|
||||||
ToolTip.text: qsTr("Back")
|
|
||||||
onClicked: mainWindow.pop()
|
onClicked: mainWindow.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,86 +14,83 @@ ColumnLayout {
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
source: "qrc:/logos/splash.png"
|
|
||||||
Layout.preferredHeight: 256
|
Layout.preferredHeight: 256
|
||||||
Layout.preferredWidth: 256
|
Layout.preferredWidth: 256
|
||||||
|
source: "qrc:/logos/splash.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingLarge
|
|
||||||
Layout.bottomMargin: 0
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.bottomMargin: 0
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.")
|
Layout.margins: Nheko.paddingLarge
|
||||||
color: palette.text
|
color: palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
wrapMode: Text.Wrap
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingLarge
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: qsTr("Enjoy your stay!")
|
Layout.margins: Nheko.paddingLarge
|
||||||
color: palette.text
|
color: palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.5
|
font.pointSize: fontMetrics.font.pointSize * 1.5
|
||||||
wrapMode: Text.Wrap
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Enjoy your stay!")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
FlatButton {
|
FlatButton {
|
||||||
Layout.margins: Nheko.paddingLarge
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.margins: Nheko.paddingLarge
|
||||||
text: qsTr("REGISTER")
|
text: qsTr("REGISTER")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainWindow.push(registerPage);
|
mainWindow.push(registerPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FlatButton {
|
FlatButton {
|
||||||
Layout.margins: Nheko.paddingLarge
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.margins: Nheko.paddingLarge
|
||||||
text: qsTr("LOGIN")
|
text: qsTr("LOGIN")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainWindow.push(loginPage);
|
mainWindow.push(loginPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.margins: Nheko.paddingLarge
|
Layout.margins: Nheko.paddingLarge
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
Layout.margins: Nheko.paddingLarge
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
Layout.margins: Nheko.paddingLarge
|
||||||
checked: Settings.reducedMotion
|
checked: Settings.reducedMotion
|
||||||
|
|
||||||
onCheckedChanged: Settings.reducedMotion = checked
|
onCheckedChanged: Settings.reducedMotion = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.margins: Nheko.paddingLarge
|
Layout.margins: Nheko.paddingLarge
|
||||||
text: qsTr("Reduce animations")
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Nheko uses animations in several places to make stuff pretty. This allows you to turn those off if they make you feel unwell.")
|
||||||
|
ToolTip.visible: hovered.hovered
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: qsTr("Reduce animations")
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hovered
|
id: hovered
|
||||||
|
|
||||||
}
|
}
|
||||||
ToolTip.visible: hovered.hovered
|
|
||||||
ToolTip.text: qsTr("Nheko uses animations in several places to make stuff pretty. This allows you to turn those off if they make you feel unwell.")
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -9,42 +9,39 @@ import im.nheko 1.0
|
||||||
Slider {
|
Slider {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property color progressColor: palette.highlight
|
|
||||||
property bool alwaysShowSlider: true
|
property bool alwaysShowSlider: true
|
||||||
|
property color progressColor: palette.highlight
|
||||||
property int sliderRadius: 16
|
property int sliderRadius: 16
|
||||||
|
|
||||||
value: 0
|
|
||||||
implicitHeight: sliderRadius
|
implicitHeight: sliderRadius
|
||||||
padding: 0
|
padding: 0
|
||||||
|
value: 0
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
|
color: palette.buttonText
|
||||||
|
height: implicitHeight
|
||||||
|
implicitHeight: control.sliderRadius / 4
|
||||||
|
implicitWidth: 200
|
||||||
|
radius: height / 2
|
||||||
|
width: control.availableWidth - handle.width
|
||||||
x: control.leftPadding + handle.width / 2
|
x: control.leftPadding + handle.width / 2
|
||||||
y: control.topPadding + control.availableHeight / 2 - height / 2
|
y: control.topPadding + control.availableHeight / 2 - height / 2
|
||||||
implicitWidth: 200
|
|
||||||
implicitHeight: control.sliderRadius / 4
|
|
||||||
width: control.availableWidth - handle.width
|
|
||||||
height: implicitHeight
|
|
||||||
radius: height / 2
|
|
||||||
color: palette.buttonText
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: control.visualPosition * parent.width
|
|
||||||
height: parent.height
|
|
||||||
color: control.progressColor
|
color: control.progressColor
|
||||||
|
height: parent.height
|
||||||
radius: 2
|
radius: 2
|
||||||
|
width: control.visualPosition * parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handle: Rectangle {
|
handle: Rectangle {
|
||||||
|
border.color: control.progressColor
|
||||||
|
color: control.progressColor
|
||||||
|
implicitHeight: control.sliderRadius
|
||||||
|
implicitWidth: control.sliderRadius
|
||||||
|
radius: control.sliderRadius / 2
|
||||||
|
visible: Settings.mobileMode || control.alwaysShowSlider || control.hovered || control.pressed
|
||||||
x: control.leftPadding + control.visualPosition * background.width
|
x: control.leftPadding + control.visualPosition * background.width
|
||||||
y: control.topPadding + control.availableHeight / 2 - height / 2
|
y: control.topPadding + control.availableHeight / 2 - height / 2
|
||||||
implicitWidth: control.sliderRadius
|
|
||||||
implicitHeight: control.sliderRadius
|
|
||||||
radius: control.sliderRadius / 2
|
|
||||||
color: control.progressColor
|
|
||||||
visible: Settings.mobileMode || control.alwaysShowSlider || control.hovered || control.pressed
|
|
||||||
border.color: control.progressColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ Item {
|
||||||
|
|
||||||
property color color: "#22000000"
|
property color color: "#22000000"
|
||||||
property real maxRadius: Math.max(width, height)
|
property real maxRadius: Math.max(width, height)
|
||||||
|
readonly property real opacityAnimationDuration: 300
|
||||||
readonly property real radiusAnimationRate: 0.05
|
readonly property real radiusAnimationRate: 0.05
|
||||||
readonly property real radiusTailAnimationRate: 0.5
|
readonly property real radiusTailAnimationRate: 0.5
|
||||||
readonly property real opacityAnimationDuration: 300
|
|
||||||
property var rippleTarget: parent
|
property var rippleTarget: parent
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -19,18 +19,13 @@ Item {
|
||||||
PointHandler {
|
PointHandler {
|
||||||
id: ph
|
id: ph
|
||||||
|
|
||||||
onGrabChanged: (_, point) => {
|
|
||||||
circle.centerX = point.position.x
|
|
||||||
circle.centerY = point.position.y
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Rectangle {
|
target: Rectangle {
|
||||||
id: backgroundLayer
|
id: backgroundLayer
|
||||||
parent: rippleTarget
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
|
||||||
clip: true
|
clip: true
|
||||||
|
color: "transparent"
|
||||||
|
parent: rippleTarget
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: circle
|
id: circle
|
||||||
|
@ -38,15 +33,14 @@ Item {
|
||||||
property real centerX
|
property real centerX
|
||||||
property real centerY
|
property real centerY
|
||||||
|
|
||||||
|
color: ripple.color
|
||||||
|
height: radius * 2
|
||||||
|
radius: 0
|
||||||
|
state: ph.active ? "ACTIVE" : "NORMAL"
|
||||||
|
width: radius * 2
|
||||||
x: centerX - radius
|
x: centerX - radius
|
||||||
y: centerY - radius
|
y: centerY - radius
|
||||||
|
|
||||||
height: radius*2
|
|
||||||
width: radius*2
|
|
||||||
radius: 0
|
|
||||||
color: ripple.color
|
|
||||||
|
|
||||||
state: ph.active ? "ACTIVE" : "NORMAL"
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "NORMAL"
|
name: "NORMAL"
|
||||||
|
@ -63,26 +57,30 @@ Item {
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
//PropertyAction { target: circle; property: "centerX"; value: ph.point.position.x }
|
//PropertyAction { target: circle; property: "centerX"; value: ph.point.position.x }
|
||||||
//PropertyAction { target: circle; property: "centerY"; value: ph.point.position.y }
|
//PropertyAction { target: circle; property: "centerY"; value: ph.point.position.y }
|
||||||
PropertyAction { target: circle; property: "visible"; value: true }
|
PropertyAction {
|
||||||
PropertyAction { target: circle; property: "opacity"; value: 1 }
|
property: "visible"
|
||||||
|
target: circle
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
PropertyAction {
|
||||||
|
property: "opacity"
|
||||||
|
target: circle
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: radius_animation
|
id: radius_animation
|
||||||
|
|
||||||
target: circle
|
|
||||||
properties: "radius"
|
|
||||||
from: 0
|
|
||||||
to: ripple.maxRadius
|
|
||||||
duration: ripple.maxRadius / ripple.radiusAnimationRate
|
duration: ripple.maxRadius / ripple.radiusAnimationRate
|
||||||
|
from: 0
|
||||||
|
properties: "radius"
|
||||||
|
target: circle
|
||||||
|
to: ripple.maxRadius
|
||||||
|
|
||||||
easing {
|
easing {
|
||||||
type: Easing.OutQuad
|
type: Easing.OutQuad
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Transition {
|
Transition {
|
||||||
from: "ACTIVE"
|
from: "ACTIVE"
|
||||||
|
@ -93,37 +91,42 @@ Item {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: radius_tail_animation
|
id: radius_tail_animation
|
||||||
|
|
||||||
target: circle
|
|
||||||
properties: "radius"
|
|
||||||
to: ripple.maxRadius
|
|
||||||
duration: ripple.maxRadius / ripple.radiusTailAnimationRate
|
duration: ripple.maxRadius / ripple.radiusTailAnimationRate
|
||||||
|
properties: "radius"
|
||||||
|
target: circle
|
||||||
|
to: ripple.maxRadius
|
||||||
|
|
||||||
easing {
|
easing {
|
||||||
type: Easing.Linear
|
type: Easing.Linear
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: opacity_animation
|
id: opacity_animation
|
||||||
|
|
||||||
target: circle
|
|
||||||
properties: "opacity"
|
|
||||||
to: 0
|
|
||||||
duration: ripple.opacityAnimationDuration
|
duration: ripple.opacityAnimationDuration
|
||||||
|
properties: "opacity"
|
||||||
|
target: circle
|
||||||
|
to: 0
|
||||||
|
|
||||||
easing {
|
easing {
|
||||||
type: Easing.InQuad
|
type: Easing.InQuad
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
PropertyAction { target: circle; property: "visible"; value: false }
|
PropertyAction {
|
||||||
|
property: "visible"
|
||||||
|
target: circle
|
||||||
|
value: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onGrabChanged: (_, point) => {
|
||||||
|
circle.centerX = point.position.x;
|
||||||
|
circle.centerY = point.position.y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,8 @@ import im.nheko 1.0
|
||||||
Popup {
|
Popup {
|
||||||
id: snackbar
|
id: snackbar
|
||||||
|
|
||||||
// Workaround palettes not inheriting for popups
|
|
||||||
palette: timelineRoot.palette
|
|
||||||
|
|
||||||
property var messages: []
|
|
||||||
property string currentMessage: ""
|
property string currentMessage: ""
|
||||||
|
property var messages: []
|
||||||
|
|
||||||
function showNotification(msg) {
|
function showNotification(msg) {
|
||||||
messages.push(msg);
|
messages.push(msg);
|
||||||
|
@ -24,10 +21,61 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
opacity: 0
|
||||||
id: dismissTimer
|
padding: Nheko.paddingLarge
|
||||||
interval: 10000
|
|
||||||
onTriggered: snackbar.close()
|
// Workaround palettes not inheriting for popups
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
parent: Overlay.overlay
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: -100
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: palette.dark
|
||||||
|
opacity: 0.8
|
||||||
|
radius: Nheko.paddingLarge
|
||||||
|
}
|
||||||
|
contentItem: Label {
|
||||||
|
color: palette.light
|
||||||
|
font.bold: true
|
||||||
|
text: snackbar.currentMessage
|
||||||
|
width: Math.max(snackbar.Overlay.overlay ? snackbar.Overlay.overlay.width / 2 : 0, 400)
|
||||||
|
}
|
||||||
|
enter: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
from: 0.0
|
||||||
|
property: "opacity"
|
||||||
|
target: snackbar
|
||||||
|
to: 1.0
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
from: -100
|
||||||
|
properties: "y"
|
||||||
|
target: snackbar
|
||||||
|
to: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InCubic
|
||||||
|
from: 1.0
|
||||||
|
property: "opacity"
|
||||||
|
target: snackbar
|
||||||
|
to: 0.0
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InCubic
|
||||||
|
from: 100
|
||||||
|
properties: "y"
|
||||||
|
target: snackbar
|
||||||
|
to: -100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAboutToHide: {
|
onAboutToHide: {
|
||||||
|
@ -41,61 +89,11 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parent: Overlay.overlay
|
Timer {
|
||||||
opacity: 0
|
id: dismissTimer
|
||||||
y: -100
|
|
||||||
x: (parent.width - width)/2
|
|
||||||
padding: Nheko.paddingLarge
|
|
||||||
|
|
||||||
contentItem: Label {
|
interval: 10000
|
||||||
color: palette.light
|
|
||||||
width: Math.max(snackbar.Overlay.overlay? snackbar.Overlay.overlay.width/2 : 0, 400)
|
|
||||||
text: snackbar.currentMessage
|
|
||||||
font.bold: true
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
onTriggered: snackbar.close()
|
||||||
radius: Nheko.paddingLarge
|
|
||||||
color: palette.dark
|
|
||||||
opacity: 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
enter: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
target: snackbar
|
|
||||||
property: "opacity"
|
|
||||||
from: 0.0
|
|
||||||
to: 1.0
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
NumberAnimation {
|
|
||||||
target: snackbar
|
|
||||||
properties: "y"
|
|
||||||
from: -100
|
|
||||||
to: 100
|
|
||||||
duration: 1000
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exit: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
target: snackbar
|
|
||||||
property: "opacity"
|
|
||||||
from: 1.0
|
|
||||||
to: 0.0
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.InCubic
|
|
||||||
}
|
|
||||||
NumberAnimation {
|
|
||||||
target: snackbar
|
|
||||||
properties: "y"
|
|
||||||
to: -100
|
|
||||||
from: 100
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.InCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,15 @@ import QtQuick.Effects
|
||||||
Item {
|
Item {
|
||||||
id: spinner
|
id: spinner
|
||||||
|
|
||||||
property int spacing: 0
|
|
||||||
property bool running: true
|
|
||||||
property var foreground: "#333"
|
|
||||||
readonly property int barCount: 6
|
|
||||||
readonly property real a: Math.PI / 6
|
readonly property real a: Math.PI / 6
|
||||||
readonly property var colors: ["#c0def5", "#87aade", "white"]
|
|
||||||
readonly property var anims: [anim1, anim2, anim3, anim4, anim5, anim6]
|
readonly property var anims: [anim1, anim2, anim3, anim4, anim5, anim6]
|
||||||
readonly property int pauseDuration: barCount * 150
|
readonly property int barCount: 6
|
||||||
|
readonly property var colors: ["#c0def5", "#87aade", "white"]
|
||||||
|
property var foreground: "#333"
|
||||||
readonly property int glowDuration: 300
|
readonly property int glowDuration: 300
|
||||||
|
readonly property int pauseDuration: barCount * 150
|
||||||
|
property bool running: true
|
||||||
|
property int spacing: 0
|
||||||
|
|
||||||
height: 40
|
height: 40
|
||||||
width: barCount * (height * 0.375)
|
width: barCount * (height * 0.375)
|
||||||
|
@ -25,131 +25,116 @@ Item {
|
||||||
Row {
|
Row {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect1
|
|
||||||
|
|
||||||
width: ((spinner.width / spinner.barCount) - (spinner.spacing)) * 1.5
|
|
||||||
height: spinner.height / 3.5
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect2
|
|
||||||
|
|
||||||
width: (spinner.width / spinner.barCount) - spinner.spacing
|
|
||||||
height: spinner.height
|
|
||||||
color: spinner.colors[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect3
|
|
||||||
|
|
||||||
width: (spinner.width / spinner.barCount) - spinner.spacing
|
|
||||||
height: spinner.height
|
|
||||||
color: spinner.colors[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect4
|
|
||||||
|
|
||||||
width: (spinner.width / spinner.barCount) - spinner.spacing
|
|
||||||
height: spinner.height
|
|
||||||
color: spinner.colors[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect5
|
|
||||||
|
|
||||||
width: (spinner.width / (spinner.barCount + 1)) - spinner.spacing
|
|
||||||
height: spinner.height / 3.5
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rect6
|
|
||||||
|
|
||||||
width: (spinner.width / spinner.barCount) - spinner.spacing
|
|
||||||
height: spinner.height
|
|
||||||
color: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim1
|
|
||||||
|
|
||||||
target: rect1
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 0 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim2
|
|
||||||
|
|
||||||
target: rect2
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 1 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim3
|
|
||||||
|
|
||||||
target: rect3
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 2 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim4
|
|
||||||
|
|
||||||
target: rect4
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 3 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim5
|
|
||||||
|
|
||||||
target: rect5
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 4 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
BlinkAnimation {
|
|
||||||
id: anim6
|
|
||||||
|
|
||||||
target: rect6
|
|
||||||
running: spinner.running
|
|
||||||
pauseDuration: spinner.pauseDuration
|
|
||||||
glowDuration: spinner.glowDuration
|
|
||||||
offset: 5 / spinner.barCount
|
|
||||||
}
|
|
||||||
|
|
||||||
transform: Matrix4x4 {
|
transform: Matrix4x4 {
|
||||||
matrix: Qt.matrix4x4(Math.cos(spinner.a), -Math.sin(spinner.a), 0, 0, 0, Math.cos(spinner.a), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
matrix: Qt.matrix4x4(Math.cos(spinner.a), -Math.sin(spinner.a), 0, 0, 0, Math.cos(spinner.a), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Rectangle {
|
||||||
|
id: rect1
|
||||||
|
|
||||||
|
color: "white"
|
||||||
|
height: spinner.height / 3.5
|
||||||
|
width: ((spinner.width / spinner.barCount) - (spinner.spacing)) * 1.5
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect2
|
||||||
|
|
||||||
|
color: spinner.colors[0]
|
||||||
|
height: spinner.height
|
||||||
|
width: (spinner.width / spinner.barCount) - spinner.spacing
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect3
|
||||||
|
|
||||||
|
color: spinner.colors[1]
|
||||||
|
height: spinner.height
|
||||||
|
width: (spinner.width / spinner.barCount) - spinner.spacing
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect4
|
||||||
|
|
||||||
|
color: spinner.colors[2]
|
||||||
|
height: spinner.height
|
||||||
|
width: (spinner.width / spinner.barCount) - spinner.spacing
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect5
|
||||||
|
|
||||||
|
color: "white"
|
||||||
|
height: spinner.height / 3.5
|
||||||
|
width: (spinner.width / (spinner.barCount + 1)) - spinner.spacing
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: rect6
|
||||||
|
|
||||||
|
color: "white"
|
||||||
|
height: spinner.height
|
||||||
|
width: (spinner.width / spinner.barCount) - spinner.spacing
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim1
|
||||||
|
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 0 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect1
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim2
|
||||||
|
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 1 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect2
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim3
|
||||||
|
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 2 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect3
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim4
|
||||||
|
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 3 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect4
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim5
|
||||||
|
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 4 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect5
|
||||||
|
}
|
||||||
|
BlinkAnimation {
|
||||||
|
id: anim6
|
||||||
|
|
||||||
|
glowDuration: spinner.glowDuration
|
||||||
|
offset: 5 / spinner.barCount
|
||||||
|
pauseDuration: spinner.pauseDuration
|
||||||
|
running: spinner.running
|
||||||
|
target: rect6
|
||||||
|
}
|
||||||
|
}
|
||||||
MultiEffect {
|
MultiEffect {
|
||||||
anchors.fill: row
|
anchors.fill: row
|
||||||
shadowBlur: 14
|
shadowBlur: 14
|
||||||
shadowEnabled: true
|
|
||||||
shadowColor: spinner.foreground
|
shadowColor: spinner.foreground
|
||||||
|
shadowEnabled: true
|
||||||
source: row
|
source: row
|
||||||
|
|
||||||
transform: Matrix4x4 {
|
transform: Matrix4x4 {
|
||||||
matrix: Qt.matrix4x4(Math.cos(spinner.a), -Math.sin(spinner.a), 0, 0, 0, Math.cos(spinner.a), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
matrix: Qt.matrix4x4(Math.cos(spinner.a), -Math.sin(spinner.a), 0, 0, 0, Math.cos(spinner.a), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,102 +7,99 @@ import QtQuick.Particles 2.15
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: effectRoot
|
id: effectRoot
|
||||||
|
|
||||||
readonly property int maxLifespan: Math.max(confettiEmitter.lifeSpan, rainfallEmitter.lifeSpan)
|
readonly property int maxLifespan: Math.max(confettiEmitter.lifeSpan, rainfallEmitter.lifeSpan)
|
||||||
required property bool shouldEffectsRun
|
required property bool shouldEffectsRun
|
||||||
|
|
||||||
|
function pulseConfetti() {
|
||||||
|
confettiEmitter.pulse(effectRoot.height * 2);
|
||||||
|
}
|
||||||
|
function pulseRainfall() {
|
||||||
|
rainfallEmitter.pulse(effectRoot.height * 3.3);
|
||||||
|
}
|
||||||
|
function removeParticles() {
|
||||||
|
particleSystem.reset();
|
||||||
|
}
|
||||||
|
|
||||||
visible: effectRoot.shouldEffectsRun
|
visible: effectRoot.shouldEffectsRun
|
||||||
|
|
||||||
function pulseConfetti()
|
|
||||||
{
|
|
||||||
confettiEmitter.pulse(effectRoot.height * 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
function pulseRainfall()
|
|
||||||
{
|
|
||||||
rainfallEmitter.pulse(effectRoot.height * 3.3)
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeParticles()
|
|
||||||
{
|
|
||||||
particleSystem.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
ParticleSystem {
|
ParticleSystem {
|
||||||
id: particleSystem
|
id: particleSystem
|
||||||
|
|
||||||
Component.onCompleted: stop();
|
|
||||||
paused: !effectRoot.shouldEffectsRun
|
paused: !effectRoot.shouldEffectsRun
|
||||||
running: effectRoot.shouldEffectsRun
|
running: effectRoot.shouldEffectsRun
|
||||||
}
|
|
||||||
|
|
||||||
|
Component.onCompleted: stop()
|
||||||
|
}
|
||||||
Emitter {
|
Emitter {
|
||||||
id: confettiEmitter
|
id: confettiEmitter
|
||||||
|
|
||||||
group: "confetti"
|
|
||||||
width: effectRoot.width * 3/4
|
|
||||||
enabled: false
|
|
||||||
anchors.horizontalCenter: effectRoot.horizontalCenter
|
anchors.horizontalCenter: effectRoot.horizontalCenter
|
||||||
y: effectRoot.height
|
|
||||||
emitRate: Math.min(400 * Math.sqrt(effectRoot.width * effectRoot.height) / 870, 1000)
|
emitRate: Math.min(400 * Math.sqrt(effectRoot.width * effectRoot.height) / 870, 1000)
|
||||||
|
enabled: false
|
||||||
|
group: "confetti"
|
||||||
lifeSpan: 15000
|
lifeSpan: 15000
|
||||||
system: particleSystem
|
|
||||||
maximumEmitted: 500
|
maximumEmitted: 500
|
||||||
velocityFromMovement: 8
|
|
||||||
size: 16
|
size: 16
|
||||||
sizeVariation: 4
|
sizeVariation: 4
|
||||||
|
system: particleSystem
|
||||||
|
velocityFromMovement: 8
|
||||||
|
width: effectRoot.width * 3 / 4
|
||||||
|
y: effectRoot.height
|
||||||
|
|
||||||
velocity: PointDirection {
|
velocity: PointDirection {
|
||||||
x: 0
|
x: 0
|
||||||
y: -Math.min(450 * effectRoot.height / 700, 1000)
|
|
||||||
xVariation: Math.min(4 * effectRoot.width / 7, 450)
|
xVariation: Math.min(4 * effectRoot.width / 7, 450)
|
||||||
|
y: -Math.min(450 * effectRoot.height / 700, 1000)
|
||||||
yVariation: 250
|
yVariation: 250
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageParticle {
|
ImageParticle {
|
||||||
system: particleSystem
|
color: "white"
|
||||||
|
colorVariation: 1
|
||||||
|
entryEffect: ImageParticle.None
|
||||||
groups: ["confetti"]
|
groups: ["confetti"]
|
||||||
source: "qrc:/confettiparticle.svg"
|
|
||||||
rotationVelocity: 0
|
rotationVelocity: 0
|
||||||
rotationVelocityVariation: 360
|
rotationVelocityVariation: 360
|
||||||
colorVariation: 1
|
source: "qrc:/confettiparticle.svg"
|
||||||
color: "white"
|
system: particleSystem
|
||||||
entryEffect: ImageParticle.None
|
|
||||||
xVector: PointDirection {
|
xVector: PointDirection {
|
||||||
x: 1
|
x: 1
|
||||||
y: 0
|
|
||||||
xVariation: 0.2
|
xVariation: 0.2
|
||||||
|
y: 0
|
||||||
yVariation: 0.2
|
yVariation: 0.2
|
||||||
}
|
}
|
||||||
yVector: PointDirection {
|
yVector: PointDirection {
|
||||||
x: 0
|
x: 0
|
||||||
y: 0.5
|
|
||||||
xVariation: 0.2
|
xVariation: 0.2
|
||||||
|
y: 0.5
|
||||||
yVariation: 0.2
|
yVariation: 0.2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gravity {
|
Gravity {
|
||||||
system: particleSystem
|
|
||||||
groups: ["confetti"]
|
|
||||||
anchors.fill: effectRoot
|
anchors.fill: effectRoot
|
||||||
magnitude: 350
|
|
||||||
angle: 90
|
angle: 90
|
||||||
|
groups: ["confetti"]
|
||||||
|
magnitude: 350
|
||||||
|
system: particleSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
Emitter {
|
Emitter {
|
||||||
id: rainfallEmitter
|
id: rainfallEmitter
|
||||||
|
|
||||||
group: "rain"
|
|
||||||
width: effectRoot.width
|
|
||||||
enabled: false
|
|
||||||
anchors.horizontalCenter: effectRoot.horizontalCenter
|
anchors.horizontalCenter: effectRoot.horizontalCenter
|
||||||
y: -60
|
|
||||||
emitRate: effectRoot.width / 30
|
emitRate: effectRoot.width / 30
|
||||||
|
enabled: false
|
||||||
|
group: "rain"
|
||||||
lifeSpan: 10000
|
lifeSpan: 10000
|
||||||
system: particleSystem
|
system: particleSystem
|
||||||
|
width: effectRoot.width
|
||||||
|
y: -60
|
||||||
|
|
||||||
velocity: PointDirection {
|
velocity: PointDirection {
|
||||||
x: 0
|
x: 0
|
||||||
y: 400
|
|
||||||
xVariation: 0
|
xVariation: 0
|
||||||
|
y: 400
|
||||||
yVariation: 75
|
yVariation: 75
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,14 +118,15 @@ Item {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
ImageParticle {
|
ImageParticle {
|
||||||
system: particleSystem
|
color: "#0099ff"
|
||||||
|
colorVariation: 0
|
||||||
|
entryEffect: ImageParticle.None
|
||||||
groups: ["rain"]
|
groups: ["rain"]
|
||||||
source: "qrc:/confettiparticle.svg"
|
|
||||||
rotationVelocity: 0
|
rotationVelocity: 0
|
||||||
rotationVelocityVariation: 0
|
rotationVelocityVariation: 0
|
||||||
colorVariation: 0
|
source: "qrc:/confettiparticle.svg"
|
||||||
color: "#0099ff"
|
system: particleSystem
|
||||||
entryEffect: ImageParticle.None
|
|
||||||
xVector: PointDirection {
|
xVector: PointDirection {
|
||||||
x: 0.01
|
x: 0.01
|
||||||
y: 0
|
y: 0
|
||||||
|
|
|
@ -5,27 +5,24 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
property alias target: numberAnimation.target
|
|
||||||
property alias glowDuration: numberAnimation.duration
|
property alias glowDuration: numberAnimation.duration
|
||||||
property int pauseDuration: 150
|
|
||||||
property double offset: 0
|
property double offset: 0
|
||||||
|
property int pauseDuration: 150
|
||||||
|
property alias target: numberAnimation.target
|
||||||
|
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
|
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: pauseDuration * offset
|
duration: pauseDuration * offset
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: numberAnimation
|
id: numberAnimation
|
||||||
|
|
||||||
property: "opacity"
|
|
||||||
from: 0
|
from: 0
|
||||||
|
property: "opacity"
|
||||||
to: 1
|
to: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: pauseDuration * (1 - offset)
|
duration: pauseDuration * (1 - offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,27 +14,22 @@ Rectangle {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property alias desiredVolume: volumeSlider.desiredVolume
|
property alias desiredVolume: volumeSlider.desiredVolume
|
||||||
|
property var duration
|
||||||
|
property bool mediaLoaded: false
|
||||||
|
property var mediaState
|
||||||
property bool muted: false
|
property bool muted: false
|
||||||
property bool playingVideo: false
|
property bool playingVideo: false
|
||||||
property var mediaState
|
|
||||||
property bool mediaLoaded: false
|
|
||||||
property var duration
|
|
||||||
property var positionValue: 0
|
|
||||||
property var position
|
property var position
|
||||||
|
property var positionValue: 0
|
||||||
property bool shouldShowControls: !playingVideo || playerMouseArea.shouldShowControls || volumeSlider.state == "shown"
|
property bool shouldShowControls: !playingVideo || playerMouseArea.shouldShowControls || volumeSlider.state == "shown"
|
||||||
|
|
||||||
signal playPauseActivated()
|
signal loadActivated
|
||||||
signal loadActivated()
|
signal playPauseActivated
|
||||||
|
|
||||||
function showControls() {
|
|
||||||
controlHideTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
function durationToString(duration) {
|
function durationToString(duration) {
|
||||||
function maybeZeroPrepend(time) {
|
function maybeZeroPrepend(time) {
|
||||||
return (time < 10) ? "0" + time.toString() : time.toString();
|
return (time < 10) ? "0" + time.toString() : time.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalSeconds = Math.floor(duration / 1000);
|
var totalSeconds = Math.floor(duration / 1000);
|
||||||
var seconds = totalSeconds % 60;
|
var seconds = totalSeconds % 60;
|
||||||
var minutes = (Math.floor(totalSeconds / 60)) % 60;
|
var minutes = (Math.floor(totalSeconds / 60)) % 60;
|
||||||
|
@ -45,16 +40,25 @@ Rectangle {
|
||||||
var hh = hours.toString();
|
var hh = hours.toString();
|
||||||
if (hours < 1)
|
if (hours < 1)
|
||||||
return mm + ":" + ss;
|
return mm + ":" + ss;
|
||||||
|
|
||||||
return hh + ":" + mm + ":" + ss;
|
return hh + ":" + mm + ":" + ss;
|
||||||
}
|
}
|
||||||
|
function showControls() {
|
||||||
|
controlHideTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
var wc = palette.alternateBase;
|
var wc = palette.alternateBase;
|
||||||
return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
|
return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
|
||||||
}
|
}
|
||||||
opacity: control.shouldShowControls ? 1 : 0
|
|
||||||
height: controlLayout.implicitHeight
|
height: controlLayout.implicitHeight
|
||||||
|
opacity: control.shouldShowControls ? 1 : 0
|
||||||
|
|
||||||
|
// Fade controls in/out
|
||||||
|
Behavior on opacity {
|
||||||
|
OpacityAnimator {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: playerMouseArea
|
id: playerMouseArea
|
||||||
|
@ -63,41 +67,40 @@ Rectangle {
|
||||||
|
|
||||||
onHoveredChanged: showControls()
|
onHoveredChanged: showControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: controlLayout
|
id: controlLayout
|
||||||
|
|
||||||
enabled: control.shouldShowControls
|
|
||||||
spacing: 0
|
|
||||||
anchors.bottom: control.bottom
|
anchors.bottom: control.bottom
|
||||||
anchors.left: control.left
|
anchors.left: control.left
|
||||||
anchors.right: control.right
|
anchors.right: control.right
|
||||||
|
enabled: control.shouldShowControls
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
NhekoSlider {
|
NhekoSlider {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: Nheko.paddingSmall
|
Layout.leftMargin: Nheko.paddingSmall
|
||||||
Layout.rightMargin: Nheko.paddingSmall
|
Layout.rightMargin: Nheko.paddingSmall
|
||||||
|
alwaysShowSlider: false
|
||||||
enabled: control.mediaLoaded
|
enabled: control.mediaLoaded
|
||||||
value: control.positionValue
|
|
||||||
onMoved: control.position = value
|
|
||||||
from: 0
|
from: 0
|
||||||
to: control.duration
|
to: control.duration
|
||||||
alwaysShowSlider: false
|
value: control.positionValue
|
||||||
}
|
|
||||||
|
|
||||||
|
onMoved: control.position = value
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.margins: Nheko.paddingSmall
|
Layout.margins: Nheko.paddingSmall
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
// Cache/Play/pause button
|
// Cache/Play/pause button
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: playbackStateImage
|
id: playbackStateImage
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
buttonTextColor: palette.text
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
|
buttonTextColor: palette.text
|
||||||
image: {
|
image: {
|
||||||
if (control.mediaLoaded) {
|
if (control.mediaLoaded) {
|
||||||
if (control.mediaState == MediaPlayer.PlayingState)
|
if (control.mediaState == MediaPlayer.PlayingState)
|
||||||
|
@ -108,38 +111,47 @@ Rectangle {
|
||||||
return ":/icons/icons/ui/download.svg";
|
return ":/icons/icons/ui/download.svg";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated()
|
onClicked: control.mediaLoaded ? control.playPauseActivated() : control.loadActivated()
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: volumeButton
|
id: volumeButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
buttonTextColor: palette.text
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
|
buttonTextColor: palette.text
|
||||||
image: {
|
image: {
|
||||||
if (control.muted || control.desiredVolume <= 0)
|
if (control.muted || control.desiredVolume <= 0)
|
||||||
return ":/icons/icons/ui/volume-off-indicator.svg";
|
return ":/icons/icons/ui/volume-off-indicator.svg";
|
||||||
else
|
else
|
||||||
return ":/icons/icons/ui/volume-up.svg";
|
return ":/icons/icons/ui/volume-up.svg";
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: control.muted = !control.muted
|
onClicked: control.muted = !control.muted
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoSlider {
|
NhekoSlider {
|
||||||
id: volumeSlider
|
id: volumeSlider
|
||||||
|
|
||||||
property real desiredVolume: volumeSlider.value
|
property real desiredVolume: volumeSlider.value
|
||||||
|
|
||||||
state: ""
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.preferredWidth: 0
|
Layout.preferredWidth: 0
|
||||||
opacity: 0
|
opacity: 0
|
||||||
orientation: Qt.Horizontal
|
orientation: Qt.Horizontal
|
||||||
|
state: ""
|
||||||
value: 1
|
value: 1
|
||||||
onDesiredVolumeChanged: {
|
|
||||||
control.muted = !(desiredVolume > 0);
|
states: State {
|
||||||
|
name: "shown"
|
||||||
|
when: Settings.mobileMode || volumeButton.hovered || volumeSlider.hovered || volumeSlider.pressed
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
volumeSlider.implicitWidth: 100
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
volumeSlider.opacity: 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
transitions: [
|
transitions: [
|
||||||
Transition {
|
Transition {
|
||||||
|
@ -150,20 +162,16 @@ Rectangle {
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 50
|
duration: 50
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: 100
|
duration: 100
|
||||||
properties: "opacity"
|
|
||||||
easing.type: Easing.InQuad
|
easing.type: Easing.InQuad
|
||||||
|
properties: "opacity"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: "Layout.preferredWidth"
|
|
||||||
duration: 150
|
duration: 150
|
||||||
|
properties: "Layout.preferredWidth"
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Transition {
|
Transition {
|
||||||
from: "shown"
|
from: "shown"
|
||||||
|
@ -173,54 +181,34 @@ Rectangle {
|
||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 100
|
duration: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: 100
|
duration: 100
|
||||||
properties: "opacity"
|
|
||||||
easing.type: Easing.InQuad
|
easing.type: Easing.InQuad
|
||||||
|
properties: "opacity"
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: "Layout.preferredWidth"
|
|
||||||
duration: 150
|
duration: 150
|
||||||
|
properties: "Layout.preferredWidth"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
states: State {
|
onDesiredVolumeChanged: {
|
||||||
name: "shown"
|
control.muted = !(desiredVolume > 0);
|
||||||
when: Settings.mobileMode || volumeButton.hovered || volumeSlider.hovered || volumeSlider.pressed
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
volumeSlider.implicitWidth: 100
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
volumeSlider.opacity: 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: (!control.mediaLoaded ? "-- " : durationToString(control.positionValue)) + " / " + durationToString(control.duration)
|
|
||||||
color: palette.text
|
color: palette.text
|
||||||
|
text: (!control.mediaLoaded ? "-- " : durationToString(control.positionValue)) + " / " + durationToString(control.duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For hiding controls on stationary cursor
|
// For hiding controls on stationary cursor
|
||||||
|
@ -230,13 +218,4 @@ Rectangle {
|
||||||
interval: 1500 //ms
|
interval: 1500 //ms
|
||||||
repeat: false
|
repeat: false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fade controls in/out
|
|
||||||
Behavior on opacity {
|
|
||||||
OpacityAnimator {
|
|
||||||
duration: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,51 +9,48 @@ import QtQuick.Layouts 1.2
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: CallManager.isOnCall
|
|
||||||
color: callInviteBar.color
|
color: callInviteBar.color
|
||||||
implicitHeight: visible ? rowLayout.height + 8 : 0
|
implicitHeight: visible ? rowLayout.height + 8 : 0
|
||||||
|
visible: CallManager.isOnCall
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (CallManager.callType != Voip.VOICE)
|
if (CallManager.callType != Voip.VOICE)
|
||||||
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
|
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: rowLayout
|
id: rowLayout
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: 8
|
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
implicitWidth: Nheko.avatarSize
|
displayName: CallManager.callPartyDisplayName
|
||||||
implicitHeight: Nheko.avatarSize
|
implicitHeight: Nheko.avatarSize
|
||||||
|
implicitWidth: Nheko.avatarSize
|
||||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: CallManager.callParty
|
userid: CallManager.callParty
|
||||||
displayName: CallManager.callPartyDisplayName
|
|
||||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
|
color: "#000000"
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
text: CallManager.callPartyDisplayName
|
text: CallManager.callPartyDisplayName
|
||||||
color: "#000000"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: callTypeIcon
|
id: callTypeIcon
|
||||||
|
|
||||||
Layout.leftMargin: 4
|
Layout.leftMargin: 4
|
||||||
Layout.preferredWidth: 24
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
|
Layout.preferredWidth: 24
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
|
@ -63,7 +60,6 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
callTypeIcon.source: "qrc:/icons/icons/ui/place-call.svg"
|
callTypeIcon.source: "qrc:/icons/icons/ui/place-call.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "VIDEO"
|
name: "VIDEO"
|
||||||
|
@ -72,7 +68,6 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
callTypeIcon.source: "qrc:/icons/icons/ui/video.svg"
|
callTypeIcon.source: "qrc:/icons/icons/ui/video.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "SCREEN"
|
name: "SCREEN"
|
||||||
|
@ -81,18 +76,15 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
callTypeIcon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
callTypeIcon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: callStateLabel
|
id: callStateLabel
|
||||||
|
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
|
||||||
color: "#000000"
|
color: "#000000"
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
|
@ -102,7 +94,6 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
callStateLabel.text: qsTr("Calling...")
|
callStateLabel.text: qsTr("Calling...")
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "CONNECTING"
|
name: "CONNECTING"
|
||||||
|
@ -111,7 +102,6 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
callStateLabel.text: qsTr("Connecting...")
|
callStateLabel.text: qsTr("Connecting...")
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "ANSWERSENT"
|
name: "ANSWERSENT"
|
||||||
|
@ -120,7 +110,6 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
callStateLabel.text: qsTr("Connecting...")
|
callStateLabel.text: qsTr("Connecting...")
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "CONNECTED"
|
name: "CONNECTED"
|
||||||
|
@ -129,15 +118,12 @@ Rectangle {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
callStateLabel.text: "00:00"
|
callStateLabel.text: "00:00"
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
callTimer.startTime: Math.floor((new Date()).getTime() / 1000)
|
callTimer.startTime: Math.floor((new Date()).getTime() / 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
stackLayout.currentIndex: CallManager.callType != Voip.VOICE ? 1 : 0
|
stackLayout.currentIndex: CallManager.callType != Voip.VOICE ? 1 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "DISCONNECTED"
|
name: "DISCONNECTED"
|
||||||
|
@ -152,14 +138,12 @@ Rectangle {
|
||||||
// stackLayout.currentIndex: 0
|
// stackLayout.currentIndex: 0
|
||||||
//}
|
//}
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: stackLayout
|
|
||||||
currentIndex: 0 // qmllint disable Quick.property-changes-parsed
|
currentIndex: 0 // qmllint disable Quick.property-changes-parsed
|
||||||
|
target: stackLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: callTimer
|
id: callTimer
|
||||||
|
|
||||||
|
@ -170,8 +154,9 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
interval: 1000
|
interval: 1000
|
||||||
running: CallManager.callState == Voip.CONNECTED
|
|
||||||
repeat: true
|
repeat: true
|
||||||
|
running: CallManager.callState == Voip.CONNECTED
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
let seconds = Math.floor(d.getTime() / 1000 - startTime);
|
let seconds = Math.floor(d.getTime() / 1000 - startTime);
|
||||||
|
@ -181,44 +166,40 @@ Rectangle {
|
||||||
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s);
|
callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
visible: CallManager.callType == Voip.SCREEN && CallManager.callState == Voip.CONNECTED
|
|
||||||
text: qsTr("You are screen sharing")
|
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
|
||||||
color: "#000000"
|
color: "#000000"
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
|
text: qsTr("You are screen sharing")
|
||||||
|
visible: CallManager.callType == Voip.SCREEN && CallManager.callState == Voip.CONNECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
visible: CallManager.haveLocalPiP
|
|
||||||
Layout.preferredWidth: 24
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
buttonTextColor: "#000000"
|
Layout.preferredWidth: 24
|
||||||
image: ":/icons/icons/ui/picture-in-picture.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Hide/Show Picture-in-Picture")
|
ToolTip.text: qsTr("Hide/Show Picture-in-Picture")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
buttonTextColor: "#000000"
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/picture-in-picture.svg"
|
||||||
|
visible: CallManager.haveLocalPiP
|
||||||
|
|
||||||
onClicked: CallManager.toggleLocalPiP()
|
onClicked: CallManager.toggleLocalPiP()
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.preferredWidth: 24
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
buttonTextColor: "#000000"
|
Layout.preferredWidth: 24
|
||||||
image: CallManager.isMicMuted ? ":/icons/icons/ui/microphone-unmute.svg" : ":/icons/icons/ui/microphone-mute.svg"
|
Layout.rightMargin: 16
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: CallManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
|
ToolTip.text: CallManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
buttonTextColor: "#000000"
|
||||||
|
hoverEnabled: true
|
||||||
|
image: CallManager.isMicMuted ? ":/icons/icons/ui/microphone-unmute.svg" : ":/icons/icons/ui/microphone-mute.svg"
|
||||||
|
|
||||||
onClicked: CallManager.toggleMicMute()
|
onClicked: CallManager.toggleMicMute()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,79 +9,70 @@ import im.nheko 1.0
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
modal: true
|
modal: true
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: palette.windowText
|
||||||
|
color: palette.window
|
||||||
|
}
|
||||||
|
|
||||||
// only set the anchors on Qt 5.12 or higher
|
// only set the anchors on Qt 5.12 or higher
|
||||||
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (anchors)
|
if (anchors)
|
||||||
anchors.centerIn = parent;
|
anchors.centerIn = parent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 8
|
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
|
Layout.topMargin: 8
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 22
|
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: micCombo
|
id: micCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.mics
|
model: CallManager.mics
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 22
|
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
source: "image://colorimage/:/icons/icons/ui/video-call.svg?" + palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/video-call.svg?" + palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: cameraCombo
|
id: cameraCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.cameras
|
model: CallManager.cameras
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogButtonBox {
|
DialogButtonBox {
|
||||||
Layout.leftMargin: 128
|
Layout.leftMargin: 128
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
if (cameraCombo.visible)
|
if (cameraCombo.visible)
|
||||||
Settings.camera = cameraCombo.currentText;
|
Settings.camera = cameraCombo.currentText;
|
||||||
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
onRejected: {
|
onRejected: {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: palette.window
|
|
||||||
border.color: palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,51 +12,50 @@ Popup {
|
||||||
id: callInv
|
id: callInv
|
||||||
|
|
||||||
closePolicy: Popup.NoAutoClose
|
closePolicy: Popup.NoAutoClose
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: palette.windowText
|
||||||
|
color: palette.window
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: deviceError
|
id: deviceError
|
||||||
|
|
||||||
DeviceError {
|
DeviceError {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onNewInviteState() {
|
function onNewInviteState() {
|
||||||
if (!CallManager.haveCallInvite)
|
if (!CallManager.haveCallInvite)
|
||||||
close();
|
close();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: CallManager
|
target: CallManager
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.topMargin: callInv.parent.height / 25
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: CallManager.callPartyDisplayName
|
Layout.topMargin: callInv.parent.height / 25
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
|
||||||
color: palette.windowText
|
color: palette.windowText
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: CallManager.callPartyDisplayName
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.preferredHeight: callInv.height / 5
|
Layout.preferredHeight: callInv.height / 5
|
||||||
Layout.preferredWidth: callInv.height / 5
|
Layout.preferredWidth: callInv.height / 5
|
||||||
|
displayName: CallManager.callPartyDisplayName
|
||||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: CallManager.callParty
|
userid: CallManager.callParty
|
||||||
displayName: CallManager.callPartyDisplayName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.bottomMargin: callInv.height / 25
|
Layout.bottomMargin: callInv.height / 25
|
||||||
|
@ -65,20 +64,17 @@ Popup {
|
||||||
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.preferredWidth: callInv.height / 10
|
|
||||||
Layout.preferredHeight: callInv.height / 10
|
Layout.preferredHeight: callInv.height / 10
|
||||||
|
Layout.preferredWidth: callInv.height / 10
|
||||||
source: "image://colorimage/" + image + "?" + palette.windowText
|
source: "image://colorimage/" + image + "?" + palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
|
||||||
color: palette.windowText
|
color: palette.windowText
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
|
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: deviceCombos
|
id: deviceCombos
|
||||||
|
|
||||||
|
@ -91,41 +87,34 @@ Popup {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: deviceCombos.imageSize
|
|
||||||
Layout.preferredHeight: deviceCombos.imageSize
|
Layout.preferredHeight: deviceCombos.imageSize
|
||||||
|
Layout.preferredWidth: deviceCombos.imageSize
|
||||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: micCombo
|
id: micCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.mics
|
model: CallManager.mics
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: deviceCombos.imageSize
|
|
||||||
Layout.preferredHeight: deviceCombos.imageSize
|
Layout.preferredHeight: deviceCombos.imageSize
|
||||||
|
Layout.preferredWidth: deviceCombos.imageSize
|
||||||
source: "image://colorimage/:/icons/icons/ui/video.svg?" + palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/video.svg?" + palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: cameraCombo
|
id: cameraCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.cameras
|
model: CallManager.cameras
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: buttonLayout
|
id: buttonLayout
|
||||||
|
|
||||||
|
@ -148,60 +137,48 @@ Popup {
|
||||||
spacing: callInv.height / 6
|
spacing: callInv.height / 6
|
||||||
|
|
||||||
RoundButton {
|
RoundButton {
|
||||||
implicitWidth: buttonLayout.buttonSize
|
|
||||||
implicitHeight: buttonLayout.buttonSize
|
implicitHeight: buttonLayout.buttonSize
|
||||||
onClicked: {
|
implicitWidth: buttonLayout.buttonSize
|
||||||
CallManager.rejectInvite();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: buttonLayout.buttonSize / 2
|
|
||||||
color: "#ff0000"
|
color: "#ff0000"
|
||||||
|
radius: buttonLayout.buttonSize / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Image {
|
contentItem: Image {
|
||||||
source: "image://colorimage/:/icons/icons/ui/end-call.svg?#ffffff"
|
source: "image://colorimage/:/icons/icons/ui/end-call.svg?#ffffff"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
CallManager.rejectInvite();
|
||||||
|
close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RoundButton {
|
RoundButton {
|
||||||
id: acceptButton
|
id: acceptButton
|
||||||
|
|
||||||
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
|
||||||
|
|
||||||
implicitWidth: buttonLayout.buttonSize
|
|
||||||
implicitHeight: buttonLayout.buttonSize
|
implicitHeight: buttonLayout.buttonSize
|
||||||
|
implicitWidth: buttonLayout.buttonSize
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: "#00ff00"
|
||||||
|
radius: buttonLayout.buttonSize / 2
|
||||||
|
}
|
||||||
|
contentItem: Image {
|
||||||
|
source: "image://colorimage/" + acceptButton.image + "?#ffffff"
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buttonLayout.validateMic()) {
|
if (buttonLayout.validateMic()) {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
if (cameraCombo.visible)
|
if (cameraCombo.visible)
|
||||||
Settings.camera = cameraCombo.currentText;
|
Settings.camera = cameraCombo.currentText;
|
||||||
|
|
||||||
CallManager.acceptInvite();
|
CallManager.acceptInvite();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
radius: buttonLayout.buttonSize / 2
|
|
||||||
color: "#00ff00"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Image {
|
|
||||||
source: "image://colorimage/" + acceptButton.image + "?#ffffff"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: palette.window
|
|
||||||
border.color: palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,87 +9,80 @@ import QtQuick.Layouts
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: CallManager.haveCallInvite && !Settings.mobileMode
|
|
||||||
color: "#2ECC71"
|
color: "#2ECC71"
|
||||||
implicitHeight: visible ? rowLayout.height + 8 : 0
|
implicitHeight: visible ? rowLayout.height + 8 : 0
|
||||||
|
visible: CallManager.haveCallInvite && !Settings.mobileMode
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: devicesDialog
|
id: devicesDialog
|
||||||
|
|
||||||
CallDevices {
|
CallDevices {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: deviceError
|
id: deviceError
|
||||||
|
|
||||||
DeviceError {
|
DeviceError {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: rowLayout
|
id: rowLayout
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: 8
|
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
implicitWidth: Nheko.avatarSize
|
displayName: CallManager.callPartyDisplayName
|
||||||
implicitHeight: Nheko.avatarSize
|
implicitHeight: Nheko.avatarSize
|
||||||
|
implicitWidth: Nheko.avatarSize
|
||||||
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
userid: CallManager.callParty
|
userid: CallManager.callParty
|
||||||
displayName: CallManager.callPartyDisplayName
|
|
||||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
|
color: "#000000"
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
text: CallManager.callPartyDisplayName
|
text: CallManager.callPartyDisplayName
|
||||||
color: "#000000"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.leftMargin: 4
|
Layout.leftMargin: 4
|
||||||
Layout.preferredWidth: 24
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
|
Layout.preferredWidth: 24
|
||||||
source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
|
source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: "#000000"
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
|
||||||
color: "#000000"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.preferredWidth: 20
|
|
||||||
Layout.preferredHeight: 20
|
Layout.preferredHeight: 20
|
||||||
buttonTextColor: "#000000"
|
Layout.preferredWidth: 20
|
||||||
image: ":/icons/icons/ui/settings.svg"
|
Layout.rightMargin: 16
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Devices")
|
ToolTip.text: qsTr("Devices")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
buttonTextColor: "#000000"
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/settings.svg"
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var dialog = devicesDialog.createObject(timelineRoot);
|
var dialog = devicesDialog.createObject(timelineRoot);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.rightMargin: 4
|
Layout.rightMargin: 4
|
||||||
icon.source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
|
icon.source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
|
||||||
text: qsTr("Accept")
|
text: qsTr("Accept")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (CallManager.mics.length == 0) {
|
if (CallManager.mics.length == 0) {
|
||||||
var dialog = deviceError.createObject(timelineRoot, {
|
var dialog = deviceError.createObject(timelineRoot, {
|
||||||
|
@ -120,16 +113,14 @@ Rectangle {
|
||||||
CallManager.acceptInvite();
|
CallManager.acceptInvite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
icon.source: "qrc:/icons/icons/ui/end-call.svg"
|
icon.source: "qrc:/icons/icons/ui/end-call.svg"
|
||||||
text: qsTr("Decline")
|
text: qsTr("Decline")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
CallManager.rejectInvite();
|
CallManager.rejectInvite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,35 +8,33 @@ import QtQuick.Layouts 1.2
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: r
|
id: r
|
||||||
|
|
||||||
property string errorString
|
property string errorString
|
||||||
property var image
|
property var image
|
||||||
|
|
||||||
modal: true
|
modal: true
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: palette.windowText
|
||||||
|
color: palette.window
|
||||||
|
}
|
||||||
|
|
||||||
// only set the anchors on Qt 5.12 or higher
|
// only set the anchors on Qt 5.12 or higher
|
||||||
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (anchors)
|
if (anchors)
|
||||||
anchors.centerIn = parent;
|
anchors.centerIn = parent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 16
|
|
||||||
Layout.preferredHeight: 16
|
Layout.preferredHeight: 16
|
||||||
|
Layout.preferredWidth: 16
|
||||||
source: "image://colorimage/" + r.image + "?" + palette.windowText
|
source: "image://colorimage/" + r.image + "?" + palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: r.errorString
|
|
||||||
color: palette.windowText
|
color: palette.windowText
|
||||||
|
text: r.errorString
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: palette.window
|
|
||||||
border.color: palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,17 @@ import im.nheko 1.0
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
modal: true
|
modal: true
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: palette.windowText
|
||||||
|
color: palette.window
|
||||||
|
}
|
||||||
|
|
||||||
// only set the anchors on Qt 5.12 or higher
|
// only set the anchors on Qt 5.12 or higher
|
||||||
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (anchors)
|
if (anchors)
|
||||||
anchors.centerIn = parent;
|
anchors.centerIn = parent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
@ -23,29 +28,24 @@ Popup {
|
||||||
|
|
||||||
DeviceError {
|
DeviceError {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: columnLayout
|
id: columnLayout
|
||||||
|
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
|
Layout.topMargin: 8
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Place a call to %1?").arg(room.roomName)
|
|
||||||
color: palette.windowText
|
color: palette.windowText
|
||||||
|
text: qsTr("Place a call to %1?").arg(room.roomName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: buttonLayout
|
id: buttonLayout
|
||||||
|
|
||||||
|
@ -66,18 +66,19 @@ Popup {
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
Layout.rightMargin: cameraCombo.visible ? 16 : 64
|
|
||||||
Layout.preferredWidth: Nheko.avatarSize
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize
|
Layout.preferredHeight: Nheko.avatarSize
|
||||||
url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
Layout.preferredWidth: Nheko.avatarSize
|
||||||
|
Layout.rightMargin: cameraCombo.visible ? 16 : 64
|
||||||
displayName: room.roomName
|
displayName: room.roomName
|
||||||
roomid: room.roomId
|
roomid: room.roomId
|
||||||
|
url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
|
||||||
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
onClicked: TimelineManager.openImageOverlay(room, room.avatarUrl(userid), room.data.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Voice")
|
|
||||||
icon.source: "qrc:/icons/icons/ui/place-call.svg"
|
icon.source: "qrc:/icons/icons/ui/place-call.svg"
|
||||||
|
text: qsTr("Voice")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buttonLayout.validateMic()) {
|
if (buttonLayout.validateMic()) {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
|
@ -86,11 +87,11 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: CallManager.cameras.length > 0
|
|
||||||
text: qsTr("Video")
|
|
||||||
icon.source: "qrc:/icons/icons/ui/video.svg"
|
icon.source: "qrc:/icons/icons/ui/video.svg"
|
||||||
|
text: qsTr("Video")
|
||||||
|
visible: CallManager.cameras.length > 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buttonLayout.validateMic()) {
|
if (buttonLayout.validateMic()) {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
|
@ -100,15 +101,14 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Screen")
|
|
||||||
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||||
|
text: qsTr("Screen")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (buttonLayout.validateMic()) {
|
if (buttonLayout.validateMic()) {
|
||||||
Settings.microphone = micCombo.currentText;
|
Settings.microphone = micCombo.currentText;
|
||||||
Settings.camera = cameraCombo.currentText;
|
Settings.camera = cameraCombo.currentText;
|
||||||
|
|
||||||
var dialog = screenShareDialog.createObject(timelineRoot);
|
var dialog = screenShareDialog.createObject(timelineRoot);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
|
@ -116,67 +116,52 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Cancel")
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.bottomMargin: cameraCombo.visible ? 0 : 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: cameraCombo.visible ? 0 : 8
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 22
|
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: micCombo
|
id: micCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.mics
|
model: CallManager.mics
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: CallManager.cameras.length > 0
|
Layout.bottomMargin: 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: 8
|
visible: CallManager.cameras.length > 0
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.preferredWidth: 22
|
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 22
|
||||||
|
Layout.preferredWidth: 22
|
||||||
source: "image://colorimage/:/icons/icons/ui/video.svg?" + palette.windowText
|
source: "image://colorimage/:/icons/icons/ui/video.svg?" + palette.windowText
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: cameraCombo
|
id: cameraCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.cameras
|
model: CallManager.cameras
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: palette.window
|
|
||||||
border.color: palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,13 @@ import QtQuick.Layouts
|
||||||
import im.nheko
|
import im.nheko
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
|
anchors.centerIn: parent
|
||||||
modal: true
|
modal: true
|
||||||
|
|
||||||
anchors.centerIn: parent;
|
background: Rectangle {
|
||||||
|
border.color: palette.windowText
|
||||||
|
color: palette.window
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
frameRateCombo.currentIndex = frameRateCombo.find(Settings.screenShareFrameRate);
|
frameRateCombo.currentIndex = frameRateCombo.find(Settings.screenShareFrameRate);
|
||||||
|
@ -22,176 +26,151 @@ Popup {
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Label {
|
Label {
|
||||||
Layout.topMargin: 16
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.topMargin: 16
|
||||||
text: qsTr("Share desktop with %1?").arg(room.roomName)
|
|
||||||
color: palette.windowText
|
color: palette.windowText
|
||||||
|
text: qsTr("Share desktop with %1?").arg(room.roomName)
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.bottomMargin: 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: 8
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Method:")
|
|
||||||
color: palette.windowText
|
color: palette.windowText
|
||||||
|
text: qsTr("Method:")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: screenshareType
|
id: screenshareType
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.screenShareTypeList()
|
model: CallManager.screenShareTypeList()
|
||||||
onCurrentIndexChanged: CallManager.setScreenShareType(currentIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
onCurrentIndexChanged: CallManager.setScreenShareType(currentIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.bottomMargin: 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: 8
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Window:")
|
|
||||||
color: palette.windowText
|
color: palette.windowText
|
||||||
|
text: qsTr("Window:")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
visible: CallManager.screenShareType == Voip.X11
|
|
||||||
id: windowCombo
|
id: windowCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: CallManager.windowList()
|
model: CallManager.windowList()
|
||||||
|
visible: CallManager.screenShareType == Voip.X11
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: CallManager.screenShareType == Voip.XDP
|
|
||||||
highlighted: !CallManager.screenShareReady
|
highlighted: !CallManager.screenShareReady
|
||||||
text: qsTr("Request screencast")
|
text: qsTr("Request screencast")
|
||||||
|
visible: CallManager.screenShareType == Voip.XDP
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
|
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
|
||||||
CallManager.setupScreenShareXDP();
|
CallManager.setupScreenShareXDP();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
Layout.bottomMargin: 8
|
||||||
Layout.leftMargin: 8
|
Layout.leftMargin: 8
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
Layout.bottomMargin: 8
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Frame rate:")
|
|
||||||
color: palette.windowText
|
color: palette.windowText
|
||||||
|
text: qsTr("Frame rate:")
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: frameRateCombo
|
id: frameRateCombo
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: ["25", "20", "15", "10", "5", "2", "1"]
|
model: ["25", "20", "15", "10", "5", "2", "1"]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
Layout.margins: 8
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: 10
|
rowSpacing: 10
|
||||||
Layout.margins: 8
|
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Include your camera picture-in-picture")
|
text: qsTr("Include your camera picture-in-picture")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: pipCheckBox
|
id: pipCheckBox
|
||||||
|
|
||||||
enabled: CallManager.cameras.length > 0
|
|
||||||
checked: CallManager.cameras.length > 0 && Settings.screenSharePiP
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: CallManager.cameras.length > 0 && Settings.screenSharePiP
|
||||||
|
enabled: CallManager.cameras.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Request remote camera")
|
|
||||||
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
text: qsTr("Request remote camera")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: remoteVideoCheckBox
|
id: remoteVideoCheckBox
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: Settings.screenShareRemoteVideo
|
|
||||||
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
ToolTip.text: qsTr("View your callee's camera like a regular video call")
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
checked: Settings.screenShareRemoteVideo
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Hide mouse cursor")
|
text: qsTr("Hide mouse cursor")
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: hideCursorCheckBox
|
id: hideCursorCheckBox
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
checked: Settings.screenShareHideCursor
|
checked: Settings.screenShareHideCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: CallManager.screenShareReady
|
|
||||||
text: qsTr("Share")
|
|
||||||
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
icon.source: "qrc:/icons/icons/ui/screen-share.svg"
|
||||||
|
text: qsTr("Share")
|
||||||
|
visible: CallManager.screenShareReady
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Settings.screenShareFrameRate = frameRateCombo.currentText;
|
Settings.screenShareFrameRate = frameRateCombo.currentText;
|
||||||
Settings.screenSharePiP = pipCheckBox.checked;
|
Settings.screenSharePiP = pipCheckBox.checked;
|
||||||
Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
|
Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
|
||||||
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
|
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
|
||||||
|
|
||||||
CallManager.sendInvite(room.roomId, Voip.SCREEN, windowCombo.currentIndex);
|
CallManager.sendInvite(room.roomId, Voip.SCREEN, windowCombo.currentIndex);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: CallManager.screenShareReady
|
|
||||||
text: qsTr("Preview")
|
text: qsTr("Preview")
|
||||||
|
visible: CallManager.screenShareReady
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
CallManager.previewWindow(windowCombo.currentIndex);
|
CallManager.previewWindow(windowCombo.currentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: qsTr("Cancel")
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: palette.window
|
|
||||||
border.color: palette.windowText
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue