-
Change-Id: I91111ff3b4d54dda85e649e62eef91ad9b935bb8
Change-Id: I91111ff3b4d54dda85e649e62eef91ad9b935bb8
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MessageBar.qml 61.17 KiB
/*
* Copyright (C) 2020-2023 Savoir-faire Linux Inc.
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import SortFilterProxyModel 0.2
import net.jami.Adapters 1.1
import net.jami.Models 1.1
import net.jami.Enums 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
RowLayout {
id: root
property alias text: messageBarTextArea.text
property var textAreaObj: messageBarTextArea
property real marginSize: JamiTheme.messageBarMarginSize
property bool sendButtonVisibility: true
property bool animate: false
property bool showDefault: !UtilsAdapter.getAppValue(Settings.Key.ShowSendOption)
property bool showTypo: UtilsAdapter.getAppValue(Settings.Key.ShowMardownOption)
property bool chatViewEnterIsNewLine: UtilsAdapter.getAppValue(Settings.Key.ChatViewEnterIsNewLine)
property bool showTypoSecond: false
property bool showPreview: false
property bool multiLine: messageBarTextArea.tooMuch
property int messageBarLayoutMaximumWidth: 486
readonly property bool isFullScreen: visibility === Window.FullScreen
signal sendMessageButtonClicked
signal sendFileButtonClicked
signal audioRecordMessageButtonClicked
signal videoRecordMessageButtonClicked
signal showMapClicked
signal emojiButtonClicked
height: showTypo || multiLine ? messageBarTextArea.height + 25 + 3 * marginSize + 1 : textAreaObj.height + marginSize + 1
Rectangle {
Layout.preferredHeight: parent.height
Layout.preferredWidth: childrenRect.width
visible: !CurrentConversation.isSip
color: JamiTheme.transparentColor
ComboBox {
id: showMoreButton
width: JamiTheme.chatViewFooterButtonSize
height: JamiTheme.chatViewFooterButtonSize
anchors.bottom: parent.bottom
anchors.bottomMargin: marginSize / 2
background: Rectangle {
implicitWidth: showMoreButton.width
implicitHeight: showMoreButton.height
radius: 5
color: JamiTheme.transparentColor
}
MaterialToolTip {
id: toolTipMoreButton
parent: showMoreButton
visible: showMoreButton.hovered && (text.length > 0)
delay: Qt.styleHints.mousePressAndHoldInterval
text: JamiStrings.showMore
}
indicator: ResponsiveImage {
width: 25
height: 25
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
source: JamiResources.more_menu_black_24dp_svg
color: JamiTheme.chatViewFooterImgColor
}
onHoveredChanged: {
if (!sharePopup.opened) {
showMoreButton.indicator.color = hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor;
showMoreButton.background.color = hovered ? JamiTheme.hoveredButtonColor : JamiTheme.transparentColor;
}
toolTipMoreButton.text = sharePopup.opened ? JamiStrings.showLess : JamiStrings.showMore;
}
onPressedChanged: {
if (sharePopup.enabled)
sharePopup.close();
}
popup: SharePopup {
id: sharePopup
y: -180
x: -20
menuMoreButton: listViewMoreButton.menuMoreButton
onClosed: messageBar.textAreaObj.forceActiveFocus()
}
}
}
Connections {
target: sharePopup
function onOpenedChanged() {
showMoreButton.indicator.color = (showMoreButton.parent && showMoreButton.parent.hovered) || (sharePopup != null && sharePopup.opened) ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor;
showMoreButton.background.color = (showMoreButton.parent && showMoreButton.parent.hovered) || sharePopup.opened ? JamiTheme.hoveredButtonColor : JamiTheme.transparentColor;
}
}
Rectangle {
id: rectangle
Layout.fillWidth: true
Layout.fillHeight: true
Layout.rightMargin: marginSize
Layout.leftMargin: marginSize
radius: 5
color: JamiTheme.transparentColor
border.color: JamiTheme.chatViewFooterRectangleBorderColor
border.width: 2
onWidthChanged: {
height = Qt.binding(() => root.height);
if (width < 468) {
showTypoSecond = false;
} else {
if (width >= 468) {
showTypoSecond = true;
}
}
}
GridLayout {
id: rowLayout
columns: 2
rows: 2
columnSpacing: 0
rowSpacing: 0
anchors.fill: parent
MessageBarTextArea {
id: messageBarTextArea
objectName: "messageBarTextArea"
maxWidth: rectangle.width - messageBarRowLayout.width - 35
Layout.row: showTypo || multiLine ? 0 : 1
Layout.column: 0
// forward activeFocus to the actual text area object
onActiveFocusChanged: {
if (activeFocus)
textAreaObj.forceActiveFocus();
}
placeholderText: JamiStrings.writeTo.arg(CurrentConversation.title)
Layout.alignment: showTypo ? Qt.AlignLeft | Qt.AlignBottom : Qt.AlignBottom
Layout.fillWidth: true
Layout.leftMargin: marginSize / 2
Layout.topMargin: marginSize / 2
Layout.bottomMargin: marginSize / 2
Layout.rightMargin: marginSize / 2
Layout.minimumHeight: JamiTheme.chatViewFooterPreferredHeight
Layout.preferredHeight: contentHeight
Layout.maximumHeight: JamiTheme.chatViewFooterTextAreaMaximumHeight - marginSize / 2
onSendMessagesRequired: {
sendMessageButtonClicked();
}
onTextChanged: {
MessagesAdapter.userIsComposing(text ? true : false);
if (!text) {
messageBarTextArea.heightBinding();
}
}
property var markdownShortCut: {
"Bold": function () {
if (!showPreview) {
listViewTypoFirst.itemAtIndex(0).action.triggered();
}
},
"Italic": function () {
if (!showPreview) {
listViewTypoFirst.itemAtIndex(1).action.triggered();
}
},
"Barre": function () {
if (!showPreview) {
listViewTypoFirst.itemAtIndex(2).action.triggered();
}
},
"Heading": function () {
if (!showPreview) {
listViewTypoFirst.itemAtIndex(3).action.triggered();
}
},
"Link": function () {
if (!showPreview) {
listViewTypoSecond.itemAtIndex(0).action.triggered();
}
},
"Code": function () {
if (!showPreview) {
listViewTypoSecond.itemAtIndex(1).action.triggered();
}
},
"Quote": function () {
if (!showPreview) {
listViewTypoSecond.itemAtIndex(2).action.triggered();
}
},
"Unordered list": function () {
if (!showPreview) {
listViewTypoSecond.itemAtIndex(3).action.triggered();
}
},
"Ordered list": function () {
if (!showPreview) {
listViewTypoSecond.itemAtIndex(4).action.triggered();
}
},
"Enter is new line": function () {
if (!showPreview) {
listViewTypoSecond.itemAtIndex(5).action.triggered();
}
}
}
Shortcut {
sequence: "Ctrl+B"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Bold"]()
}
Shortcut {
sequence: "Ctrl+I"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Italic"]()
}
Shortcut {
sequence: "Shift+Alt+X"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Barre"]()
}
Shortcut {
sequence: "Ctrl+Alt+H"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Heading"]()
}
Shortcut {
sequence: "Ctrl+Alt+K"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Link"]()
}
Shortcut {
sequence: "Ctrl+Alt+C"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Code"]()
}
Shortcut {
sequence: "Shift+Alt+9"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Quote"]()
}
Shortcut {
sequence: "Shift+Alt+8"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Unordered list"]()
}
Shortcut {
sequence: "Shift+Alt+7"
context: Qt.ApplicationShortcut
onActivated: messageBarTextArea.markdownShortCut["Ordered list"]()
}
Shortcut {
sequence: "Shift+Alt+T"
context: Qt.ApplicationShortcut
onActivated: {
showTypo = !showTypo;
}
}
Shortcut {
sequence: "Shift+Alt+P"
context: Qt.ApplicationShortcut
onActivated: {
showPreview = !showPreview;
messageBarTextArea.showPreview = showPreview;
}
}
}
Row {
id: messageBarRowLayout
Layout.row: showTypo || multiLine ? 1 : 1
Layout.column: showTypo || multiLine ? 0 : 1
Layout.alignment: showTypo || multiLine ? Qt.AlignRight : Qt.AlignBottom
Layout.columnSpan: showTypo || multiLine ? 2 : 1
Layout.topMargin: marginSize / 2
Layout.rightMargin: 0
Row {
anchors.bottom: parent.bottom
anchors.bottomMargin: marginSize / 2
Row {
id: listViewTypo
height: JamiTheme.chatViewFooterButtonSize
function isStyle(text, start, end, char1, char2, regex) {
if (char1 === "**") {
return isStarStyle(text, start, end, "bold");
}
if (char1 === "*") {
return isStarStyle(text, start, end, "italic");
}
var selectedText = text.substring(start - char1.length, end + char2.length);
return (selectedText.startsWith(char1) && selectedText.endsWith(char2));
var res = regex.test(text.substring(start - char1.length, end + char2.length));
return res && start - char1.length >= 0 && end + char2.length <= text.length;
}
function isStarStyle(text, selectionStart, selectionEnd, type) {
let start = selectionStart;
while (start > 0 && text[start - 1] === "*") {
start--;
}
let end = selectionEnd;
while (end < text.length && text[end] === "*") {
end++;
}
const starCount = Math.min(selectionStart - start, end - selectionEnd);
if (type === "italic") {
return starCount === 1 || starCount === 3;
}
return starCount === 2 || starCount === 3;
}
function addStyle(text, start, end, char1, char2, regex) {
// get the selected text with markdown effect
var selectedText = text.substring(start - char1.length, end + char2.length);
if (isStyle(text, start, end, char1, char2, regex)) {
// If the selected text is already formatted with the given characters, remove them
selectedText = text.substring(start, end);
root.text = text.substring(0, start - char1.length) + selectedText + text.substring(end + char2.length);
messageBarTextArea.selectText(start - char1.length, end - char1.length);
} else {
// Otherwise, add the formatting characters to the selected text
root.text = text.substring(0, start) + char1 + text.substring(start, end) + char2 + text.substring(end);
messageBarTextArea.selectText(start + char1.length, end + char1.length);
}
}
function isPrefixSyle(message, selectionStart, selectionEnd, delimiter, isOrderedList) {
//represents all the selected lines
var multilineSelection;
var newPrefix;
var newSuffix;
var newStartPos;
var newEndPos;
function nextIndexOf(text, char1, startPos) {
return text.indexOf(char1, startPos + 1);
}
//get the previous index of the multilineSelection text
if (message[selectionStart] === "\n")
newStartPos = message.lastIndexOf('\n', selectionStart - 1);
else
newStartPos = message.lastIndexOf('\n', selectionStart);
//get the next index of the multilineSelection text
if (message[selectionEnd] === "\n" || message[selectionEnd] === undefined)
newEndPos = selectionEnd;
else
newEndPos = nextIndexOf(message, "\n", selectionEnd);
//if the text is empty
if (newStartPos === -1)
newStartPos = 0;
newPrefix = message.slice(0, newStartPos);
multilineSelection = message.slice(newStartPos, newEndPos);
newSuffix = message.slice(newEndPos);
var isFirstLineSelected = !multilineSelection.startsWith('\n') || newPrefix === "";
var getDelimiter_counter = 1;
function getDelimiter() {
return `${getDelimiter_counter++}. `;
}
function getHasCurrentMarkdown() {
const linesQuantity = (multilineSelection.match(/\n/g) || []).length;
const newLinesWithDelimitersQuantity = (multilineSelection.match(new RegExp(`\n${delimiter}`, 'g')) || []).length;
if (newLinesWithDelimitersQuantity === linesQuantity && !isFirstLineSelected)
return true;
return linesQuantity === newLinesWithDelimitersQuantity && multilineSelection.startsWith(delimiter);
}
function getHasCurrentMarkdownBullet() {
const linesQuantity = (multilineSelection.match(/\n/g) || []).length;
const newLinesWithDelimitersQuantity = (multilineSelection.match(/\n\d+\. /g) || []).length;
if (newLinesWithDelimitersQuantity === linesQuantity && !isFirstLineSelected)
return true;
return linesQuantity === newLinesWithDelimitersQuantity && (/^\d\. /).test(multilineSelection);
}
var newValue;
var newStart;
var newEnd;
var count;
var startPos;
var multilineSelectionLength;
if (!isOrderedList) {
return getHasCurrentMarkdown();
} else {
return getHasCurrentMarkdownBullet();
}
}
function addPrefixStyle(message, selectionStart, selectionEnd, delimiter, isOrderedList) {
//represents all the selected lines
var multilineSelection;
var newPrefix;
var newSuffix;
var newStartPos;
var newEndPos;
function nextIndexOf(text, char1, startPos) {
return text.indexOf(char1, startPos + 1);
}
//get the previous index of the multilineSelection text
if (message[selectionStart] === "\n")
newStartPos = message.lastIndexOf('\n', selectionStart - 1);
else
newStartPos = message.lastIndexOf('\n', selectionStart);
//get the next index of the multilineSelection text
if (message[selectionEnd] === "\n" || message[selectionEnd] === undefined)
newEndPos = selectionEnd;
else
newEndPos = nextIndexOf(message, "\n", selectionEnd);
//if the text is empty
if (newStartPos === -1)
newStartPos = 0;
newPrefix = message.slice(0, newStartPos);
multilineSelection = message.slice(newStartPos, newEndPos);
newSuffix = message.slice(newEndPos);
var isFirstLineSelected = !multilineSelection.startsWith('\n') || newPrefix === "";
var getDelimiter_counter = 1;
function getDelimiter() {
return `${getDelimiter_counter++}. `;
}
function getHasCurrentMarkdown() {
const linesQuantity = (multilineSelection.match(/\n/g) || []).length;
const newLinesWithDelimitersQuantity = (multilineSelection.match(new RegExp(`\n${delimiter}`, 'g')) || []).length;
if (newLinesWithDelimitersQuantity === linesQuantity && !isFirstLineSelected)
return true;
return linesQuantity === newLinesWithDelimitersQuantity && multilineSelection.startsWith(delimiter);
}
function getHasCurrentMarkdownBullet() {
const linesQuantity = (multilineSelection.match(/\n/g) || []).length;
const newLinesWithDelimitersQuantity = (multilineSelection.match(/\n\d+\. /g) || []).length;
if (newLinesWithDelimitersQuantity === linesQuantity && !isFirstLineSelected)
return true;
return linesQuantity === newLinesWithDelimitersQuantity && (/^\d\. /).test(multilineSelection);
}
var newValue;
var newStart;
var newEnd;
var count;
var startPos;
var multilineSelectionLength;
if (!isOrderedList) {
if (getHasCurrentMarkdown()) {
// clear first line from delimiter
if (isFirstLineSelected)
multilineSelection = multilineSelection.slice(delimiter.length);
newValue = newPrefix + multilineSelection.replace(new RegExp(`\n${delimiter}`, 'g'), '\n') + newSuffix;
count = 0;
if (isFirstLineSelected)
count++;
count += (multilineSelection.match(/\n/g) || []).length;
newStart = Math.max(selectionStart - delimiter.length, 0);
newEnd = Math.max(selectionEnd - (delimiter.length * count), 0);
} else {
newValue = newPrefix + multilineSelection.replace(/\n/g, `\n${delimiter}`) + newSuffix;
count = 0;
if (isFirstLineSelected) {
newValue = delimiter + newValue;
count++;
}
count += (multilineSelection.match(new RegExp('\\n', 'g')) || []).length;
newStart = selectionStart + delimiter.length;
newEnd = selectionEnd + (delimiter.length * count);
}
} else if (getHasCurrentMarkdownBullet()) {
if (message[selectionStart] === "\n")
startPos = message.lastIndexOf('\n', selectionStart - 1) + 1;
else
startPos = message.lastIndexOf('\n', selectionStart) + 1;
newStart = startPos;
multilineSelection = multilineSelection.replace(/^\d+\.\s/gm, '');
newValue = newPrefix + multilineSelection + newSuffix;
multilineSelectionLength = multilineSelection.length;
//if the first line is not selected, we need to remove the first "\n" of multilineSelection
if (newStart)
multilineSelectionLength = multilineSelection.length - 1;
newEnd = Math.max(newStart + multilineSelectionLength, 0);
} else {
if (message[selectionStart] === "\n")
startPos = message.lastIndexOf('\n', selectionStart - 1) + 1;
else
startPos = message.lastIndexOf('\n', selectionStart) + 1;
newStart = startPos;
// if no text is selected
if (selectionStart === selectionEnd)
newStart = newStart + 3;
if (isFirstLineSelected)
multilineSelection = getDelimiter() + multilineSelection;
const selectionArr = Array.from(multilineSelection);
for (var i = 0; i < selectionArr.length; i++) {
if (selectionArr[i] === '\n')
selectionArr[i] = `\n${getDelimiter()}`;
}
multilineSelection = selectionArr.join('');
newValue = newPrefix + multilineSelection + newSuffix;
multilineSelectionLength = multilineSelection.length;
//if the first line is not selected, we meed to remove the first "\n" of multilineSelection
if (startPos)
multilineSelectionLength = multilineSelection.length - 1;
newEnd = Math.max(startPos + multilineSelectionLength, 0);
}
root.text = newValue;
messageBarTextArea.selectText(newStart, newEnd);
}
ListView {
id: listViewTypoFirst
objectName: "listViewTypoFirst"
visible: showTypo
width: contentWidth + 2 * leftMargin
Behavior on width {
NumberAnimation {
duration: JamiTheme.longFadeDuration / 2
}
}
height: JamiTheme.chatViewFooterButtonSize
orientation: ListView.Horizontal
interactive: false
leftMargin: 5
rightMargin: 5
spacing: 5
property list<Action> menuTypoActionsFirst: [
Action {
id: boldAction
property var iconSrc: JamiResources.bold_black_24dp_svg
property var shortcutText: JamiStrings.bold
property string shortcutKey: "Ctrl+B"
property bool isStyle: listViewTypo.isStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "**", "**", /\\*\*.+\\*\*/)
onTriggered: function clickAction() {
listViewTypo.addStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "**", "**", /\\*\*.+\\*\*/);
}
},
Action {
id: italicAction
property var iconSrc: JamiResources.italic_black_24dp_svg
property var shortcutText: JamiStrings.italic
property string shortcutKey: "Ctrl+I"
property bool isStyle: listViewTypo.isStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "*", "*", /(?:\*.+\*|\*\*\*.+\*\*\*)/)
onTriggered: function clickAction() {
listViewTypo.addStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "*", "*", /(?:\*.+\*|\*\*\*.+\*\*\*)/);
}
},
Action {
id: strikethroughAction
property var iconSrc: JamiResources.s_barre_black_24dp_svg
property var shortcutText: JamiStrings.strikethrough
property string shortcutKey: "Shift+Alt+X"
property bool isStyle: listViewTypo.isStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "~~", "~~", /\~\~.+\~\~/)
onTriggered: function clickAction() {
listViewTypo.addStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "~~", "~~", /\~\~.+\~\~/);
}
},
Action {
id: titleAction
property var iconSrc: JamiResources.title_black_24dp_svg
property var shortcutText: JamiStrings.heading
property string shortcutKey: "Ctrl+Alt+H"
property bool isStyle: listViewTypo.isPrefixSyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "### ", false)
onTriggered: function clickAction() {
listViewTypo.addPrefixStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "### ", false);
}
},
Action {
id: linkAction
property var iconSrc: JamiResources.link_web_black_24dp_svg
property var shortcutText: JamiStrings.link
property string shortcutKey: "Ctrl+Alt+K"
property bool isStyle: listViewTypo.isStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "[", "](url)", /\[.+\]\(.+\)/)
onTriggered: function clickAction() {
listViewTypo.addStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "[", "](url)", /\[.+\]\(.+\)/);
}
},
Action {
id: codeAction
property var iconSrc: JamiResources.code_black_24dp_svg
property var shortcutText: JamiStrings.code
property string shortcutKey: "Ctrl+Alt+C"
property bool isStyle: listViewTypo.isStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "```", "```", /\`\`\`.+\`\`\`/)
onTriggered: function clickAction() {
listViewTypo.addStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "```", "```", /\`\`\`.+\`\`\`/);
}
}
]
model: menuTypoActionsFirst
delegate: PushButton {
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
preferredSize: JamiTheme.chatViewFooterRealButtonSize
imageContainerWidth: 15
imageContainerHeight: 15
radius: 5
hoverEnabled: !showPreview
enabled: !showPreview
toolTipText: modelData.shortcutText
shortcutKey: modelData.shortcutKey
hasShortcut: true
source: modelData.iconSrc
focusPolicy: Qt.TabFocus
normalColor: {
if (showPreview) {
return JamiTheme.transparentColor;
} else if (modelData.isStyle) {
return JamiTheme.hoveredButtonColor;
} else {
return JamiTheme.transparentColor;
}
}
imageColor: {
if (showPreview) {
return JamiTheme.chatViewFooterImgDisableColor;
} else if (hovered) {
return JamiTheme.chatViewFooterImgHoverColor;
} else if (modelData.isStyle) {
return JamiTheme.chatViewFooterImgHoverColor;
} else {
return JamiTheme.chatViewFooterImgColor;
}
}
hoveredColor: JamiTheme.hoveredButtonColor
pressedColor: hoveredColor
action: modelData
}
}
Rectangle {
height: JamiTheme.chatViewFooterButtonSize
color: JamiTheme.transparentColor
visible: showTypo && showTypoSecond
width: 2
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2
height: JamiTheme.chatViewFooterButtonSize / 2
color: showPreview ? JamiTheme.chatViewFooterImgDisableColor : JamiTheme.chatViewFooterSeparateLineColor
}
}
Rectangle {
z: -1
radius: 0
color: JamiTheme.transparentColor
width: JamiTheme.chatViewFooterButtonSize
height: JamiTheme.chatViewFooterButtonSize
visible: showTypo && !showTypoSecond
ComboBox {
id: showMoreTypoButton
width: JamiTheme.chatViewFooterRealButtonSize
height: width
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
enabled: !showPreview
hoverEnabled: !showPreview
MaterialToolTip {
id: toolTip
parent: showMoreTypoButton
visible: showMoreTypoButton.hovered && (text.length > 0)
delay: Qt.styleHints.mousePressAndHoldInterval
text: markdownPopup.visible ? JamiStrings.showLess : JamiStrings.showMore
}
background: Rectangle {
implicitWidth: showMoreTypoButton.width
implicitHeight: showMoreTypoButton.height
radius: 5
color: showPreview ? JamiTheme.transparentColor : (parent && parent.hovered ? JamiTheme.hoveredButtonColor : JamiTheme.transparentColor)
}
indicator: ResponsiveImage {
containerHeight: 20
containerWidth: 20
width: 18
height: 18
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
source: JamiResources.more_vert_24dp_svg
color: showPreview ? JamiTheme.chatViewFooterImgDisableColor : (parent && parent.hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor)
}
popup: MarkdownPopup {
id: markdownPopup
y: 1.5 * parent.height
x: -parent.width * 2
width: 155
height: JamiTheme.chatViewFooterButtonSize
menuTypoActionsSecond: listViewTypoSecond.menuTypoActionsSecond
}
}
}
ListView {
id: listViewTypoSecond
visible: showTypo && showTypoSecond
width: contentWidth + 2 * leftMargin
height: JamiTheme.chatViewFooterButtonSize
orientation: ListView.Horizontal
interactive: false
leftMargin: 10
rightMargin: 10
spacing: 10
Rectangle {
anchors.fill: parent
color: JamiTheme.transparentColor
z: -1
}
property list<Action> menuTypoActionsSecond: [
Action {
id: quoteAction
property var iconSrc: JamiResources.quote_black_24dp_svg
property var shortcutText: JamiStrings.quote
property string shortcutKey: "Shift+Alt+9"
property bool isStyle: listViewTypo.isPrefixSyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "> ", false)
onTriggered: function clickAction() {
listViewTypo.addPrefixStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "> ", false);
}
},
Action {
id: unorderedListAction
property var iconSrc: JamiResources.bullet_point_black_24dp_svg
property var shortcutText: JamiStrings.unorderedList
property string shortcutKey: "Shift+Alt+8"
property bool isStyle: listViewTypo.isPrefixSyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "- ", false)
onTriggered: function clickAction() {
listViewTypo.addPrefixStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "- ", false);
}
},
Action {
id: orderedListAction
property var iconSrc: JamiResources.bullet_number_black_24dp_svg
property var shortcutText: JamiStrings.orderedList
property string shortcutKey: "Shift+Alt+7"
property bool isStyle: listViewTypo.isPrefixSyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "", true)
onTriggered: function clickAction() {
listViewTypo.addPrefixStyle(root.text, messageBarTextArea.selectionStart, messageBarTextArea.selectionEnd, "", true);
}
},
Action {
id: shiftEnterActiom
property var iconSrc: JamiResources.shift_enter_black_24dp_svg
property var shortcutText: chatViewEnterIsNewLine ? JamiStrings.enterNewLine : JamiStrings.shiftEnterNewLine
property var imageColor: chatViewEnterIsNewLine ? JamiTheme.chatViewFooterImgHoverColor : "#7f7f7f"
property var normalColor: chatViewEnterIsNewLine ? JamiTheme.hoveredButtonColor : JamiTheme.transparentColor
property var hasShortcut: false
property var shortcutKey: null
property bool isStyle: false
onTriggered: function clickAction() {
root.chatViewEnterIsNewLine = !root.chatViewEnterIsNewLine;
UtilsAdapter.setAppValue(Settings.Key.ChatViewEnterIsNewLine, chatViewEnterIsNewLine);
}
}
]
model: menuTypoActionsSecond
delegate: PushButton {
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
preferredSize: JamiTheme.chatViewFooterRealButtonSize
imageContainerWidth: 20
imageContainerHeight: 20
radius: 5
hoverEnabled: !showPreview
enabled: !showPreview
toolTipText: modelData.shortcutText
shortcutKey: modelData.shortcutKey
hasShortcut: modelData.hasShortcut != null ? modelData.hasShortcut : true
source: modelData.iconSrc
focusPolicy: Qt.TabFocus
normalColor: {
if (showPreview) {
return JamiTheme.transparentColor;
} else if (modelData.normalColor != null) {
return modelData.normalColor;
} else if (modelData.isStyle) {
return JamiTheme.hoveredButtonColor;
} else {
return JamiTheme.transparentColor;
}
}
imageColor: {
if (showPreview) {
return JamiTheme.chatViewFooterImgDisableColor;
} else if (hovered) {
return JamiTheme.chatViewFooterImgHoverColor;
} else if (modelData.imageColor != null) {
return modelData.imageColor;
} else if (modelData.isStyle) {
return JamiTheme.chatViewFooterImgHoverColor;
} else {
return JamiTheme.chatViewFooterImgColor;
}
}
hoveredColor: JamiTheme.hoveredButtonColor
pressedColor: hoveredColor
action: modelData
}
}
}
PushButton {
id: typoButton
preferredSize: JamiTheme.chatViewFooterButtonSize
imageContainerWidth: 24
imageContainerHeight: 24
radius: JamiTheme.chatViewFooterButtonRadius
hoverEnabled: !showPreview
enabled: !showPreview
toolTipText: showTypo ? JamiStrings.hideFormatting : JamiStrings.showFormatting
source: JamiResources.text_edit_black_24dp_svg
normalColor: showPreview ? JamiTheme.transparentColor : (showTypo ? JamiTheme.hoveredButtonColor : JamiTheme.transparentColor)
imageColor: showPreview ? JamiTheme.chatViewFooterImgDisableColor : (hovered || showTypo ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor)
hoveredColor: JamiTheme.hoveredButtonColor
pressedColor: hoveredColor
onClicked: {
showTypo = !showTypo;
if (messageBar.width < messageBarLayoutMaximumWidth + sendMessageButton.width + 2 * JamiTheme.preferredMarginSize)
showTypoSecond = false;
if (!showDefault)
showDefault = true;
if (showTypo) {
root.chatViewEnterIsNewLine = true;
UtilsAdapter.setAppValue(Settings.Key.ChatViewEnterIsNewLine, true);
} else {
root.chatViewEnterIsNewLine = false;
UtilsAdapter.setAppValue(Settings.Key.ChatViewEnterIsNewLine, false);
}
UtilsAdapter.setAppValue(Settings.Key.ShowMardownOption, showTypo);
UtilsAdapter.setAppValue(Settings.Key.ShowSendOption, !showDefault);
}
}
}
Row {
anchors.bottom: parent.bottom
anchors.bottomMargin: marginSize / 2
ListView {
id: listViewAction
width: contentWidth + 2 * leftMargin
Behavior on width {
NumberAnimation {
duration: JamiTheme.longFadeDuration / 2
}
}
height: JamiTheme.chatViewFooterButtonSize
orientation: ListView.Horizontal
interactive: false
leftMargin: 5
rightMargin: 5
spacing: 5
property list<Action> menuActions: [
Action {
id: sendFile
property var iconSrc: JamiResources.link_black_24dp_svg
property var toolTip: JamiStrings.sendFile
property bool show: true
property bool needWebEngine: false
property bool needVideoDevice: false
property bool noSip: false
onTriggered: function clickAction() {
sendFileButtonClicked();
textAreaObj.forceActiveFocus();
}
},
Action {
id: addEmoji
property var iconSrc: JamiResources.emoji_black_24dp_svg
property var toolTip: JamiStrings.addEmoji
property bool show: true
property bool needWebEngine: true
property bool needVideoDevice: false
property bool noSip: true
onTriggered: function clickAction() {
emojiButtonClicked();
}
}
]
ListModel {
id: listActions
Component.onCompleted: {
for (var i = 0; i < listViewAction.menuActions.length; i++) {
append({
"menuAction": listViewAction.menuActions[i]
});
}
}
}
model: SortFilterProxyModel {
sourceModel: listActions
filters: [
ExpressionFilter {
expression: menuAction.show === true
enabled: root.showDefault
},
ExpressionFilter {
expression: menuAction.needWebEngine === false
enabled: !WITH_WEBENGINE
},
ExpressionFilter {
expression: menuAction.noSip === true
enabled: CurrentConversation.isSip
},
ExpressionFilter {
expression: menuAction.needVideoDevice === false
enabled: VideoDevices.listSize === 0
}
]
}
delegate: PushButton {
id: buttonDelegate
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
preferredSize: JamiTheme.chatViewFooterButtonSize
imageContainerWidth: 25
imageContainerHeight: 25
radius: 5
hoverEnabled: !showPreview
enabled: !showPreview
toolTipText: modelData.toolTip
source: modelData.iconSrc
normalColor: JamiTheme.transparentColor
imageColor: showPreview ? JamiTheme.chatViewFooterImgDisableColor : (hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor)
hoveredColor: JamiTheme.hoveredButtonColor
pressedColor: hoveredColor
action: modelData
}
}
ListView {
id: listViewMoreButton
width: 0
Behavior on width {
NumberAnimation {
duration: JamiTheme.longFadeDuration / 2
}
}
height: JamiTheme.chatViewFooterButtonSize
orientation: ListView.Horizontal
interactive: false
leftMargin: 10
rightMargin: 10
spacing: 10
property list<Action> menuMoreButton: [
Action {
id: leaveAudioMessage
property var iconSrc: JamiResources.message_audio_black_24dp_svg
property var toolTip: JamiStrings.leaveAudioMessage
property bool show: false
property bool needWebEngine: false
property bool needVideoDevice: false
property bool noSip: false
onTriggered: function clickAction() {
audioRecordMessageButtonClicked();
}
},
Action {
id: leaveVideoMessage
property var iconSrc: JamiResources.message_video_black_24dp_svg
property var toolTip: JamiStrings.leaveVideoMessage
property bool show: false
property bool needWebEngine: false
property bool needVideoDevice: true
property bool noSip: false
onTriggered: function clickAction() {
videoRecordMessageButtonClicked();
}
},
Action {
id: shareLocation
property var iconSrc: JamiResources.localisation_sharing_send_pin_svg
property var toolTip: JamiStrings.shareLocation
property bool show: false
property bool needWebEngine: true
property bool needVideoDevice: false
property bool noSip: false
onTriggered: function clickAction() {
showMapClicked();
}
}
]
ListModel {
id: listMoreButton
Component.onCompleted: {
for (var i = 0; i < listViewMoreButton.menuMoreButton.length; i++) {
append({
"menuAction": listViewMoreButton.menuMoreButton[i]
});
}
}
}
model: SortFilterProxyModel {
sourceModel: listMoreButton
filters: [
ExpressionFilter {
expression: menuAction.show === true
enabled: showDefault
},
ExpressionFilter {
expression: menuAction.needWebEngine === false
enabled: !WITH_WEBENGINE
},
ExpressionFilter {
expression: menuAction.noSip === true
enabled: CurrentConversation.isSip
},
ExpressionFilter {
expression: menuAction.needVideoDevice === false
enabled: VideoDevices.listSize === 0
}
]
}
delegate: PushButton {
id: buttonDelegateMoreButton
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
preferredSize: JamiTheme.chatViewFooterRealButtonSize
imageContainerWidth: 20
imageContainerHeight: 20
radius: 5
toolTipText: modelData.toolTip
source: modelData.iconSrc
normalColor: JamiTheme.chatViewFooterListColor
imageColor: JamiTheme.chatViewFooterImgHoverColor
hoveredColor: JamiTheme.hoveredButtonColor
pressedColor: hoveredColor
action: modelData
}
}
}
}
Rectangle {
color: JamiTheme.transparentColor
visible: showTypo
height: 50
width: previewButton.width + marginSize
Layout.row: showTypo ? 0 : 0
Layout.column: showTypo ? 1 : 1
PushButton {
id: previewButton
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: marginSize
preferredSize: JamiTheme.chatViewFooterButtonSize
imageContainerWidth: 25
imageContainerHeight: 25
radius: 5
source: JamiResources.preview_black_24dp_svg
normalColor: showPreview ? hoveredColor : JamiTheme.transparentColor
imageColor: (hovered || showPreview) ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor
hoveredColor: JamiTheme.hoveredButtonColor
pressedColor: hoveredColor
onClicked: {
showPreview = !showPreview;
messageBarTextArea.showPreview = showPreview;
}
}
}
}
}
Rectangle {
Layout.preferredHeight: parent.height
Layout.preferredWidth: childrenRect.width
visible: sendButtonVisibility
color: JamiTheme.transparentColor
PushButton {
id: sendMessageButton
objectName: "sendMessageButton"
anchors.bottom: parent.bottom
anchors.bottomMargin: marginSize / 2
enabled: sendButtonVisibility
hoverEnabled: enabled
width: scale * JamiTheme.chatViewFooterButtonSize
height: JamiTheme.chatViewFooterButtonSize
radius: JamiTheme.chatViewFooterButtonRadius
preferredSize: JamiTheme.chatViewFooterButtonIconSize - 6
imageContainerWidth: 25
imageContainerHeight: 25
toolTipText: JamiStrings.send
mirror: UtilsAdapter.isRTL
source: JamiResources.send_black_24dp_svg
normalColor: enabled ? JamiTheme.chatViewFooterSendButtonColor : JamiTheme.chatViewFooterSendButtonDisableColor
imageColor: enabled ? JamiTheme.chatViewFooterSendButtonImgColor : JamiTheme.chatViewFooterSendButtonImgColorDisable
hoveredColor: JamiTheme.buttonTintedBlueHovered
pressedColor: hoveredColor
opacity: 1
scale: opacity
Behavior on opacity {
enabled: animate
NumberAnimation {
duration: JamiTheme.shortFadeDuration
easing.type: Easing.InOutQuad
}
}
onClicked: {
root.showPreview = false;
sendMessageButtonClicked();
}
}
}
}