mirror of
https://github.com/savoirfairelinux/jami-client-qt.git
synced 2025-12-18 00:36:35 +08:00
callbuttons: create alternate layouts
GitLab: #729 Change-Id: Ice67d8649c1ad2a92eba7c02cebc446eac5ac90e
This commit is contained in:
2
qml.qrc
2
qml.qrc
@@ -136,6 +136,8 @@
|
|||||||
<file>src/app/mainview/components/SmartListItemDelegate.qml</file>
|
<file>src/app/mainview/components/SmartListItemDelegate.qml</file>
|
||||||
<file>src/app/mainview/components/BadgeNotifier.qml</file>
|
<file>src/app/mainview/components/BadgeNotifier.qml</file>
|
||||||
<file>src/app/mainview/components/ParticipantsLayer.qml</file>
|
<file>src/app/mainview/components/ParticipantsLayer.qml</file>
|
||||||
|
<file>src/app/mainview/components/ParticipantsLayoutVertical.qml</file>
|
||||||
|
<file>src/app/mainview/components/ParticipantsLayoutHorizontal.qml</file>
|
||||||
<file>src/app/mainview/components/MainOverlay.qml</file>
|
<file>src/app/mainview/components/MainOverlay.qml</file>
|
||||||
<file>src/app/mainview/components/CallButtonDelegate.qml</file>
|
<file>src/app/mainview/components/CallButtonDelegate.qml</file>
|
||||||
<file>src/app/mainview/components/CallActionBar.qml</file>
|
<file>src/app/mainview/components/CallActionBar.qml</file>
|
||||||
|
|||||||
8
resources/icons/ontheside_black_24dp.svg
Normal file
8
resources/icons/ontheside_black_24dp.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||||
|
<path d="M18.3,2H5.8C3.7,2,2,3.7,2,5.8v12.4C2,20.3,3.7,22,5.8,22h12.4c2.1,0,3.8-1.7,3.8-3.7V5.8C22,3.7,20.3,2,18.3,2z M20.6,5.8
|
||||||
|
v2.7h-3.7v-5h1.4C19.6,3.5,20.6,4.5,20.6,5.8z M16.9,10h3.7v4.3h-3.7V10z M3.5,18.2V5.7c0-1.2,1-2.2,2.3-2.2h9.6v17H5.8
|
||||||
|
C4.6,20.5,3.5,19.5,3.5,18.2z M18.3,20.5h-1.4v-4.8h3.7v2.5C20.6,19.5,19.6,20.5,18.3,20.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 690 B |
8
resources/icons/onthetop_black_24dp.svg
Normal file
8
resources/icons/onthetop_black_24dp.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||||
|
<path d="M18.3,2H5.8C3.7,2,2,3.7,2,5.8v12.4C2,20.3,3.7,22,5.8,22h12.4c2.1,0,3.8-1.7,3.8-3.7V5.8C22,3.7,20.3,2,18.3,2z M20.6,5.8
|
||||||
|
V7h-4.8V3.5h2.5C19.6,3.5,20.6,4.5,20.6,5.8z M10.1,3.5h4.3V7h-4.3V3.5z M5.8,3.5h2.8V7H3.5V5.7C3.5,4.5,4.5,3.5,5.8,3.5z
|
||||||
|
M18.3,20.5H5.8c-1.2,0-2.3-1-2.3-2.3V8.5h17.1v9.7C20.6,19.5,19.6,20.5,18.3,20.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 684 B |
@@ -46,6 +46,7 @@ extern const QString defaultDownloadPath;
|
|||||||
X(EnableExperimentalSwarm, false) \
|
X(EnableExperimentalSwarm, false) \
|
||||||
X(EnableDarkTheme, false) \
|
X(EnableDarkTheme, false) \
|
||||||
X(BaseZoom, 1.0) \
|
X(BaseZoom, 1.0) \
|
||||||
|
X(ParticipantsSide, false) \
|
||||||
X(AutoUpdate, true) \
|
X(AutoUpdate, true) \
|
||||||
X(StartMinimized, false) \
|
X(StartMinimized, false) \
|
||||||
X(ShowChatviewHorizontally, true) \
|
X(ShowChatviewHorizontally, true) \
|
||||||
|
|||||||
@@ -679,7 +679,6 @@ CallAdapter::sipInputPanelPlayDTMF(const QString& key)
|
|||||||
void
|
void
|
||||||
CallAdapter::updateCallOverlay(const lrc::api::conversation::Info& convInfo)
|
CallAdapter::updateCallOverlay(const lrc::api::conversation::Info& convInfo)
|
||||||
{
|
{
|
||||||
qWarning() << "CallAdapter::updateCallOverlay";
|
|
||||||
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
|
||||||
auto* callModel = accInfo.callModel.get();
|
auto* callModel = accInfo.callModel.get();
|
||||||
|
|
||||||
|
|||||||
@@ -250,6 +250,8 @@ Item {
|
|||||||
property string bothMuted: qsTr("Local and Moderator muted")
|
property string bothMuted: qsTr("Local and Moderator muted")
|
||||||
property string moderatorMuted: qsTr("Moderator muted")
|
property string moderatorMuted: qsTr("Moderator muted")
|
||||||
property string notMuted: qsTr("Not muted")
|
property string notMuted: qsTr("Not muted")
|
||||||
|
property string participantsSide: qsTr("On the side")
|
||||||
|
property string participantsTop: qsTr("On the top")
|
||||||
|
|
||||||
// LineEditContextMenu
|
// LineEditContextMenu
|
||||||
property string copy: qsTr("Copy")
|
property string copy: qsTr("Copy")
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import QtQuick.Layouts
|
|||||||
import net.jami.Models 1.1
|
import net.jami.Models 1.1
|
||||||
import net.jami.Adapters 1.1
|
import net.jami.Adapters 1.1
|
||||||
import net.jami.Constants 1.1
|
import net.jami.Constants 1.1
|
||||||
|
import net.jami.Enums 1.1
|
||||||
|
|
||||||
import "../../commoncomponents"
|
import "../../commoncomponents"
|
||||||
|
|
||||||
@@ -163,6 +164,16 @@ Control {
|
|||||||
if (!isGrid)
|
if (!isGrid)
|
||||||
CallAdapter.showGridConferenceLayout()
|
CallAdapter.showGridConferenceLayout()
|
||||||
break
|
break
|
||||||
|
case JamiStrings.participantsSide:
|
||||||
|
var onTheSide = UtilsAdapter.getAppValue(Settings.ParticipantsSide)
|
||||||
|
UtilsAdapter.setAppValue(Settings.ParticipantsSide, !onTheSide)
|
||||||
|
participantsSide = !onTheSide
|
||||||
|
break
|
||||||
|
case JamiStrings.participantsTop:
|
||||||
|
var onTheSide = UtilsAdapter.getAppValue(Settings.ParticipantsSide)
|
||||||
|
UtilsAdapter.setAppValue(Settings.ParticipantsSide, !onTheSide)
|
||||||
|
participantsSide = !onTheSide
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
@@ -172,6 +183,11 @@ Control {
|
|||||||
"ActiveSetting": layoutManager.isCallFullscreen})
|
"ActiveSetting": layoutManager.isCallFullscreen})
|
||||||
if (isConference) {
|
if (isConference) {
|
||||||
layoutModel.append({})
|
layoutModel.append({})
|
||||||
|
var onTheSide = UtilsAdapter.getAppValue(Settings.ParticipantsSide)
|
||||||
|
layoutModel.append({"Name": onTheSide ? JamiStrings.participantsSide : JamiStrings.participantsTop,
|
||||||
|
"IconSource": onTheSide ? JamiResources.ontheside_black_24dp_svg : JamiResources.onthetop_black_24dp_svg,
|
||||||
|
"ActiveSetting": true})
|
||||||
|
layoutModel.append({})
|
||||||
layoutModel.append({"Name": JamiStrings.mosaic,
|
layoutModel.append({"Name": JamiStrings.mosaic,
|
||||||
"IconSource": JamiResources.mosaic_black_24dp_svg,
|
"IconSource": JamiResources.mosaic_black_24dp_svg,
|
||||||
"ActiveSetting": isGrid})
|
"ActiveSetting": isGrid})
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import QtQuick
|
|||||||
import net.jami.Models 1.1
|
import net.jami.Models 1.1
|
||||||
import net.jami.Adapters 1.1
|
import net.jami.Adapters 1.1
|
||||||
import net.jami.Constants 1.1
|
import net.jami.Constants 1.1
|
||||||
|
import net.jami.Enums 1.1
|
||||||
|
|
||||||
import "../js/contactpickercreation.js" as ContactPickerCreation
|
import "../js/contactpickercreation.js" as ContactPickerCreation
|
||||||
import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation
|
import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation
|
||||||
@@ -44,6 +45,7 @@ Item {
|
|||||||
property bool isModerator
|
property bool isModerator
|
||||||
property bool isConference
|
property bool isConference
|
||||||
property bool isGrid
|
property bool isGrid
|
||||||
|
property bool participantsSide: UtilsAdapter.getAppValue(Settings.ParticipantsSide)
|
||||||
property bool localHandRaised
|
property bool localHandRaised
|
||||||
property bool sharingActive: AvAdapter.isSharing()
|
property bool sharingActive: AvAdapter.isSharing()
|
||||||
property string callId: ""
|
property string callId: ""
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ Rectangle {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
anchors.margins: 3
|
anchors.margins: 3
|
||||||
visible: participantsLayer.count !== 0
|
visible: participantsLayer.count !== 0
|
||||||
|
participantsSide: callOverlay.participantsSide
|
||||||
|
|
||||||
onCountChanged: {
|
onCountChanged: {
|
||||||
callOverlay.isConference = participantsLayer.count > 0
|
callOverlay.isConference = participantsLayer.count > 0
|
||||||
|
|||||||
@@ -25,12 +25,15 @@ import QtQuick.Controls 2.15
|
|||||||
import net.jami.Adapters 1.1
|
import net.jami.Adapters 1.1
|
||||||
import net.jami.Models 1.1
|
import net.jami.Models 1.1
|
||||||
import net.jami.Constants 1.1
|
import net.jami.Constants 1.1
|
||||||
|
import net.jami.Enums 1.1
|
||||||
|
import "../../commoncomponents"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int count: commonParticipants.count + activeParticipants.count
|
property int count: 0
|
||||||
property bool inLine: CallParticipantsModel.conferenceLayout === CallParticipantsModel.ONE_WITH_SMALL
|
property bool inLine: CallParticipantsModel.conferenceLayout === CallParticipantsModel.ONE_WITH_SMALL
|
||||||
|
property bool participantsSide
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: callVideoMedia
|
id: callVideoMedia
|
||||||
@@ -70,265 +73,18 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SplitView {
|
ParticipantsLayoutVertical {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
participantComponent: callVideoMedia
|
||||||
|
visible: !participantsSide
|
||||||
|
|
||||||
orientation: Qt.Vertical
|
onLayoutCountChanged: root.count = layoutCount
|
||||||
handle: Rectangle {
|
}
|
||||||
implicitWidth: root.width
|
|
||||||
implicitHeight: 11
|
|
||||||
color: "transparent"
|
|
||||||
Rectangle {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
height: 1
|
|
||||||
width: parent.implicitWidth - 40
|
|
||||||
color: JamiTheme.darkGreyColor
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
ParticipantsLayoutHorizontal {
|
||||||
width: 45
|
anchors.fill: parent
|
||||||
anchors.centerIn: parent
|
participantComponent: callVideoMedia
|
||||||
height: 1
|
visible: participantsSide
|
||||||
color: "black"
|
onLayoutCountChanged: root.count = layoutCount
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
height: 11
|
|
||||||
width: 45
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: 10
|
|
||||||
Layout.rightMargin: 10
|
|
||||||
height: 2
|
|
||||||
color: JamiTheme.darkGreyColor
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: 10
|
|
||||||
Layout.rightMargin: 10
|
|
||||||
height: 2
|
|
||||||
color: JamiTheme.darkGreyColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: genericParticipantsRect
|
|
||||||
|
|
||||||
TapHandler { acceptedButtons: Qt.LeftButton | Qt.RightButton }
|
|
||||||
|
|
||||||
SplitView.preferredHeight: (parent.height / 4)
|
|
||||||
SplitView.minimumHeight: parent.height / 6
|
|
||||||
SplitView.maximumHeight: inLine? parent.height / 2 : parent.height
|
|
||||||
|
|
||||||
visible: inLine || CallParticipantsModel.conferenceLayout === CallParticipantsModel.GRID
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
property int lowLimit: 0
|
|
||||||
property int topLimit: commonParticipants.count
|
|
||||||
property int currentPos: 0
|
|
||||||
property int showable: {
|
|
||||||
if (!inLine)
|
|
||||||
return commonParticipants.count
|
|
||||||
if (commonParticipantsFlow.componentWidth === 0)
|
|
||||||
return 1
|
|
||||||
var placeableElements = Math.floor((width * 0.9)/commonParticipantsFlow.componentWidth)
|
|
||||||
if (commonParticipants.count - placeableElements < currentPos)
|
|
||||||
currentPos = Math.max(commonParticipants.count - placeableElements, 0)
|
|
||||||
return Math.max(1, placeableElements)
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
RoundButton {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
width : 30
|
|
||||||
height : 30
|
|
||||||
radius: 10
|
|
||||||
text: "<"
|
|
||||||
visible: genericParticipantsRect.currentPos > 0
|
|
||||||
&& activeParticipantsFlow.visible
|
|
||||||
onClicked: {
|
|
||||||
if (genericParticipantsRect.currentPos > 0)
|
|
||||||
genericParticipantsRect.currentPos--
|
|
||||||
}
|
|
||||||
background: Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: JamiTheme.lightGrey_
|
|
||||||
radius: JamiTheme.primaryRadius
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: centerItem
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.margins: 4
|
|
||||||
|
|
||||||
// GENERIC
|
|
||||||
Flow {
|
|
||||||
id: commonParticipantsFlow
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
spacing: 4
|
|
||||||
property int columns: {
|
|
||||||
if (inLine)
|
|
||||||
return commonParticipants.count
|
|
||||||
var ratio = Math.floor(root.width / root.height)
|
|
||||||
// If ratio is 2 we can have 2 times more elements on each columns
|
|
||||||
var wantedCol = Math.max(1, Math.round(Math.sqrt(commonParticipants.count) * ratio))
|
|
||||||
var cols = Math.min(commonParticipants.count, wantedCol)
|
|
||||||
// Optimize with the rows (eg 7 with ratio 2 should have 4 and 3 items, not 6 and 1)
|
|
||||||
var rows = Math.max(1, Math.ceil(commonParticipants.count/cols))
|
|
||||||
return Math.min(Math.ceil(commonParticipants.count / rows), cols)
|
|
||||||
}
|
|
||||||
property int rows: Math.max(1, Math.ceil(commonParticipants.count/columns))
|
|
||||||
property int componentWidth: {
|
|
||||||
var totalSpacing = commonParticipantsFlow.spacing * commonParticipantsFlow.columns
|
|
||||||
var w = Math.floor((commonParticipantsFlow.width - totalSpacing)/ commonParticipantsFlow.columns)
|
|
||||||
if (inLine) {
|
|
||||||
w = Math.max(w, height)
|
|
||||||
w = Math.min(w, height * 4 / 3) // Avoid too wide elements
|
|
||||||
}
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
height: parent.height
|
|
||||||
width: {
|
|
||||||
if (!inLine)
|
|
||||||
return 0
|
|
||||||
var showed = Math.min(genericParticipantsRect.showable, commonParticipantsFlow.columns)
|
|
||||||
return Math.max(0, Math.ceil((centerItem.width - commonParticipantsFlow.componentWidth * showed) / 2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
id: commonParticipants
|
|
||||||
|
|
||||||
model: GenericParticipantsFilterModel
|
|
||||||
delegate: Loader {
|
|
||||||
sourceComponent: callVideoMedia
|
|
||||||
active: root.visible
|
|
||||||
asynchronous: true
|
|
||||||
visible: {
|
|
||||||
if (status !== Loader.Ready)
|
|
||||||
return false
|
|
||||||
if (inLine)
|
|
||||||
return index >= genericParticipantsRect.currentPos
|
|
||||||
&& index < genericParticipantsRect.currentPos + genericParticipantsRect.showable
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
width: commonParticipantsFlow.componentWidth + leftMargin_
|
|
||||||
height: {
|
|
||||||
if (inLine || commonParticipantsFlow.rows === 1)
|
|
||||||
return genericParticipantsRect.height
|
|
||||||
var totalSpacing = commonParticipantsFlow.spacing * commonParticipantsFlow.rows
|
|
||||||
return Math.floor((genericParticipantsRect.height - totalSpacing)/ commonParticipantsFlow.rows)
|
|
||||||
}
|
|
||||||
|
|
||||||
property int leftMargin_: {
|
|
||||||
if (inLine || commonParticipantsFlow.rows === 1)
|
|
||||||
return 0
|
|
||||||
var lastParticipants = (commonParticipants.count % commonParticipantsFlow.columns)
|
|
||||||
if (lastParticipants !== 0 && index === commonParticipants.count - lastParticipants) {
|
|
||||||
var compW = commonParticipantsFlow.componentWidth + commonParticipantsFlow.spacing
|
|
||||||
var lastLineW = lastParticipants * compW
|
|
||||||
return Math.floor((commonParticipantsFlow.width - lastLineW) / 2)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
property string uri_: Uri
|
|
||||||
property string deviceId_: Device
|
|
||||||
property string bestName_: BestName
|
|
||||||
property string avatar_: Avatar ? Avatar : ""
|
|
||||||
property string sinkId_: SinkId ? SinkId : ""
|
|
||||||
property bool isLocal_: IsLocal
|
|
||||||
property bool active_: Active
|
|
||||||
property bool videoMuted_: VideoMuted
|
|
||||||
property bool isContact_: IsContact
|
|
||||||
property bool isModerator_: IsModerator
|
|
||||||
property bool audioLocalMuted_: AudioLocalMuted
|
|
||||||
property bool audioModeratorMuted_: AudioModeratorMuted
|
|
||||||
property bool isHandRaised_: HandRaised
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RoundButton {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
width : 30
|
|
||||||
height : 30
|
|
||||||
radius: 10
|
|
||||||
text: ">"
|
|
||||||
visible: genericParticipantsRect.topLimit - genericParticipantsRect.showable > genericParticipantsRect.currentPos
|
|
||||||
&& activeParticipantsFlow.visible
|
|
||||||
onClicked: {
|
|
||||||
if (genericParticipantsRect.topLimit - genericParticipantsRect.showable > genericParticipantsRect.currentPos)
|
|
||||||
genericParticipantsRect.currentPos++
|
|
||||||
}
|
|
||||||
background: Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: JamiTheme.lightGrey_
|
|
||||||
radius: JamiTheme.primaryRadius
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ACTIVE
|
|
||||||
Flow {
|
|
||||||
id: activeParticipantsFlow
|
|
||||||
|
|
||||||
TapHandler { acceptedButtons: Qt.LeftButton | Qt.RightButton }
|
|
||||||
|
|
||||||
SplitView.minimumHeight: parent.height / 4
|
|
||||||
SplitView.maximumHeight: parent.height
|
|
||||||
SplitView.fillHeight: true
|
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
property int columns: Math.max(1, Math.ceil(Math.sqrt(activeParticipants.count)))
|
|
||||||
property int rows: Math.max(1, Math.ceil(activeParticipants.count/columns))
|
|
||||||
property int columnsSpacing: 5 * (columns - 1)
|
|
||||||
property int rowsSpacing: 5 * (rows - 1)
|
|
||||||
|
|
||||||
visible: inLine || CallParticipantsModel.conferenceLayout === CallParticipantsModel.ONE
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
id: activeParticipants
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
model: ActiveParticipantsFilterModel
|
|
||||||
delegate: Loader {
|
|
||||||
active: root.visible
|
|
||||||
asynchronous: true
|
|
||||||
sourceComponent: callVideoMedia
|
|
||||||
visible: status == Loader.Ready
|
|
||||||
|
|
||||||
width: Math.ceil(activeParticipantsFlow.width / activeParticipantsFlow.columns) - activeParticipantsFlow.columnsSpacing
|
|
||||||
height: Math.ceil(activeParticipantsFlow.height / activeParticipantsFlow.rows) - activeParticipantsFlow.rowsSpacing
|
|
||||||
|
|
||||||
property string uri_: Uri
|
|
||||||
property string bestName_: BestName
|
|
||||||
property string avatar_: Avatar ? Avatar : ""
|
|
||||||
property string sinkId_: SinkId ? SinkId : ""
|
|
||||||
property string deviceId_: Device
|
|
||||||
property int leftMargin_: 0
|
|
||||||
property bool isLocal_: IsLocal
|
|
||||||
property bool active_: Active
|
|
||||||
property bool videoMuted_: VideoMuted
|
|
||||||
property bool isContact_: IsContact
|
|
||||||
property bool isModerator_: IsModerator
|
|
||||||
property bool audioLocalMuted_: AudioLocalMuted
|
|
||||||
property bool audioModeratorMuted_: AudioModeratorMuted
|
|
||||||
property bool isHandRaised_: HandRaised
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
321
src/app/mainview/components/ParticipantsLayoutHorizontal.qml
Normal file
321
src/app/mainview/components/ParticipantsLayoutHorizontal.qml
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2022 Savoir-faire Linux Inc.
|
||||||
|
* Authors: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||||
|
* Aline Gondim Santos <aline.gondimsantos@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 1.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
import net.jami.Enums 1.1
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int layoutCount: commonParticipants.count + activeParticipants.count
|
||||||
|
property var participantComponent
|
||||||
|
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
handle: Rectangle {
|
||||||
|
implicitHeight: root.height
|
||||||
|
implicitWidth: 11
|
||||||
|
color: "transparent"
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 1
|
||||||
|
height: parent.implicitHeight - 40
|
||||||
|
color: JamiTheme.darkGreyColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 45
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 1
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: 45
|
||||||
|
width: 11
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: 10
|
||||||
|
Layout.bottomMargin: 10
|
||||||
|
width: 2
|
||||||
|
color: JamiTheme.darkGreyColor
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: 10
|
||||||
|
Layout.bottomMargin: 10
|
||||||
|
width: 2
|
||||||
|
color: JamiTheme.darkGreyColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACTIVE
|
||||||
|
Flow {
|
||||||
|
id: activeParticipantsFlow
|
||||||
|
|
||||||
|
TapHandler { acceptedButtons: Qt.LeftButton | Qt.RightButton }
|
||||||
|
|
||||||
|
SplitView.minimumWidth: parent.width / 4
|
||||||
|
SplitView.maximumWidth: parent.width
|
||||||
|
SplitView.fillWidth: true
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
property int columns: Math.max(1, Math.ceil(Math.sqrt(activeParticipants.count)))
|
||||||
|
property int rows: Math.max(1, Math.ceil(activeParticipants.count/columns))
|
||||||
|
property int columnsSpacing: 5 * (columns - 1)
|
||||||
|
property int rowsSpacing: 5 * (rows - 1)
|
||||||
|
|
||||||
|
visible: inLine || CallParticipantsModel.conferenceLayout === CallParticipantsModel.ONE
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: activeParticipants
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
model: ActiveParticipantsFilterModel
|
||||||
|
delegate: Loader {
|
||||||
|
active: root.visible
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: callVideoMedia
|
||||||
|
visible: status == Loader.Ready
|
||||||
|
|
||||||
|
width: Math.ceil(activeParticipantsFlow.width / activeParticipantsFlow.columns) - activeParticipantsFlow.columnsSpacing
|
||||||
|
height: Math.ceil(activeParticipantsFlow.height / activeParticipantsFlow.rows) - activeParticipantsFlow.rowsSpacing
|
||||||
|
|
||||||
|
property string uri_: Uri
|
||||||
|
property string bestName_: BestName
|
||||||
|
property string avatar_: Avatar ? Avatar : ""
|
||||||
|
property string sinkId_: SinkId ? SinkId : ""
|
||||||
|
property string deviceId_: Device
|
||||||
|
property int leftMargin_: 0
|
||||||
|
property bool isLocal_: IsLocal
|
||||||
|
property bool active_: Active
|
||||||
|
property bool videoMuted_: VideoMuted
|
||||||
|
property bool isContact_: IsContact
|
||||||
|
property bool isModerator_: IsModerator
|
||||||
|
property bool audioLocalMuted_: AudioLocalMuted
|
||||||
|
property bool audioModeratorMuted_: AudioModeratorMuted
|
||||||
|
property bool isHandRaised_: HandRaised
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: genericParticipantsRect
|
||||||
|
|
||||||
|
TapHandler { acceptedButtons: Qt.TopButton | Qt.BottomButton }
|
||||||
|
|
||||||
|
SplitView.preferredWidth: (parent.width / 4)
|
||||||
|
SplitView.minimumWidth: parent.width / 6
|
||||||
|
SplitView.maximumWidth: inLine? parent.width / 2 : parent.width
|
||||||
|
|
||||||
|
visible: inLine || CallParticipantsModel.conferenceLayout === CallParticipantsModel.GRID
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
property int lowLimit: 0
|
||||||
|
property int topLimit: commonParticipants.count
|
||||||
|
property int currentPos: 0
|
||||||
|
property int showable: {
|
||||||
|
if (!inLine)
|
||||||
|
return commonParticipants.count
|
||||||
|
if (commonParticipantsFlow.componentHeight === 0)
|
||||||
|
return 1
|
||||||
|
var placeableElements = Math.floor((height * 0.9)/commonParticipantsFlow.componentHeight)
|
||||||
|
if (commonParticipants.count - placeableElements < currentPos)
|
||||||
|
currentPos = Math.max(commonParticipants.count - placeableElements, 0)
|
||||||
|
return Math.max(1, placeableElements)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
width: parent.width
|
||||||
|
height: 30
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
Layout.topMargin: 16
|
||||||
|
spacing: 8
|
||||||
|
visible: (genericParticipantsRect.currentPos > 0 && activeParticipantsFlow.visible) ||
|
||||||
|
(genericParticipantsRect.topLimit - genericParticipantsRect.showable > genericParticipantsRect.currentPos && activeParticipantsFlow.visible)
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
width : 30
|
||||||
|
height : 30
|
||||||
|
radius: 10
|
||||||
|
text: "^"
|
||||||
|
visible: genericParticipantsRect.currentPos > 0
|
||||||
|
&& activeParticipantsFlow.visible
|
||||||
|
onClicked: {
|
||||||
|
if (genericParticipantsRect.currentPos > 0)
|
||||||
|
genericParticipantsRect.currentPos--
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: JamiTheme.lightGrey_
|
||||||
|
radius: JamiTheme.primaryRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
width : 30
|
||||||
|
height : 30
|
||||||
|
radius: 10
|
||||||
|
text: "v"
|
||||||
|
visible: genericParticipantsRect.topLimit - genericParticipantsRect.showable > genericParticipantsRect.currentPos
|
||||||
|
&& activeParticipantsFlow.visible
|
||||||
|
onClicked: {
|
||||||
|
if (genericParticipantsRect.topLimit - genericParticipantsRect.showable > genericParticipantsRect.currentPos)
|
||||||
|
genericParticipantsRect.currentPos++
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: JamiTheme.lightGrey_
|
||||||
|
radius: JamiTheme.primaryRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: centerItem
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 4
|
||||||
|
|
||||||
|
// GENERIC
|
||||||
|
Flow {
|
||||||
|
id: commonParticipantsFlow
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
spacing: 4
|
||||||
|
property int columns: {
|
||||||
|
if (inLine)
|
||||||
|
return 1
|
||||||
|
var ratio = Math.floor(root.width / root.height)
|
||||||
|
// If ratio is 2 we can have 2 times more elements on each columns
|
||||||
|
var wantedCol = Math.max(1, Math.round(Math.sqrt(commonParticipants.count) * ratio))
|
||||||
|
var cols = Math.min(commonParticipants.count, wantedCol)
|
||||||
|
// Optimize with the rows (eg 7 with ratio 2 should have 4 and 3 items, not 6 and 1)
|
||||||
|
var rows = Math.max(1, Math.ceil(commonParticipants.count/cols))
|
||||||
|
return Math.min(Math.ceil(commonParticipants.count / rows), cols)
|
||||||
|
}
|
||||||
|
property int rows: {
|
||||||
|
if (inLine)
|
||||||
|
return commonParticipants.count
|
||||||
|
Math.max(1, Math.ceil(commonParticipants.count/columns))
|
||||||
|
}
|
||||||
|
property int componentHeight: {
|
||||||
|
var totalSpacing = commonParticipantsFlow.spacing * commonParticipantsFlow.rows
|
||||||
|
var h = Math.floor((commonParticipantsFlow.height - totalSpacing)/ commonParticipantsFlow.rows)
|
||||||
|
if (inLine) {
|
||||||
|
h = Math.max(width, h)
|
||||||
|
h = Math.min(width, h * 4 / 3) // Avoid too high elements
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
property int componentWidth: {
|
||||||
|
var totalSpacing = commonParticipantsFlow.spacing * commonParticipantsFlow.columns
|
||||||
|
var w = Math.floor((commonParticipantsFlow.width - totalSpacing)/ commonParticipantsFlow.columns)
|
||||||
|
if (inLine) {
|
||||||
|
w = commonParticipantsFlow.width
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: {
|
||||||
|
if (!inLine)
|
||||||
|
return 0
|
||||||
|
var showed = Math.min(genericParticipantsRect.showable, commonParticipantsFlow.rows)
|
||||||
|
return Math.max(0, Math.ceil((centerItem.height - commonParticipantsFlow.componentHeight * showed) / 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: commonParticipants
|
||||||
|
|
||||||
|
model: GenericParticipantsFilterModel
|
||||||
|
delegate: Loader {
|
||||||
|
sourceComponent: callVideoMedia
|
||||||
|
active: root.visible
|
||||||
|
asynchronous: true
|
||||||
|
visible: {
|
||||||
|
if (status !== Loader.Ready)
|
||||||
|
return false
|
||||||
|
if (inLine)
|
||||||
|
return index >= genericParticipantsRect.currentPos
|
||||||
|
&& index < genericParticipantsRect.currentPos + genericParticipantsRect.showable
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
width: commonParticipantsFlow.componentWidth + leftMargin_
|
||||||
|
height: {
|
||||||
|
if (inLine || commonParticipantsFlow.columns === 1)
|
||||||
|
return commonParticipantsFlow.componentHeight
|
||||||
|
var totalSpacing = commonParticipantsFlow.spacing * commonParticipantsFlow.rows
|
||||||
|
return Math.floor((genericParticipantsRect.height - totalSpacing) / commonParticipantsFlow.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
property int leftMargin_: {
|
||||||
|
if (inLine || commonParticipantsFlow.rows === 1)
|
||||||
|
return 0
|
||||||
|
var lastParticipants = (commonParticipants.count % commonParticipantsFlow.columns)
|
||||||
|
if (lastParticipants !== 0 && index === commonParticipants.count - lastParticipants) {
|
||||||
|
var compW = commonParticipantsFlow.componentWidth + commonParticipantsFlow.spacing
|
||||||
|
var lastLineW = lastParticipants * compW
|
||||||
|
return Math.floor((commonParticipantsFlow.width - lastLineW) / 2)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
property string uri_: Uri
|
||||||
|
property string deviceId_: Device
|
||||||
|
property string bestName_: BestName
|
||||||
|
property string avatar_: Avatar ? Avatar : ""
|
||||||
|
property string sinkId_: SinkId ? SinkId : ""
|
||||||
|
property bool isLocal_: IsLocal
|
||||||
|
property bool active_: Active
|
||||||
|
property bool videoMuted_: VideoMuted
|
||||||
|
property bool isContact_: IsContact
|
||||||
|
property bool isModerator_: IsModerator
|
||||||
|
property bool audioLocalMuted_: AudioLocalMuted
|
||||||
|
property bool audioModeratorMuted_: AudioModeratorMuted
|
||||||
|
property bool isHandRaised_: HandRaised
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
width: parent.width
|
||||||
|
height : 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
293
src/app/mainview/components/ParticipantsLayoutVertical.qml
Normal file
293
src/app/mainview/components/ParticipantsLayoutVertical.qml
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2022 Savoir-faire Linux Inc.
|
||||||
|
* Authors: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||||
|
* Aline Gondim Santos <aline.gondimsantos@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 1.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
|
import net.jami.Adapters 1.1
|
||||||
|
import net.jami.Models 1.1
|
||||||
|
import net.jami.Constants 1.1
|
||||||
|
import net.jami.Enums 1.1
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int layoutCount: commonParticipants.count + activeParticipants.count
|
||||||
|
property var participantComponent
|
||||||
|
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
handle: Rectangle {
|
||||||
|
implicitWidth: root.width
|
||||||
|
implicitHeight: 11
|
||||||
|
color: "transparent"
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: 1
|
||||||
|
width: parent.implicitWidth - 40
|
||||||
|
color: JamiTheme.darkGreyColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 45
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: 1
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: 11
|
||||||
|
width: 45
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 10
|
||||||
|
Layout.rightMargin: 10
|
||||||
|
height: 2
|
||||||
|
color: JamiTheme.darkGreyColor
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 10
|
||||||
|
Layout.rightMargin: 10
|
||||||
|
height: 2
|
||||||
|
color: JamiTheme.darkGreyColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: genericParticipantsRect
|
||||||
|
|
||||||
|
TapHandler { acceptedButtons: Qt.LeftButton | Qt.RightButton }
|
||||||
|
|
||||||
|
SplitView.preferredHeight: (parent.height / 4)
|
||||||
|
SplitView.minimumHeight: parent.height / 6
|
||||||
|
SplitView.maximumHeight: inLine? parent.height / 2 : parent.height
|
||||||
|
|
||||||
|
visible: inLine || CallParticipantsModel.conferenceLayout === CallParticipantsModel.GRID
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
property int lowLimit: 0
|
||||||
|
property int topLimit: commonParticipants.count
|
||||||
|
property int currentPos: 0
|
||||||
|
property int showable: {
|
||||||
|
if (!inLine)
|
||||||
|
return commonParticipants.count
|
||||||
|
if (commonParticipantsFlow.componentWidth === 0)
|
||||||
|
return 1
|
||||||
|
var placeableElements = Math.floor((width * 0.9)/commonParticipantsFlow.componentWidth)
|
||||||
|
if (commonParticipants.count - placeableElements < currentPos)
|
||||||
|
currentPos = Math.max(commonParticipants.count - placeableElements, 0)
|
||||||
|
return Math.max(1, placeableElements)
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
width : 30
|
||||||
|
height : 30
|
||||||
|
radius: 10
|
||||||
|
text: "<"
|
||||||
|
visible: genericParticipantsRect.currentPos > 0
|
||||||
|
&& activeParticipantsFlow.visible
|
||||||
|
onClicked: {
|
||||||
|
if (genericParticipantsRect.currentPos > 0)
|
||||||
|
genericParticipantsRect.currentPos--
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: JamiTheme.lightGrey_
|
||||||
|
radius: JamiTheme.primaryRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: centerItem
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 4
|
||||||
|
|
||||||
|
// GENERIC
|
||||||
|
Flow {
|
||||||
|
id: commonParticipantsFlow
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
spacing: 4
|
||||||
|
property int columns: {
|
||||||
|
if (inLine)
|
||||||
|
return commonParticipants.count
|
||||||
|
var ratio = Math.floor(root.width / root.height)
|
||||||
|
// If ratio is 2 we can have 2 times more elements on each columns
|
||||||
|
var wantedCol = Math.max(1, Math.round(Math.sqrt(commonParticipants.count) * ratio))
|
||||||
|
var cols = Math.min(commonParticipants.count, wantedCol)
|
||||||
|
// Optimize with the rows (eg 7 with ratio 2 should have 4 and 3 items, not 6 and 1)
|
||||||
|
var rows = Math.max(1, Math.ceil(commonParticipants.count/cols))
|
||||||
|
return Math.min(Math.ceil(commonParticipants.count / rows), cols)
|
||||||
|
}
|
||||||
|
property int rows: Math.max(1, Math.ceil(commonParticipants.count/columns))
|
||||||
|
property int componentWidth: {
|
||||||
|
var totalSpacing = commonParticipantsFlow.spacing * commonParticipantsFlow.columns
|
||||||
|
var w = Math.floor((commonParticipantsFlow.width - totalSpacing)/ commonParticipantsFlow.columns)
|
||||||
|
if (inLine) {
|
||||||
|
w = Math.max(w, height)
|
||||||
|
w = Math.min(w, height * 4 / 3) // Avoid too wide elements
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: parent.height
|
||||||
|
width: {
|
||||||
|
if (!inLine)
|
||||||
|
return 0
|
||||||
|
var showed = Math.min(genericParticipantsRect.showable, commonParticipantsFlow.columns)
|
||||||
|
return Math.max(0, Math.ceil((centerItem.width - commonParticipantsFlow.componentWidth * showed) / 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: commonParticipants
|
||||||
|
|
||||||
|
model: GenericParticipantsFilterModel
|
||||||
|
delegate: Loader {
|
||||||
|
sourceComponent: callVideoMedia
|
||||||
|
active: root.visible
|
||||||
|
asynchronous: true
|
||||||
|
visible: {
|
||||||
|
if (status !== Loader.Ready)
|
||||||
|
return false
|
||||||
|
if (inLine)
|
||||||
|
return index >= genericParticipantsRect.currentPos
|
||||||
|
&& index < genericParticipantsRect.currentPos + genericParticipantsRect.showable
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
width: commonParticipantsFlow.componentWidth + leftMargin_
|
||||||
|
height: {
|
||||||
|
if (inLine || commonParticipantsFlow.rows === 1)
|
||||||
|
return genericParticipantsRect.height
|
||||||
|
var totalSpacing = commonParticipantsFlow.spacing * commonParticipantsFlow.rows
|
||||||
|
return Math.floor((genericParticipantsRect.height - totalSpacing)/ commonParticipantsFlow.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
property int leftMargin_: {
|
||||||
|
if (inLine || commonParticipantsFlow.rows === 1)
|
||||||
|
return 0
|
||||||
|
var lastParticipants = (commonParticipants.count % commonParticipantsFlow.columns)
|
||||||
|
if (lastParticipants !== 0 && index === commonParticipants.count - lastParticipants) {
|
||||||
|
var compW = commonParticipantsFlow.componentWidth + commonParticipantsFlow.spacing
|
||||||
|
var lastLineW = lastParticipants * compW
|
||||||
|
return Math.floor((commonParticipantsFlow.width - lastLineW) / 2)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
property string uri_: Uri
|
||||||
|
property string deviceId_: Device
|
||||||
|
property string bestName_: BestName
|
||||||
|
property string avatar_: Avatar ? Avatar : ""
|
||||||
|
property string sinkId_: SinkId ? SinkId : ""
|
||||||
|
property bool isLocal_: IsLocal
|
||||||
|
property bool active_: Active
|
||||||
|
property bool videoMuted_: VideoMuted
|
||||||
|
property bool isContact_: IsContact
|
||||||
|
property bool isModerator_: IsModerator
|
||||||
|
property bool audioLocalMuted_: AudioLocalMuted
|
||||||
|
property bool audioModeratorMuted_: AudioModeratorMuted
|
||||||
|
property bool isHandRaised_: HandRaised
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
width : 30
|
||||||
|
height : 30
|
||||||
|
radius: 10
|
||||||
|
text: ">"
|
||||||
|
visible: genericParticipantsRect.topLimit - genericParticipantsRect.showable > genericParticipantsRect.currentPos
|
||||||
|
&& activeParticipantsFlow.visible
|
||||||
|
onClicked: {
|
||||||
|
if (genericParticipantsRect.topLimit - genericParticipantsRect.showable > genericParticipantsRect.currentPos)
|
||||||
|
genericParticipantsRect.currentPos++
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: JamiTheme.lightGrey_
|
||||||
|
radius: JamiTheme.primaryRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACTIVE
|
||||||
|
Flow {
|
||||||
|
id: activeParticipantsFlow
|
||||||
|
|
||||||
|
TapHandler { acceptedButtons: Qt.LeftButton | Qt.RightButton }
|
||||||
|
|
||||||
|
SplitView.minimumHeight: parent.height / 4
|
||||||
|
SplitView.maximumHeight: parent.height
|
||||||
|
SplitView.fillHeight: true
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
property int columns: Math.max(1, Math.ceil(Math.sqrt(activeParticipants.count)))
|
||||||
|
property int rows: Math.max(1, Math.ceil(activeParticipants.count/columns))
|
||||||
|
property int columnsSpacing: 5 * (columns - 1)
|
||||||
|
property int rowsSpacing: 5 * (rows - 1)
|
||||||
|
|
||||||
|
visible: inLine || CallParticipantsModel.conferenceLayout === CallParticipantsModel.ONE
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: activeParticipants
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
model: ActiveParticipantsFilterModel
|
||||||
|
delegate: Loader {
|
||||||
|
active: root.visible
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: callVideoMedia
|
||||||
|
visible: status == Loader.Ready
|
||||||
|
|
||||||
|
width: Math.ceil(activeParticipantsFlow.width / activeParticipantsFlow.columns) - activeParticipantsFlow.columnsSpacing
|
||||||
|
height: Math.ceil(activeParticipantsFlow.height / activeParticipantsFlow.rows) - activeParticipantsFlow.rowsSpacing
|
||||||
|
|
||||||
|
property string uri_: Uri
|
||||||
|
property string bestName_: BestName
|
||||||
|
property string avatar_: Avatar ? Avatar : ""
|
||||||
|
property string sinkId_: SinkId ? SinkId : ""
|
||||||
|
property string deviceId_: Device
|
||||||
|
property int leftMargin_: 0
|
||||||
|
property bool isLocal_: IsLocal
|
||||||
|
property bool active_: Active
|
||||||
|
property bool videoMuted_: VideoMuted
|
||||||
|
property bool isContact_: IsContact
|
||||||
|
property bool isModerator_: IsModerator
|
||||||
|
property bool audioLocalMuted_: AudioLocalMuted
|
||||||
|
property bool audioModeratorMuted_: AudioModeratorMuted
|
||||||
|
property bool isHandRaised_: HandRaised
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user