1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-08-30 19:53:33 +02:00

mainview: make all context menus generated at run time with the same style

By giving a base context menu, all context menus are generated at run time
and kept the same style. Some issues are fixed along with the patch.

Gitlab: #8
Gitlab: #35
Change-Id: Ieb812420fcb44c33d161a62c8574f6705dc5e1a9
This commit is contained in:
Ming Rui Zhang 2020-08-27 12:59:09 -04:00
parent 6e3aacfc43
commit 968088785d
19 changed files with 418 additions and 525 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

View file

@ -103,5 +103,7 @@
<file>src/mainview/components/RecordBox.qml</file> <file>src/mainview/components/RecordBox.qml</file>
<file>src/commoncomponents/ElidedTextLabel.qml</file> <file>src/commoncomponents/ElidedTextLabel.qml</file>
<file>src/mainview/components/SipInputPanel.qml</file> <file>src/mainview/components/SipInputPanel.qml</file>
<file>src/commoncomponents/js/contextmenugenerator.js</file>
<file>src/commoncomponents/BaseContextMenu.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -23,6 +23,8 @@
<file>images/icons/ic_add_black_18dp_2x.png</file> <file>images/icons/ic_add_black_18dp_2x.png</file>
<file>images/icons/info-24px.svg</file> <file>images/icons/info-24px.svg</file>
<file>images/icons/backup-24px.svg</file> <file>images/icons/backup-24px.svg</file>
<file>images/icons/check_box_outline_blank-24px.svg</file>
<file>images/icons/check_box-24px.svg</file>
<file>images/icons/devices-24px.svg</file> <file>images/icons/devices-24px.svg</file>
<file>images/icons/ic_arrow_back_24px.svg</file> <file>images/icons/ic_arrow_back_24px.svg</file>
<file>images/icons/ic_arrow_back_white_24dp.png</file> <file>images/icons/ic_arrow_back_white_24dp.png</file>
@ -52,8 +54,6 @@
<file>images/icons/pause_circle_outline-24px.svg</file> <file>images/icons/pause_circle_outline-24px.svg</file>
<file>images/icons/play_circle_outline-24px.svg</file> <file>images/icons/play_circle_outline-24px.svg</file>
<file>images/icons/ic_pause_white_100px.png</file> <file>images/icons/ic_pause_white_100px.png</file>
<file>images/icons/ic_person_add_black_24dp_2x.png</file>
<file>images/icons/ic_person_add_white_24dp.png</file>
<file>images/icons/ic_phone_24px.svg</file> <file>images/icons/ic_phone_24px.svg</file>
<file>images/icons/ic_photo_camera_white_24dp_2x.png</file> <file>images/icons/ic_photo_camera_white_24dp_2x.png</file>
<file>images/icons/ic_baseline-search-24px.svg</file> <file>images/icons/ic_baseline-search-24px.svg</file>

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2020 by Savoir-faire Linux
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtGraphicalEffects 1.12
import net.jami.Models 1.0
Menu {
id: root
property int menuItemsPreferredWidth: 220
property int menuItemsPreferredHeight: 48
property int generalMenuSeparatorCount: 0
property int commonBorderWidth: 1
font.pointSize: JamiTheme.menuFontSize
function openMenu(){
visible = true
visible = false
visible = true
}
background: Rectangle {
implicitWidth: menuItemsPreferredWidth
implicitHeight: menuItemsPreferredHeight
* (root.count - generalMenuSeparatorCount)
border.width: commonBorderWidth
border.color: JamiTheme.tabbarBorderColor
}
}

View file

@ -35,8 +35,7 @@ MenuItem {
property string iconSource: "" property string iconSource: ""
property int preferredWidth: 220 property int preferredWidth: 220
property int preferredHeight: 48 property int preferredHeight: 48
property int topBorderWidth: 0
property int bottomBorderWidth: 0
property int leftBorderWidth: 0 property int leftBorderWidth: 0
property int rightBorderWidth: 0 property int rightBorderWidth: 0
@ -99,8 +98,6 @@ MenuItem {
id: contextMenuBackgroundRect id: contextMenuBackgroundRect
anchors.fill: parent anchors.fill: parent
anchors.topMargin: topBorderWidth
anchors.bottomMargin: bottomBorderWidth
anchors.leftMargin: leftBorderWidth anchors.leftMargin: leftBorderWidth
anchors.rightMargin: rightBorderWidth anchors.rightMargin: rightBorderWidth
@ -132,8 +129,8 @@ MenuItem {
commonBorder: false commonBorder: false
lBorderwidth: leftBorderWidth lBorderwidth: leftBorderWidth
rBorderwidth: rightBorderWidth rBorderwidth: rightBorderWidth
tBorderwidth: topBorderWidth tBorderwidth: 0
bBorderwidth: bottomBorderWidth bBorderwidth: 0
borderColor: JamiTheme.tabbarBorderColor borderColor: JamiTheme.tabbarBorderColor
} }
} }

View file

@ -0,0 +1,116 @@
/*
* Copyright (C) 2020 by Savoir-faire Linux
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Global base context menu, object variable for creation.
var baseContextMenuComponent
var baseContextMenuObject
var menuItemList = []
function createBaseContextMenuObjects(parent) {
// If already created, return, since object cannot be destroyed.
if (baseContextMenuObject)
return
baseContextMenuComponent = Qt.createComponent(
"../BaseContextMenu.qml")
if (baseContextMenuComponent.status === Component.Ready)
finishCreation(parent)
else if (baseContextMenuComponent.status === Component.Error)
console.log("Error loading component:",
baseContextMenuComponent.errorString())
}
function finishCreation(parent) {
baseContextMenuObject = baseContextMenuComponent.createObject(parent)
if (baseContextMenuObject === null) {
// Error Handling.
console.log("Error creating object for base context menu")
}
baseContextMenuObject.closed.connect(function (){
// Remove the menu items when hidden.
for (var i = 0; i < menuItemList.length; i++) {
baseContextMenuObject.removeItem(menuItemList[i])
}
})
baseContextMenuObject.aboutToShow.connect(function (){
// Add default separator at the bottom.
addMenuSeparator(8, "transparent")
})
}
function addMenuSeparator(separatorHeight, separatorColor) {
var menuSeparatorObject
var menuSeparatorComponent = Qt.createComponent("../GeneralMenuSeparator.qml");
if (menuSeparatorComponent.status === Component.Ready) {
baseContextMenuObject.generalMenuSeparatorCount ++
menuSeparatorObject = menuSeparatorComponent.createObject(null,
{preferredWidth: baseContextMenuObject.menuItemsPreferredWidth,
preferredHeight: separatorHeight ? separatorHeight : 1})
if (separatorColor)
menuSeparatorObject.separatorColor = separatorColor
} else if (menuSeparatorComponent.status === Component.Error)
console.log("Error loading component:",
menuSeparatorComponent.errorString())
if (menuSeparatorObject !== null) {
baseContextMenuObject.addItem(menuSeparatorObject)
menuItemList.push(menuSeparatorObject)
} else {
// Error handling.
console.log("Error creating object")
}
}
function addMenuItem(itemName,
iconSource,
onClickedCallback) {
if (!baseContextMenuObject.count){
// Add default separator at the top.
addMenuSeparator(8, "transparent")
}
var menuItemObject
var menuItemComponent = Qt.createComponent("../GeneralMenuItem.qml");
if (menuItemComponent.status === Component.Ready) {
menuItemObject = menuItemComponent.createObject(null,
{itemName: itemName,
iconSource: iconSource,
leftBorderWidth: baseContextMenuObject.commonBorderWidth,
rightBorderWidth: baseContextMenuObject.commonBorderWidth})
} else if (menuItemComponent.status === Component.Error)
console.log("Error loading component:",
menuItemComponent.errorString())
if (menuItemObject !== null) {
menuItemObject.clicked.connect(function () {baseContextMenuObject.close()})
menuItemObject.clicked.connect(onClickedCallback)
menuItemObject.icon.color = "black"
baseContextMenuObject.addItem(menuItemObject)
menuItemList.push(menuItemObject)
} else {
// Error handling.
console.log("Error creating object")
}
}
function getMenu() {
return baseContextMenuObject
}

View file

@ -230,7 +230,7 @@ Rectangle {
onClicked: { onClicked: {
var rectPos = mapToItem(callStackViewWindow, optionsButton.x, optionsButton.y) var rectPos = mapToItem(callStackViewWindow, optionsButton.x, optionsButton.y)
callViewContextMenu.activate() callViewContextMenu.openMenu()
callViewContextMenu.x = rectPos.x + optionsButton.width/2 - callViewContextMenu.width/2 callViewContextMenu.x = rectPos.x + optionsButton.width/2 - callViewContextMenu.width/2
callViewContextMenu.y = rectPos.y - 12 - callViewContextMenu.height callViewContextMenu.y = rectPos.y - 12 - callViewContextMenu.height
} }

View file

@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import QtQuick 2.14 import QtQuick 2.14
import QtQuick.Controls 2.14 import QtQuick.Controls 2.14
import QtGraphicalEffects 1.12 import QtGraphicalEffects 1.12
@ -24,61 +25,125 @@ import net.jami.Models 1.0
import "../../commoncomponents" import "../../commoncomponents"
import "../../commoncomponents/js/contextmenugenerator.js" as ContextMenuGenerator
import "../js/videodevicecontextmenuitemcreation.js" as VideoDeviceContextMenuItemCreation import "../js/videodevicecontextmenuitemcreation.js" as VideoDeviceContextMenuItemCreation
import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation
import "../js/screenrubberbandcreation.js" as ScreenRubberBandCreation
Menu { Item {
id: root id: root
property int generalMenuSeparatorCount: 0
property int commonBorderWidth: 1
signal pluginItemClicked
font.pointSize: JamiTheme.textFontSize+3
property bool isSIP: false property bool isSIP: false
property bool isPaused: false property bool isPaused: false
property bool isAudioOnly: false property bool isAudioOnly: false
property bool isRecording: false property bool isRecording: false
signal pluginItemClicked
signal transferCallButtonClicked signal transferCallButtonClicked
function activate() { function openMenu(){
if (isSIP){
ContextMenuGenerator.addMenuItem(isPaused ? qsTr("Resume call") : qsTr("Hold call"),
isPaused ?
"qrc:/images/icons/play_circle_outline-24px.svg" :
"qrc:/images/icons/pause_circle_outline-24px.svg",
function (){
CallAdapter.holdThisCallToggle()
})
ContextMenuGenerator.addMenuItem(qsTr("Sip Input Panel"),
"qrc:/images/icons/ic_keypad.svg",
function (){
sipInputPanel.open()
})
ContextMenuGenerator.addMenuItem(qsTr("Transfer call"),
"qrc:/images/icons/phone_forwarded-24px.svg",
function (){
root.transferCallButtonClicked()
})
ContextMenuGenerator.addMenuSeparator()
}
if (!isAudioOnly) {
ContextMenuGenerator.addMenuItem(isRecording ? qsTr("Stop recording") :
qsTr("Start recording"),
"qrc:/images/icons/ic_video_call_24px.svg",
function (){
CallAdapter.recordThisCallToggle()
})
ContextMenuGenerator.addMenuItem(videoCallPage.isFullscreen ? qsTr("Exit full screen") :
qsTr("Full screen mode"),
videoCallPage.isFullscreen ?
"qrc:/images/icons/close_fullscreen-24px.svg" :
"qrc:/images/icons/open_in_full-24px.svg",
function (){
videoCallPageRect.needToShowInFullScreen()
})
ContextMenuGenerator.addMenuSeparator()
generateDeviceMenuItem()
ContextMenuGenerator.addMenuSeparator()
ContextMenuGenerator.addMenuItem(qsTr("Share entire screen"),
"qrc:/images/icons/screen_share-24px.svg",
function (){
if (Qt.application.screens.length === 1) {
AvAdapter.shareEntireScreen(0)
} else {
SelectScreenWindowCreation.createSelectScreenWindowObject()
SelectScreenWindowCreation.showSelectScreenWindow()
}
})
ContextMenuGenerator.addMenuItem(qsTr("Share screen area"),
"qrc:/images/icons/screen_share-24px.svg",
function (){
if (Qt.application.screens.length === 1) {
ScreenRubberBandCreation.createScreenRubberBandWindowObject(
null, 0)
ScreenRubberBandCreation.showScreenRubberBandWindow()
} else {
SelectScreenWindowCreation.createSelectScreenWindowObject(true)
SelectScreenWindowCreation.showSelectScreenWindow()
}
})
ContextMenuGenerator.addMenuItem(qsTr("Share file"),
"qrc:/images/icons/insert_photo-24px.svg",
function (){
jamiFileDialog.open()
})
}
ContextMenuGenerator.addMenuItem(qsTr("Toggle plugin"),
"qrc:/images/icons/extension_24dp.svg",
function (){
root.pluginItemClicked()
})
root.height = ContextMenuGenerator.getMenu().height
root.width = ContextMenuGenerator.getMenu().width
ContextMenuGenerator.getMenu().open()
}
function generateDeviceMenuItem() {
var deviceContextMenuInfoMap = AvAdapter.populateVideoDeviceContextMenuItem() var deviceContextMenuInfoMap = AvAdapter.populateVideoDeviceContextMenuItem()
/* /*
* Somehow, the map size is undefined, so use this instead. * Somehow, the map size is undefined, so use this instead.
*/ */
var mapSize = deviceContextMenuInfoMap["size"] var mapSize = deviceContextMenuInfoMap["size"]
var count = 2 if (mapSize === 0)
for (var deviceName in deviceContextMenuInfoMap) {
if (deviceName === "size" || root.isAudioOnly)
continue
if (videoDeviceItem.itemName === "No video device") {
videoDeviceItem.checkable = true
videoDeviceItem.itemName = deviceName
videoDeviceItem.checked = deviceContextMenuInfoMap[deviceName]
if (count === mapSize)
root.open()
} else {
VideoDeviceContextMenuItemCreation.createVideoDeviceContextMenuItemObjects( VideoDeviceContextMenuItemCreation.createVideoDeviceContextMenuItemObjects(
deviceName, deviceContextMenuInfoMap[deviceName], qsTr("No video device"), false)
count === mapSize)
}
count++
}
root.open()
}
Component.onCompleted: { for (var deviceName in deviceContextMenuInfoMap) {
VideoDeviceContextMenuItemCreation.setVideoContextMenuObject(root) if (deviceName === "size")
continue
VideoDeviceContextMenuItemCreation.createVideoDeviceContextMenuItemObjects(
deviceName, deviceContextMenuInfoMap[deviceName])
} }
onClosed: {
videoDeviceItem.itemName = "No video device"
VideoDeviceContextMenuItemCreation.removeCreatedItems()
} }
JamiFileDialog { JamiFileDialog {
@ -92,187 +157,13 @@ Menu {
} }
} }
/*
* All GeneralMenuItems should remain the same width / height.
*/
GeneralMenuItem {
id: holdCallButton
visible: isSIP
height: isSIP? undefined : 0
itemName: isPaused? qsTr("Resume call") : qsTr("Hold call")
iconSource: isPaused? "qrc:/images/icons/play_circle_outline-24px.svg" : "qrc:/images/icons/pause_circle_outline-24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
CallAdapter.holdThisCallToggle()
root.close()
}
}
GeneralMenuItem {
id: showSipInputPanelButton
visible: isSIP
height: isSIP? undefined : 0
itemName: qsTr("Sip Input Panel")
iconSource: "qrc:/images/icons/ic_keypad.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
root.close()
sipInputPanel.open()
}
}
GeneralMenuItem {
id: transferCallButton
visible: isSIP
height: isSIP? undefined : 0
itemName: qsTr("Transfer call")
iconSource: "qrc:/images/icons/phone_forwarded-24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
root.transferCallButtonClicked()
root.close()
}
}
GeneralMenuSeparator {
preferredWidth: startRecordingItem.preferredWidth
preferredHeight: commonBorderWidth
visible: isSIP
height: isSIP? undefined : 0
Component.onCompleted: { Component.onCompleted: {
generalMenuSeparatorCount++ ContextMenuGenerator.createBaseContextMenuObjects(root)
} VideoDeviceContextMenuItemCreation.setVideoContextMenuObject(ContextMenuGenerator.getMenu())
}
GeneralMenuItem { ContextMenuGenerator.getMenu().closed.connect(function (){
id: startRecordingItem VideoDeviceContextMenuItemCreation.removeCreatedItems()
})
itemName: isRecording? qsTr("Stop recording") : qsTr("Start recording")
iconSource: "qrc:/images/icons/ic_video_call_24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
root.close()
CallAdapter.recordThisCallToggle()
}
}
GeneralMenuItem {
id: fullScreenItem
itemName: videoCallPage.isFullscreen ? qsTr("Exit full screen") : qsTr(
"Full screen mode")
iconSource: videoCallPage.isFullscreen ? "qrc:/images/icons/close_fullscreen-24px.svg" : "qrc:/images/icons/open_in_full-24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
root.close()
videoCallPageRect.needToShowInFullScreen()
}
}
GeneralMenuSeparator {
preferredWidth: startRecordingItem.preferredWidth
preferredHeight: commonBorderWidth
Component.onCompleted: {
generalMenuSeparatorCount++
}
}
VideoCallPageContextMenuDeviceItem {
id: videoDeviceItem
visible: !isAudioOnly
height: !isAudioOnly? undefined : 0
contextMenuPreferredWidth: root.implicitWidth
}
GeneralMenuSeparator {
preferredWidth: startRecordingItem.preferredWidth
preferredHeight: commonBorderWidth
visible: !isAudioOnly
height: !isAudioOnly? undefined : 0
Component.onCompleted: {
generalMenuSeparatorCount++
}
}
GeneralMenuItem {
id: shareEntireScreenItem
itemName: qsTr("Share entire screen")
iconSource: "qrc:/images/icons/screen_share-24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
visible: !isAudioOnly
height: !isAudioOnly? undefined : 0
onClicked: {
root.close()
if (Qt.application.screens.length === 1) {
AvAdapter.shareEntireScreen(0)
} else {
SelectScreenWindowCreation.createSelectScreenWindowObject()
SelectScreenWindowCreation.showSelectScreenWindow()
}
}
}
GeneralMenuItem {
id: shareScreenAreaItem
itemName: qsTr("Share screen area")
iconSource: "qrc:/images/icons/screen_share-24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
visible: !isAudioOnly
height: !isAudioOnly? undefined : 0
onClicked: {
root.close()
if (Qt.application.screens.length === 1) {
ScreenRubberBandCreation.createScreenRubberBandWindowObject(
null, 0)
ScreenRubberBandCreation.showScreenRubberBandWindow()
} else {
SelectScreenWindowCreation.createSelectScreenWindowObject(true)
SelectScreenWindowCreation.showSelectScreenWindow()
}
}
}
GeneralMenuItem {
id: shareFileItem
itemName: qsTr("Share file")
iconSource: "qrc:/images/icons/insert_photo-24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
visible: !isAudioOnly
height: !isAudioOnly? undefined : 0
onClicked: {
root.close()
jamiFileDialog.open()
}
} }
/* TODO: In the future we want to implement this /* TODO: In the future we want to implement this
@ -289,31 +180,5 @@ Menu {
root.close() root.close()
} }
}*/ }*/
GeneralMenuItem {
id: pluginItem
itemName: qsTr("Toggle plugin")
iconSource: "qrc:/images/icons/extension_24dp.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
root.pluginItemClicked()
root.close()
}
}
background: Rectangle {
implicitWidth: startRecordingItem.preferredWidth
implicitHeight: startRecordingItem.preferredHeight
* (root.count
- (isSIP? 0 : 2)
- (isAudioOnly? 6 : 0)
- generalMenuSeparatorCount)
border.width: commonBorderWidth
border.color: JamiTheme.tabbarBorderColor
}
} }

View file

@ -1,4 +1,3 @@
/* /*
* Copyright (C) 2020 by Savoir-faire Linux * Copyright (C) 2020 by Savoir-faire Linux
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
@ -16,6 +15,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import QtQuick 2.14 import QtQuick 2.14
import QtQuick.Controls 2.14 import QtQuick.Controls 2.14
import QtGraphicalEffects 1.12 import QtGraphicalEffects 1.12
@ -23,157 +23,88 @@ import net.jami.Models 1.0
import "../../commoncomponents" import "../../commoncomponents"
Menu { import "../../commoncomponents/js/contextmenugenerator.js" as ContextMenuGenerator
id: contextMenu
Item {
id: root
property string responsibleAccountId: "" property string responsibleAccountId: ""
property string responsibleConvUid: "" property string responsibleConvUid: ""
property int contactType: Profile.Type.INVALID
property int generalMenuSeparatorCount: 0
property int commonBorderWidth: 1
font.pointSize: JamiTheme.menuFontSize
function openMenu(){ function openMenu(){
visible = true ContextMenuGenerator.addMenuItem(qsTr("Start video call"),
visible = false "qrc:/images/icons/ic_video_call_24px.svg",
visible = true function (){
} ConversationsAdapter.selectConversation(
responsibleAccountId,
GeneralMenuSeparator {
preferredWidth: startVideoCallItem.preferredWidth
preferredHeight: 8
separatorColor: "transparent"
Component.onCompleted: {
generalMenuSeparatorCount++
}
}
/*
* All GeneralMenuItems should remain the same width / height.
*/
GeneralMenuItem {
id: startVideoCallItem
itemName: qsTr("Start video call")
iconSource: "qrc:/images/icons/ic_video_call_24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
contextMenu.close()
ConversationsAdapter.selectConversation(responsibleAccountId,
responsibleConvUid, false) responsibleConvUid, false)
CallAdapter.placeCall() CallAdapter.placeCall()
} })
} ContextMenuGenerator.addMenuItem(qsTr("Start audio call"),
"qrc:/images/icons/ic_phone_24px.svg",
GeneralMenuItem { function (){
id: startAudioCallItem ConversationsAdapter.selectConversation(
responsibleAccountId,
itemName: qsTr("Start audio call")
iconSource: "qrc:/images/icons/ic_phone_24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
contextMenu.close()
ConversationsAdapter.selectConversation(responsibleAccountId,
responsibleConvUid, false) responsibleConvUid, false)
CallAdapter.placeAudioOnlyCall() CallAdapter.placeAudioOnlyCall()
} })
} ContextMenuGenerator.addMenuItem(qsTr("Clear conversation"),
"qrc:/images/icons/ic_clear_24px.svg",
GeneralMenuItem { function (){
id: clearConversationItem ClientWrapper.utilsAdaptor.clearConversationHistory(
responsibleAccountId,
itemName: qsTr("Clear conversation")
iconSource: "qrc:/images/icons/ic_clear_24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
contextMenu.close()
ClientWrapper.utilsAdaptor.clearConversationHistory(responsibleAccountId,
responsibleConvUid) responsibleConvUid)
} })
}
GeneralMenuItem { if (contactType === Profile.Type.RING || contactType === Profile.Type.SIP) {
id: removeContactItem ContextMenuGenerator.addMenuItem(qsTr("Remove contact"),
"qrc:/images/icons/round-remove_circle-24px.svg",
itemName: qsTr("Remove contact") function (){
iconSource: "qrc:/images/icons/round-remove_circle-24px.svg" ClientWrapper.utilsAdaptor.removeConversation(
leftBorderWidth: commonBorderWidth responsibleAccountId,
rightBorderWidth: commonBorderWidth
onClicked: {
contextMenu.close()
ClientWrapper.utilsAdaptor.removeConversation(responsibleAccountId,
responsibleConvUid) responsibleConvUid)
} })
} }
GeneralMenuSeparator { if (contactType === Profile.Type.RING || contactType === Profile.Type.PENDING) {
preferredWidth: startVideoCallItem.preferredWidth ContextMenuGenerator.addMenuSeparator()
preferredHeight: commonBorderWidth
Component.onCompleted: { if (contactType === Profile.Type.PENDING) {
generalMenuSeparatorCount++ ContextMenuGenerator.addMenuItem(qsTr("Accept request"),
} "qrc:/images/icons/person_add-24px.svg",
function (){
MessagesAdapter.acceptInvitation(
responsibleConvUid)
})
ContextMenuGenerator.addMenuItem(qsTr("Decline request"),
"qrc:/images/icons/round-close-24px.svg",
function (){
MessagesAdapter.refuseInvitation(
responsibleConvUid)
})
} }
ContextMenuGenerator.addMenuItem(qsTr("Block contact"),
"qrc:/images/icons/ic_block_24px.svg",
function (){
MessagesAdapter.blockConversation(
responsibleConvUid)
})
GeneralMenuItem { ContextMenuGenerator.addMenuSeparator()
id: blockContactItem ContextMenuGenerator.addMenuItem(qsTr("Profile"),
"qrc:/images/icons/person-24px.svg",
itemName: qsTr("Block contact") function (){
iconSource: "qrc:/images/icons/ic_block_24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
contextMenu.close()
ClientWrapper.utilsAdaptor.removeConversation(responsibleAccountId,
responsibleConvUid, true)
}
}
GeneralMenuSeparator {
preferredWidth: startVideoCallItem.preferredWidth
preferredHeight: commonBorderWidth
Component.onCompleted: {
generalMenuSeparatorCount++
}
}
GeneralMenuItem {
id: profileItem
itemName: qsTr("Profile")
iconSource: "qrc:/images/icons/person-24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
contextMenu.close()
userProfile.open() userProfile.open()
} })
}
root.height = ContextMenuGenerator.getMenu().height
root.width = ContextMenuGenerator.getMenu().width
ContextMenuGenerator.getMenu().open()
} }
GeneralMenuSeparator {
preferredWidth: startVideoCallItem.preferredWidth
preferredHeight: 8
separatorColor: "transparent"
Component.onCompleted: { Component.onCompleted: {
generalMenuSeparatorCount++ ContextMenuGenerator.createBaseContextMenuObjects(root)
}
}
background: Rectangle {
implicitWidth: startVideoCallItem.preferredWidth
implicitHeight: startVideoCallItem.preferredHeight
* (contextMenu.count - generalMenuSeparatorCount)
border.width: commonBorderWidth
border.color: JamiTheme.tabbarBorderColor
} }
} }

View file

@ -82,6 +82,10 @@ ListView {
conversationSmartListView.model.setAccount(accountId) conversationSmartListView.model.setAccount(accountId)
} }
ConversationSmartListContextMenu {
id: smartListContextMenu
}
Connections { Connections {
target: CallAdapter target: CallAdapter

View file

@ -180,7 +180,7 @@ ItemDelegate {
itemSmartListBackground.color = JamiTheme.releaseColor itemSmartListBackground.color = JamiTheme.releaseColor
} }
if (mouse.button === Qt.RightButton) { if (mouse.button === Qt.RightButton) {
smartListContextMenu.parent = mouseAreaSmartListItemDelegate
/* /*
* Make menu pos at mouse. * Make menu pos at mouse.
@ -191,6 +191,7 @@ ItemDelegate {
smartListContextMenu.y = relativeMousePos.y smartListContextMenu.y = relativeMousePos.y
smartListContextMenu.responsibleAccountId = ClientWrapper.utilsAdaptor.getCurrAccId() smartListContextMenu.responsibleAccountId = ClientWrapper.utilsAdaptor.getCurrAccId()
smartListContextMenu.responsibleConvUid = UID smartListContextMenu.responsibleConvUid = UID
smartListContextMenu.contactType = ContactType
userProfile.responsibleConvUid = UID userProfile.responsibleConvUid = UID
userProfile.aliasText = DisplayName userProfile.aliasText = DisplayName
userProfile.registeredNameText = DisplayID userProfile.registeredNameText = DisplayID
@ -225,8 +226,4 @@ ItemDelegate {
} }
} }
} }
ConversationSmartListContextMenu {
id: smartListContextMenu
}
} }

View file

@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2020 by Savoir-faire Linux * Copyright (C) 2020 by Savoir-faire Linux
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -16,6 +16,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import QtQuick 2.14 import QtQuick 2.14
import QtQuick.Controls 2.14 import QtQuick.Controls 2.14
import QtGraphicalEffects 1.12 import QtGraphicalEffects 1.12
@ -23,105 +24,46 @@ import net.jami.Models 1.0
import "../../commoncomponents" import "../../commoncomponents"
import "../js/videodevicecontextmenuitemcreation.js" as VideoDeviceContextMenuItemCreation import "../../commoncomponents/js/contextmenugenerator.js" as ContextMenuGenerator
import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation
Menu { Item {
id: root id: root
property int generalMenuSeparatorCount: 0
property int commonBorderWidth: 1
font.pointSize: JamiTheme.textFontSize + 3
property var uri: "" property var uri: ""
property var maximized: true property var maximized: true
property var active: true property var active: true
property var showHangup: false
property var showMaximize: false
property var showMinimize: false
function showHangup(show) { function openMenu(){
if (show) { if (showHangup)
hangupItem.visible = true ContextMenuGenerator.addMenuItem(qsTr("Hang up"),
hangupItem.height = hangupItem.preferredHeight "qrc:/images/icons/ic_call_end_white_24px.svg",
} else { function (){
hangupItem.visible = false
hangupItem.height = 0
}
}
function showMaximize(show) {
if (show) {
maximizeItem.visible = true
maximizeItem.height = hangupItem.preferredHeight
} else {
maximizeItem.visible = false
maximizeItem.height = 0
}
}
function showMinimize(show) {
if (show) {
minimizeItem.visible = true
minimizeItem.height = hangupItem.preferredHeight
} else {
minimizeItem.visible = false
minimizeItem.height = 0
}
}
function setHeight(visibleItems) {
root.height = hangupItem.preferredHeight * visibleItems;
}
/*
* All GeneralMenuItems should remain the same width / height.
*/
GeneralMenuItem {
id: hangupItem
itemName: qsTr("Hangup")
iconSource: "qrc:/images/icons/ic_call_end_white_24px.svg"
icon.color: "black"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
onClicked: {
CallAdapter.hangupCall(uri) CallAdapter.hangupCall(uri)
root.close() })
}
}
GeneralMenuItem {
id: maximizeItem
itemName: qsTr("Maximize participant") if (showMaximize)
iconSource: "qrc:/images/icons/open_in_full-24px.svg" ContextMenuGenerator.addMenuItem(qsTr("Maximize participant"),
leftBorderWidth: commonBorderWidth "qrc:/images/icons/open_in_full-24px.svg",
rightBorderWidth: commonBorderWidth function (){
visible: !maximized
onClicked: {
CallAdapter.maximizeParticipant(uri, active) CallAdapter.maximizeParticipant(uri, active)
root.close() })
} if (showMinimize)
} ContextMenuGenerator.addMenuItem(qsTr("Minimize participant"),
GeneralMenuItem { "qrc:/images/icons/close_fullscreen-24px.svg",
id: minimizeItem function (){
itemName: qsTr("Minimize participant")
iconSource: "qrc:/images/icons/close_fullscreen-24px.svg"
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
visible: maximized
onClicked: {
CallAdapter.minimizeParticipant() CallAdapter.minimizeParticipant()
root.close() })
}
root.height = ContextMenuGenerator.getMenu().height
root.width = ContextMenuGenerator.getMenu().width
ContextMenuGenerator.getMenu().open()
} }
background: Rectangle { Component.onCompleted: {
implicitWidth: hangupItem.preferredWidth ContextMenuGenerator.createBaseContextMenuObjects(root)
implicitHeight: hangupItem.preferredHeight * 3
border.width: commonBorderWidth
border.color: JamiTheme.tabbarBorderColor
} }
} }

View file

@ -112,18 +112,14 @@ Rectangle {
var layout = CallAdapter.getCurrentLayoutType() var layout = CallAdapter.getCurrentLayoutType()
var showMaximized = layout !== 2 var showMaximized = layout !== 2
var showMinimized = !(layout === 0 || (layout === 1 && !active)) var showMinimized = !(layout === 0 || (layout === 1 && !active))
injectedContextMenu.showHangup(!root.isLocal) injectedContextMenu.showHangup = !root.isLocal
injectedContextMenu.showMaximize(showMaximized) injectedContextMenu.showMaximize = showMaximized
injectedContextMenu.showMinimize(showMinimized) injectedContextMenu.showMinimize = showMinimized
injectedContextMenu.setHeight(
(root.isLocal ? 0 : 1)
+ (showMaximized ? 1 : 0)
+ (showMinimized ? 1 : 0))
injectedContextMenu.uri = uri injectedContextMenu.uri = uri
injectedContextMenu.active = active injectedContextMenu.active = active
injectedContextMenu.x = mousePos.x injectedContextMenu.x = mousePos.x
injectedContextMenu.y = mousePos.y - injectedContextMenu.height injectedContextMenu.y = mousePos.y - injectedContextMenu.height
injectedContextMenu.open() injectedContextMenu.openMenu()
} }
} }
} }

View file

@ -1,4 +1,3 @@
/* /*
* Copyright (C) 2020 by Savoir-faire Linux * Copyright (C) 2020 by Savoir-faire Linux
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
@ -16,24 +15,21 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import QtQuick 2.14 import QtQuick 2.14
import QtQuick.Controls 2.14 import QtQuick.Controls 2.14
import net.jami.Models 1.0 import net.jami.Models 1.0
import "../../commoncomponents" import "../../commoncomponents"
/* /*
* Take advantage of child can access parent's item (ex: contextMenu, commonBorderWidth). * Menu item wrapper for video device checkable item.
*/ */
GeneralMenuItem { GeneralMenuItem {
id: videoCallPageContextMenuDeviceItem id: videoCallPageContextMenuDeviceItem
property int contextMenuPreferredWidth: 250 property int contextMenuPreferredWidth: 250
leftBorderWidth: commonBorderWidth
rightBorderWidth: commonBorderWidth
TextMetrics { TextMetrics {
id: textMetrics id: textMetrics
elide: Text.ElideMiddle elide: Text.ElideMiddle
@ -54,7 +50,6 @@ GeneralMenuItem {
onClicked: { onClicked: {
var deviceName = videoCallPageContextMenuDeviceItem.itemName var deviceName = videoCallPageContextMenuDeviceItem.itemName
contextMenu.close()
AvAdapter.onVideoContextMenuDeviceItemClicked(deviceName) AvAdapter.onVideoContextMenuDeviceItemClicked(deviceName)
} }
} }

View file

@ -40,24 +40,28 @@ function setVideoContextMenuObject(obj) {
videoContextMenuObject = obj videoContextMenuObject = obj
} }
function createVideoDeviceContextMenuItemObjects(deviceName, setChecked, last) { function createVideoDeviceContextMenuItemObjects(deviceName, setChecked) {
videoDeviceContextMenuItemComponent = Qt.createComponent( videoDeviceContextMenuItemComponent = Qt.createComponent(
"../components/VideoCallPageContextMenuDeviceItem.qml") "../components/VideoCallPageContextMenuDeviceItem.qml")
if (videoDeviceContextMenuItemComponent.status === Component.Ready) if (videoDeviceContextMenuItemComponent.status === Component.Ready)
finishCreation(deviceName, setChecked, last) finishCreation(deviceName, setChecked)
else if (videoDeviceContextMenuItemComponent.status === Component.Error) else if (videoDeviceContextMenuItemComponent.status === Component.Error)
console.log("Error loading component:", console.log("Error loading component:",
videoDeviceContextMenuItemComponent.errorString()) videoDeviceContextMenuItemComponent.errorString())
} }
function finishCreation(deviceName, setChecked, last) { function finishCreation(deviceName, setChecked) {
videoDeviceContextMenuItemObject = videoDeviceContextMenuItemComponent.createObject() videoDeviceContextMenuItemObject = videoDeviceContextMenuItemComponent.createObject()
if (videoDeviceContextMenuItemObject === null) { if (videoDeviceContextMenuItemObject === null) {
// Error Handling. // Error Handling.
console.log("Error creating video context menu object") console.log("Error creating video context menu object")
} }
videoDeviceContextMenuItemObject.leftBorderWidth =
videoContextMenuObject.commonBorderWidth
videoDeviceContextMenuItemObject.rightBorderWidth =
videoContextMenuObject.commonBorderWidth
videoDeviceContextMenuItemObject.itemName = deviceName videoDeviceContextMenuItemObject.itemName = deviceName
videoDeviceContextMenuItemObject.checkable = true videoDeviceContextMenuItemObject.checkable = true
videoDeviceContextMenuItemObject.checked = setChecked videoDeviceContextMenuItemObject.checked = setChecked
@ -68,14 +72,11 @@ function finishCreation(deviceName, setChecked, last) {
* Push into the storage array, and insert it into context menu. * Push into the storage array, and insert it into context menu.
*/ */
itemArray.push(videoDeviceContextMenuItemObject) itemArray.push(videoDeviceContextMenuItemObject)
videoContextMenuObject.insertItem(3 /* The button is at pos 3 in the menu */, videoDeviceContextMenuItemObject) videoContextMenuObject.addItem(videoDeviceContextMenuItemObject)
videoDeviceContextMenuItemObject.clicked.connect(function () {
/* videoContextMenuObject.close()
* If it is the last device context menu item, open the context menu. })
*/
if (last)
videoContextMenuObject.open()
} }
function removeCreatedItems() { function removeCreatedItems() {

View file

@ -633,24 +633,24 @@ MessagesAdapter::contactIsComposing(const QString &uid, const QString &contactUr
} }
void void
MessagesAdapter::acceptInvitation() MessagesAdapter::acceptInvitation(const QString &convUid)
{ {
const auto convUid = LRCInstance::getCurrentConvUid(); const auto currentConvUid = convUid.isEmpty() ? LRCInstance::getCurrentConvUid() : convUid;
LRCInstance::getCurrentConversationModel()->makePermanent(convUid); LRCInstance::getCurrentConversationModel()->makePermanent(currentConvUid);
} }
void void
MessagesAdapter::refuseInvitation() MessagesAdapter::refuseInvitation(const QString &convUid)
{ {
auto convUid = LRCInstance::getCurrentConvUid(); const auto currentConvUid = convUid.isEmpty() ? LRCInstance::getCurrentConvUid() : convUid;
LRCInstance::getCurrentConversationModel()->removeConversation(convUid, false); LRCInstance::getCurrentConversationModel()->removeConversation(currentConvUid, false);
setInvitation(false); setInvitation(false);
} }
void void
MessagesAdapter::blockConversation() MessagesAdapter::blockConversation(const QString &convUid)
{ {
auto convUid = LRCInstance::getCurrentConvUid(); const auto currentConvUid = convUid.isEmpty() ? LRCInstance::getCurrentConvUid() : convUid;
LRCInstance::getCurrentConversationModel()->removeConversation(convUid, true); LRCInstance::getCurrentConversationModel()->removeConversation(currentConvUid, true);
setInvitation(false); setInvitation(false);
} }

View file

@ -41,9 +41,9 @@ public:
/* /*
* JS Q_INVOKABLE. * JS Q_INVOKABLE.
*/ */
Q_INVOKABLE void acceptInvitation(); Q_INVOKABLE void acceptInvitation(const QString &convUid = "");
Q_INVOKABLE void refuseInvitation(); Q_INVOKABLE void refuseInvitation(const QString &convUid = "");
Q_INVOKABLE void blockConversation(); Q_INVOKABLE void blockConversation(const QString &convUid = "");
Q_INVOKABLE void setNewMessagesContent(const QString &path); Q_INVOKABLE void setNewMessagesContent(const QString &path);
Q_INVOKABLE void sendMessage(const QString &message); Q_INVOKABLE void sendMessage(const QString &message);
Q_INVOKABLE void sendImage(const QString &message); Q_INVOKABLE void sendImage(const QString &message);

View file

@ -173,7 +173,7 @@ ItemDelegate {
buttonImageHeight: height - 8 buttonImageHeight: height - 8
buttonImageWidth: width - 8 buttonImageWidth: width - 8
source: "qrc:/images/icons/ic_person_add_black_24dp_2x.png" source: "qrc:/images/icons/person_add-24px.svg"
radius: height / 2 radius: height / 2
width: 25 width: 25