mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-04-21 21:52:03 +02:00
feature: save participant's view
Change-Id: I790f10542aed306a7416a4ce79f2eaf7a770135a Gitlab: #698
This commit is contained in:
parent
9ffbf4ae1c
commit
86b84ea17e
22 changed files with 395 additions and 18 deletions
|
@ -36,6 +36,7 @@ extern const QString defaultDownloadPath;
|
|||
#define KEYS \
|
||||
X(MinimizeOnClose, true) \
|
||||
X(DownloadPath, defaultDownloadPath) \
|
||||
X(ScreenshotPath, {}) \
|
||||
X(EnableNotifications, true) \
|
||||
X(EnableTypingIndicator, true) \
|
||||
X(EnableReadReceipt, true) \
|
||||
|
|
|
@ -553,7 +553,7 @@ CallAdapter::fillParticipantData(QJsonObject& participant) const
|
|||
auto uri = participant[URI].toString();
|
||||
participant[ISLOCAL] = false;
|
||||
if (uri == accInfo.profileInfo.uri && participant[DEVICE] == getCurrentDeviceId(accInfo)) {
|
||||
participant[BESTNAME] = tr("me");
|
||||
participant[BESTNAME] = tr("Me");
|
||||
participant[ISLOCAL] = true;
|
||||
} else {
|
||||
try {
|
||||
|
@ -1145,6 +1145,27 @@ CallAdapter::updateAdvancedInformation()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CallAdapter::takeScreenshot(const QImage& image, const QString& path)
|
||||
{
|
||||
QString name = QString("%1 %2")
|
||||
.arg(tr("Screenshot"))
|
||||
.arg(QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
|
||||
bool fileAlreadyExists = true;
|
||||
int nb = 0;
|
||||
QString filePath = QString("%1%2.png").arg(path).arg(name);
|
||||
while (fileAlreadyExists) {
|
||||
filePath = QString("%1%2.png").arg(path).arg(name);
|
||||
if (nb)
|
||||
filePath = QString("%1(%2).png").arg(filePath).arg(QString::number(nb));
|
||||
QFileInfo check_file(filePath);
|
||||
fileAlreadyExists = check_file.exists() && check_file.isFile();
|
||||
nb++;
|
||||
}
|
||||
return image.save(filePath, "PNG");
|
||||
}
|
||||
|
||||
void
|
||||
CallAdapter::preventScreenSaver(bool state)
|
||||
{
|
||||
|
|
|
@ -98,6 +98,8 @@ public:
|
|||
Q_INVOKABLE void setCallInfo();
|
||||
Q_INVOKABLE void updateAdvancedInformation();
|
||||
|
||||
Q_INVOKABLE bool takeScreenshot(const QImage &image, const QString &path);
|
||||
|
||||
Q_SIGNALS:
|
||||
void callStatusChanged(int index, const QString& accountId, const QString& convUid);
|
||||
void callInfosChanged(const QVariant& infos, const QString& accountId, const QString& convUid);
|
||||
|
|
|
@ -49,6 +49,7 @@ MenuItem {
|
|||
property int itemTextMargin: 20
|
||||
|
||||
signal clicked
|
||||
property bool itemHovered: menuItemContentRect.hovered
|
||||
|
||||
contentItem: AbstractButton {
|
||||
id: menuItemContentRect
|
||||
|
|
|
@ -298,6 +298,8 @@ Item {
|
|||
property string lowerHand: qsTr("Lower hand")
|
||||
property string raiseHand: qsTr("Raise hand")
|
||||
property string layoutSettings: qsTr("Layout settings")
|
||||
property string tileScreenshot: qsTr("Take tile screenshot")
|
||||
property string screenshotTaken: qsTr("Screenshot saved to %1")
|
||||
|
||||
//advanced information
|
||||
property string renderersInformation: qsTr("Renderers information")
|
||||
|
@ -508,15 +510,16 @@ Item {
|
|||
// Context Menu
|
||||
property string saveFile: qsTr("Save file")
|
||||
property string openLocation: qsTr("Open location")
|
||||
property string me: qsTr("Me")
|
||||
|
||||
// Updates
|
||||
property string betaInstall: qsTr("Install beta version")
|
||||
property string checkForUpdates: qsTr("Check for updates now")
|
||||
property string enableAutoUpdates: qsTr("Enable/Disable automatic updates")
|
||||
property string tipAutoUpdate: qsTr("toggle automatic updates")
|
||||
property string tipAutoUpdate: qsTr("Toggle automatic updates")
|
||||
property string updatesTitle: qsTr("Updates")
|
||||
property string updateDialogTitle: qsTr("Update")
|
||||
property string updateFound: qsTr("A new version of Jami was found\n Would you like to update now?")
|
||||
property string updateFound: qsTr("A new version of Jami was found\nWould you like to update now?")
|
||||
property string updateNotFound: qsTr("No new version of Jami was found")
|
||||
property string updateCheckError: qsTr("An error occured when checking for a new version")
|
||||
property string updateNetworkError: qsTr("Network error")
|
||||
|
@ -538,7 +541,8 @@ Item {
|
|||
// Recording Settings
|
||||
property string tipRecordFolder: qsTr("Select a record directory")
|
||||
property string quality: qsTr("Quality")
|
||||
property string saveIn: qsTr("Save in")
|
||||
property string saveRecordingsTo: qsTr("Save recordings to")
|
||||
property string saveScreenshotsTo: qsTr("Save screenshots to")
|
||||
property string callRecording: qsTr("Call Recording")
|
||||
property string alwaysRecordCalls: qsTr("Always record calls")
|
||||
|
||||
|
|
|
@ -146,6 +146,11 @@ Item {
|
|||
property color spinboxBackgroundColor: darkTheme ? editBackgroundColor : selectedColor
|
||||
property color spinboxBorderColor: darkTheme ? tintedBlue : Qt.rgba(0, 0.34, 0.6, 0.36)
|
||||
|
||||
//Toast
|
||||
property color toastColor: darkTheme ? "#f0f0f0" : "#000000"
|
||||
property color toastRectColor: !darkTheme ? "#f0f0f0" : "#000000"
|
||||
property real toastFontSize: calcSize(15)
|
||||
|
||||
// Call buttons
|
||||
property color acceptButtonGreen: "#4caf50"
|
||||
property color acceptButtonHoverGreen: "#5db761"
|
||||
|
|
|
@ -174,10 +174,12 @@ MainApplication::init()
|
|||
Qt::DirectConnection);
|
||||
|
||||
auto downloadPath = settingsManager_->getValue(Settings::Key::DownloadPath);
|
||||
auto screenshotPath = settingsManager_->getValue(Settings::Key::ScreenshotPath);
|
||||
auto allowTransferFromTrusted = settingsManager_->getValue(Settings::Key::AutoAcceptFiles)
|
||||
.toBool();
|
||||
auto acceptTransferBelow = settingsManager_->getValue(Settings::Key::AcceptTransferBelow).toInt();
|
||||
lrcInstance_->accountModel().downloadDirectory = downloadPath.toString() + "/";
|
||||
lrcInstance_->accountModel().screenshotDirectory = screenshotPath.toString();
|
||||
lrcInstance_->accountModel().autoTransferFromTrusted = allowTransferFromTrusted;
|
||||
lrcInstance_->accountModel().autoTransferSizeThreshold = acceptTransferBelow;
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ Item {
|
|||
|
||||
signal chatButtonClicked
|
||||
signal fullScreenClicked
|
||||
signal closeClicked
|
||||
|
||||
function closeContextMenuAndRelatedWindows() {
|
||||
ContactPickerCreation.closeContactPicker()
|
||||
|
@ -46,14 +47,22 @@ Item {
|
|||
SelectScreenWindowCreation.destroySelectScreenWindow()
|
||||
ScreenRubberBandCreation.destroyScreenRubberBandWindow()
|
||||
PluginHandlerPickerCreation.closePluginHandlerPicker()
|
||||
root.closeClicked()
|
||||
callInformationOverlay.close()
|
||||
}
|
||||
|
||||
// x, y position does not need to be translated
|
||||
// since they all fill the call page
|
||||
function openCallViewContextMenuInPos(x, y) {
|
||||
function openCallViewContextMenuInPos(x, y,
|
||||
hoveredOverlayUri,
|
||||
hoveredOverlaySinkId,
|
||||
hoveredOverVideoMuted)
|
||||
{
|
||||
callViewContextMenu.x = x
|
||||
callViewContextMenu.y = y
|
||||
callViewContextMenu.hoveredOverlayUri = hoveredOverlayUri
|
||||
callViewContextMenu.hoveredOverlaySinkId = hoveredOverlaySinkId
|
||||
callViewContextMenu.hoveredOverVideoMuted = hoveredOverVideoMuted
|
||||
callViewContextMenu.openMenu()
|
||||
}
|
||||
|
||||
|
@ -171,10 +180,16 @@ Item {
|
|||
|
||||
onTransferCallButtonClicked: openContactPicker(ContactList.TRANSFER)
|
||||
onPluginItemClicked: openPluginsMenu()
|
||||
onScreenshotTaken: {
|
||||
toastManager.instantiateToast();
|
||||
}
|
||||
onRecordCallClicked: CallAdapter.recordThisCallToggle()
|
||||
onOpenSelectionWindow: {
|
||||
SelectScreenWindowCreation.createSelectScreenWindowObject(appWindow)
|
||||
SelectScreenWindowCreation.showSelectScreenWindow(callPreviewId, windowSelection)
|
||||
}
|
||||
onScreenshotButtonHoveredChanged: {
|
||||
participantsLayer.screenshotButtonHovered = screenshotButtonHovered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,12 @@ ContextMenuAutoLoader {
|
|||
signal transferCallButtonClicked
|
||||
signal recordCallClicked
|
||||
signal openSelectionWindow
|
||||
signal screenshotTaken
|
||||
property bool screenshotButtonHovered: screenShot.itemHovered
|
||||
|
||||
property string hoveredOverlayUri: ""
|
||||
property string hoveredOverlaySinkId: ""
|
||||
property bool hoveredOverVideoMuted: true
|
||||
|
||||
property list<GeneralMenuItem> menuItems: [
|
||||
GeneralMenuItem {
|
||||
|
@ -194,8 +200,34 @@ ContextMenuAutoLoader {
|
|||
CallAdapter.startTimerInformation();
|
||||
callInformationOverlay.open()
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: screenShot
|
||||
|
||||
canTrigger: hoveredOverlayUri !== "" && hoveredOverVideoMuted === false
|
||||
itemName: JamiStrings.tileScreenshot
|
||||
iconSource: JamiResources.baseline_camera_alt_24dp_svg
|
||||
|
||||
MaterialToolTip {
|
||||
id: tooltip
|
||||
|
||||
parent: screenShot
|
||||
visible: screenShot.itemHovered
|
||||
delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
property bool isMe: CurrentAccount.uri === hoveredOverlayUri
|
||||
text: isMe ? JamiStrings.me
|
||||
: UtilsAdapter.getBestNameForUri(CurrentAccount.id, hoveredOverlayUri)
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (CallAdapter.takeScreenshot(videoProvider.captureRawVideoFrame(hoveredOverlaySinkId),
|
||||
UtilsAdapter.getDirScreenshot())) {
|
||||
screenshotTaken()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Component.onCompleted: menuItemsToLoad = menuItems
|
||||
}
|
||||
|
|
|
@ -62,7 +62,9 @@ RowLayout {
|
|||
|
||||
anchors.centerIn: parent
|
||||
|
||||
text: shortcut
|
||||
text: shortcut2 === "" ?
|
||||
shortcut :
|
||||
shortcut + " + " + shortcut2
|
||||
font.pointSize: JamiTheme.textFontSize + 3
|
||||
font.weight: Font.DemiBold
|
||||
color: JamiTheme.textColor
|
||||
|
|
|
@ -40,42 +40,52 @@ Window {
|
|||
|
||||
ListElement {
|
||||
shortcut: "Ctrl + J"
|
||||
shortcut2: ""
|
||||
description: qsTr("Open account list")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + L"
|
||||
shortcut2: ""
|
||||
description: qsTr("Focus conversations list")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + R"
|
||||
shortcut2: ""
|
||||
description: qsTr("Requests list")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + ↑"
|
||||
shortcut2: ""
|
||||
description: qsTr("Previous conversation")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + ↓"
|
||||
shortcut2: ""
|
||||
description: qsTr("Next conversation")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + F"
|
||||
shortcut2: ""
|
||||
description: qsTr("Search bar")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "F11"
|
||||
shortcut2: ""
|
||||
description: qsTr("Full screen")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + +"
|
||||
shortcut2: ""
|
||||
description: qsTr("Increase font size")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + -"
|
||||
shortcut2: ""
|
||||
description: qsTr("Decrease font size")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + 0"
|
||||
shortcut2: ""
|
||||
description: qsTr("Reset font size")
|
||||
}
|
||||
}
|
||||
|
@ -85,34 +95,42 @@ Window {
|
|||
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + C"
|
||||
shortcut2: ""
|
||||
description: qsTr("Start an audio call")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + X"
|
||||
shortcut2: ""
|
||||
description: qsTr("Start a video call")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + L"
|
||||
shortcut2: ""
|
||||
description: qsTr("Clear history")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + B"
|
||||
shortcut2: ""
|
||||
description: qsTr("Block contact")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + Delete"
|
||||
shortcut2: ""
|
||||
description: qsTr("Remove conversation")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Shift + Ctrl + A"
|
||||
shortcut2: ""
|
||||
description: qsTr("Accept contact request")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "↑"
|
||||
shortcut2: ""
|
||||
description: qsTr("Edit last message")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Esc"
|
||||
shortcut2: ""
|
||||
description: qsTr("Cancel message edition")
|
||||
}
|
||||
}
|
||||
|
@ -122,26 +140,32 @@ Window {
|
|||
|
||||
ListElement {
|
||||
shortcut: "Ctrl + M"
|
||||
shortcut2: ""
|
||||
description: qsTr("Media settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + G"
|
||||
shortcut2: ""
|
||||
description: qsTr("General settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + I"
|
||||
shortcut2: ""
|
||||
description: qsTr("Account settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + P"
|
||||
shortcut2: ""
|
||||
description: qsTr("Plugin settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + N"
|
||||
shortcut2: ""
|
||||
description: qsTr("Open account creation wizard")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "F10"
|
||||
shortcut2: ""
|
||||
description: qsTr("Open keyboard shortcut table")
|
||||
}
|
||||
}
|
||||
|
@ -151,24 +175,34 @@ Window {
|
|||
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Y"
|
||||
shortcut2: ""
|
||||
description: qsTr("Answer an incoming call")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + D"
|
||||
shortcut2: ""
|
||||
description: qsTr("End call")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + D"
|
||||
shortcut2: ""
|
||||
description: qsTr("Decline the call request")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "M"
|
||||
shortcut2: ""
|
||||
description: qsTr("Mute microphone")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "V"
|
||||
shortcut2: ""
|
||||
description: qsTr("Stop camera")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl"
|
||||
shortcut2: qsTr("Mouse middle click")
|
||||
description: qsTr("Take tile screenshot")
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -166,7 +166,10 @@ Rectangle {
|
|||
onTapped: function (eventPoint, button) {
|
||||
if (button === Qt.RightButton) {
|
||||
callOverlay.openCallViewContextMenuInPos(eventPoint.position.x,
|
||||
eventPoint.position.y)
|
||||
eventPoint.position.y,
|
||||
participantsLayer.hoveredOverlayUri,
|
||||
participantsLayer.hoveredOverlaySinkId,
|
||||
participantsLayer.hoveredOverVideoMuted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,6 +187,7 @@ Rectangle {
|
|||
|
||||
ParticipantsLayer {
|
||||
id: participantsLayer
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.centerIn: parent
|
||||
anchors.margins: 1
|
||||
|
@ -191,9 +195,18 @@ Rectangle {
|
|||
participantsSide: callOverlay.participantsSide
|
||||
}
|
||||
|
||||
ToastManager {
|
||||
id: toastManager
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
function instantiateToast() {
|
||||
instantiate(JamiStrings.screenshotTaken.arg(UtilsAdapter.getDirScreenshot()),1000,400)
|
||||
}
|
||||
}
|
||||
|
||||
LocalVideo {
|
||||
id: previewRenderer
|
||||
|
||||
visible: (CurrentCall.isSharing || !CurrentCall.isVideoMuted)
|
||||
&& !CurrentCall.isConference
|
||||
|
||||
|
@ -329,6 +342,11 @@ Rectangle {
|
|||
openInCallConversation()
|
||||
}
|
||||
}
|
||||
onCloseClicked: {
|
||||
participantsLayer.hoveredOverlayUri = ""
|
||||
participantsLayer.hoveredOverlaySinkId = ""
|
||||
participantsLayer.hoveredOverVideoMuted = true
|
||||
}
|
||||
|
||||
onChatButtonClicked: {
|
||||
inCallMessageWebViewStack.visible ?
|
||||
|
|
|
@ -67,6 +67,18 @@ Item {
|
|||
property string muteAlertMessage: ""
|
||||
property bool muteAlertActive: false
|
||||
|
||||
property bool participantHovered: hoverIndicator.hovered
|
||||
property bool isScreenshotButtonHovered: false
|
||||
|
||||
function takeScreenshot() {
|
||||
if (!hoveredOverVideoMuted) {
|
||||
if (CallAdapter.takeScreenshot(videoProvider.captureRawVideoFrame(hoveredOverlaySinkId),
|
||||
UtilsAdapter.getDirScreenshot())) {
|
||||
toastManager.instantiateToast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMuteAlertActiveChanged: {
|
||||
if (muteAlertActive) {
|
||||
alertTimer.restart()
|
||||
|
@ -94,9 +106,11 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
z: -1
|
||||
color: JamiTheme.buttonTintedBlue
|
||||
border.color: JamiTheme.buttonTintedBlue
|
||||
border.width: 2
|
||||
color: "transparent"
|
||||
radius: 10
|
||||
visible:voiceActive
|
||||
visible: voiceActive || isScreenshotButtonHovered
|
||||
width: participantIsActive ? mediaDistRender.contentRect.width + 2 : undefined
|
||||
height: participantIsActive ? mediaDistRender.contentRect.height + 2 : undefined
|
||||
anchors.centerIn: participantIsActive ? parent : undefined
|
||||
|
@ -109,7 +123,6 @@ Item {
|
|||
anchors.margins: 2
|
||||
rendererId: root.sinkId
|
||||
crop: !participantIsActive
|
||||
|
||||
underlayItems: Avatar {
|
||||
property real componentSize: Math.min(mediaDistRender.contentRect.width / 2, mediaDistRender.contentRect.height / 2)
|
||||
height: componentSize
|
||||
|
@ -140,7 +153,25 @@ Item {
|
|||
anchors.centerIn: participantIsActive ? parent : undefined
|
||||
anchors.fill: participantIsActive ? undefined : parent
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.MiddleButton
|
||||
acceptedModifiers: Qt.ControlModifier
|
||||
onTapped: {
|
||||
takeScreenshot()
|
||||
}
|
||||
}
|
||||
|
||||
MultiPointTouchArea {
|
||||
anchors.fill: parent
|
||||
minimumTouchPoints: 3
|
||||
onPressed: {
|
||||
takeScreenshot()
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverIndicator
|
||||
|
||||
onPointChanged: {
|
||||
participantRect.opacity = 1
|
||||
fadeOutTimer.restart()
|
||||
|
@ -164,6 +195,7 @@ Item {
|
|||
// Participant buttons for moderation
|
||||
ParticipantOverlayMenu {
|
||||
id: overlayMenu
|
||||
|
||||
visible: isMe || meModerator
|
||||
anchors.fill: parent
|
||||
|
||||
|
@ -209,6 +241,7 @@ Item {
|
|||
|
||||
RowLayout {
|
||||
id: participantFootInfo
|
||||
|
||||
height: parent.height
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Text {
|
||||
|
|
|
@ -37,6 +37,10 @@ Item {
|
|||
property bool inLine: CallParticipantsModel.conferenceLayout === CallParticipantsModel.ONE_WITH_SMALL
|
||||
property bool participantsSide
|
||||
property bool enableHideSpectators: CallParticipantsModel.count > 1 && CurrentCall.hideSpectators
|
||||
property string hoveredOverlayUri: ""
|
||||
property string hoveredOverlaySinkId: ""
|
||||
property bool hoveredOverVideoMuted: true
|
||||
property bool screenshotButtonHovered: false
|
||||
|
||||
onVisibleChanged: {
|
||||
CurrentCall.hideSelf = UtilsAdapter.getAppValue(Settings.HideSelf)
|
||||
|
@ -51,7 +55,10 @@ Item {
|
|||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: leftMargin_
|
||||
|
||||
isScreenshotButtonHovered: screenshotButtonHovered && hoveredOverlaySinkId === sinkId_
|
||||
opacity: screenshotButtonHovered
|
||||
? hoveredOverlaySinkId !== sinkId ? 0.1 : 1
|
||||
: 1
|
||||
sinkId: sinkId_
|
||||
uri: uri_
|
||||
deviceId: deviceId_
|
||||
|
@ -70,6 +77,14 @@ Item {
|
|||
participantIsModeratorMuted: audioModeratorMuted_
|
||||
participantHandIsRaised: isHandRaised_
|
||||
|
||||
onParticipantHoveredChanged: {
|
||||
if (participantHovered) {
|
||||
hoveredOverlayUri = overlay.uri
|
||||
hoveredOverlaySinkId = overlay.sinkId
|
||||
hoveredOverVideoMuted = videoMuted_
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
id: registeredNameFoundConnection
|
||||
|
||||
|
|
72
src/app/mainview/components/Toast.qml
Normal file
72
src/app/mainview/components/Toast.qml
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Savoir-faire Linux Inc.
|
||||
* Author: Vengeon Nicolas <nicolas.vengeon@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 net.jami.Constants 1.1
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: textMessage.width + 20
|
||||
height: textMessage.height + 10
|
||||
anchors.topMargin: 10
|
||||
radius: 15
|
||||
color: JamiTheme.toastRectColor
|
||||
|
||||
property int duration
|
||||
property int fadingTime
|
||||
property string message
|
||||
|
||||
Component.onCompleted: {
|
||||
anim.start();
|
||||
}
|
||||
|
||||
Text {
|
||||
id: textMessage
|
||||
|
||||
anchors.centerIn: root
|
||||
text: message
|
||||
font.pointSize: JamiTheme.toastFontSize
|
||||
color: JamiTheme.toastColor
|
||||
}
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
id: anim
|
||||
|
||||
running: false
|
||||
|
||||
NumberAnimation {
|
||||
to: 0.9
|
||||
duration: root.fadingTime
|
||||
}
|
||||
PauseAnimation {
|
||||
duration: root.duration
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: root.fadingTime
|
||||
}
|
||||
|
||||
onRunningChanged: {
|
||||
if (!running)
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
}
|
28
src/app/mainview/components/ToastManager.qml
Normal file
28
src/app/mainview/components/ToastManager.qml
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Savoir-faire Linux Inc.
|
||||
* Author: Vengeon Nicolas <nicolas.vengeon@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
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
function instantiate(message, duration, fadingTime) {
|
||||
var component = Qt.createComponent("Toast.qml");
|
||||
var sprite = component.createObject(root, {message: message, duration: duration, fadingTime: fadingTime});
|
||||
}
|
||||
}
|
|
@ -28,24 +28,32 @@ import net.jami.Constants 1.1
|
|||
import "../../commoncomponents"
|
||||
|
||||
ColumnLayout {
|
||||
id:root
|
||||
id: root
|
||||
|
||||
property int itemWidth
|
||||
property string recordPath: AVModel.getRecordPath()
|
||||
property string screenshotPath: UtilsAdapter.getDirScreenshot()
|
||||
|
||||
onRecordPathChanged: {
|
||||
if(recordPath === "") return
|
||||
if(recordPath === "")
|
||||
return
|
||||
|
||||
if(AVModel){
|
||||
if(AVModel) {
|
||||
AVModel.setRecordPath(recordPath)
|
||||
}
|
||||
}
|
||||
|
||||
onScreenshotPathChanged: {
|
||||
if (screenshotPath === "")
|
||||
return
|
||||
UtilsAdapter.setScreenshotPath(screenshotPath)
|
||||
}
|
||||
|
||||
FolderDialog {
|
||||
id: recordPathDialog
|
||||
|
||||
title: JamiStrings.selectFolder
|
||||
currentFolder: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||
currentFolder: UtilsAdapter.getDirScreenshot()
|
||||
options: FolderDialog.ShowDirsOnly
|
||||
|
||||
onAccepted: {
|
||||
|
@ -54,6 +62,19 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
|
||||
FolderDialog {
|
||||
id: screenshotPathDialog
|
||||
|
||||
title: JamiStrings.selectFolder
|
||||
currentFolder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
||||
options: FolderDialog.ShowDirsOnly
|
||||
|
||||
onAccepted: {
|
||||
var dir = UtilsAdapter.getAbsPath(folder.toString())
|
||||
screenshotPath = dir
|
||||
}
|
||||
}
|
||||
|
||||
Timer{
|
||||
id: updateRecordQualityTimer
|
||||
|
||||
|
@ -172,7 +193,7 @@ ColumnLayout {
|
|||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: JamiStrings.saveIn
|
||||
text: JamiStrings.saveRecordingsTo
|
||||
color: JamiTheme.textColor
|
||||
font.pointSize: JamiTheme.settingsFontSize
|
||||
font.kerning: true
|
||||
|
@ -199,4 +220,41 @@ ColumnLayout {
|
|||
onClicked: recordPathDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: JamiTheme.preferredFieldHeight
|
||||
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: JamiStrings.saveScreenshotsTo
|
||||
color: JamiTheme.textColor
|
||||
font.pointSize: JamiTheme.settingsFontSize
|
||||
font.kerning: true
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
MaterialButton {
|
||||
id: screenshotPathButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
preferredWidth: itemWidth
|
||||
preferredHeight: JamiTheme.preferredFieldHeight
|
||||
|
||||
toolTipText: UtilsAdapter.getDirScreenshot()
|
||||
text: screenshotPath
|
||||
iconSource: JamiResources.round_folder_24dp_svg
|
||||
color: JamiTheme.buttonTintedGrey
|
||||
hoveredColor: JamiTheme.buttonTintedGreyHovered
|
||||
pressedColor: JamiTheme.buttonTintedGreyPressed
|
||||
|
||||
onClicked: screenshotPathDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -387,6 +387,22 @@ UtilsAdapter::getDirDocument()
|
|||
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
|
||||
}
|
||||
|
||||
QString
|
||||
UtilsAdapter::getDirScreenshot()
|
||||
{
|
||||
QString screenshotPath = lrcInstance_->accountModel().screenshotDirectory;
|
||||
if (screenshotPath.isEmpty()) {
|
||||
QString folderName = "Jami";
|
||||
auto picture = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
||||
QDir dir;
|
||||
dir.mkdir(picture + QDir::separator() + folderName);
|
||||
screenshotPath = picture + QDir::separator() + folderName;
|
||||
setScreenshotPath(screenshotPath);
|
||||
lrcInstance_->accountModel().screenshotDirectory = screenshotPath;
|
||||
}
|
||||
return screenshotPath;
|
||||
}
|
||||
|
||||
QString
|
||||
UtilsAdapter::getDirDownload()
|
||||
{
|
||||
|
@ -425,6 +441,13 @@ UtilsAdapter::setDownloadPath(QString dir)
|
|||
lrcInstance_->accountModel().downloadDirectory = dir + "/";
|
||||
}
|
||||
|
||||
void
|
||||
UtilsAdapter::setScreenshotPath(QString dir)
|
||||
{
|
||||
setAppValue(Settings::Key::ScreenshotPath, dir);
|
||||
lrcInstance_->accountModel().screenshotDirectory = dir + QDir::separator();
|
||||
}
|
||||
|
||||
void
|
||||
UtilsAdapter::monitor(const bool& continuous)
|
||||
{
|
||||
|
|
|
@ -107,9 +107,11 @@ public:
|
|||
Q_INVOKABLE QVariant getAppValue(const Settings::Key key);
|
||||
Q_INVOKABLE void setAppValue(const Settings::Key key, const QVariant& value);
|
||||
Q_INVOKABLE QString getDirDocument();
|
||||
Q_INVOKABLE QString getDirScreenshot();
|
||||
Q_INVOKABLE QString getDirDownload();
|
||||
Q_INVOKABLE void setRunOnStartUp(bool state);
|
||||
Q_INVOKABLE void setDownloadPath(QString dir);
|
||||
Q_INVOKABLE void setScreenshotPath(QString dir);
|
||||
Q_INVOKABLE void monitor(const bool& continuous);
|
||||
Q_INVOKABLE void clearInteractionsCache(const QString& accountId, const QString& convUid);
|
||||
Q_INVOKABLE QVariantMap supportedLang();
|
||||
|
|
|
@ -117,6 +117,13 @@ VideoProvider::frame(const QString& id)
|
|||
|
||||
QString
|
||||
VideoProvider::captureVideoFrame(const QString& id)
|
||||
{
|
||||
auto img = captureRawVideoFrame(id);
|
||||
return Utils::byteArrayToBase64String(Utils::QImageToByteArray(img));
|
||||
}
|
||||
|
||||
QImage
|
||||
VideoProvider::captureRawVideoFrame(const QString& id)
|
||||
{
|
||||
QMutexLocker framesLk(&framesObjsMutex_);
|
||||
if (auto* videoFrame = frame(id)) {
|
||||
|
@ -127,7 +134,7 @@ VideoProvider::captureVideoFrame(const QString& id)
|
|||
videoFrame->height(),
|
||||
videoFrame->bytesPerLine(0),
|
||||
imageFormat);
|
||||
return Utils::byteArrayToBase64String(Utils::QImageToByteArray(img));
|
||||
return img;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
Q_INVOKABLE void registerSink(const QString& id, QVideoSink* obj);
|
||||
Q_INVOKABLE void unregisterSink(QVideoSink* obj);
|
||||
Q_INVOKABLE QString captureVideoFrame(const QString& id);
|
||||
Q_INVOKABLE QImage captureRawVideoFrame(const QString& id);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onRendererStarted(const QString& id, const QSize& size);
|
||||
|
|
|
@ -73,6 +73,7 @@ public:
|
|||
* Should contains the full directory with the end marker (/ on linux for example)
|
||||
*/
|
||||
QString downloadDirectory;
|
||||
QString screenshotDirectory;
|
||||
/**
|
||||
* Accept transfer from trusted contacts
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue