mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-04-21 21:52:03 +02:00

In conference, this patch depends on using local sinks ids to avoid mirroring video from sharing resources. GitLab: https://git.jami.net/savoirfairelinux/jami-client-gnome/-/issues/1284 Change-Id: I0c74f0780ebf17c68ffcffdca62eb33784189741
660 lines
26 KiB
QML
660 lines
26 KiB
QML
/*
|
|
* Copyright (C) 2021-2023 Savoir-faire Linux Inc.
|
|
* Author: Andreas Traczyk <andreas.traczyk@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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
|
|
import net.jami.Models 1.1
|
|
import net.jami.Adapters 1.1
|
|
import net.jami.Constants 1.1
|
|
import net.jami.Enums 1.1
|
|
|
|
import "../../commoncomponents"
|
|
|
|
Control {
|
|
id: root
|
|
|
|
enum ActionPopupMode {
|
|
MediaDevice = 0,
|
|
ListElement,
|
|
LayoutOption
|
|
}
|
|
|
|
property alias overflowOpen: overflowButton.popup.visible
|
|
property bool subMenuOpen: false
|
|
property real parentHeight
|
|
property bool barHovered: false
|
|
|
|
property real itemSpacing: 2
|
|
|
|
signal chatClicked
|
|
signal addToConferenceClicked
|
|
signal transferClicked
|
|
signal resumePauseCallClicked
|
|
signal showInputPanelClicked
|
|
signal shareScreenClicked
|
|
signal shareWindowClicked
|
|
signal stopSharingClicked
|
|
signal shareScreenAreaClicked
|
|
signal shareFileClicked
|
|
signal pluginsClicked
|
|
signal recordCallClicked
|
|
signal fullScreenClicked
|
|
signal swarmDetailsClicked
|
|
|
|
Component {
|
|
id: buttonDelegate
|
|
|
|
CallButtonDelegate {
|
|
width: root.height
|
|
height: width
|
|
barWidth: root.width
|
|
onSubMenuVisibleChanged: subMenuOpen = subMenuVisible
|
|
onHoveredChanged: root.barHovered = hovered
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: AvAdapter
|
|
|
|
function onAudioDeviceListChanged(inputs, outputs) {
|
|
audioInputDeviceListModel.reset();
|
|
audioInputMenuAction.enabled = inputs
|
|
audioOutputDeviceListModel.reset();
|
|
audioOutputMenuAction.enabled = outputs
|
|
}
|
|
}
|
|
|
|
property list<Action> menuActions: [
|
|
Action {
|
|
id: audioInputMenuAction
|
|
text: JamiStrings.selectAudioInputDevice
|
|
Component.onCompleted: enabled = audioInputDeviceListModel.rowCount()
|
|
property var listModel: AudioDeviceModel {
|
|
id: audioInputDeviceListModel
|
|
lrcInstance: LRCInstance
|
|
type: AudioDeviceModel.Type.Record
|
|
}
|
|
function accept(index) {
|
|
AvAdapter.stopAudioMeter()
|
|
AVModel.setInputDevice(index)
|
|
AvAdapter.startAudioMeter()
|
|
}
|
|
},
|
|
Action {
|
|
id: audioOutputMenuAction
|
|
text: JamiStrings.selectAudioOutputDevice
|
|
Component.onCompleted: enabled = audioOutputDeviceListModel.rowCount()
|
|
property var listModel: AudioDeviceModel {
|
|
id: audioOutputDeviceListModel
|
|
lrcInstance: LRCInstance
|
|
type: AudioDeviceModel.Type.Playback
|
|
}
|
|
function accept(index) {
|
|
AvAdapter.stopAudioMeter()
|
|
AVModel.setOutputDevice(index)
|
|
AvAdapter.startAudioMeter()
|
|
}
|
|
},
|
|
Action {
|
|
id: shareMenuAction
|
|
text: JamiStrings.selectShareMethod
|
|
property int popupMode: CallActionBar.ActionPopupMode.ListElement
|
|
property var listModel: ListModel {
|
|
id: shareModel
|
|
}
|
|
onTriggered: {
|
|
shareModel.clear()
|
|
shareModel.append({"Name": JamiStrings.shareScreen,
|
|
"IconSource": JamiResources.laptop_black_24dp_svg})
|
|
if (Qt.platform.os.toString() !== "osx") {
|
|
shareModel.append({"Name": JamiStrings.shareWindow,
|
|
"IconSource" : JamiResources.window_black_24dp_svg})
|
|
}
|
|
if (Qt.platform.os.toString() !== "windows") { // temporarily disable for windows
|
|
shareModel.append({"Name": JamiStrings.shareScreenArea,
|
|
"IconSource" : JamiResources.share_area_black_24dp_svg})
|
|
}
|
|
shareModel.append({"Name": JamiStrings.shareFile,
|
|
"IconSource" : JamiResources.file_black_24dp_svg})
|
|
}
|
|
function accept(index) {
|
|
switch(shareModel.get(index).Name) {
|
|
case JamiStrings.shareScreen:
|
|
shareScreenClicked()
|
|
break
|
|
case JamiStrings.shareWindow:
|
|
shareWindowClicked()
|
|
break
|
|
case JamiStrings.shareScreenArea:
|
|
shareScreenAreaClicked()
|
|
break
|
|
case JamiStrings.shareFile:
|
|
shareFileClicked()
|
|
break
|
|
}
|
|
}
|
|
},
|
|
Action {
|
|
id: layoutMenuAction
|
|
text: JamiStrings.layoutSettings
|
|
property int popupMode: CallActionBar.ActionPopupMode.LayoutOption
|
|
property var listModel: ListModel {
|
|
id: layoutModel
|
|
}
|
|
function accept(index) {
|
|
switch(layoutModel.get(index).Name) {
|
|
case JamiStrings.viewFullScreen:
|
|
root.fullScreenClicked()
|
|
layoutModel.get(index).ActiveSetting = layoutManager.isCallFullscreen
|
|
break
|
|
case JamiStrings.mosaic:
|
|
if (!CurrentCall.isGrid)
|
|
CallAdapter.showGridConferenceLayout()
|
|
break
|
|
case JamiStrings.participantsSide:
|
|
if (!UtilsAdapter.getAppValue(Settings.ParticipantsSide)) {
|
|
UtilsAdapter.setAppValue(Settings.ParticipantsSide, true)
|
|
participantsSide = true
|
|
}
|
|
break
|
|
case JamiStrings.participantsTop:
|
|
if (UtilsAdapter.getAppValue(Settings.ParticipantsSide)) {
|
|
UtilsAdapter.setAppValue(Settings.ParticipantsSide, false)
|
|
participantsSide = false
|
|
}
|
|
break
|
|
case JamiStrings.hideSelf:
|
|
UtilsAdapter.setAppValue(Settings.HideSelf, !layoutModel.get(index).ActiveSetting)
|
|
CurrentCall.hideSelf = UtilsAdapter.getAppValue(Settings.HideSelf)
|
|
break
|
|
case JamiStrings.hideSpectators:
|
|
UtilsAdapter.setAppValue(Settings.HideSpectators, !layoutModel.get(index).ActiveSetting)
|
|
CurrentCall.hideSpectators = UtilsAdapter.getAppValue(Settings.HideSpectators)
|
|
break
|
|
case JamiStrings.mirrorLocalVideo:
|
|
UtilsAdapter.setAppValue(Settings.FlipSelf, !layoutModel.get(index).ActiveSetting)
|
|
CurrentCall.flipSelf = UtilsAdapter.getAppValue(Settings.FlipSelf)
|
|
break
|
|
}
|
|
}
|
|
onTriggered: {
|
|
layoutModel.clear()
|
|
if (CurrentCall.isConference) {
|
|
layoutModel.append({"Name": JamiStrings.mosaic,
|
|
"IconSource": JamiResources.mosaic_black_24dp_svg,
|
|
"ActiveSetting": CurrentCall.isGrid,
|
|
"TopMargin": true,
|
|
"BottomMargin": true,
|
|
"SectionEnd": true})
|
|
|
|
var onTheSide = UtilsAdapter.getAppValue(Settings.ParticipantsSide)
|
|
layoutModel.append({"Name": JamiStrings.participantsTop,
|
|
"IconSource": JamiResources.onthetop_black_24dp_svg,
|
|
"ActiveSetting": !onTheSide,
|
|
"TopMargin": true,
|
|
"BottomMargin": false,
|
|
"SectionEnd": false})
|
|
layoutModel.append({"Name": JamiStrings.participantsSide,
|
|
"IconSource": JamiResources.ontheside_black_24dp_svg,
|
|
"ActiveSetting": onTheSide,
|
|
"TopMargin": false,
|
|
"BottomMargin": true,
|
|
"SectionEnd": true})
|
|
|
|
layoutModel.append({"Name": JamiStrings.hideSelf,
|
|
"IconSource": JamiResources.hidemyself_black_24dp_svg,
|
|
"ActiveSetting": UtilsAdapter.getAppValue(Settings.HideSelf),
|
|
"TopMargin": true,
|
|
"BottomMargin": false,
|
|
"SectionEnd": false})
|
|
}
|
|
|
|
layoutModel.append({"Name": JamiStrings.mirrorLocalVideo,
|
|
"IconSource": JamiResources.flip_24dp_svg,
|
|
"ActiveSetting": UtilsAdapter.getAppValue(Settings.FlipSelf),
|
|
"TopMargin": !CurrentCall.isConference,
|
|
"BottomMargin": true,
|
|
"SectionEnd": true})
|
|
|
|
layoutModel.append({"Name": JamiStrings.viewFullScreen,
|
|
"IconSource": JamiResources.open_in_full_24dp_svg,
|
|
"ActiveSetting": layoutManager.isCallFullscreen,
|
|
"TopMargin": true,
|
|
"BottomMargin": true,
|
|
"SectionEnd": CurrentCall.isConference})
|
|
if (CurrentCall.isConference) {
|
|
layoutModel.append({"Name": JamiStrings.hideSpectators,
|
|
"IconSource": JamiResources.videocam_off_24dp_svg,
|
|
"ActiveSetting": UtilsAdapter.getAppValue(Settings.HideSpectators),
|
|
"TopMargin": true,
|
|
"BottomMargin": true})
|
|
}
|
|
}
|
|
},
|
|
Action {
|
|
id: videoInputMenuAction
|
|
enabled: VideoDevices.listSize !== 0
|
|
text: JamiStrings.selectVideoDevice
|
|
property var listModel: VideoDevices.deviceSourceModel
|
|
function accept(index) {
|
|
VideoDevices.setDefaultDevice(index)
|
|
}
|
|
}
|
|
]
|
|
|
|
property list<Action> primaryActions: [
|
|
Action {
|
|
id: muteAudioAction
|
|
onTriggered: {
|
|
var muteState = CallAdapter.getMuteState(CurrentAccount.uri)
|
|
var modMuted = muteState === CallAdapter.MODERATOR_MUTED
|
|
|| muteState === CallAdapter.BOTH_MUTED
|
|
if (muteAudioAction.checked && modMuted) {
|
|
muteAlertActive = true
|
|
muteAlertMessage = JamiStrings.participantModIsStillMuted
|
|
}
|
|
CallAdapter.muteAudioToggle()
|
|
}
|
|
checkable: true
|
|
icon.source: checked ?
|
|
JamiResources.micro_off_black_24dp_svg :
|
|
JamiResources.micro_black_24dp_svg
|
|
icon.color: checked ? "red" : "white"
|
|
text: !checked ? JamiStrings.mute : JamiStrings.unmute
|
|
checked: CurrentCall.isAudioMuted
|
|
property var menuAction: audioInputMenuAction
|
|
},
|
|
Action {
|
|
id: hangupAction
|
|
onTriggered: CallAdapter.hangUpThisCall()
|
|
icon.source: JamiResources.ic_call_end_white_24dp_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.endCall
|
|
property bool hasBg: true
|
|
},
|
|
Action {
|
|
id: muteVideoAction
|
|
onTriggered: CallAdapter.muteCameraToggle()
|
|
checkable: true
|
|
icon.source: checked ?
|
|
JamiResources.videocam_off_24dp_svg :
|
|
JamiResources.videocam_24dp_svg
|
|
icon.color: checked ? "red" : "white"
|
|
text: !checked ? JamiStrings.muteCamera : JamiStrings.unmuteCamera
|
|
checked: !CurrentCall.isCapturing
|
|
property var menuAction: videoInputMenuAction
|
|
}
|
|
]
|
|
|
|
property list<Action> secondaryActions: [
|
|
Action {
|
|
id: audioOutputAction
|
|
// temp hack for missing back-end, just open device selection
|
|
property bool openPopupWhenClicked: true
|
|
checkable: !openPopupWhenClicked
|
|
icon.source: JamiResources.spk_black_24dp_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.selectAudioOutputDevice
|
|
property var menuAction: audioOutputMenuAction
|
|
},
|
|
Action {
|
|
id: addPersonAction
|
|
onTriggered: root.addToConferenceClicked()
|
|
icon.source: JamiResources.add_people_black_24dp_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.addParticipants
|
|
},
|
|
Action {
|
|
id: chatAction
|
|
onTriggered: root.chatClicked()
|
|
icon.source: JamiResources.chat_black_24dp_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.chat
|
|
},
|
|
Action {
|
|
id: resumePauseCallAction
|
|
onTriggered: root.resumePauseCallClicked()
|
|
icon.source: CurrentCall.isPaused ?
|
|
JamiResources.play_circle_outline_24dp_svg :
|
|
JamiResources.pause_circle_outline_24dp_svg
|
|
icon.color: "white"
|
|
text: CurrentCall.isPaused ?
|
|
JamiStrings.resumeCall :
|
|
JamiStrings.pauseCall
|
|
},
|
|
Action {
|
|
id: inputPanelSIPAction
|
|
onTriggered: root.showInputPanelClicked()
|
|
icon.source: JamiResources.ic_keypad_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.sipInputPanel
|
|
},
|
|
Action {
|
|
id: callTransferAction
|
|
onTriggered: root.transferClicked()
|
|
icon.source: JamiResources.phone_forwarded_24dp_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.transferCall
|
|
},
|
|
Action {
|
|
id: shareAction
|
|
onTriggered: {
|
|
if (CurrentCall.isSharing)
|
|
root.stopSharingClicked()
|
|
else
|
|
root.shareScreenClicked()
|
|
}
|
|
icon.source: CurrentCall.isSharing ?
|
|
JamiResources.share_stop_black_24dp_svg :
|
|
JamiResources.share_screen_black_24dp_svg
|
|
icon.color: CurrentCall.isSharing ?
|
|
"red" : "white"
|
|
text: CurrentCall.isSharing ?
|
|
JamiStrings.stopSharing :
|
|
JamiStrings.shareScreen
|
|
property real size: 34
|
|
property var menuAction: shareMenuAction
|
|
},
|
|
Action {
|
|
id: raiseHandAction
|
|
onTriggered: CallAdapter.raiseHand("", "", !CallAdapter.isHandRaised())
|
|
checkable: true
|
|
icon.source: JamiResources.hand_black_24dp_svg
|
|
icon.color: checked ? JamiTheme.raiseHandColor : "white"
|
|
text: checked ?
|
|
JamiStrings.lowerHand :
|
|
JamiStrings.raiseHand
|
|
checked: CurrentCall.isHandRaised
|
|
property real size: 34
|
|
},
|
|
Action {
|
|
id: layoutAction
|
|
onTriggered: {
|
|
if (!CurrentCall.isGrid)
|
|
CallAdapter.showGridConferenceLayout()
|
|
}
|
|
checkable: true
|
|
icon.source: JamiResources.mosaic_black_24dp_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.layoutSettings
|
|
property real size: 28
|
|
property var menuAction: layoutMenuAction
|
|
},
|
|
Action {
|
|
id: recordAction
|
|
onTriggered: root.recordCallClicked()
|
|
checkable: true
|
|
icon.source: JamiResources.record_black_24dp_svg
|
|
icon.color: checked ? "red" : "white"
|
|
text: !checked ? JamiStrings.startRec : JamiStrings.stopRec
|
|
property bool blinksWhenChecked: true
|
|
property real size: 28
|
|
checked: CurrentCall.isRecordingLocally
|
|
onCheckedChanged: function(checked) {
|
|
CallOverlayModel.setUrgentCount(recordAction,
|
|
checked ? -1 : 0)
|
|
}
|
|
},
|
|
Action {
|
|
id: pluginsAction
|
|
onTriggered: root.pluginsClicked()
|
|
icon.source: JamiResources.plugins_24dp_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.viewPlugin
|
|
enabled: PluginAdapter.isEnabled
|
|
&& PluginAdapter.callMediaHandlersListCount
|
|
},
|
|
Action {
|
|
id: swarmDetailsAction
|
|
onTriggered: root.swarmDetailsClicked()
|
|
icon.source: JamiResources.swarm_details_panel_svg
|
|
icon.color: "white"
|
|
text: JamiStrings.details
|
|
enabled: {
|
|
if (LRCInstance.currentAccountType === Profile.Type.SIP)
|
|
return true
|
|
if (!CurrentConversation.isTemporary && !CurrentConversation.isSwarm)
|
|
return false
|
|
if (CurrentConversation.isRequest || CurrentConversation.needsSyncing)
|
|
return false
|
|
return true
|
|
}
|
|
}
|
|
]
|
|
|
|
property var overflowItemCount
|
|
|
|
Connections {
|
|
target: CurrentCall
|
|
|
|
function onIsActiveChanged() { if (CurrentCall.isActive) reset() }
|
|
function onIsRecordingLocallyChanged() { Qt.callLater(reset) }
|
|
function onIsHandRaisedChanged() { Qt.callLater(reset) }
|
|
function onIsConferenceChanged() { Qt.callLater(reset) }
|
|
function onIsModeratorChanged() { Qt.callLater(reset) }
|
|
function onIsSIPChanged() { Qt.callLater(reset) }
|
|
function onIsAudioOnlyChanged() { Qt.callLater(reset) }
|
|
function onIsAudioMutedChanged() { Qt.callLater(reset) }
|
|
function onIsVideoMutedChanged() { Qt.callLater(reset) }
|
|
}
|
|
|
|
Connections {
|
|
target: CurrentAccount
|
|
function onVideoEnabledVideoChanged() { reset() }
|
|
}
|
|
|
|
function reset() {
|
|
CallOverlayModel.clearControls()
|
|
|
|
// centered controls
|
|
CallOverlayModel.addPrimaryControl(muteAudioAction)
|
|
CallOverlayModel.addPrimaryControl(hangupAction)
|
|
|
|
if (CurrentAccount.videoEnabled_Video)
|
|
CallOverlayModel.addPrimaryControl(muteVideoAction)
|
|
|
|
// overflow controls
|
|
CallOverlayModel.addSecondaryControl(audioOutputAction)
|
|
if (CurrentCall.isConference) {
|
|
CallOverlayModel.addSecondaryControl(raiseHandAction)
|
|
}
|
|
if (CurrentCall.isModerator && !CurrentCall.isSIP)
|
|
CallOverlayModel.addSecondaryControl(addPersonAction)
|
|
if (CurrentCall.isSIP) {
|
|
CallOverlayModel.addSecondaryControl(resumePauseCallAction)
|
|
CallOverlayModel.addSecondaryControl(inputPanelSIPAction)
|
|
CallOverlayModel.addSecondaryControl(callTransferAction)
|
|
}
|
|
CallOverlayModel.addSecondaryControl(chatAction)
|
|
if (CurrentAccount.videoEnabled_Video && !CurrentCall.isSIP)
|
|
CallOverlayModel.addSecondaryControl(shareAction)
|
|
CallOverlayModel.addSecondaryControl(layoutAction)
|
|
CallOverlayModel.addSecondaryControl(recordAction)
|
|
if (pluginsAction.enabled)
|
|
CallOverlayModel.addSecondaryControl(pluginsAction)
|
|
if (swarmDetailsAction.enabled)
|
|
CallOverlayModel.addSecondaryControl(swarmDetailsAction)
|
|
overflowItemCount = CallOverlayModel.secondaryModel().rowCount()
|
|
}
|
|
|
|
Item {
|
|
id: centralControls
|
|
anchors.centerIn: parent
|
|
width: childrenRect.width
|
|
height: root.height
|
|
|
|
RowLayout {
|
|
spacing: 0
|
|
|
|
ListView {
|
|
id: itemListView
|
|
|
|
property bool centeredGroup: true
|
|
|
|
orientation: ListView.Horizontal
|
|
implicitWidth: contentWidth
|
|
implicitHeight: contentHeight
|
|
interactive: false
|
|
|
|
model: CallOverlayModel.primaryModel()
|
|
delegate: buttonDelegate
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: overflowRect
|
|
property real remainingSpace: (root.width - centralControls.width) / 2
|
|
anchors.right: parent.right
|
|
width: childrenRect.width
|
|
height: root.height
|
|
|
|
RowLayout {
|
|
spacing: itemSpacing
|
|
|
|
ListView {
|
|
id: overflowItemListView
|
|
|
|
orientation: ListView.Horizontal
|
|
implicitWidth: contentWidth
|
|
implicitHeight: overflowRect.height
|
|
|
|
interactive: false
|
|
spacing: itemSpacing
|
|
|
|
property int overflowIndex: {
|
|
var maxItems = Math.floor(
|
|
(overflowRect.remainingSpace) / (root.height + itemSpacing)) - 2
|
|
return Math.min(overflowItemCount, maxItems)
|
|
}
|
|
property int nOverflowItems: overflowItemCount - overflowIndex
|
|
onNOverflowItemsChanged: {
|
|
var diff = overflowItemListView.count - nOverflowItems
|
|
var effectiveOverflowIndex = overflowIndex
|
|
if (effectiveOverflowIndex === overflowItemCount - 2)
|
|
effectiveOverflowIndex += diff
|
|
|
|
CallOverlayModel.overflowIndex = effectiveOverflowIndex
|
|
}
|
|
|
|
model: CallOverlayModel.overflowModel()
|
|
delegate: buttonDelegate
|
|
}
|
|
|
|
ComboBox {
|
|
id: overflowButton
|
|
|
|
visible: CallOverlayModel.overflowIndex < overflowItemCount - 2
|
|
width: root.height
|
|
height: width
|
|
|
|
model: CallOverlayModel.overflowHiddenModel()
|
|
|
|
delegate: buttonDelegate
|
|
|
|
indicator: null
|
|
|
|
contentItem: ResponsiveImage {
|
|
color: "white"
|
|
source: JamiResources.more_vert_24dp_svg
|
|
anchors.fill: parent
|
|
anchors.margins: 17
|
|
}
|
|
|
|
background: HalfPill {
|
|
implicitWidth: root.height
|
|
implicitHeight: implicitWidth
|
|
radius: type === HalfPill.None ? 0 : 5
|
|
color: overflowButton.down ?
|
|
"#c4777777":
|
|
overflowButton.hovered ?
|
|
"#c4444444" :
|
|
"#c4272727"
|
|
type: {
|
|
if (overflowItemListView.count ||
|
|
urgentOverflowListView.count ||
|
|
(overflowHiddenListView.count &&
|
|
overflowButton.popup.visible)) {
|
|
return HalfPill.None
|
|
} else {
|
|
return HalfPill.Left
|
|
}
|
|
}
|
|
|
|
Behavior on color {
|
|
ColorAnimation { duration: JamiTheme.shortFadeDuration }
|
|
}
|
|
}
|
|
|
|
Item {
|
|
implicitHeight: (overflowButton.width + itemSpacing) * urgentOverflowListView.count
|
|
width: overflowButton.width
|
|
anchors.bottom: parent.top
|
|
anchors.bottomMargin: itemSpacing
|
|
visible: !overflowButton.popup.visible
|
|
JamiListView {
|
|
id: urgentOverflowListView
|
|
|
|
spacing: itemSpacing
|
|
anchors.fill: parent
|
|
model: !overflowButton.popup.visible ?
|
|
CallOverlayModel.overflowVisibleModel() :
|
|
null
|
|
|
|
delegate: buttonDelegate
|
|
|
|
add: Transition {
|
|
NumberAnimation {
|
|
property: "opacity"
|
|
from: 0 ; to: 1.0; duration: 80
|
|
}
|
|
NumberAnimation {
|
|
property: "scale"
|
|
from: 0; to: 1.0; duration: 80
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
popup: Popup {
|
|
y: overflowButton.height + itemSpacing
|
|
width: overflowButton.width
|
|
implicitHeight: Math.min(root.parentHeight - itemSpacing,
|
|
(overflowButton.width + itemSpacing) * overflowHiddenListView.count)
|
|
padding: 0
|
|
|
|
contentItem: JamiListView {
|
|
id: overflowHiddenListView
|
|
spacing: itemSpacing
|
|
implicitHeight: Math.min(contentHeight, parent.height)
|
|
interactive: true
|
|
model: overflowButton.popup.visible ?
|
|
overflowButton.delegateModel :
|
|
null
|
|
}
|
|
|
|
background: Rectangle {
|
|
color: "transparent"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|