From 968088785d4e1579e1fd32ce71d0d7c63da782a0 Mon Sep 17 00:00:00 2001 From: Ming Rui Zhang <mingrui.zhang@savoirfairelinux.com> Date: Thu, 27 Aug 2020 12:59:09 -0400 Subject: [PATCH] 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 --- images/icons/ic_person_add_black_24dp_2x.png | Bin 323 -> 0 bytes images/icons/ic_person_add_white_24dp.png | Bin 329 -> 0 bytes qml.qrc | 2 + ressources.qrc | 4 +- src/commoncomponents/BaseContextMenu.qml | 47 +++ src/commoncomponents/GeneralMenuItem.qml | 9 +- .../js/contextmenugenerator.js | 116 ++++++ .../components/CallOverlayButtonGroup.qml | 2 +- .../components/CallViewContextMenu.qml | 345 ++++++------------ .../ConversationSmartListContextMenu.qml | 217 ++++------- .../components/ConversationSmartListView.qml | 4 + .../ConversationSmartListViewItemDelegate.qml | 7 +- .../components/ParticipantContextMenu.qml | 126 ++----- .../components/ParticipantOverlay.qml | 14 +- .../VideoCallPageContextMenuDeviceItem.qml | 9 +- .../js/videodevicecontextmenuitemcreation.js | 21 +- src/messagesadapter.cpp | 18 +- src/messagesadapter.h | 6 +- .../components/BannedItemDelegate.qml | 2 +- 19 files changed, 421 insertions(+), 528 deletions(-) delete mode 100644 images/icons/ic_person_add_black_24dp_2x.png delete mode 100644 images/icons/ic_person_add_white_24dp.png create mode 100644 src/commoncomponents/BaseContextMenu.qml create mode 100644 src/commoncomponents/js/contextmenugenerator.js diff --git a/images/icons/ic_person_add_black_24dp_2x.png b/images/icons/ic_person_add_black_24dp_2x.png deleted file mode 100644 index f3e8931b62c359a28c6cdd1151f3c1b49c5d9889..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 323 zcmV-J0lfZ+P)<h;3K|Lk000e1NJLTq001xm001xu1ONa4{R=S+0003BNkl<ZcmeH_ zu}T9$5I|q5oFAdBSjd@w5bVW9Kgrl7;jYLZ2$tGO5Jch!B)R(u4@F4|Z@j`rxy-G* z(+K-sKW}#!B9VwE19D0>jRhGgrtC09epr)}*o}WcTAW!WrpOjEo88M+Yf^&A5yc5k z$hU2jYG4^2*r<W^KL`|RU=be3)W9e_kWwpWdZ-Q6bve{ASOgMgNQ?LMZ32*x|5{Yd zm_Aqq08%C_SvM{iX}i~AHrNMZV2=x~xMM}dXJf^RTZWvo8yXleq(l@8E;+)39%now zj?OuyhdUhbg#6J*_HpEdH^jqNj@@4Wfq44v@{1cJz-v0Gf&wpeB4IiPVjvQUd;vR7 VdYQP=*mM8@002ovPDHLkV1flSe@p-X diff --git a/images/icons/ic_person_add_white_24dp.png b/images/icons/ic_person_add_white_24dp.png deleted file mode 100644 index 7e7c289d4971337ec3693780d13b26c146c58a5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 329 zcmV-P0k-~$P)<h;3K|Lk000e1NJLTq001xm001xu1ONa4{R=S+0003HNkl<ZcmeIw zPfEi;97pkVWqSzSmM$db4uX4eqerst+LA1K1Hq;4qzEE<fVRmc42se&oM$#N2$?_0 zFBA&%zWe#W%tWDfg+e4`6l|;Iq{Kw#MKs-jIsa2+^t%EEJW<y|6Ea|qsk93q!JHzN z0~xkve!yRKK!L559N1!Ovj;$qt)(1Du{DtcF$z<PV>95&=c7)Ex&ZntsOymfDWHiA zg$@)`eY^zFi<z-vQ*Fsa-@W*e-3!1DfFrKB;ej<JKh;Xs+%x8qLn)9jrodLtH76Fp zxE>e0;oB?c^qK%8UTE4gLto&GPuldssShyXo3?#1bOU!tx^)93lHT2bFewlK`St_| bKmdLLcf8r0$|reu00000NkvXXu0mjflV6Dp diff --git a/qml.qrc b/qml.qrc index d9e8085f7..619d53da6 100644 --- a/qml.qrc +++ b/qml.qrc @@ -103,5 +103,7 @@ <file>src/mainview/components/RecordBox.qml</file> <file>src/commoncomponents/ElidedTextLabel.qml</file> <file>src/mainview/components/SipInputPanel.qml</file> + <file>src/commoncomponents/js/contextmenugenerator.js</file> + <file>src/commoncomponents/BaseContextMenu.qml</file> </qresource> </RCC> diff --git a/ressources.qrc b/ressources.qrc index 357fe0bf7..14eb8f575 100644 --- a/ressources.qrc +++ b/ressources.qrc @@ -23,6 +23,8 @@ <file>images/icons/ic_add_black_18dp_2x.png</file> <file>images/icons/info-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/ic_arrow_back_24px.svg</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/play_circle_outline-24px.svg</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_photo_camera_white_24dp_2x.png</file> <file>images/icons/ic_baseline-search-24px.svg</file> diff --git a/src/commoncomponents/BaseContextMenu.qml b/src/commoncomponents/BaseContextMenu.qml new file mode 100644 index 000000000..05227a5ae --- /dev/null +++ b/src/commoncomponents/BaseContextMenu.qml @@ -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 + } +} diff --git a/src/commoncomponents/GeneralMenuItem.qml b/src/commoncomponents/GeneralMenuItem.qml index a457f8d88..199f59d2f 100644 --- a/src/commoncomponents/GeneralMenuItem.qml +++ b/src/commoncomponents/GeneralMenuItem.qml @@ -35,8 +35,7 @@ MenuItem { property string iconSource: "" property int preferredWidth: 220 property int preferredHeight: 48 - property int topBorderWidth: 0 - property int bottomBorderWidth: 0 + property int leftBorderWidth: 0 property int rightBorderWidth: 0 @@ -99,8 +98,6 @@ MenuItem { id: contextMenuBackgroundRect anchors.fill: parent - anchors.topMargin: topBorderWidth - anchors.bottomMargin: bottomBorderWidth anchors.leftMargin: leftBorderWidth anchors.rightMargin: rightBorderWidth @@ -132,8 +129,8 @@ MenuItem { commonBorder: false lBorderwidth: leftBorderWidth rBorderwidth: rightBorderWidth - tBorderwidth: topBorderWidth - bBorderwidth: bottomBorderWidth + tBorderwidth: 0 + bBorderwidth: 0 borderColor: JamiTheme.tabbarBorderColor } } diff --git a/src/commoncomponents/js/contextmenugenerator.js b/src/commoncomponents/js/contextmenugenerator.js new file mode 100644 index 000000000..7c55365a1 --- /dev/null +++ b/src/commoncomponents/js/contextmenugenerator.js @@ -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 +} diff --git a/src/mainview/components/CallOverlayButtonGroup.qml b/src/mainview/components/CallOverlayButtonGroup.qml index 107ab9e25..88a097fac 100644 --- a/src/mainview/components/CallOverlayButtonGroup.qml +++ b/src/mainview/components/CallOverlayButtonGroup.qml @@ -230,7 +230,7 @@ Rectangle { onClicked: { var rectPos = mapToItem(callStackViewWindow, optionsButton.x, optionsButton.y) - callViewContextMenu.activate() + callViewContextMenu.openMenu() callViewContextMenu.x = rectPos.x + optionsButton.width/2 - callViewContextMenu.width/2 callViewContextMenu.y = rectPos.y - 12 - callViewContextMenu.height } diff --git a/src/mainview/components/CallViewContextMenu.qml b/src/mainview/components/CallViewContextMenu.qml index 2deeeaac2..5b2272a7a 100644 --- a/src/mainview/components/CallViewContextMenu.qml +++ b/src/mainview/components/CallViewContextMenu.qml @@ -17,6 +17,7 @@ * 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 @@ -24,61 +25,125 @@ import net.jami.Models 1.0 import "../../commoncomponents" +import "../../commoncomponents/js/contextmenugenerator.js" as ContextMenuGenerator import "../js/videodevicecontextmenuitemcreation.js" as VideoDeviceContextMenuItemCreation import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation +import "../js/screenrubberbandcreation.js" as ScreenRubberBandCreation -Menu { +Item { id: root - property int generalMenuSeparatorCount: 0 - property int commonBorderWidth: 1 - - signal pluginItemClicked - - font.pointSize: JamiTheme.textFontSize+3 - property bool isSIP: false property bool isPaused: false property bool isAudioOnly: false property bool isRecording: false + signal pluginItemClicked 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() + /* * Somehow, the map size is undefined, so use this instead. */ var mapSize = deviceContextMenuInfoMap["size"] - var count = 2 + if (mapSize === 0) + VideoDeviceContextMenuItemCreation.createVideoDeviceContextMenuItemObjects( + qsTr("No video device"), false) + for (var deviceName in deviceContextMenuInfoMap) { - if (deviceName === "size" || root.isAudioOnly) + if (deviceName === "size") 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( - deviceName, deviceContextMenuInfoMap[deviceName], - count === mapSize) - } - count++ + VideoDeviceContextMenuItemCreation.createVideoDeviceContextMenuItemObjects( + deviceName, deviceContextMenuInfoMap[deviceName]) } - root.open() - } - - Component.onCompleted: { - VideoDeviceContextMenuItemCreation.setVideoContextMenuObject(root) - } - - - onClosed: { - videoDeviceItem.itemName = "No video device" - VideoDeviceContextMenuItemCreation.removeCreatedItems() } 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: { - generalMenuSeparatorCount++ - } - } - - GeneralMenuItem { - id: startRecordingItem - - 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 + Component.onCompleted: { + ContextMenuGenerator.createBaseContextMenuObjects(root) + VideoDeviceContextMenuItemCreation.setVideoContextMenuObject(ContextMenuGenerator.getMenu()) - onClicked: { - root.close() - jamiFileDialog.open() - } + ContextMenuGenerator.getMenu().closed.connect(function (){ + VideoDeviceContextMenuItemCreation.removeCreatedItems() + }) } /* TODO: In the future we want to implement this @@ -289,31 +180,5 @@ Menu { 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 - } } diff --git a/src/mainview/components/ConversationSmartListContextMenu.qml b/src/mainview/components/ConversationSmartListContextMenu.qml index 26c5406f2..c5aa47138 100644 --- a/src/mainview/components/ConversationSmartListContextMenu.qml +++ b/src/mainview/components/ConversationSmartListContextMenu.qml @@ -1,4 +1,3 @@ - /* * Copyright (C) 2020 by Savoir-faire Linux * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> @@ -16,6 +15,7 @@ * 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 @@ -23,157 +23,88 @@ import net.jami.Models 1.0 import "../../commoncomponents" -Menu { - id: contextMenu +import "../../commoncomponents/js/contextmenugenerator.js" as ContextMenuGenerator + +Item { + id: root + property string responsibleAccountId: "" property string responsibleConvUid: "" - - property int generalMenuSeparatorCount: 0 - property int commonBorderWidth: 1 - font.pointSize: JamiTheme.menuFontSize + property int contactType: Profile.Type.INVALID function openMenu(){ - visible = true - visible = false - visible = true - } - - 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) - CallAdapter.placeCall() - } - } - - GeneralMenuItem { - id: startAudioCallItem - - 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) - CallAdapter.placeAudioOnlyCall() - } - } - - GeneralMenuItem { - id: clearConversationItem - - itemName: qsTr("Clear conversation") - iconSource: "qrc:/images/icons/ic_clear_24px.svg" - leftBorderWidth: commonBorderWidth - rightBorderWidth: commonBorderWidth - - onClicked: { - contextMenu.close() - ClientWrapper.utilsAdaptor.clearConversationHistory(responsibleAccountId, - responsibleConvUid) - } - } - - GeneralMenuItem { - id: removeContactItem - - itemName: qsTr("Remove contact") - iconSource: "qrc:/images/icons/round-remove_circle-24px.svg" - leftBorderWidth: commonBorderWidth - rightBorderWidth: commonBorderWidth - - onClicked: { - contextMenu.close() - ClientWrapper.utilsAdaptor.removeConversation(responsibleAccountId, - responsibleConvUid) + ContextMenuGenerator.addMenuItem(qsTr("Start video call"), + "qrc:/images/icons/ic_video_call_24px.svg", + function (){ + ConversationsAdapter.selectConversation( + responsibleAccountId, + responsibleConvUid, false) + CallAdapter.placeCall() + }) + ContextMenuGenerator.addMenuItem(qsTr("Start audio call"), + "qrc:/images/icons/ic_phone_24px.svg", + function (){ + ConversationsAdapter.selectConversation( + responsibleAccountId, + responsibleConvUid, false) + CallAdapter.placeAudioOnlyCall() + }) + ContextMenuGenerator.addMenuItem(qsTr("Clear conversation"), + "qrc:/images/icons/ic_clear_24px.svg", + function (){ + ClientWrapper.utilsAdaptor.clearConversationHistory( + responsibleAccountId, + responsibleConvUid) + }) + + if (contactType === Profile.Type.RING || contactType === Profile.Type.SIP) { + ContextMenuGenerator.addMenuItem(qsTr("Remove contact"), + "qrc:/images/icons/round-remove_circle-24px.svg", + function (){ + ClientWrapper.utilsAdaptor.removeConversation( + responsibleAccountId, + responsibleConvUid) + }) } - } - - GeneralMenuSeparator { - preferredWidth: startVideoCallItem.preferredWidth - preferredHeight: commonBorderWidth - Component.onCompleted: { - generalMenuSeparatorCount++ + if (contactType === Profile.Type.RING || contactType === Profile.Type.PENDING) { + ContextMenuGenerator.addMenuSeparator() + + if (contactType === Profile.Type.PENDING) { + 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) + }) + + ContextMenuGenerator.addMenuSeparator() + ContextMenuGenerator.addMenuItem(qsTr("Profile"), + "qrc:/images/icons/person-24px.svg", + function (){ + userProfile.open() + }) } - } - - GeneralMenuItem { - id: blockContactItem - - itemName: qsTr("Block contact") - iconSource: "qrc:/images/icons/ic_block_24px.svg" - leftBorderWidth: commonBorderWidth - rightBorderWidth: commonBorderWidth - onClicked: { - contextMenu.close() - ClientWrapper.utilsAdaptor.removeConversation(responsibleAccountId, - responsibleConvUid, true) - } + root.height = ContextMenuGenerator.getMenu().height + root.width = ContextMenuGenerator.getMenu().width + ContextMenuGenerator.getMenu().open() } - 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() - } - } - - GeneralMenuSeparator { - preferredWidth: startVideoCallItem.preferredWidth - preferredHeight: 8 - separatorColor: "transparent" - Component.onCompleted: { - generalMenuSeparatorCount++ - } - } - - background: Rectangle { - implicitWidth: startVideoCallItem.preferredWidth - implicitHeight: startVideoCallItem.preferredHeight - * (contextMenu.count - generalMenuSeparatorCount) - - border.width: commonBorderWidth - border.color: JamiTheme.tabbarBorderColor + Component.onCompleted: { + ContextMenuGenerator.createBaseContextMenuObjects(root) } } diff --git a/src/mainview/components/ConversationSmartListView.qml b/src/mainview/components/ConversationSmartListView.qml index ee787f780..d16b96d81 100644 --- a/src/mainview/components/ConversationSmartListView.qml +++ b/src/mainview/components/ConversationSmartListView.qml @@ -82,6 +82,10 @@ ListView { conversationSmartListView.model.setAccount(accountId) } + ConversationSmartListContextMenu { + id: smartListContextMenu + } + Connections { target: CallAdapter diff --git a/src/mainview/components/ConversationSmartListViewItemDelegate.qml b/src/mainview/components/ConversationSmartListViewItemDelegate.qml index 7b564a855..74d07dbc8 100644 --- a/src/mainview/components/ConversationSmartListViewItemDelegate.qml +++ b/src/mainview/components/ConversationSmartListViewItemDelegate.qml @@ -180,7 +180,7 @@ ItemDelegate { itemSmartListBackground.color = JamiTheme.releaseColor } if (mouse.button === Qt.RightButton) { - + smartListContextMenu.parent = mouseAreaSmartListItemDelegate /* * Make menu pos at mouse. @@ -191,6 +191,7 @@ ItemDelegate { smartListContextMenu.y = relativeMousePos.y smartListContextMenu.responsibleAccountId = ClientWrapper.utilsAdaptor.getCurrAccId() smartListContextMenu.responsibleConvUid = UID + smartListContextMenu.contactType = ContactType userProfile.responsibleConvUid = UID userProfile.aliasText = DisplayName userProfile.registeredNameText = DisplayID @@ -225,8 +226,4 @@ ItemDelegate { } } } - - ConversationSmartListContextMenu { - id: smartListContextMenu - } } diff --git a/src/mainview/components/ParticipantContextMenu.qml b/src/mainview/components/ParticipantContextMenu.qml index 9397701cf..87d883a5d 100644 --- a/src/mainview/components/ParticipantContextMenu.qml +++ b/src/mainview/components/ParticipantContextMenu.qml @@ -1,7 +1,7 @@ - /* * Copyright (C) 2020 by Savoir-faire Linux * 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 * 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 * 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 @@ -23,105 +24,46 @@ import net.jami.Models 1.0 import "../../commoncomponents" -import "../js/videodevicecontextmenuitemcreation.js" as VideoDeviceContextMenuItemCreation -import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation +import "../../commoncomponents/js/contextmenugenerator.js" as ContextMenuGenerator -Menu { +Item { id: root - property int generalMenuSeparatorCount: 0 - property int commonBorderWidth: 1 - font.pointSize: JamiTheme.textFontSize + 3 property var uri: "" property var maximized: true property var active: true - - function showHangup(show) { - if (show) { - hangupItem.visible = true - hangupItem.height = hangupItem.preferredHeight - } else { - 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) - root.close() - } - } - GeneralMenuItem { - id: maximizeItem - - itemName: qsTr("Maximize participant") - iconSource: "qrc:/images/icons/open_in_full-24px.svg" - leftBorderWidth: commonBorderWidth - rightBorderWidth: commonBorderWidth - visible: !maximized - - onClicked: { - CallAdapter.maximizeParticipant(uri, active) - root.close() - } + property var showHangup: false + property var showMaximize: false + property var showMinimize: false + + function openMenu(){ + if (showHangup) + ContextMenuGenerator.addMenuItem(qsTr("Hang up"), + "qrc:/images/icons/ic_call_end_white_24px.svg", + function (){ + CallAdapter.hangupCall(uri) + }) + + if (showMaximize) + ContextMenuGenerator.addMenuItem(qsTr("Maximize participant"), + "qrc:/images/icons/open_in_full-24px.svg", + function (){ + CallAdapter.maximizeParticipant(uri, active) + }) + if (showMinimize) + ContextMenuGenerator.addMenuItem(qsTr("Minimize participant"), + "qrc:/images/icons/close_fullscreen-24px.svg", + function (){ + CallAdapter.minimizeParticipant() + }) + + root.height = ContextMenuGenerator.getMenu().height + root.width = ContextMenuGenerator.getMenu().width + ContextMenuGenerator.getMenu().open() } - GeneralMenuItem { - id: minimizeItem - - itemName: qsTr("Minimize participant") - iconSource: "qrc:/images/icons/close_fullscreen-24px.svg" - leftBorderWidth: commonBorderWidth - rightBorderWidth: commonBorderWidth - visible: maximized - - onClicked: { - CallAdapter.minimizeParticipant() - root.close() - } - } - - background: Rectangle { - implicitWidth: hangupItem.preferredWidth - implicitHeight: hangupItem.preferredHeight * 3 - border.width: commonBorderWidth - border.color: JamiTheme.tabbarBorderColor + Component.onCompleted: { + ContextMenuGenerator.createBaseContextMenuObjects(root) } } diff --git a/src/mainview/components/ParticipantOverlay.qml b/src/mainview/components/ParticipantOverlay.qml index fb49a4687..2f17b0880 100644 --- a/src/mainview/components/ParticipantOverlay.qml +++ b/src/mainview/components/ParticipantOverlay.qml @@ -112,18 +112,14 @@ Rectangle { var layout = CallAdapter.getCurrentLayoutType() var showMaximized = layout !== 2 var showMinimized = !(layout === 0 || (layout === 1 && !active)) - injectedContextMenu.showHangup(!root.isLocal) - injectedContextMenu.showMaximize(showMaximized) - injectedContextMenu.showMinimize(showMinimized) - injectedContextMenu.setHeight( - (root.isLocal ? 0 : 1) - + (showMaximized ? 1 : 0) - + (showMinimized ? 1 : 0)) + injectedContextMenu.showHangup = !root.isLocal + injectedContextMenu.showMaximize = showMaximized + injectedContextMenu.showMinimize = showMinimized injectedContextMenu.uri = uri injectedContextMenu.active = active injectedContextMenu.x = mousePos.x injectedContextMenu.y = mousePos.y - injectedContextMenu.height - injectedContextMenu.open() + injectedContextMenu.openMenu() } } } @@ -166,4 +162,4 @@ Rectangle { duration: 500 } } -} \ No newline at end of file +} diff --git a/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml b/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml index 73e692b7d..a6a4f8043 100644 --- a/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml +++ b/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml @@ -1,4 +1,3 @@ - /* * Copyright (C) 2020 by Savoir-faire Linux * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> @@ -16,24 +15,21 @@ * 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 net.jami.Models 1.0 import "../../commoncomponents" - /* - * Take advantage of child can access parent's item (ex: contextMenu, commonBorderWidth). + * Menu item wrapper for video device checkable item. */ GeneralMenuItem { id: videoCallPageContextMenuDeviceItem property int contextMenuPreferredWidth: 250 - leftBorderWidth: commonBorderWidth - rightBorderWidth: commonBorderWidth - TextMetrics { id: textMetrics elide: Text.ElideMiddle @@ -54,7 +50,6 @@ GeneralMenuItem { onClicked: { var deviceName = videoCallPageContextMenuDeviceItem.itemName - contextMenu.close() AvAdapter.onVideoContextMenuDeviceItemClicked(deviceName) } } diff --git a/src/mainview/js/videodevicecontextmenuitemcreation.js b/src/mainview/js/videodevicecontextmenuitemcreation.js index 02aa29253..ca17fc7cf 100644 --- a/src/mainview/js/videodevicecontextmenuitemcreation.js +++ b/src/mainview/js/videodevicecontextmenuitemcreation.js @@ -40,24 +40,28 @@ function setVideoContextMenuObject(obj) { videoContextMenuObject = obj } -function createVideoDeviceContextMenuItemObjects(deviceName, setChecked, last) { +function createVideoDeviceContextMenuItemObjects(deviceName, setChecked) { videoDeviceContextMenuItemComponent = Qt.createComponent( "../components/VideoCallPageContextMenuDeviceItem.qml") if (videoDeviceContextMenuItemComponent.status === Component.Ready) - finishCreation(deviceName, setChecked, last) + finishCreation(deviceName, setChecked) else if (videoDeviceContextMenuItemComponent.status === Component.Error) console.log("Error loading component:", videoDeviceContextMenuItemComponent.errorString()) } -function finishCreation(deviceName, setChecked, last) { +function finishCreation(deviceName, setChecked) { videoDeviceContextMenuItemObject = videoDeviceContextMenuItemComponent.createObject() if (videoDeviceContextMenuItemObject === null) { // Error Handling. console.log("Error creating video context menu object") } + videoDeviceContextMenuItemObject.leftBorderWidth = + videoContextMenuObject.commonBorderWidth + videoDeviceContextMenuItemObject.rightBorderWidth = + videoContextMenuObject.commonBorderWidth videoDeviceContextMenuItemObject.itemName = deviceName videoDeviceContextMenuItemObject.checkable = true videoDeviceContextMenuItemObject.checked = setChecked @@ -68,14 +72,11 @@ function finishCreation(deviceName, setChecked, last) { * Push into the storage array, and insert it into context menu. */ itemArray.push(videoDeviceContextMenuItemObject) - videoContextMenuObject.insertItem(3 /* The button is at pos 3 in the menu */, videoDeviceContextMenuItemObject) + videoContextMenuObject.addItem(videoDeviceContextMenuItemObject) - - /* - * If it is the last device context menu item, open the context menu. - */ - if (last) - videoContextMenuObject.open() + videoDeviceContextMenuItemObject.clicked.connect(function () { + videoContextMenuObject.close() + }) } function removeCreatedItems() { diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp index 5214d0bd4..37b477f9e 100644 --- a/src/messagesadapter.cpp +++ b/src/messagesadapter.cpp @@ -633,24 +633,24 @@ MessagesAdapter::contactIsComposing(const QString &uid, const QString &contactUr } void -MessagesAdapter::acceptInvitation() +MessagesAdapter::acceptInvitation(const QString &convUid) { - const auto convUid = LRCInstance::getCurrentConvUid(); - LRCInstance::getCurrentConversationModel()->makePermanent(convUid); + const auto currentConvUid = convUid.isEmpty() ? LRCInstance::getCurrentConvUid() : convUid; + LRCInstance::getCurrentConversationModel()->makePermanent(currentConvUid); } void -MessagesAdapter::refuseInvitation() +MessagesAdapter::refuseInvitation(const QString &convUid) { - auto convUid = LRCInstance::getCurrentConvUid(); - LRCInstance::getCurrentConversationModel()->removeConversation(convUid, false); + const auto currentConvUid = convUid.isEmpty() ? LRCInstance::getCurrentConvUid() : convUid; + LRCInstance::getCurrentConversationModel()->removeConversation(currentConvUid, false); setInvitation(false); } void -MessagesAdapter::blockConversation() +MessagesAdapter::blockConversation(const QString &convUid) { - auto convUid = LRCInstance::getCurrentConvUid(); - LRCInstance::getCurrentConversationModel()->removeConversation(convUid, true); + const auto currentConvUid = convUid.isEmpty() ? LRCInstance::getCurrentConvUid() : convUid; + LRCInstance::getCurrentConversationModel()->removeConversation(currentConvUid, true); setInvitation(false); } diff --git a/src/messagesadapter.h b/src/messagesadapter.h index 6b520d2bd..aaf11da72 100644 --- a/src/messagesadapter.h +++ b/src/messagesadapter.h @@ -41,9 +41,9 @@ public: /* * JS Q_INVOKABLE. */ - Q_INVOKABLE void acceptInvitation(); - Q_INVOKABLE void refuseInvitation(); - Q_INVOKABLE void blockConversation(); + Q_INVOKABLE void acceptInvitation(const QString &convUid = ""); + Q_INVOKABLE void refuseInvitation(const QString &convUid = ""); + Q_INVOKABLE void blockConversation(const QString &convUid = ""); Q_INVOKABLE void setNewMessagesContent(const QString &path); Q_INVOKABLE void sendMessage(const QString &message); Q_INVOKABLE void sendImage(const QString &message); diff --git a/src/settingsview/components/BannedItemDelegate.qml b/src/settingsview/components/BannedItemDelegate.qml index 0322bb01f..999b49446 100644 --- a/src/settingsview/components/BannedItemDelegate.qml +++ b/src/settingsview/components/BannedItemDelegate.qml @@ -173,7 +173,7 @@ ItemDelegate { buttonImageHeight: height - 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 width: 25 -- GitLab