Refactor and fix ChatBox layouting

BUG: 474616
This commit is contained in:
Tobias Fella
2023-10-15 19:30:28 +02:00
parent 0beb5df08d
commit 84cad630cd
12 changed files with 544 additions and 660 deletions

View File

@@ -173,7 +173,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
qml/TypingPane.qml
qml/QuickSwitcher.qml
qml/HoverActions.qml
qml/ChatBox.qml
qml/ChatBar.qml
qml/AttachmentPane.qml
qml/ReplyPane.qml
@@ -290,6 +289,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
qml/Security.qml
qml/QrCodeMaximizeComponent.qml
qml/SelectSpacesDialog.qml
qml/AttachDialog.qml
RESOURCES
qml/confetti.png
qml/glowdot.png

View File

@@ -142,6 +142,7 @@ CustomEmojiModel::CustomEmojiModel(QObject *parent)
fetchEmojis();
});
});
CustomEmojiModel::fetchEmojis();
}
QVariant CustomEmojiModel::data(const QModelIndex &idx, int role) const

66
src/qml/AttachDialog.qml Normal file
View File

@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtCore
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
QQC2.Popup {
id: root
padding: 16
signal chosen(string path)
contentItem: RowLayout {
QQC2.ToolButton {
Layout.preferredWidth: 160
Layout.fillHeight: true
icon.name: 'mail-attachment'
text: i18n("Choose local file")
onClicked: {
root.close()
var fileDialog = openFileDialog.createObject(QQC2.ApplicationWindow.overlay)
fileDialog.chosen.connect(path => root.chosen(path))
fileDialog.open()
}
}
Kirigami.Separator {}
QQC2.ToolButton {
Layout.preferredWidth: 160
Layout.fillHeight: true
padding: 16
icon.name: 'insert-image'
text: i18n("Clipboard image")
onClicked: {
const path = StandardPaths.standardLocations(StandardPaths.CacheLocation)[0] + "/screenshots/" + (new Date()).getTime() + ".png"
if (!Clipboard.saveImage(path)) {
return;
}
root.chosen(path)
root.close();
}
}
}
Component {
id: openFileDialog
OpenFileDialog {
parentWindow: Window.window
currentFolder: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
}
}
}

View File

@@ -2,35 +2,25 @@
// SPDX-FileCopyrightText: 2020 Noah Davis <noahadvs@gmail.com>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtCore
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as QQC2
import QtQuick.Window
import Qt.labs.platform as Platform
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
import org.kde.neochat.config
/**
* @brief The component which handles the message sending.
* @brief A component for typing and sending chat messages.
*
* The ChatBox deals with laying out the visual elements with the ChatBar handling
* the core functionality of displaying the current message composition before sending.
* This is designed to go to the bottom of the timeline and provides all the functionality
* required for the user to send messages to the room.
*
* This includes support for the following message types:
* - text
* - media (video, image, file)
* - emojis/stickers
* - location
*
* In addition, when replying, this component supports showing the message that is being
* In addition when replying this component supports showing the message that is being
* replied to.
*
* @note There is no edit functionality here this, is handled inline by the timeline
* text delegate.
*
* @sa ChatBox
* @sa ChatBar
*/
QQC2.Control {
id: root
@@ -39,17 +29,13 @@ QQC2.Control {
* @brief The current room that user is viewing.
*/
required property NeoChatRoom currentRoom
required property NeoChatConnection connection
onActiveFocusChanged: textField.forceActiveFocus()
onCurrentRoomChanged: _private.chatBarCache = currentRoom.mainCache
/**
* @brief The QQC2.TextArea object.
*
* @sa QQC2.TextArea
*/
property alias textField: textField
property NeoChatConnection connection
/**
* @brief The ActionsHandler object to use.
*
@@ -64,31 +50,22 @@ QQC2.Control {
* Each of these will be visualised in the ChatBar so new actions can be added
* by appending to this list.
*/
property list<Kirigami.Action> actions : [
property list<Kirigami.Action> actions: [
Kirigami.Action {
id: attachmentAction
property bool isBusy: root.currentRoom && root.currentRoom.hasFileUploading
// Matrix does not allow sending attachments in replies
visible: _private.chatBarCache.isReplying && _private.chatBarCache.attachmentPath.length === 0
visible: _private.chatBarCache.replyId.length === 0 && _private.chatBarCache.attachmentPath.length === 0
icon.name: "mail-attachment"
text: i18n("Attach an image or file")
displayHint: Kirigami.DisplayHint.IconOnly
onTriggered: {
if (Clipboard.hasImage) {
attachDialog.open()
} else {
var fileDialog = openFileDialog.createObject(QQC2.ApplicationWindow.overlay)
fileDialog.chosen.connect((path) => {
if (!path) {
return;
}
_private.chatBarCache.attachmentPath = path;
})
fileDialog.open()
}
let dialog = (Clipboard.hasImage ? attachDialog : openFileDialog).createObject(applicationWindow().overlay)
dialog.chosen.connect(path => _private.chatBarCache.attachmentPath = path)
dialog.open()
}
tooltip: text
@@ -105,10 +82,10 @@ QQC2.Control {
checkable: true
onTriggered: {
if (emojiDialog.item.visible) {
emojiDialog.item.close()
if (emojiDialog.visible) {
emojiDialog.close()
} else {
emojiDialog.item.open()
emojiDialog.open()
}
}
tooltip: text
@@ -121,7 +98,7 @@ QQC2.Control {
displayHint: QQC2.AbstractButton.IconOnly
onTriggered: {
locationChooserComponent.createObject(QQC2.ApplicationWindow.overlay, {room: root.currentRoom}).open()
locationChooser.createObject(QQC2.ApplicationWindow.overlay, {room: root.currentRoom}).open()
}
tooltip: text
},
@@ -136,7 +113,7 @@ QQC2.Control {
checkable: true
onTriggered: {
root.postMessage()
_private.postMessage()
}
tooltip: text
@@ -148,15 +125,51 @@ QQC2.Control {
*/
signal messageSent()
leftPadding: 0
rightPadding: 0
spacing: 0
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Separator {
anchors.left: parent.left
anchors.right:parent.right
anchors.top: parent.top
}
}
leftPadding: rightPadding
rightPadding: (root.width - chatBarSizeHelper.currentWidth) / 2
topPadding: 0
bottomPadding: 0
contentItem: QQC2.ScrollView {
contentItem: ColumnLayout {
spacing: 0
Item { // Required to adjust for the top separator
Layout.preferredHeight: 1
Layout.fillWidth: true
}
Loader {
id: paneLoader
Layout.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing
active: visible
visible: root.currentRoom.mainCache.replyId.length > 0 || root.currentRoom.mainCache.attachmentPath.length > 0
sourceComponent: root.currentRoom.mainCache.replyId.length > 0 ? replyPane : attachmentPane
}
RowLayout {
QQC2.ScrollView {
id: chatBarScrollView
property var textFieldHeight: textField.height
Layout.fillWidth: true
Layout.maximumHeight: Kirigami.Units.gridUnit * 8
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.minimumHeight: Kirigami.Units.gridUnit * 2
// HACK: This is to stop the ScrollBar flickering on and off as the height is increased
QQC2.ScrollBar.vertical.policy: chatBarHeightAnimation.running && implicitHeight <= height ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
@@ -172,19 +185,12 @@ QQC2.Control {
QQC2.TextArea {
id: textField
x: Math.round((root.width - chatBarSizeHelper.currentWidth) / 2) - (root.width > chatBarSizeHelper.currentWidth + Kirigami.Units.largeSpacing * 2.5 ? Kirigami.Units.largeSpacing * 1.5 : 0)
topPadding: Kirigami.Units.largeSpacing + (paneLoader.visible ? paneLoader.height : 0)
bottomPadding: Kirigami.Units.largeSpacing
leftPadding: LayoutMirroring.enabled ? actionsRow.width : Kirigami.Units.largeSpacing
rightPadding: LayoutMirroring.enabled ? Kirigami.Units.largeSpacing : actionsRow.width + x * 2 + Kirigami.Units.largeSpacing * 2
placeholderText: root.currentRoom.usesEncryption ? i18n("Send an encrypted message…") : _private.chatBarCache.attachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
placeholderText: root.currentRoom.usesEncryption ? i18n("Send an encrypted message…") : root.currentRoom.mainCache.attachmentPath.length > 0 ? i18n("Set an attachment caption…") : i18n("Send a message…")
verticalAlignment: TextEdit.AlignVCenter
wrapMode: Text.Wrap
Accessible.description: placeholderText
// opt-out of whatever spell checker a styled TextArea might come with
Kirigami.SpellCheck.enabled: false
Timer {
@@ -200,7 +206,6 @@ QQC2.Control {
}
_private.chatBarCache.text = text
}
onCursorRectangleChanged: chatBarScrollView.ensureVisible(cursorRectangle)
onSelectedTextChanged: {
if (selectedText.length > 0) {
quickFormatBar.selectionStart = selectionStart
@@ -238,7 +243,7 @@ QQC2.Control {
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) {
textField.insert(cursorPosition, "\n")
} else {
root.postMessage();
_private.postMessage();
}
}
Keys.onReturnPressed: event => {
@@ -247,7 +252,7 @@ QQC2.Control {
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) {
textField.insert(cursorPosition, "\n")
} else {
root.postMessage();
_private.postMessage();
}
}
Keys.onTabPressed: {
@@ -257,7 +262,7 @@ QQC2.Control {
}
Keys.onPressed: (event) => {
if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
event.accepted = root.pasteImage();
event.accepted = _private.pasteImage();
} else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) {
root.currentRoom.replyLastMessage();
} else if (event.key === Qt.Key_Up && textField.text.length === 0) {
@@ -277,110 +282,28 @@ QQC2.Control {
}
}
Keys.onShortcutOverride: event => {
// Accept the event only when there was something to cancel. Otherwise, let the event go to the RoomPage.
if (cancelButton.visible && event.key === Qt.Key_Escape) {
cancelButton.action.trigger();
if (completionMenu.visible) {
completionMenu.close()
} else if ((_private.chatBarCache.isReplying || _private.chatBarCache.attachmentPath.length > 0) && event.key === Qt.Key_Escape) {
_private.chatBarCache.attachmentPath = ""
_private.chatBarCache.replyId = ""
_private.chatBarCache.threadId = ""
event.accepted = true;
}
}
Loader {
id: paneLoader
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: Kirigami.Units.largeSpacing
anchors.right: parent.right
anchors.rightMargin: root.width > chatBarSizeHelper.currentWidth ? 0 : (chatBarScrollView.QQC2.ScrollBar.vertical.visible ? Kirigami.Units.largeSpacing * 3.5 : Kirigami.Units.largeSpacing)
active: visible
visible: _private.chatBarCache.isReplying || _private.chatBarCache.attachmentPath.length > 0
sourceComponent: _private.chatBarCache.isReplying ? replyPane : attachmentPane
}
Component {
id: replyPane
ReplyPane {
userName: _private.chatBarCache.relationUser.displayName
userColor: _private.chatBarCache.relationUser.color
userAvatar: _private.chatBarCache.relationUser.avatarSource
text: _private.chatBarCache.relationMessage
}
}
Component {
id: attachmentPane
AttachmentPane {
attachmentPath: _private.chatBarCache.attachmentPath
onAttachmentCancelled: {
_private.chatBarCache.attachmentPath = "";
root.forceActiveFocus()
}
}
}
background: MouseArea {
acceptedButtons: Qt.NoButton
cursorShape: Qt.IBeamCursor
z: 1
}
}
/**
* Because of the paneLoader we have to manage the scroll
* position manually or it doesn't keep the cursor visible properly all the time.
*/
function ensureVisible(r) {
// Find the child that is the Flickable created by ScrollView.
let flickable = undefined;
for (var index in children) {
if (children[index] instanceof Flickable) {
flickable = children[index];
}
}
if (flickable) {
if (flickable.contentX >= r.x) {
flickable.contentX = r.x;
} else if (flickable.contentX + width <= r.x + r.width) {
flickable.contentX = r.x + r.width - width;
} if (flickable.contentY >= r.y) {
flickable.contentY = r.y;
} else if (flickable.contentY + height <= r.y + r.height) {
flickable.contentY = r.y + r.height - height + textField.bottomPadding;
}
}
}
}
QQC2.ToolButton {
id: cancelButton
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: (root.width - chatBarSizeHelper.currentWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBarSizeHelper.currentWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0)
visible: _private.chatBarCache.isReplying
display: QQC2.AbstractButton.IconOnly
action: Kirigami.Action {
text: i18nc("@action:button", "Cancel reply")
icon.name: "dialog-close"
onTriggered: {
_private.chatBarCache.replyId = "";
_private.chatBarCache.attachmentPath = "";
root.forceActiveFocus()
}
}
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
}
RowLayout {
id: actionsRow
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: layoutDirection === Qt.RightToLeft ? requiredMargin : 0
anchors.rightMargin: layoutDirection === Qt.RightToLeft ? 0 : requiredMargin
anchors.bottomMargin: Kirigami.Units.smallSpacing
spacing: 0
property var requiredMargin: (root.width - chatBarSizeHelper.currentWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBarSizeHelper.currentWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0)
Layout.alignment: Qt.AlignBottom
Layout.bottomMargin: Kirigami.Units.smallSpacing * 1.5
Repeater {
model: root.actions
@@ -400,55 +323,6 @@ QQC2.Control {
}
}
}
Loader {
id: emojiDialog
active: !Kirigami.Settings.isMobile
sourceComponent: EmojiDialog {
x: root.width - width
y: -implicitHeight // - Kirigami.Units.smallSpacing
modal: false
includeCustom: true
closeOnChosen: false
currentRoom: root.currentRoom
onChosen: emoji => insertText(emoji)
onClosed: if (emojiAction.checked) emojiAction.checked = false
}
}
background: Rectangle {
color: Kirigami.Theme.backgroundColor
}
CompletionMenu {
id: completionMenu
height: implicitHeight
y: -height - 5
z: 1
chatDocumentHandler: documentHandler
connection: root.connection
Behavior on height {
NumberAnimation {
property: "height"
duration: Kirigami.Units.shortDuration
easing.type: Easing.OutCubic
}
}
}
ChatDocumentHandler {
id: documentHandler
document: textField.textDocument
cursorPosition: textField.cursorPosition
selectionStart: textField.selectionStart
selectionEnd: textField.selectionEnd
mentionColor: Kirigami.Theme.linkColor
errorColor: Kirigami.Theme.negativeTextColor
Component.onCompleted: {
RoomManager.chatDocumentHandler = documentHandler;
}
}
@@ -463,27 +337,36 @@ QQC2.Control {
parentWidth: root.width
}
function forceActiveFocus() {
textField.forceActiveFocus();
// set the cursor to the end of the text
textField.cursorPosition = textField.length;
Component {
id: replyPane
ReplyPane {
userName: _private.chatBarCache.relationUser.displayName
userColor: _private.chatBarCache.relationUser.color
userAvatar: _private.chatBarCache.relationUser.avatarSource
text: _private.chatBarCache.relationMessage
onCancel: {
_private.chatBarCache.replyId = "";
_private.chatBarCache.attachmentPath = "";
}
}
}
Component {
id: attachmentPane
AttachmentPane {
attachmentPath: _private.chatBarCache.attachmentPath
onAttachmentCancelled: {
_private.chatBarCache.attachmentPath = "";
root.forceActiveFocus()
}
}
}
function insertText(text) {
let initialCursorPosition = textField.cursorPosition;
textField.text = textField.text.substr(0, initialCursorPosition) + text + textField.text.substr(initialCursorPosition)
textField.cursorPosition = initialCursorPosition + text.length
}
function pasteImage() {
let localPath = Clipboard.saveImage();
if (localPath.length === 0) {
return false;
}
_private.chatBarCache.attachmentPath = localPath;
return true;
}
QtObject {
id: _private
property ChatBarCache chatBarCache
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
function postMessage() {
root.actionsHandler.handleMessageEvent(_private.chatBarCache);
@@ -537,63 +420,26 @@ QQC2.Control {
}
}
Component {
id: locationChooserComponent
LocationChooser {}
}
QQC2.Popup {
anchors.centerIn: parent
id: attachDialog
padding: 16
contentItem: RowLayout {
QQC2.ToolButton {
Layout.preferredWidth: 160
Layout.fillHeight: true
icon.name: 'mail-attachment'
text: i18n("Choose local file")
onClicked: {
attachDialog.close()
var fileDialog = openFileDialog.createObject(QQC2.ApplicationWindow.overlay)
fileDialog.chosen.connect(function (path) {
if (!path) {
return;
}
_private.chatBarCache.attachmentPath = path;
})
fileDialog.open()
}
}
Kirigami.Separator {
}
QQC2.ToolButton {
Layout.preferredWidth: 160
Layout.fillHeight: true
padding: 16
icon.name: 'insert-image'
text: i18n("Clipboard image")
onClicked: {
const localPath = Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png"
if (!Clipboard.saveImage(localPath)) {
return;
function pasteImage() {
let localPath = Clipboard.saveImage();
if (localPath.length === 0) {
return false;
}
_private.chatBarCache.attachmentPath = localPath;
attachDialog.close();
return true;
}
}
ChatDocumentHandler {
id: documentHandler
document: textField.textDocument
cursorPosition: textField.cursorPosition
selectionStart: textField.selectionStart
selectionEnd: textField.selectionEnd
mentionColor: Kirigami.Theme.linkColor
errorColor: Kirigami.Theme.negativeTextColor
Component.onCompleted: {
RoomManager.chatDocumentHandler = documentHandler;
}
}
@@ -602,12 +448,60 @@ QQC2.Control {
OpenFileDialog {
parentWindow: Window.window
currentFolder: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
}
}
QtObject {
id: _private
property ChatBarCache chatBarCache
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
Component {
id: attachDialog
AttachDialog {
anchors.centerIn: parent
}
}
Component {
id: locationChooser
LocationChooser {}
}
CompletionMenu {
id: completionMenu
chatDocumentHandler: documentHandler
connection: root.connection
x: 1
y: -height
width: parent.width - 1
Behavior on height {
NumberAnimation {
property: "height"
duration: Kirigami.Units.shortDuration
easing.type: Easing.OutCubic
}
}
}
EmojiDialog {
id: emojiDialog
x: root.width - width
y: -implicitHeight
modal: false
includeCustom: true
closeOnChosen: false
currentRoom: root.currentRoom
onChosen: emoji => insertText(emoji)
onClosed: if (emojiAction.checked) emojiAction.checked = false
}
function insertText(text) {
let initialCursorPosition = textField.cursorPosition;
textField.text = textField.text.substr(0, initialCursorPosition) + text + textField.text.substr(initialCursorPosition)
textField.cursorPosition = initialCursorPosition + text.length
}
}

View File

@@ -1,99 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.de>
// SPDX-FileCopyrightText: 2020 Noah Davis <noahadvs@gmail.com>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
/**
* @brief A component for typing and sending chat messages.
*
* This is designed to go to the bottom of the timeline and provides all the functionality
* required for the user to send messages to the room.
*
* This includes support for the following message types:
* - text
* - media (video, image, file)
* - emojis/stickers
* - location
*
* In addition when replying this component supports showing the message that is being
* replied to.
*
* @note The main role of this component is to layout the elements. The main functionality
* is handled by ChatBar
*
* @sa ChatBar
*/
ColumnLayout {
id: root
/**
* @brief The current room that user is viewing.
*/
required property NeoChatRoom currentRoom
required property NeoChatConnection connection
/**
* @brief The ActionsHandler object to use.
*
* This is expected to have the correct room set otherwise messages will be sent
* to the wrong room.
*/
required property ActionsHandler actionsHandler
/**
* @brief A message has been sent from the chat bar.
*/
signal messageSent()
/**
* @brief Insert the given text into the ChatBar.
*
* The text is inserted at the current cursor location.
*/
function insertText(text) {
chatBar.insertText(text)
}
spacing: 0
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
Kirigami.Separator {
Layout.fillWidth: true
}
ChatBar {
id: chatBar
connection: root.connection
visible: root.currentRoom.canSendEvent("m.room.message")
Layout.fillWidth: true
Layout.minimumHeight: Math.max(Kirigami.Units.gridUnit * 2, Math.round(implicitHeight) + Kirigami.Units.largeSpacing)
// lineSpacing is height+leading, so subtract leading once since leading only exists between lines.
Layout.maximumHeight: chatBarFontMetrics.lineSpacing * 8 - chatBarFontMetrics.leading + textField.topPadding + textField.bottomPadding
Layout.preferredHeight: Math.round(implicitHeight)
currentRoom: root.currentRoom
actionsHandler: root.actionsHandler
FontMetrics {
id: chatBarFontMetrics
font: chatBar.textField.font
}
onMessageSent: {
root.messageSent();
}
}
onActiveFocusChanged: chatBar.forceActiveFocus()
}

View File

@@ -15,18 +15,19 @@ import org.kde.neochat
QQC2.Popup {
id: root
width: parent.width
required property NeoChatConnection connection
required property var chatDocumentHandler
visible: completions.count > 0
onVisibleChanged: if (visible) root.open()
RoomListModel {
id: roomListModel
connection: root.connection
}
property var chatDocumentHandler
Component.onCompleted: {
chatDocumentHandler.completionModel.roomListModel = roomListModel;
}
@@ -50,14 +51,28 @@ QQC2.Popup {
implicitHeight: Math.min(completions.contentHeight, Kirigami.Units.gridUnit * 10)
contentItem: ListView {
contentItem: ColumnLayout {
spacing: 0
Kirigami.Separator {
Layout.fillWidth: true
}
QQC2.ScrollView {
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
Layout.maximumHeight: Kirigami.Units.gridUnit * 10
background: Rectangle {
color: Kirigami.Theme.backgroundColor
}
ListView {
id: completions
anchors.fill: parent
model: root.chatDocumentHandler.completionModel
currentIndex: 0
keyNavigationWraps: true
highlightMoveDuration: 100
onCountChanged: currentIndex = 0
delegate: Delegates.RoundedItemDelegate {
id: completionDelegate
@@ -86,6 +101,8 @@ QQC2.Popup {
onClicked: root.chatDocumentHandler.complete(completionDelegate.index)
}
}
}
}
background: Rectangle {
color: Kirigami.Theme.backgroundColor

View File

@@ -132,7 +132,7 @@ FormCard.FormCardPage {
id: openFileDialog
OpenFileDialog {
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
parentWindow: root.Window.window
}
}

View File

@@ -1,15 +1,14 @@
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick
import Qt.labs.platform
import QtQuick.Dialogs
FileDialog {
signal chosen(string path)
id: root
title: i18n("Please choose a file")
signal chosen(string path)
onAccepted: chosen(file)
title: i18n("Select a File")
onAccepted: root.chosen(selectedFile)
}

View File

@@ -11,36 +11,26 @@ import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat
GridLayout {
RowLayout {
id: root
property string userName
property color userColor: Kirigami.Theme.highlightColor
property color userColor
property url userAvatar: ""
property var text
rows: 3
columns: 3
rowSpacing: Kirigami.Units.smallSpacing
columnSpacing: Kirigami.Units.largeSpacing
signal cancel
QQC2.Label {
id: replyLabel
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
Layout.columnSpan: 3
topPadding: Kirigami.Units.smallSpacing
text: i18n("Replying to:")
}
Rectangle {
id: verticalBorder
Layout.fillHeight: true
Layout.rowSpan: 2
implicitWidth: Kirigami.Units.smallSpacing
color: userColor
}
ColumnLayout {
RowLayout {
KirigamiComponents.Avatar {
id: replyAvatar
@@ -59,11 +49,11 @@ GridLayout {
text: userName
elide: Text.ElideRight
}
}
QQC2.TextArea {
id: textArea
Layout.fillWidth: true
Layout.columnSpan: 2
leftPadding: 0
rightPadding: 0
@@ -89,4 +79,20 @@ GridLayout {
elideWidth: textArea.width * 2 - Kirigami.Units.smallSpacing * 2
}
}
}
QQC2.ToolButton {
id: cancelButton
Layout.alignment: Qt.AlignVCenter
display: QQC2.AbstractButton.IconOnly
text: i18nc("@action:button", "Cancel reply")
icon.name: "dialog-close"
onClicked: {
root.cancel()
}
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
}
}

View File

@@ -88,8 +88,8 @@ Kirigami.Page {
onCurrentRoomChanged: {
banner.visible = false;
if (!Kirigami.Settings.isMobile && chatBoxLoader.item) {
chatBoxLoader.item.forceActiveFocus();
if (!Kirigami.Settings.isMobile && chatBarLoader.item) {
chatBarLoader.item.forceActiveFocus();
}
}
@@ -123,9 +123,9 @@ Kirigami.Page {
messageEventModel: root.messageEventModel
messageFilterModel: root.messageFilterModel
actionsHandler: root.actionsHandler
onFocusChatBox: {
if (chatBoxLoader.item) {
chatBoxLoader.item.forceActiveFocus()
onFocusChatBar: {
if (chatBarLoader.item) {
chatBarLoader.item.forceActiveFocus()
}
}
connection: root.connection
@@ -157,10 +157,10 @@ Kirigami.Page {
}
footer: Loader {
id: chatBoxLoader
active: timelineViewLoader.active
sourceComponent: ChatBox {
id: chatBox
id: chatBarLoader
active: timelineViewLoader.active && root.currentRoom.canSendEvent("m.room.message") // TODO make this update in real time
sourceComponent: ChatBar {
id: chatBar
width: parent.width
currentRoom: root.currentRoom
connection: root.connection
@@ -215,8 +215,8 @@ Kirigami.Page {
Keys.onPressed: event => {
if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
event.accepted = true;
chatBoxLoader.item.insertText(event.text);
chatBoxLoader.item.forceActiveFocus();
chatBarLoader.item.insertText(event.text);
chatBarLoader.item.forceActiveFocus();
return;
} else if (event.key === Qt.Key_PageUp) {
event.accepted = true;
@@ -228,7 +228,7 @@ Kirigami.Page {
}
Connections {
target: currentRoom
target: root.currentRoom
function onShowMessage(messageType, message) {
banner.text = message;
banner.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : messageType === ActionsHandler.Positive ? Kirigami.MessageType.Positive : Kirigami.MessageType.Information;

View File

@@ -55,7 +55,7 @@ QQC2.ScrollView {
/// Used to determine if scrolling to the bottom should mark the message as unread
property bool hasScrolledUpBefore: false;
signal focusChatBox()
signal focusChatBar()
ListView {
id: messageListView
@@ -166,7 +166,7 @@ QQC2.ScrollView {
action: Kirigami.Action {
onTriggered: {
if (!Kirigami.Settings.isMobile) {
root.focusChatBox();
root.focusChatBar();
}
messageListView.goToEvent(root.currentRoom.readMarkerEventId)
}
@@ -258,7 +258,7 @@ QQC2.ScrollView {
HoverActions {
id: hoverActions
currentRoom: root.currentRoom
onFocusChatBar: root.focusChatBox()
onFocusChatBar: root.focusChatBar()
}
onContentYChanged: {

View File

@@ -25,7 +25,7 @@ RowLayout {
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 2 - 2 : -1 // HACK: -2 here is to ensure the ChatBox and the UserInfo have the same height
Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 2 : -1
onVisibleChanged: {
if (!visible) {