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