diff --git a/images/icons/mozaic_black_24dp.svg b/images/icons/mozaic_black_24dp.svg
new file mode 100644
index 00000000..114bc3f9
--- /dev/null
+++ b/images/icons/mozaic_black_24dp.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/resources.qrc b/resources.qrc
index e5e4217a..1b86aec0 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -6,6 +6,7 @@
images/jami_rolling_spinner.gif
images/icons/baseline-close-24px.svg
images/icons/cross_black_24dp.svg
+ images/icons/mozaic_black_24dp.svg
images/icons/baseline-done-24px.svg
images/icons/baseline-error_outline-24px.svg
projectcredits.html
diff --git a/src/calladapter.cpp b/src/calladapter.cpp
index cd5f72b6..b0a6d7ef 100644
--- a/src/calladapter.cpp
+++ b/src/calladapter.cpp
@@ -629,6 +629,9 @@ CallAdapter::updateCallOverlay(const lrc::api::conversation::Info& convInfo)
bool isAudioMuted = call->audioMuted && (call->status != lrc::api::call::Status::PAUSED);
bool isVideoMuted = call->videoMuted && !isPaused && !call->isAudioOnly;
bool isRecording = isRecordingThisCall();
+ bool isConferenceCall = !convInfo.confId.isEmpty()
+ || (convInfo.confId.isEmpty() && call->participantsInfos.size() != 0);
+ bool isGrid = call->layout == lrc::api::call::Layout::GRID;
auto bestName = convInfo.participants.isEmpty()
? QString()
: accInfo.contactModel->bestNameForContact(convInfo.participants[0]);
@@ -639,6 +642,8 @@ CallAdapter::updateCallOverlay(const lrc::api::conversation::Info& convInfo)
isVideoMuted,
isRecording,
accInfo.profileInfo.type == lrc::api::profile::Type::SIP,
+ isConferenceCall,
+ isGrid,
bestName);
}
@@ -712,6 +717,20 @@ CallAdapter::minimizeParticipant(const QString& uri)
}
}
+void
+CallAdapter::showGridConferenceLayout()
+{
+ auto* callModel = lrcInstance_->getAccountInfo(accountId_).callModel.get();
+ const auto& convInfo
+ = lrcInstance_->getConversationFromConvUid(lrcInstance_->get_selectedConvUid(), accountId_);
+
+ auto confId = convInfo.confId;
+ if (confId.isEmpty())
+ confId = convInfo.callId;
+
+ callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID);
+}
+
void
CallAdapter::hangUpThisCall()
{
diff --git a/src/calladapter.h b/src/calladapter.h
index 1e56d3df..2b181b7f 100644
--- a/src/calladapter.h
+++ b/src/calladapter.h
@@ -59,6 +59,7 @@ public:
Q_INVOKABLE void hangUpCall(const QString& callId);
Q_INVOKABLE void maximizeParticipant(const QString& uri);
Q_INVOKABLE void minimizeParticipant(const QString& uri);
+ Q_INVOKABLE void showGridConferenceLayout();
Q_INVOKABLE void hangUpThisCall();
Q_INVOKABLE bool isCurrentHost() const;
Q_INVOKABLE bool participantIsHost(const QString& uri) const;
@@ -95,6 +96,8 @@ Q_SIGNALS:
bool isVideoMuted,
bool isRecording,
bool isSIP,
+ bool isConferenceCall,
+ bool isGrid,
const QString& bestName);
void remoteRecordingChanged(const QStringList& peers, bool state);
void eraseRemoteRecording();
diff --git a/src/commoncomponents/PhotoboothView.qml b/src/commoncomponents/PhotoboothView.qml
index 19434d54..b8edc565 100644
--- a/src/commoncomponents/PhotoboothView.qml
+++ b/src/commoncomponents/PhotoboothView.qml
@@ -240,9 +240,6 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
- text: ""
- font.pointSize: 10
- font.kerning: true
imageColor: JamiTheme.textColor
toolTipText: JamiStrings.takePhoto
@@ -284,10 +281,6 @@ ColumnLayout {
Layout.preferredHeight: JamiTheme.preferredFieldHeight
Layout.alignment: Qt.AlignHCenter
- text: ""
- font.pointSize: 10
- font.kerning: true
-
radius: height / 6
source: "qrc:/images/icons/round-folder-24px.svg"
diff --git a/src/commoncomponents/PushButton.qml b/src/commoncomponents/PushButton.qml
index 255fb853..f9481e5b 100644
--- a/src/commoncomponents/PushButton.qml
+++ b/src/commoncomponents/PushButton.qml
@@ -37,17 +37,18 @@ AbstractButton {
// Shape will default to a 15px circle
// but can be sized accordingly.
property int preferredSize: 30
- property int preferredWidth: preferredSize
- property int preferredHeight: preferredSize
property int preferredMargin: 16
- property alias textHAlign: textContent.horizontalAlignment
// Note the radius will default to preferredSize
property alias radius: background.radius
// Text properties
property alias buttonText: textContent.text
+ property alias buttonTextHeight: textContent.height
+ readonly property alias buttonTextWidth: textContent.width
+ property alias buttonTextFont: textContent.font
property alias buttonTextColor: textContent.color
- property alias fontPointSize: textContent.font.pointSize
+ property alias textHAlign: textContent.horizontalAlignment
+ property bool buttonTextEnableElide: false
property string toolTipText: ""
@@ -61,6 +62,8 @@ AbstractButton {
property int duration: JamiTheme.shortFadeDuration
// Image properties
+ property alias imageContainerWidth: image.containerWidth
+ property alias imageContainerHeight: image.containerHeight
property alias source: image.source
property var imageColor: null
property string normalImageSource
@@ -81,6 +84,63 @@ AbstractButton {
ToolTip.visible: hovered && (toolTipText.length > 0)
ToolTip.text: toolTipText
+ ResponsiveImage {
+ id: image
+
+ anchors.centerIn: textContent.text ? undefined : root
+ anchors.left: textContent.text ? root.left : undefined
+ anchors.leftMargin: textContent.text ? preferredMargin : 0
+ anchors.verticalCenter: root.verticalCenter
+
+ containerHeight: preferredSize
+ containerWidth: preferredSize
+
+ source: {
+ if (checkable && checkedImageSource)
+ return checked ? checkedImageSource : normalImageSource
+ else
+ return normalImageSource
+ }
+
+ layer {
+ enabled: imageColor || checkedColor
+ effect: ColorOverlay {
+ id: overlay
+ color: {
+ if (checked && checkedImageColor)
+ return checkedImageColor
+ else if (imageColor)
+ return imageColor
+ else
+ return JamiTheme.transparentColor
+ }
+ }
+ // Mipmap does not render correctly on linux
+ mipmap: false
+ smooth: true
+ }
+ }
+
+ Text {
+ id: textContent
+
+ anchors.left: image.right
+ anchors.leftMargin: preferredMargin
+ anchors.verticalCenter: root.verticalCenter
+
+ anchors.right: buttonTextEnableElide ? root.right : undefined
+ anchors.rightMargin: preferredMargin
+
+ visible: text ? true : false
+
+ horizontalAlignment: Text.AlignHCenter
+
+ color: JamiTheme.primaryForegroundColor
+ font.kerning: true
+ font.pointSize: 9
+ elide: Qt.ElideRight
+ }
+
background: Rectangle {
id: background
@@ -119,64 +179,5 @@ AbstractButton {
ColorAnimation { duration: root.duration }
}
]
-
- ResponsiveImage {
- id: image
-
- containerWidth: preferredSize
- containerHeight: preferredSize
-
- anchors.verticalCenter: background.verticalCenter
- anchors.horizontalCenter: textContent.text ? undefined : parent.horizontalCenter
- anchors.left: textContent.text ? parent.left : undefined
- anchors.leftMargin: preferredMargin
-
- source: {
- if (checkable && checkedImageSource)
- return checked ? checkedImageSource : normalImageSource
- else
- return normalImageSource
- }
-
- layer {
- enabled: imageColor || checkedColor
- effect: ColorOverlay {
- id: overlay
- color: {
- if (checked && checkedImageColor)
- return checkedImageColor
- else if (imageColor)
- return imageColor
- else
- return JamiTheme.transparentColor
- }
- }
- // Mipmap does not render correctly on linux
- mipmap: false
- smooth: true
- }
- }
-
- Text {
- id: textContent
-
- anchors.left: {
- if (image.source.toString() !== '')
- return image.right
- else
- return background.left
- }
- anchors.leftMargin: preferredMargin
- anchors.right: background.right
- anchors.rightMargin: preferredMargin
- anchors.verticalCenter: background.verticalCenter
-
- horizontalAlignment: Text.AlignHCenter
-
- color: JamiTheme.primaryForegroundColor
- font.kerning: true
- font.pointSize: 9
- elide: Qt.ElideRight
- }
}
}
diff --git a/src/constant/JamiStrings.qml b/src/constant/JamiStrings.qml
index 208c73b7..bd3853aa 100644
--- a/src/constant/JamiStrings.qml
+++ b/src/constant/JamiStrings.qml
@@ -197,6 +197,7 @@ Item {
property string addParticipants: qsTr("Add participants")
property string chat: qsTr("Chat")
property string moreOptions: qsTr("More options")
+ property string mozaic: qsTr("Mozaic")
// CallViewContextMenu
property string hold: qsTr("Hold")
diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml
index a3357ba0..7bc53698 100644
--- a/src/constant/JamiTheme.qml
+++ b/src/constant/JamiTheme.qml
@@ -91,6 +91,7 @@ Item {
property color acceptGreenTransparency: rgba256(11, 130, 113, 56)
property color refuseRed: rgba256(204, 0, 34, 100)
property color refuseRedTransparent: rgba256(204, 0, 34, 56)
+ property color mozaicButtonNormalColor: "#272727"
property color closeButtonLighterBlack: "#4c4c4c"
@@ -204,6 +205,14 @@ Item {
property int participantCallInStatusFontSize: 9
property int participantCallInStatusTextWidthLimit: 100
property int participantCallInStatusTextWidth: 68
+ property int mozaicButtonRadius: 5
+ property int mozaicButtonPreferredMargin: 5
+ property real mozaicButtonOpacity: 0.77
+ property int mozaicButtonTextPreferredWidth: 40
+ property int mozaicButtonTextPreferredHeight: 16
+ property int mozaicButtonTextPointSize: 8
+ property int mozaicButtonPreferredWidth: 70
+ property int mozaicButtonMaxWidth: 100
property real maximumWidthSettingsView: 600
property real settingsHeaderpreferredHeight: 64
diff --git a/src/mainview/components/CallOverlay.qml b/src/mainview/components/CallOverlay.qml
index 8e99e8e9..17593078 100644
--- a/src/mainview/components/CallOverlay.qml
+++ b/src/mainview/components/CallOverlay.qml
@@ -47,12 +47,17 @@ Item {
property bool isRecording
property bool isSIP
property bool isModerator
+ property bool isConferenceCall
+ property bool isGrid
property string bestName: ""
signal chatButtonClicked
- onVisibleChanged: if (!visible) callViewContextMenu.close()
+ onVisibleChanged: {
+ if (!visible)
+ callViewContextMenu.close()
+ }
ParticipantsLayer {
id: __participantsLayer
@@ -66,7 +71,8 @@ Item {
}
function updateUI(isPaused, isAudioOnly, isAudioMuted,
- isVideoMuted, isRecording, isSIP) {
+ isVideoMuted, isRecording, isSIP,
+ isConferenceCall, isGrid) {
if (isPaused !== undefined) {
root.isPaused = isPaused
root.isAudioOnly = isAudioOnly
@@ -74,9 +80,10 @@ Item {
root.isVideoMuted = isVideoMuted
root.isRecording = isRecording
root.isSIP = isSIP
+ root.isConferenceCall = isConferenceCall
+ root.isGrid = isGrid
mainOverlay.recordingVisible = isRecording
}
-
root.isModerator = CallAdapter.isCurrentModerator()
}
@@ -110,13 +117,10 @@ Item {
label += ", "
i += 1
}
- label += " " + ((peers.length > 1)? JamiStrings.areRecording
- : JamiStrings.isRecording)
+ label += " " + ((peers.length > 1) ? JamiStrings.areRecording : JamiStrings.isRecording)
}
- mainOverlay.remoteRecordingLabel = state ?
- label :
- JamiStrings.peerStoppedRecording
+ mainOverlay.remoteRecordingLabel = state ? label : JamiStrings.peerStoppedRecording
callViewContextMenu.peerIsRecording = state
mainOverlay.recordingVisible = callViewContextMenu.localIsRecording
|| callViewContextMenu.peerIsRecording
diff --git a/src/mainview/components/MainOverlay.qml b/src/mainview/components/MainOverlay.qml
index d47de099..a7faeefe 100644
--- a/src/mainview/components/MainOverlay.qml
+++ b/src/mainview/components/MainOverlay.qml
@@ -51,9 +51,10 @@ Item {
// (un)subscribe to an app-wide mouse move event trap filtered
// for the overlay's geometry
- onVisibleChanged: visible ?
- CallOverlayModel.registerFilter(appWindow, this) :
- CallOverlayModel.unregisterFilter(appWindow, this)
+ onVisibleChanged: visible ? CallOverlayModel.registerFilter(
+ appWindow,
+ this) : CallOverlayModel.unregisterFilter(
+ appWindow, this)
Connections {
target: CallOverlayModel
@@ -104,80 +105,117 @@ Item {
RowLayout {
anchors.fill: parent
+ spacing: 0
+
Text {
id: jamiBestNameText
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
- Layout.preferredWidth: overlayUpperPartRect.width / 3
+ Layout.preferredWidth: overlayUpperPartRect.width / 2
Layout.preferredHeight: 50
- leftPadding: 16
- font.pointSize: JamiTheme.textFontSize
+ leftPadding: 16
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
- text: textMetricsjamiBestNameText.elidedText
- color: "white"
-
- TextMetrics {
- id: textMetricsjamiBestNameText
- font: jamiBestNameText.font
- text: {
- if (!root.isAudioOnly) {
- if (remoteRecordingLabel === "") {
- return root.bestName
- } else {
- return remoteRecordingLabel
- }
+ font.pointSize: JamiTheme.textFontSize
+ text: {
+ if (!root.isAudioOnly) {
+ if (remoteRecordingLabel === "") {
+ return root.bestName
+ } else {
+ return remoteRecordingLabel
}
- return ""
}
- elideWidth: overlayUpperPartRect.width / 3
- elide: Qt.ElideRight
+ return ""
}
+ color: JamiTheme.whiteColor
+ elide: Qt.ElideRight
+ }
+
+ PushButton {
+ id: mozaicButton
+
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
+ Layout.preferredWidth: JamiTheme.mozaicButtonPreferredWidth
+ Layout.preferredHeight: 30
+ Layout.rightMargin: 5
+
+ visible: isConferenceCall && !isGrid
+
+ preferredMargin: JamiTheme.mozaicButtonPreferredMargin
+ radius: JamiTheme.mozaicButtonRadius
+ opacity: JamiTheme.mozaicButtonOpacity
+
+ buttonText: JamiStrings.mozaic
+ buttonTextColor: JamiTheme.whiteColor
+ buttonTextHeight: JamiTheme.mozaicButtonTextPreferredHeight
+ buttonTextFont.weight: Font.DemiBold
+ buttonTextFont.pointSize: JamiTheme.mozaicButtonTextPointSize
+ textHAlign: Text.AlignLeft
+
+ imageColor: JamiTheme.whiteColor
+ imageContainerHeight: 20
+ imageContainerWidth: 20
+ source: "qrc:/images/icons/mozaic_black_24dp.svg"
+
+ normalColor: JamiTheme.mozaicButtonNormalColor
+ onButtonTextWidthChanged: {
+ if (buttonTextWidth > JamiTheme.mozaicButtonTextPreferredWidth) {
+ if (mozaicButton.Layout.preferredWidth + buttonTextWidth
+ - JamiTheme.mozaicButtonTextPreferredWidth
+ > JamiTheme.mozaicButtonMaxWidth) {
+ mozaicButton.Layout.preferredWidth = JamiTheme.mozaicButtonMaxWidth
+ buttonTextEnableElide = true
+ } else
+ mozaicButton.Layout.preferredWidth += buttonTextWidth
+ - JamiTheme.mozaicButtonTextPreferredWidth
+ }
+ }
+
+ onClicked: CallAdapter.showGridConferenceLayout()
}
Text {
id: callTimerText
+
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
- Layout.preferredWidth: 64
- Layout.minimumWidth: 64
Layout.preferredHeight: 48
- Layout.rightMargin: recordingRect.visible?
- 0 : JamiTheme.preferredMarginSize
+ Layout.rightMargin: recordingRect.visible ? 0 : JamiTheme.preferredMarginSize
+
font.pointSize: JamiTheme.textFontSize
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
- text: textMetricscallTimerText.elidedText
- color: "white"
- TextMetrics {
- id: textMetricscallTimerText
- font: callTimerText.font
- text: timeText
- elideWidth: overlayUpperPartRect.width / 4
- elide: Qt.ElideRight
- }
+
+ text: timeText
+ color: JamiTheme.whiteColor
+ elide: Qt.ElideRight
}
Rectangle {
id: recordingRect
+
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.rightMargin: JamiTheme.preferredMarginSize
+
height: 16
width: 16
+
radius: height / 2
- color: "red"
+ color: JamiTheme.recordIconColor
SequentialAnimation on color {
loops: Animation.Infinite
running: true
ColorAnimation {
- from: "red"; to: "transparent";
+ from: JamiTheme.recordIconColor
+ to: "transparent"
duration: JamiTheme.recordBlinkDuration
}
ColorAnimation {
- from: "transparent"; to: "red";
+ from: "transparent"
+ to: JamiTheme.recordIconColor
duration: JamiTheme.recordBlinkDuration
}
}
@@ -206,5 +244,9 @@ Item {
height: 55
}
- Behavior on opacity { NumberAnimation { duration: JamiTheme.overlayFadeDuration }}
+ Behavior on opacity {
+ NumberAnimation {
+ duration: JamiTheme.overlayFadeDuration
+ }
+ }
}
diff --git a/src/mainview/components/OngoingCallPage.qml b/src/mainview/components/OngoingCallPage.qml
index a68f2f42..8ecd59cd 100644
--- a/src/mainview/components/OngoingCallPage.qml
+++ b/src/mainview/components/OngoingCallPage.qml
@@ -30,7 +30,7 @@ import net.jami.Constants 1.0
import "../../commoncomponents"
-Rectangle {
+Rectangle {
id: root
property var accountPeerPair: ["", ""]
@@ -50,15 +50,17 @@ Rectangle {
onAccountPeerPairChanged: {
if (accountPeerPair[0] === "" || accountPeerPair[1] === "")
- return;
+ return
contactImage.updateImage(accountPeerPair[1])
callOverlay.participantsLayer.update(CallAdapter.getConferencesInfos())
- bestName = UtilsAdapter.getBestName(accountPeerPair[0], accountPeerPair[1])
+ bestName = UtilsAdapter.getBestName(accountPeerPair[0],
+ accountPeerPair[1])
var id = UtilsAdapter.getBestId(accountPeerPair[0], accountPeerPair[1])
bestId = (bestName !== id) ? id : ""
- root.callId = UtilsAdapter.getCallId(accountPeerPair[0], accountPeerPair[1])
+ root.callId = UtilsAdapter.getCallId(accountPeerPair[0],
+ accountPeerPair[1])
}
function setLinkedWebview(webViewId) {
@@ -262,11 +264,10 @@ Rectangle {
onPositionChanged: {
// Calculate mouse position relative change.
var delta = Qt.point(mouse.x - clickPos.x,
- mouse.y - clickPos.y)
+ mouse.y - clickPos.y)
var deltaW = previewRenderer.x + delta.x + previewRenderer.width
var deltaH = previewRenderer.y + delta.y + previewRenderer.height
-
// Check if the previewRenderer exceeds the border of callPageMainRect.
if (deltaW < callPageMainRect.width
&& previewRenderer.x + delta.x > 1)
@@ -303,11 +304,14 @@ Rectangle {
target: CallAdapter
function onUpdateOverlay(isPaused, isAudioOnly, isAudioMuted, isVideoMuted,
- isRecording, isSIP, bestName) {
+ isRecording, isSIP, isConferenceCall, isGrid,
+ bestName) {
callOverlay.showOnHoldImage(isPaused)
audioCallPageRectCentralRect.visible = !isPaused && root.isAudioOnly
- callOverlay.updateUI(isPaused, isAudioOnly, isAudioMuted, isVideoMuted,
- isRecording, isSIP)
+ callOverlay.updateUI(isPaused, isAudioOnly,
+ isAudioMuted, isVideoMuted,
+ isRecording, isSIP,
+ isConferenceCall, isGrid)
root.bestName = bestName
callOverlay.participantsLayer.update(CallAdapter.getConferencesInfos())
}
diff --git a/src/settingsview/components/SettingsMenu.qml b/src/settingsview/components/SettingsMenu.qml
index cb820a96..0a2eb8e5 100644
--- a/src/settingsview/components/SettingsMenu.qml
+++ b/src/settingsview/components/SettingsMenu.qml
@@ -78,7 +78,7 @@ Rectangle {
pressedColor: Qt.lighter(JamiTheme.pressedButtonColor, 1.25)
checkedColor: JamiTheme.selectedColor
hoveredColor: JamiTheme.hoverColor
- fontPointSize: JamiTheme.textFontSize + 2
+ buttonTextFont.pointSize: JamiTheme.textFontSize + 2
duration: 0
textHAlign: Text.AlignLeft
preferredMargin: 24