From 2065eb668420eb4ae4574e5bfd6a7a9de716290e Mon Sep 17 00:00:00 2001 From: James Graham Date: Fri, 3 Nov 2023 17:22:57 +0000 Subject: [PATCH] Mobile explore component Create a mobile version of explore component and place it at the bottom for single handed use. This also refactors the UserInfo component so it can be at the top on mobile as well as the bottom on dektop. This should have no effect on desktop and should be identical. ![image](/uploads/9b3133fbde74ca27069d6b039efb1079/image.png) --- src/CMakeLists.txt | 2 + src/qml/ExploreComponentMobile.qml | 159 +++++++++++++++++ src/qml/RoomListPage.qml | 48 +++++- src/qml/UserInfo.qml | 264 +++++++++++++++-------------- src/qml/UserInfoDesktop.qml | 32 ++++ 5 files changed, 375 insertions(+), 130 deletions(-) create mode 100644 src/qml/ExploreComponentMobile.qml create mode 100644 src/qml/UserInfoDesktop.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ef3aa22f..dc9d7fe1c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -146,12 +146,14 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN qml/main.qml qml/AccountMenu.qml qml/ExploreComponent.qml + qml/ExploreComponentMobile.qml qml/ContextMenu.qml qml/CollapsedRoomDelegate.qml qml/RoomDelegate.qml qml/RoomListPage.qml qml/SpaceListContextMenu.qml qml/UserInfo.qml + qml/UserInfoDesktop.qml qml/LoadingPage.qml qml/RoomPage.qml qml/RoomWindow.qml diff --git a/src/qml/ExploreComponentMobile.qml b/src/qml/ExploreComponentMobile.qml new file mode 100644 index 000000000..04c15060b --- /dev/null +++ b/src/qml/ExploreComponentMobile.qml @@ -0,0 +1,159 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.delegates as Delegates + +import org.kde.neochat + +ColumnLayout { + id: root + spacing: 0 + + /** + * @brief The connection for the current user. + */ + required property NeoChatConnection connection + + /** + * @brief Emitted when the text is changed in the search field. + */ + signal textChanged(string newText) + + Kirigami.Separator { + Layout.fillWidth: true + } + Kirigami.NavigationTabBar { + id: exploreTabBar + Layout.fillWidth: true + actions: [ + Kirigami.Action { + id: infoAction + text: i18n("Search") + icon.name: "search" + onTriggered: { + if (explorePopup.visible && explorePopupLoader.sourceComponent == search) { + explorePopup.close(); + exploreTabBar.currentIndex = -1; + } else if (explorePopup.visible && explorePopupLoader.sourceComponent != search) { + explorePopup.close(); + explorePopup.open(); + } else { + explorePopup.open(); + } + explorePopupLoader.sourceComponent = search; + } + }, + Kirigami.Action { + text: i18n("Explore rooms") + icon.name: "compass" + onTriggered: { + let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")}) + dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => { + if (isJoined) { + RoomManager.enterRoom(root.connection.room(roomId)); + } else { + Controller.joinRoom(roomId.length > 0 ? roomId : alias); + } + }) + exploreTabBar.currentIndex = -1; + } + }, + Kirigami.Action { + text: i18n("Start a Chat") + icon.name: "list-add-user" + onTriggered: { + pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/StartChatPage.qml", {connection: root.connection}, {title: i18nc("@title", "Start a Chat")}) + exploreTabBar.currentIndex = -1; + } + }, + Kirigami.Action { + text: i18n("Create New") + icon.name: "list-add" + onTriggered: { + if (explorePopup.visible && explorePopupLoader.sourceComponent == create) { + explorePopup.close(); + exploreTabBar.currentIndex = -1; + } else if (explorePopup.visible && explorePopupLoader.sourceComponent != create) { + explorePopup.close(); + explorePopup.open(); + } else { + explorePopup.open(); + } + explorePopupLoader.sourceComponent = create; + } + } + ] + } + + QQC2.Popup { + id: explorePopup + parent: root + + y: -height + 1 + width: root.width + leftPadding: Kirigami.Units.largeSpacing + rightPadding: Kirigami.Units.largeSpacing + bottomPadding: Kirigami.Units.largeSpacing + topPadding: Kirigami.Units.largeSpacing + + closePolicy: QQC2.Popup.CloseOnEscape + + contentItem: Loader { + id: explorePopupLoader + sourceComponent: search + } + + background: ColumnLayout { + spacing: 0 + Kirigami.Separator { + Layout.fillWidth: true + } + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: Kirigami.Theme.backgroundColor + } + } + + Component { + id: search + Kirigami.SearchField { + onTextChanged: root.textChanged(text) + } + } + Component { + id: create + ColumnLayout { + spacing: 0 + Delegates.RoundedItemDelegate { + Layout.fillWidth: true + action: Kirigami.Action { + text: i18n("Create a Room") + icon.name: "system-users-symbolic" + onTriggered: { + pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/CreateRoomDialog.qml", {connection: root.connection}, {title: i18nc("@title", "Create a Room")}) + explorePopup.close() + } + shortcut: StandardKey.New + } + } + Delegates.RoundedItemDelegate { + Layout.fillWidth: true + action: Kirigami.Action { + text: i18n("Create a Space") + icon.name: "list-add" + onTriggered: { + pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/CreateRoomDialog.qml", {connection: root.connection, isSpace: true, title: i18nc("@title", "Create a Space")}, {title: i18nc("@title", "Create a Space")}) + explorePopup.close() + } + } + } + } + } + } +} diff --git a/src/qml/RoomListPage.qml b/src/qml/RoomListPage.qml index 7e2f15525..9199b7a4e 100644 --- a/src/qml/RoomListPage.qml +++ b/src/qml/RoomListPage.qml @@ -84,11 +84,9 @@ Kirigami.Page { goToPreviousRoomFiltered((item) => (item.visible && item.hasUnread)); } - titleDelegate: ExploreComponent { + titleDelegate: Loader { Layout.fillWidth: true - desiredWidth: root.width - Kirigami.Units.largeSpacing - collapsed: root.collapsed - connection: root.connection + sourceComponent: Kirigami.Settings.isMobile ? userInfo : exploreComponent } padding: 0 @@ -284,10 +282,9 @@ Kirigami.Page { } } - footer: UserInfo { + footer: Loader { width: parent.width - visible: !root.collapsed - connection: root.connection + sourceComponent: Kirigami.Settings.isMobile ? exploreComponentMobile : userInfoDesktop } MouseArea { @@ -333,6 +330,43 @@ Kirigami.Page { } } + Component { + id: userInfo + UserInfo { + visible: !root.collapsed + bottomEdge: false + connection: root.connection + } + } + + Component { + id: userInfoDesktop + UserInfoDesktop { + visible: !root.collapsed + connection: root.connection + } + } + + Component { + id: exploreComponent + ExploreComponent { + desiredWidth: root.width - Kirigami.Units.largeSpacing + collapsed: root.collapsed + connection: root.connection + } + } + + Component { + id: exploreComponentMobile + ExploreComponentMobile { + connection: root.connection + + onTextChanged: (newText) => { + sortFilterRoomListModel.filterText = newText + } + } + } + /* * Hold the modifiable currentWidth in a private object so that only internal * members can modify it. diff --git a/src/qml/UserInfo.qml b/src/qml/UserInfo.qml index 36c32182e..a1f659845 100644 --- a/src/qml/UserInfo.qml +++ b/src/qml/UserInfo.qml @@ -12,22 +12,146 @@ import org.kde.neochat import org.kde.neochat.config import org.kde.neochat.accounts -QQC2.ToolBar { +RowLayout { id: root - padding: 0 - required property NeoChatConnection connection + property bool bottomEdge: true + property var addAccount - contentItem: ColumnLayout { - id: content + spacing: Kirigami.Units.largeSpacing + Layout.topMargin: Kirigami.Units.smallSpacing + Layout.bottomMargin: Kirigami.Units.smallSpacing + Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 2 - 2 : -1 // HACK: -2 here is to ensure the ChatBox and the UserInfo have the same height + + QQC2.AbstractButton { + id: accountButton + + Layout.preferredWidth: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing + Layout.preferredHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing + Layout.leftMargin: Kirigami.Units.largeSpacing + + TapHandler { + acceptedButtons: Qt.RightButton | Qt.LeftButton + onTapped: (eventPoint, button) => { + // TODO Qt6 remove + if (!button) { + button = eventPoint.event.button; + } + if (button == Qt.RightButton) { + accountMenu.open(); + } else { + pageStack.pushDialogLayer(Qt.resolvedUrl('qrc:/org/kde/neochat/qml/AccountEditorPage.qml'), { + connection: root.connection + }, { + title: i18n("Account editor") + }); + } + } + } + + text: i18n("Edit this account") + + contentItem: KirigamiComponents.Avatar { + readonly property string mediaId: root.connection.localUser.avatarMediaId + + source: mediaId ? ("image://mxc/" + mediaId) : "" + name: root.connection.localUser.displayName ?? root.connection.localUser.id + } + } + + ColumnLayout { + Layout.fillWidth: true spacing: 0 + QQC2.Label { + id: displayNameLabel + text: root.connection.localUser.displayName + textFormat: Text.PlainText + elide: Text.ElideRight + Layout.fillWidth: true + } + QQC2.Label { + text: (root.connection.label.length > 0 ? (root.connection.label + " ") : "") + root.connection.localUser.id + font.pointSize: displayNameLabel.font.pointSize * 0.8 + opacity: 0.7 + textFormat: Text.PlainText + elide: Text.ElideRight + Layout.fillWidth: true + } + } + QQC2.ToolButton { + id: switchUserButton + icon.name: "system-switch-user" + checkable: true + text: i18n("Switch User") + display: QQC2.AbstractButton.IconOnly + Accessible.name: text + QQC2.ToolTip.text: text + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + Layout.minimumWidth: Layout.preferredWidth + Layout.alignment: Qt.AlignRight + Shortcut { + sequence: "Ctrl+U" + onActivated: switchUserButton.toggle() + } + } + QQC2.ToolButton { + icon.name: "list-add" + onClicked: ; //TODO + text: i18n("Add") //TODO find better message + display: QQC2.AbstractButton.IconOnly + QQC2.ToolTip.text: text + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + Layout.minimumWidth: Layout.preferredWidth + Layout.alignment: Qt.AlignRight + visible: false + } + QQC2.ToolButton { + icon.name: "settings-configure" + onClicked: pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/SettingsPage.qml", {connection: root.connection}, { title: i18n("Configure") }) + text: i18n("Open Settings") + display: QQC2.AbstractButton.IconOnly + Layout.minimumWidth: Layout.preferredWidth + Layout.alignment: Qt.AlignRight + Layout.rightMargin: Kirigami.Units.largeSpacing + QQC2.ToolTip.text: text + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + } + Item { + width: 1 + } - ListView { + AccountMenu { + id: accountMenu + y: root.bottomEdge ? -height : accountButton.height + connection: root.connection + } + QQC2.Popup { + id: accountsPopup + parent: root + + visible: switchUserButton.checked + onVisibleChanged: if (visible) accounts.forceActiveFocus() + + x: -Kirigami.Units.smallSpacing + y: root.bottomEdge ? -height - Kirigami.Units.smallSpacing - 1 : root.height + Kirigami.Units.smallSpacing - 1 + width: root.width + (root.bottomEdge ? 0 : Kirigami.Units.smallSpacing * 2) + leftPadding: 0 + rightPadding: 0 + bottomPadding: Kirigami.Units.smallSpacing + topPadding: Kirigami.Units.smallSpacing + + closePolicy: QQC2.Popup.CloseOnEscape + + contentItem: ListView { id: accounts + implicitHeight: contentHeight currentIndex: Controller.activeConnectionIndex @@ -69,9 +193,6 @@ QQC2.ToolBar { accounts.forceActiveFocus() } } - - visible: switchUserButton.checked - onVisibleChanged: if (visible) accounts.forceActiveFocus() clip: true model: AccountRegistry @@ -99,8 +220,6 @@ QQC2.ToolBar { } } - Layout.fillWidth: true - Layout.preferredHeight: contentHeight delegate: Delegates.RoundedItemDelegate { id: userDelegate @@ -138,121 +257,20 @@ QQC2.ToolBar { } } - Kirigami.Separator { - Layout.fillWidth: true - } - - RowLayout { - spacing: Kirigami.Units.largeSpacing - - Layout.topMargin: Kirigami.Units.smallSpacing - Layout.bottomMargin: Kirigami.Units.smallSpacing - Layout.minimumHeight: Kirigami.Units.gridUnit * 2 - 2 // HACK: -2 here is to ensure the ChatBox and the UserInfo have the same height - - QQC2.AbstractButton { - id: accountButton - - Layout.preferredWidth: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing - Layout.preferredHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing - Layout.leftMargin: Kirigami.Units.largeSpacing - - TapHandler { - acceptedButtons: Qt.RightButton | Qt.LeftButton - onTapped: (eventPoint, button) => { - // TODO Qt6 remove - if (!button) { - button = eventPoint.event.button; - } - if (button == Qt.RightButton) { - accountMenu.open(); - } else { - pageStack.pushDialogLayer(Qt.resolvedUrl('qrc:/org/kde/neochat/qml/AccountEditorPage.qml'), { - connection: root.connection - }, { - title: i18n("Account editor") - }); - } - } - } - - text: i18n("Edit this account") - - contentItem: KirigamiComponents.Avatar { - readonly property string mediaId: root.connection.localUser.avatarMediaId - - source: mediaId ? ("image://mxc/" + mediaId) : "" - name: root.connection.localUser.displayName ?? root.connection.localUser.id - } - } - - ColumnLayout { + background: ColumnLayout { + spacing: 0 + Kirigami.Separator { Layout.fillWidth: true - spacing: 0 - QQC2.Label { - id: displayNameLabel - text: root.connection.localUser.displayName - textFormat: Text.PlainText - elide: Text.ElideRight - Layout.fillWidth: true - } - QQC2.Label { - text: (root.connection.label.length > 0 ? (root.connection.label + " ") : "") + root.connection.localUser.id - font.pointSize: displayNameLabel.font.pointSize * 0.8 - opacity: 0.7 - textFormat: Text.PlainText - elide: Text.ElideRight - Layout.fillWidth: true - } + visible: root.bottomEdge } - QQC2.ToolButton { - id: switchUserButton - icon.name: "system-switch-user" - checkable: true - text: i18n("Switch User") - display: QQC2.AbstractButton.IconOnly - Accessible.name: text - QQC2.ToolTip.text: text - QQC2.ToolTip.visible: hovered - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - Layout.minimumWidth: Layout.preferredWidth - Layout.alignment: Qt.AlignRight - Shortcut { - sequence: "Ctrl+U" - onActivated: switchUserButton.toggle() - } + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: Kirigami.Theme.backgroundColor } - QQC2.ToolButton { - icon.name: "list-add" - onClicked: ; //TODO - text: i18n("Add") //TODO find better message - display: QQC2.AbstractButton.IconOnly - QQC2.ToolTip.text: text - QQC2.ToolTip.visible: hovered - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - Layout.minimumWidth: Layout.preferredWidth - Layout.alignment: Qt.AlignRight - visible: false - } - QQC2.ToolButton { - icon.name: "settings-configure" - onClicked: pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/SettingsPage.qml", {connection: root.connection}, { title: i18n("Configure") }) - text: i18n("Open Settings") - display: QQC2.AbstractButton.IconOnly - Layout.minimumWidth: Layout.preferredWidth - Layout.alignment: Qt.AlignRight - Layout.rightMargin: Kirigami.Units.largeSpacing - QQC2.ToolTip.text: text - QQC2.ToolTip.visible: hovered - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - } - Item { - width: 1 - } - - AccountMenu { - id: accountMenu - y: -height - connection: root.connection + Kirigami.Separator { + Layout.fillWidth: true + visible: !root.bottomEdge } } } diff --git a/src/qml/UserInfoDesktop.qml b/src/qml/UserInfoDesktop.qml new file mode 100644 index 000000000..d6bea23ab --- /dev/null +++ b/src/qml/UserInfoDesktop.qml @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami + +import org.kde.neochat + +QQC2.ToolBar { + id: root + + /** + * @brief The connection for the current user. + */ + required property NeoChatConnection connection + + padding: 0 + + contentItem: ColumnLayout { + spacing: 0 + Kirigami.Separator { + Layout.fillWidth: true + } + UserInfo { + bottomEdge: true + connection: root.connection + } + } +}