From 0d6d94d1240a91efd9a882412abc5c385714d68e Mon Sep 17 00:00:00 2001 From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> Date: Wed, 28 Apr 2021 18:44:30 -0400 Subject: [PATCH] accountcombobox: cleanup and simplify qml layer - remove some faulty hover state management, excessive text metrics objects. Split the code up too and increase declarativeness. - fix the rendering of account avatars in the dropdown list - make the 'add account' item always visible - make the popup modal with transparent overlay Gitlab: #388 Change-Id: If0e5f95d1250ee77902c518469878bdb678c8b9d --- qml.qrc | 1 + src/accountadapter.cpp | 6 +- src/accountadapter.h | 2 +- src/commoncomponents/AvatarImage.qml | 4 +- src/constant/JamiTheme.qml | 2 + src/mainview/MainView.qml | 8 +- src/mainview/components/AccountComboBox.qml | 328 ++++++++---------- .../components/AccountComboBoxPopup.qml | 173 +++------ .../components/AccountItemDelegate.qml | 95 +++++ src/mainview/components/VideoCallPage.qml | 2 +- src/wizardview/WizardView.qml | 2 +- 11 files changed, 291 insertions(+), 332 deletions(-) create mode 100644 src/mainview/components/AccountItemDelegate.qml diff --git a/qml.qrc b/qml.qrc index 8f55670bb..b2782a70a 100644 --- a/qml.qrc +++ b/qml.qrc @@ -137,5 +137,6 @@ <file>src/mainview/js/contactpickercreation.js</file> <file>src/mainview/js/pluginhandlerpickercreation.js</file> <file>src/mainview/components/FilterTabButton.qml</file> + <file>src/mainview/components/AccountItemDelegate.qml</file> </qresource> </RCC> diff --git a/src/accountadapter.cpp b/src/accountadapter.cpp index d05f58e1f..eefb8c6bc 100644 --- a/src/accountadapter.cpp +++ b/src/accountadapter.cpp @@ -63,12 +63,12 @@ AccountAdapter::getDeviceModel() } void -AccountAdapter::accountChanged(int index) +AccountAdapter::changeAccount(int row) { deselectConversation(); // Hack UI auto accountList = lrcInstance_->accountModel().getAccountList(); - if (accountList.size() > index) { - lrcInstance_->setSelectedAccountId(accountList.at(index)); + if (accountList.size() > row) { + lrcInstance_->setSelectedAccountId(accountList.at(row)); } } diff --git a/src/accountadapter.h b/src/accountadapter.h index 72042f878..5e11d0509 100644 --- a/src/accountadapter.h +++ b/src/accountadapter.h @@ -62,7 +62,7 @@ protected: public: // Change to account corresponding to combox box index. - Q_INVOKABLE void accountChanged(int index); + Q_INVOKABLE void changeAccount(int row); // Create normal Jami account, SIP account and JAMS accounts. Q_INVOKABLE void createJamiAccount(QString registeredName, diff --git a/src/commoncomponents/AvatarImage.qml b/src/commoncomponents/AvatarImage.qml index f9dcf353b..06a0edd26 100644 --- a/src/commoncomponents/AvatarImage.qml +++ b/src/commoncomponents/AvatarImage.qml @@ -114,7 +114,7 @@ Item { anchors.fill: root - smooth: false + smooth: true antialiasing: true asynchronous: true @@ -146,7 +146,7 @@ Item { anchors.fill: rootImage - smooth: false + smooth: true antialiasing: true asynchronous: true diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml index 4a9c6aaae..d7b5db5f9 100644 --- a/src/constant/JamiTheme.qml +++ b/src/constant/JamiTheme.qml @@ -176,6 +176,8 @@ Item { property real smartlistItemFontSize: 10.5 property real filterItemFontSize: smartlistItemFontSize property real filterBadgeFontSize: 8.25 + property real accountListItemHeight: 64 + property real accountListAvatarSize: 40 property real maximumWidthSettingsView: 600 property real settingsHeaderpreferredHeight: 64 diff --git a/src/mainview/MainView.qml b/src/mainview/MainView.qml index 635b099d5..b1669e49c 100644 --- a/src/mainview/MainView.qml +++ b/src/mainview/MainView.qml @@ -278,18 +278,16 @@ Rectangle { SplitView.fillHeight: true color: JamiTheme.backgroundColor - // AccountComboBox is always visible + // AccountComboBox is not a ComboBox AccountComboBox { id: accountComboBox anchors.top: mainViewSidePanelRect.top width: mainViewSidePanelRect.width - height: 64 + height: JamiTheme.accountListItemHeight visible: (mainViewSidePanel.visible || settingsMenu.visible) - currentIndex: 0 - Connections { target: AccountAdapter @@ -416,7 +414,7 @@ Rectangle { visible: false onSettingsViewNeedToShowMainView: { - AccountAdapter.accountChanged(0) + AccountAdapter.changeAccount(0) toggleSettingsView() } diff --git a/src/mainview/components/AccountComboBox.qml b/src/mainview/components/AccountComboBox.qml index 84c799f48..80df7b93f 100644 --- a/src/mainview/components/AccountComboBox.qml +++ b/src/mainview/components/AccountComboBox.qml @@ -28,10 +28,11 @@ import net.jami.Constants 1.0 import "../../commoncomponents" -ComboBox { +Label { id: root signal settingBtnClicked + property alias popup: comboBoxPopup // Reset accountListModel. function resetAccountListModel(accountId) { @@ -39,80 +40,140 @@ ComboBox { accountListModel.reset() } - Connections { - target: accountListModel + function togglePopup() { + if (root.popup.opened) { + root.popup.close() + } else { + root.popup.open() + } + } - function onModelReset() { - userImageRoot.updateImage( - AccountAdapter.currentAccountId, - accountListModel.data( - accountListModel.index(0, 0), AccountListModel.PictureUid)) - userImageRoot.presenceStatus = - accountListModel.data(accountListModel.index(0, 0), AccountListModel.Status) - textMetricsUserAliasRoot.text = accountListModel.data(accountListModel.index(0,0), - AccountListModel.Alias) - textMetricsUsernameRoot.text = accountListModel.data(accountListModel.index(0,0), - AccountListModel.Username) + background: Rectangle { + id: background + + implicitWidth: root.width + implicitHeight: root.height + color: root.popup.opened ? + Qt.lighter(JamiTheme.hoverColor, 1.0) : + mouseArea.containsMouse ? + Qt.lighter(JamiTheme.hoverColor, 1.05) : + JamiTheme.backgroundColor + Behavior on color { + ColorAnimation { duration: JamiTheme.fadeDuration } + } + + // TODO: this can be removed when frameless window is implemented + Rectangle { + height: 1 + anchors { + top: parent.top + left: parent.left + right: parent.right + } + color: JamiTheme.tabbarBorderColor } } - AvatarImage { - id: userImageRoot + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: togglePopup() + } - anchors.left: root.left - anchors.leftMargin: 16 - anchors.verticalCenter: root.verticalCenter + AccountComboBoxPopup { + id: comboBoxPopup - width: 40 - height: 40 + Shortcut { + sequence: "Ctrl+J" + context: Qt.ApplicationShortcut + onActivated: togglePopup() + } + } - imageId: AccountAdapter.currentAccountId + Connections { + target: accountListModel - presenceStatus: accountListModel.data(accountListModel.index(0, 0), - AccountListModel.Status) + function onModelReset() { + avatar.updateImage(AccountAdapter.currentAccountId, + accountListModel.data(accountListModel.index(0, 0), + AccountListModel.PictureUid)) + avatar.presenceStatus = accountListModel.data(accountListModel.index(0, 0), + AccountListModel.Status) + userAliasText.text = accountListModel.data(accountListModel.index(0,0), + AccountListModel.Alias) + usernameText.text = accountListModel.data(accountListModel.index(0,0), + AccountListModel.Username) + } } - ColumnLayout { - anchors.left: userImageRoot.right - anchors.leftMargin: 16 - anchors.top: background.top + RowLayout { + anchors.fill: parent + anchors.leftMargin: 15 + anchors.rightMargin: 15 + spacing: 10 + + AvatarImage { + id: avatar - height: root.height + Layout.preferredWidth: JamiTheme.accountListAvatarSize + Layout.preferredHeight: JamiTheme.accountListAvatarSize + Layout.alignment: Qt.AlignVCenter - spacing: 0 + imageId: AccountAdapter.currentAccountId + + presenceStatus: accountListModel.data(accountListModel.index(0, 0), + AccountListModel.Status) + } - RowLayout { - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.topMargin: textUsernameRoot.visible ? root.height / 2 - implicitHeight : 0 + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 2 Text { - id: textUserAliasRoot + id: userAliasText - Layout.alignment: Qt.AlignLeft + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: textMetricsUserAliasRoot.elidedText + text: accountListModel.data(accountListModel.index(0,0), + AccountListModel.Alias) font.pointSize: JamiTheme.textFontSize color: JamiTheme.textColor + elide: Text.ElideRight + } - TextMetrics { - id: textMetricsUserAliasRoot + Text { + id: usernameText - font: textUserAliasRoot.font - elide: Text.ElideRight - elideWidth: root.width - userImageRoot.width - settingsButton.width - - arrowDropDown.width - qrCodeGenerateButton.width - 55 + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: accountListModel.data(accountListModel.index(0,0), AccountListModel.Alias) - } + visible: text.length + + text: accountListModel.data(accountListModel.index(0,0), + AccountListModel.Username) + font.pointSize: JamiTheme.textFontSize + color: JamiTheme.faddedLastInteractionFontColor + elide: Text.ElideRight } + } + + Row { + id: controlRow + + spacing: 10 + + Layout.preferredWidth: childrenRect.width + Layout.preferredHeight: parent.height ResponsiveImage { id: arrowDropDown - Layout.alignment: Qt.AlignRight - width: 24 height: 24 + anchors.verticalCenter: parent.verticalCenter layer { enabled: true @@ -121,168 +182,55 @@ ComboBox { } } - source: "qrc:/images/icons/round-arrow_drop_down-24px.svg" + source: !root.popup.opened ? + "qrc:/images/icons/expand_more-24px.svg" : + "qrc:/images/icons/expand_less-24px.svg" } - } - Text { - id: textUsernameRoot - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.bottomMargin: root.height / 2 - implicitHeight + PushButton { + id: shareButton - visible: textMetricsUsernameRoot.text.length + width: visible ? preferredSize : 0 + height: visible ? preferredSize : 0 + anchors.verticalCenter: parent.verticalCenter - text: textMetricsUsernameRoot.elidedText - font.pointSize: JamiTheme.textFontSize - color: JamiTheme.faddedLastInteractionFontColor + visible: AccountAdapter.currentAccountType === Profile.Type.RING + toolTipText: JamiStrings.displayQRCode - TextMetrics { - id: textMetricsUsernameRoot + source: "qrc:/images/icons/share-24px.svg" - font: textUsernameRoot.font - elide: Text.ElideRight - elideWidth: root.width - userImageRoot.width - settingsButton.width - - qrCodeGenerateButton.width - 55 + normalColor: JamiTheme.backgroundColor + imageColor: JamiTheme.textColor - text: accountListModel.data(accountListModel.index(0,0), - AccountListModel.Username) - } - } - } - - background: Rectangle { - id: background - - implicitWidth: root.width - implicitHeight: root.height - color: JamiTheme.backgroundColor - - // TODO: this can be removed when frameless window is implemented - Rectangle { - height: 1 - anchors { - top: parent.top - left: parent.left - right: parent.right - } - color: JamiTheme.tabbarBorderColor - } - - states: [ - State { - name: "open"; when: comboBoxPopup.opened - PropertyChanges { - target: background - color: Qt.lighter(JamiTheme.hoverColor, 1.0) - } - }, - State { - name: "hovered" - PropertyChanges { - target: background - color: Qt.lighter(JamiTheme.hoverColor, 1.05) + onClicked: { + if (visible) + qrDialog.open() } - }, - State { - name: "normal" - PropertyChanges { - target: background - color: JamiTheme.backgroundColor - } - } - ] - - transitions: [ - Transition { - to: "hovered"; reversible: true - ColorAnimation { duration: JamiTheme.fadeDuration } - } - ] - } - - MouseArea { - id: comboBoxRootMouseArea - - anchors.fill: parent - hoverEnabled: true - - onClicked: { - if (comboBoxPopup.opened) { - root.popup.close() - } else { - root.popup.open() - } - } - onEntered: background.state = "hovered" - onExited: { - if (!comboBoxPopup.opened) - background.state = "normal" - } - } - - Row { - spacing: 10 - - anchors.right: root.right - anchors.rightMargin: 10 - anchors.verticalCenter: root.verticalCenter - - PushButton { - id: qrCodeGenerateButton - - width: visible ? preferredSize : 0 - height: visible ? preferredSize : 0 - - visible: AccountAdapter.currentAccountType === Profile.Type.RING - toolTipText: JamiStrings.displayQRCode - - source: "qrc:/images/icons/share-24px.svg" - - normalColor: JamiTheme.backgroundColor - imageColor: JamiTheme.textColor - - onClicked: { - if (visible) - qrDialog.open() } - } - PushButton { - id: settingsButton + PushButton { + id: settingsButton - source: !mainView.inSettingsView ? - "qrc:/images/icons/settings-24px.svg" : - "qrc:/images/icons/round-close-24px.svg" + anchors.verticalCenter: parent.verticalCenter + source: !mainView.inSettingsView ? + "qrc:/images/icons/settings-24px.svg" : + "qrc:/images/icons/round-close-24px.svg" - normalColor: JamiTheme.backgroundColor - imageColor: JamiTheme.textColor - toolTipText: !mainView.inSettingsView ? - JamiStrings.openSettings : - JamiStrings.closeSettings + normalColor: JamiTheme.backgroundColor + imageColor: JamiTheme.textColor + toolTipText: !mainView.inSettingsView ? + JamiStrings.openSettings : + JamiStrings.closeSettings - onClicked: { - settingBtnClicked() - background.state = "normal" + onClicked: { + settingBtnClicked() + background.state = "normal" + } } } } - indicator: null - - // Overwrite the combo box pop up to add footer (for add accounts). - popup: AccountComboBoxPopup { - id: comboBoxPopup - - Shortcut { - sequence: "Ctrl+J" - context: Qt.ApplicationShortcut - onActivated: comboBoxPopup.visible ? - comboBoxPopup.close() : - comboBoxPopup.open() - } - } - Shortcut { sequence: "Ctrl+," context: Qt.ApplicationShortcut diff --git a/src/mainview/components/AccountComboBoxPopup.qml b/src/mainview/components/AccountComboBoxPopup.qml index a100ba42b..4db441bcd 100644 --- a/src/mainview/components/AccountComboBoxPopup.qml +++ b/src/mainview/components/AccountComboBoxPopup.qml @@ -30,167 +30,82 @@ import "../../commoncomponents" Popup { id: root - property bool toggleUpdatePopupHeight: false - - y: accountComboBox.height - 1 - implicitWidth: accountComboBox.width - 1 - - // Hack - limite the accounts that can be shown. + y: parent.height + implicitWidth: parent.width + // limit the number of accounts shown at once implicitHeight: { - root.visible - return Math.min(accountComboBox.height * + return Math.min(JamiTheme.accountListItemHeight * Math.min(5, accountListModel.rowCount() + 1), mainViewSidePanelRect.height) } padding: 0 + modal: true + Overlay.modal: Rectangle { + color: "transparent" + } - contentItem: ListView { - id: comboBoxPopupListView - - // In list view, index is an interger. - clip: true - model: accountListModel - implicitHeight: contentHeight - delegate: Rectangle { - id: delegate - - width: root.width - height: accountComboBox.height - - color: JamiTheme.backgroundColor - - AvatarImage { - id: userImage - - anchors.left: parent.left - anchors.leftMargin: 10 - anchors.verticalCenter: parent.verticalCenter + contentItem: ColumnLayout { + spacing: 0 + ListView { + id: listView - width: 40 - height: 40 + Layout.fillHeight: true + Layout.preferredWidth: parent.width - presenceStatus: Status + clip: true - Component.onCompleted: { - return updateImage( - accountListModel.data( - accountListModel.index(index, 0), AccountListModel.ID), - accountListModel.data( - accountListModel.index(index, 0), AccountListModel.PictureUid)) + // TODO: this should use proxy model or custom filter out the + // current account + model: accountListModel + delegate: AccountItemDelegate { + height: JamiTheme.accountListItemHeight + width: root.width + onClicked: { + root.close() + AccountAdapter.changeAccount(index) } } - ColumnLayout { - anchors.left: userImage.right - anchors.leftMargin: 16 - anchors.top: delegate.top - - height: delegate.height - - spacing: 0 - - Text { - id: textUserAliasPopup + ScrollIndicator.vertical: ScrollIndicator {} + } - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.topMargin: textUsernamePopup.visible ? - delegate.height / 2 - implicitHeight : 0 + // fake footer item as workaround for Qt 5.15 bug + // https://bugreports.qt.io/browse/QTBUG-85302 + // don't use the clip trick and footer item overlay + // explained here https://stackoverflow.com/a/64625149 + // as it causes other complexities in handling the drop shadow + ItemDelegate { + id: footerItem - text: textMetricsUserAliasPopup.elidedText - font.pointSize: JamiTheme.textFontSize - color: JamiTheme.textColor + Layout.preferredHeight: JamiTheme.accountListItemHeight + Layout.preferredWidth: parent.width - TextMetrics { - id: textMetricsUserAliasPopup - elide: Text.ElideRight - elideWidth: delegate.width - userImage.width - 80 - text: Alias - } - } + background: Rectangle { + color: footerItem.hovered? + JamiTheme.hoverColor : + JamiTheme.backgroundColor Text { - id: textUsernamePopup - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.bottomMargin: delegate.height / 2 - implicitHeight - - visible: textMetricsUsernamePopup.text.length - - text: textMetricsUsernamePopup.elidedText + anchors.centerIn: parent + text: qsTr("Add Account") + "+" + color: JamiTheme.textColor font.pointSize: JamiTheme.textFontSize - color: JamiTheme.faddedLastInteractionFontColor - - TextMetrics { - id: textMetricsUsernamePopup - elide: Text.ElideRight - elideWidth: delegate.width - userImage.width - 80 - text: Username - } - } - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - onPressed: { - delegate.color = JamiTheme.pressColor - } - onReleased: { - delegate.color = JamiTheme.normalButtonColor - currentIndex = index - root.close() - AccountAdapter.accountChanged(index) - } - onEntered: { - delegate.color = JamiTheme.hoverColor - } - onExited: { - delegate.color = JamiTheme.backgroundColor } } - } - - footer: Button { - id: comboBoxFooterItem - - implicitWidth: accountComboBox.width - implicitHeight: accountComboBox.height - - background: Rectangle { - color: comboBoxFooterItem.hovered? JamiTheme.hoverColor : JamiTheme.backgroundColor - } - - contentItem: Text { - width: parent.width - height: parent.height - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - text: qsTr("Add Account") + "+" - color: JamiTheme.textColor - } - font.pointSize: JamiTheme.textFontSize onClicked: { root.close() mainView.startWizard() } } - - ScrollIndicator.vertical: ScrollIndicator {} } background: Rectangle { - id: accountComboBoxPopup - color: JamiTheme.backgroundColor CustomBorder { commonBorder: false - lBorderwidth: 2 - rBorderwidth: 1 - tBorderwidth: 1 - bBorderwidth: 2 + tBorderwidth: 1; lBorderwidth: 2 + bBorderwidth: 2; rBorderwidth: 1 borderColor: JamiTheme.tabbarBorderColor } diff --git a/src/mainview/components/AccountItemDelegate.qml b/src/mainview/components/AccountItemDelegate.qml new file mode 100644 index 000000000..fc4a21237 --- /dev/null +++ b/src/mainview/components/AccountItemDelegate.qml @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020-2021 by Savoir-faire Linux + * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> + * 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 <https://www.gnu.org/licenses/>. + */ + +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 + +import net.jami.Models 1.0 +import net.jami.Constants 1.0 + +import "../../commoncomponents" + +ItemDelegate { + id: root + + width: ListView.view.width + height: JamiTheme.accountListItemHeight + + background: Rectangle { + color: { + if (root.pressed) + return Qt.darker(JamiTheme.selectedColor, 1.1) + else if (root.hovered) + return Qt.darker(JamiTheme.selectedColor, 1.05) + else + return JamiTheme.backgroundColor + } + } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 15 + anchors.rightMargin: 15 + spacing: 10 + + AvatarImage { + Layout.preferredWidth: JamiTheme.accountListAvatarSize + Layout.preferredHeight: JamiTheme.accountListAvatarSize + Layout.alignment: Qt.AlignVCenter + + presenceStatus: Status + + Component.onCompleted: { + return updateImage( + accountListModel.data( + accountListModel.index(index, 0), AccountListModel.ID), + accountListModel.data( + accountListModel.index(index, 0), AccountListModel.PictureUid)) + } + } + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 2 + + Text { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + + text: Alias + font.pointSize: JamiTheme.textFontSize + color: JamiTheme.textColor + elide: Text.ElideRight + } + + Text { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + + visible: text.length + + text: Username + font.pointSize: JamiTheme.textFontSize + color: JamiTheme.faddedLastInteractionFontColor + elide: Text.ElideRight + } + } + } +} diff --git a/src/mainview/components/VideoCallPage.qml b/src/mainview/components/VideoCallPage.qml index a928fa3f2..ccd88266a 100644 --- a/src/mainview/components/VideoCallPage.qml +++ b/src/mainview/components/VideoCallPage.qml @@ -324,7 +324,7 @@ Rectangle { maskSource: Rectangle { width: previewRenderer.width height: previewRenderer.height - radius: JamiTheme.lineEditRadius + radius: JamiTheme.primaryRadius } } diff --git a/src/wizardview/WizardView.qml b/src/wizardview/WizardView.qml index c1e7f7a88..d30b1c22f 100644 --- a/src/wizardview/WizardView.qml +++ b/src/wizardview/WizardView.qml @@ -86,7 +86,7 @@ Rectangle { function onAccountAdded(accountId, showBackUp, index) { addedAccountIndex = index - AccountAdapter.accountChanged(index) + AccountAdapter.changeAccount(index) if (showProfile) { changePageQML(WizardView.WizardViewPageIndex.PROFILEPAGE) profilePage.readyToSaveDetails() -- GitLab