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)
This commit is contained in:
James Graham
2023-11-03 17:22:57 +00:00
parent 9cac2a8abd
commit 2065eb6684
5 changed files with 375 additions and 130 deletions

View File

@@ -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

View File

@@ -0,0 +1,159 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// 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()
}
}
}
}
}
}
}

View File

@@ -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.

View File

@@ -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
}
}
}

View File

@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// 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
}
}
}