linphone-desktop/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml
Gaelle Braud b17bc8cc27 Fixes:
fix get size with screen ratio function

fix chat sending area ui #LINQT-2068

print debug logs in linphone files for futur debugging

fix call history details ui when no video conference factory set

use remote name of each call if in local conference #LINQT-2058
2025-10-21 11:25:17 +02:00

324 lines
No EOL
9.3 KiB
QML

import QtQuick
import QtQuick.Controls.Basic as Control
import QtQuick.Dialogs
import QtQuick.Layouts
import Linphone
import UtilsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils
Control.Control {
id: mainItem
// property alias placeholderText: sendingTextArea.placeholderText
property string text
property var textArea
property int selectedFilesCount: 0
// property alias cursorPosition: sendingTextArea.cursorPosition
property bool dropEnabled: true
property bool isEphemeral : false
property bool emojiVisible: false
// disable record button if call ongoing
property bool callOngoing: false
property ChatGui chat
// ---------------------------------------------------------------------------
signal dropped (var files)
signal validText (string text)
signal sendMessage()
signal emojiClicked()
signal composing()
// ---------------------------------------------------------------------------
function _emitFiles (files) {
// Filtering files, other urls are forbidden.
files = files.reduce(function (files, file) {
if (file.toString().startsWith("file:")) {
files.push(Utils.getSystemPathFromUri(file))
}
return files
}, [])
if (files.length > 0) {
dropped(files)
}
}
FileDialog {
id: fileDialog
fileMode: FileDialog.OpenFiles
onAccepted: _emitFiles(fileDialog.selectedFiles)
}
// width: mainItem.implicitWidth
// height: mainItem.height
leftPadding: Math.round(15 * DefaultStyle.dp)
rightPadding: Math.round(15 * DefaultStyle.dp)
topPadding: Math.round(16 * DefaultStyle.dp)
bottomPadding: Math.round(16 * DefaultStyle.dp)
background: Rectangle {
anchors.fill: parent
color: DefaultStyle.grey_100
}
contentItem: Control.StackView {
id: sendingAreaStackView
initialItem: textAreaComp
onHeightChanged: {
mainItem.height = height + mainItem.topPadding + mainItem.bottomPadding
}
Component {
id: textAreaComp
RowLayout {
spacing: Math.round(16 * DefaultStyle.dp)
PopupButton {
id: emojiPickerButton
style: ButtonStyle.noBackground
icon.source: checked ? AppIcons.closeX : AppIcons.smiley
popup.width: Math.round(393 * DefaultStyle.dp)
popup.height: Math.round(291 * DefaultStyle.dp)
popup.contentItem: EmojiPicker {
editor: sendingTextArea
}
}
BigButton {
style: ButtonStyle.noBackground
icon.source: AppIcons.paperclip
onClicked: {
fileDialog.open()
}
}
Control.Control {
id: sendingControl
onHeightChanged: {
sendingAreaStackView.height = height
}
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
leftPadding: Math.round(24 * DefaultStyle.dp)
rightPadding: Math.round(20 * DefaultStyle.dp)
topPadding: Math.round(12 * DefaultStyle.dp)
bottomPadding: Math.round(12 * DefaultStyle.dp)
background: Rectangle {
id: inputBackground
anchors.fill: parent
radius: Math.round(35 * DefaultStyle.dp)
color: DefaultStyle.grey_0
MouseArea {
anchors.fill: parent
onPressed: sendingTextArea.forceActiveFocus()
cursorShape: Qt.IBeamCursor
}
}
contentItem: RowLayout {
Flickable {
id: sendingAreaFlickable
Layout.preferredHeight: Math.min(Math.round(100 * DefaultStyle.dp), contentHeight)
Layout.fillHeight: true
width: sendingControl.width - sendingControl.leftPadding - sendingControl.rightPadding
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
contentHeight: sendingTextArea.contentHeight
contentWidth: width
function ensureVisible(r) {
if (contentX >= r.x)
contentX = r.x;
else if (contentX+width <= r.x+r.width)
contentX = r.x+r.width-width;
if (contentY >= r.y)
contentY = r.y;
else if (contentY+height <= r.y+r.height)
contentY = r.y+r.height-height;
}
TextArea {
id: sendingTextArea
// RectangleTest{anchors.fill: parent}
width: sendingAreaFlickable.width
height: implicitHeight// sendingAreaFlickable.height
textFormat: TextEdit.AutoText
onTextChanged: {
mainItem.text = text
}
Component.onCompleted: {
mainItem.textArea = sendingTextArea
sendingTextArea.text = mainItem.text
}
//: Say something… : placeholder text for sending message text area
placeholderText: qsTr("chat_view_send_area_placeholder_text")
placeholderTextColor: DefaultStyle.main2_400
color: DefaultStyle.main2_700
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
onCursorRectangleChanged: sendingAreaFlickable.ensureVisible(cursorRectangle)
wrapMode: TextEdit.WordWrap
KeyNavigation.tab: recordButton.visible ? recordButton : sendMessageButton
Keys.onPressed: (event) => {
if ((event.key == Qt.Key_Enter || event.key == Qt.Key_Return))
if(!(event.modifiers & Qt.ShiftModifier)) {
mainItem.sendMessage()
event.accepted = true
}
}
Connections {
target: mainItem
function onTextChanged() {
sendingTextArea.text = mainItem.text
}
function onSendMessage() {
sendingTextArea.clear()
}
}
}
}
RowLayout {
id: stackButton
spacing: 0
Layout.preferredHeight: Math.max(recordButton.height, sendMessageButton.height)
BigButton {
id: recordButton
ToolTip.visible: !enabled && hovered
//: Cannot record a message while a call is ongoing
ToolTip.text: qsTr("cannot_record_while_in_call_tooltip")
enabled: !mainItem.callOngoing
visible: !mainItem.callOngoing && sendingTextArea.text.length === 0 && mainItem.selectedFilesCount === 0
style: ButtonStyle.noBackground
hoverEnabled: true
icon.source: AppIcons.microphone
onClicked: {
sendingAreaStackView.push(voiceMessageRecordComp)
}
}
BigButton {
id: sendMessageButton
Layout.preferredHeight: height
visible: sendingTextArea.text.length !== 0 || mainItem.selectedFilesCount > 0
style: ButtonStyle.noBackgroundOrange
icon.source: AppIcons.paperPlaneRight
onClicked: {
mainItem.sendMessage()
}
}
}
}
}
}
}
Component {
id: voiceMessageRecordComp
RowLayout {
spacing: Math.round(16 * DefaultStyle.dp)
RoundButton {
style: ButtonStyle.player
shadowEnabled: true
padding: Math.round(4 * DefaultStyle.dp)
icon.width: Math.round(22 * DefaultStyle.dp)
icon.height: Math.round(22 * DefaultStyle.dp)
icon.source: AppIcons.closeX
width: Math.round(30 * DefaultStyle.dp)
Layout.preferredWidth: width
Layout.preferredHeight: height
onClicked: {
if (voiceMessage.chatMessage) mainItem.chat.core.lDeleteMessage(voiceMessage.chatMessage)
sendingAreaStackView.pop()
}
}
ChatAudioContent {
id: voiceMessage
onHeightChanged: {
sendingAreaStackView.height = height
}
recording: true
Layout.fillWidth: true
Layout.preferredHeight: Math.round(48 * DefaultStyle.dp)
chatMessageContentGui: chatMessage ? chatMessage.core.getVoiceRecordingContent() : null
onVoiceRecordingMessageCreationRequested: (recorderGui) => {
chatMessageObj = UtilsCpp.createVoiceRecordingMessage(recorderGui, mainItem.chat)
}
}
BigButton {
id: sendButton
style: ButtonStyle.noBackgroundOrange
icon.source: AppIcons.paperPlaneRight
icon.width: Math.round(22 * DefaultStyle.dp)
icon.height: Math.round(22 * DefaultStyle.dp)
// Layout.preferredWidth: icon.width
// Layout.preferredHeight: icon.height
property bool sendVoiceRecordingOnCreated: false
onClicked: {
if (voiceMessage.chatMessage) {
voiceMessage.chatMessage.core.lSend()
sendingAreaStackView.pop()
}
else {
sendVoiceRecordingOnCreated = true
voiceMessage.stopRecording()
}
}
Connections {
target: voiceMessage
function onChatMessageChanged() {
if (sendButton.sendVoiceRecordingOnCreated) {
voiceMessage.chatMessage.core.lSend()
sendButton.sendVoiceRecordingOnCreated = false
sendingAreaStackView.pop()
}
}
}
}
}
}
}
Rectangle {
id: hoverContent
anchors.fill: parent
color: DefaultStyle.main2_0
visible: false
radius: Math.round(20 * DefaultStyle.dp)
EffectImage {
anchors.centerIn: parent
imageSource: AppIcons.filePlus
width: Math.round(37 * DefaultStyle.dp)
height: Math.round(37 * DefaultStyle.dp)
colorizationColor: DefaultStyle.main2_500_main
}
DashRectangle {
x: parent.x
y: parent.y
radius: hoverContent.radius
color: DefaultStyle.main2_500_main
width: parent.width
height: parent.height
}
}
DropArea {
anchors.fill: parent
keys: [ 'text/uri-list' ]
visible: mainItem.dropEnabled
onDropped: (drop) => {
state = ''
if (drop.hasUrls) {
_emitFiles(drop.urls)
}
}
onEntered: state = 'hover'
onExited: state = ''
states: State {
name: 'hover'
PropertyChanges { target: hoverContent; visible: true }
}
}
}