Warning, /network/neochat/src/qml/RoomListPage.qml is written in an unsupported language. File is not indexed.
0001 // SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org> 0002 // SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu> 0003 // SPDX-License-Identifier: GPL-3.0-only 0004 0005 import QtQuick 0006 import QtQuick.Controls as QQC2 0007 import QtQuick.Layouts 0008 import QtQml.Models 0009 0010 import org.kde.kirigami as Kirigami 0011 import org.kde.kirigamiaddons.components as KirigamiComponents 0012 import org.kde.kirigamiaddons.delegates as Delegates 0013 0014 import org.kde.neochat 0015 import org.kde.neochat.config 0016 import org.kde.neochat.accounts 0017 0018 Kirigami.Page { 0019 id: root 0020 0021 /** 0022 * @brief The current width of the room list. 0023 * 0024 * @note Other objects can access the value but the private function makes sure 0025 * that only the internal members can modify it. 0026 */ 0027 readonly property int currentWidth: _private.currentWidth + spaceListWidth 0028 readonly property alias spaceListWidth: spaceDrawer.width 0029 0030 required property NeoChatConnection connection 0031 0032 readonly property RoomListModel roomListModel: RoomListModel { 0033 connection: root.connection 0034 } 0035 property bool spaceChanging: false 0036 0037 readonly property bool collapsed: Config.collapsed 0038 0039 property var enteredRoom: null 0040 0041 onCollapsedChanged: if (collapsed) { 0042 sortFilterRoomListModel.filterText = ""; 0043 } 0044 0045 Component.onCompleted: Runner.roomListModel = root.roomListModel 0046 0047 Connections { 0048 target: RoomManager 0049 function onCurrentRoomChanged() { 0050 itemSelection.setCurrentIndex(roomListModel.index(roomListModel.rowForRoom(RoomManager.currentRoom), 0), ItemSelectionModel.SelectCurrent); 0051 } 0052 } 0053 0054 function goToNextRoomFiltered(condition) { 0055 let index = listView.currentIndex; 0056 while (index++ !== listView.count - 1) { 0057 if (condition(listView.itemAtIndex(index))) { 0058 listView.currentIndex = index; 0059 listView.currentItem.clicked(); 0060 return; 0061 } 0062 } 0063 } 0064 0065 function goToPreviousRoomFiltered(condition) { 0066 let index = listView.currentIndex; 0067 while (index-- !== 0) { 0068 if (condition(listView.itemAtIndex(index))) { 0069 listView.currentIndex = index; 0070 listView.currentItem.clicked(); 0071 return; 0072 } 0073 } 0074 } 0075 0076 function goToNextRoom() { 0077 goToNextRoomFiltered(item => item.visible); 0078 } 0079 0080 function goToPreviousRoom() { 0081 goToPreviousRoomFiltered(item => item.visible); 0082 } 0083 0084 function goToNextUnreadRoom() { 0085 goToNextRoomFiltered(item => (item.visible && item.hasUnread)); 0086 } 0087 0088 function goToPreviousUnreadRoom() { 0089 goToPreviousRoomFiltered(item => (item.visible && item.hasUnread)); 0090 } 0091 0092 titleDelegate: Loader { 0093 Layout.fillWidth: true 0094 sourceComponent: Kirigami.Settings.isMobile ? userInfo : exploreComponent 0095 } 0096 0097 padding: 0 0098 0099 RowLayout { 0100 anchors.fill: parent 0101 spacing: 0 0102 0103 SpaceDrawer { 0104 id: spaceDrawer 0105 Layout.preferredWidth: spaceDrawer.enabled ? Kirigami.Units.gridUnit * 3 : 0 0106 Layout.fillHeight: true 0107 0108 connection: root.connection 0109 0110 onSelectionChanged: root.spaceChanging = true 0111 onSpacesUpdated: sortFilterRoomListModel.invalidate() 0112 } 0113 0114 Kirigami.Separator { 0115 Layout.fillHeight: true 0116 Layout.preferredWidth: 1 0117 } 0118 0119 QQC2.ScrollView { 0120 id: scrollView 0121 Layout.fillWidth: true 0122 Layout.fillHeight: true 0123 0124 background: Rectangle { 0125 color: Kirigami.Theme.backgroundColor 0126 Kirigami.Theme.colorSet: Kirigami.Theme.View 0127 } 0128 0129 ListView { 0130 id: listView 0131 0132 activeFocusOnTab: true 0133 clip: true 0134 0135 topMargin: Math.round(Kirigami.Units.smallSpacing / 2) 0136 0137 header: QQC2.ItemDelegate { 0138 width: visible ? ListView.view.width : 0 0139 height: visible ? Kirigami.Units.gridUnit * 2 : 0 0140 0141 visible: root.collapsed 0142 0143 topPadding: Kirigami.Units.largeSpacing 0144 leftPadding: Kirigami.Units.largeSpacing 0145 rightPadding: Kirigami.Units.largeSpacing 0146 bottomPadding: Kirigami.Units.largeSpacing 0147 0148 onClicked: quickView.item.open() 0149 0150 Kirigami.Icon { 0151 anchors.centerIn: parent 0152 width: Kirigami.Units.iconSizes.smallMedium 0153 height: Kirigami.Units.iconSizes.smallMedium 0154 source: "search" 0155 } 0156 0157 Kirigami.Separator { 0158 width: parent.width 0159 anchors.bottom: parent.bottom 0160 } 0161 } 0162 0163 Kirigami.PlaceholderMessage { 0164 anchors.centerIn: parent 0165 width: parent.width - (Kirigami.Units.largeSpacing * 4) 0166 visible: listView.count == 0 0167 text: if (sortFilterRoomListModel.filterText.length > 0) { 0168 return spaceDrawer.showDirectChats ? i18n("No friends found") : i18n("No rooms found"); 0169 } else { 0170 return spaceDrawer.showDirectChats ? i18n("You haven't added any of your friends yet, click below to search for them.") : i18n("Join some rooms to get started"); 0171 } 0172 helpfulAction: spaceDrawer.showDirectChats ? userSearchAction : exploreRoomAction 0173 0174 Kirigami.Action { 0175 id: exploreRoomAction 0176 icon.name: sortFilterRoomListModel.filterText.length > 0 ? "search" : "list-add" 0177 text: sortFilterRoomListModel.filterText.length > 0 ? i18n("Search in room directory") : i18n("Explore rooms") 0178 onTriggered: { 0179 let dialog = pageStack.layers.push("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", { 0180 connection: root.connection, 0181 keyword: sortFilterRoomListModel.filterText 0182 }, { 0183 title: i18nc("@title", "Explore Rooms") 0184 }); 0185 dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => { 0186 RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join"); 0187 }); 0188 } 0189 } 0190 0191 Kirigami.Action { 0192 id: userSearchAction 0193 icon.name: sortFilterRoomListModel.filterText.length > 0 ? "search" : "list-add" 0194 text: sortFilterRoomListModel.filterText.length > 0 ? i18n("Search in friend directory") : i18n("Find your friends") 0195 onTriggered: pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/UserSearchPage.qml", { 0196 connection: root.connection 0197 }, { 0198 title: i18nc("@title", "Find your friends") 0199 }) 0200 } 0201 } 0202 0203 ItemSelectionModel { 0204 id: itemSelection 0205 model: root.roomListModel 0206 onCurrentChanged: (current, previous) => listView.currentIndex = sortFilterRoomListModel.mapFromSource(current).row 0207 } 0208 0209 model: SortFilterRoomListModel { 0210 id: sortFilterRoomListModel 0211 0212 sourceModel: root.roomListModel 0213 roomSortOrder: SortFilterRoomListModel.Categories 0214 onLayoutChanged: { 0215 layoutTimer.restart(); 0216 listView.currentIndex = sortFilterRoomListModel.mapFromSource(itemSelection.currentIndex).row; 0217 } 0218 activeSpaceId: spaceDrawer.selectedSpaceId 0219 mode: spaceDrawer.showDirectChats ? SortFilterRoomListModel.DirectChats : SortFilterRoomListModel.Rooms 0220 } 0221 0222 // HACK: This is the only way to guarantee the correct choice when 0223 // there are multiple property changes that invalidate the filter. I.e. 0224 // in this case activeSpaceId followed by mode. 0225 Timer { 0226 id: layoutTimer 0227 interval: 300 0228 onTriggered: if ((spaceDrawer.showDirectChats || spaceDrawer.selectedSpaceId.length < 1) && root.spaceChanging) { 0229 RoomManager.resolveResource(listView.itemAtIndex(0).currentRoom.id); 0230 root.spaceChanging = false; 0231 } 0232 } 0233 0234 section { 0235 property: "category" 0236 delegate: root.collapsed ? foldButton : sectionHeader 0237 } 0238 0239 Component { 0240 id: sectionHeader 0241 Kirigami.ListSectionHeader { 0242 height: implicitHeight 0243 width: listView.width 0244 label: roomListModel.categoryName(section) 0245 action: Kirigami.Action { 0246 onTriggered: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section)) 0247 } 0248 0249 QQC2.ToolButton { 0250 icon { 0251 name: roomListModel.categoryVisible(section) ? "go-up" : "go-down" 0252 width: Kirigami.Units.iconSizes.small 0253 height: Kirigami.Units.iconSizes.small 0254 } 0255 text: roomListModel.categoryVisible(section) ? i18nc("Collapse <section name>", "Collapse %1", roomListModel.categoryName(section)) : i18nc("Expand <section name", "Expand %1", roomListModel.categoryName(section)) 0256 display: QQC2.Button.IconOnly 0257 0258 QQC2.ToolTip.text: text 0259 QQC2.ToolTip.visible: hovered 0260 QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay 0261 0262 onClicked: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section)) 0263 } 0264 } 0265 } 0266 Component { 0267 id: foldButton 0268 Item { 0269 width: ListView.view.width 0270 height: visible ? width : 0 0271 QQC2.ToolButton { 0272 id: button 0273 anchors.centerIn: parent 0274 0275 icon { 0276 name: hovered ? (roomListModel.categoryVisible(section) ? "go-up" : "go-down") : roomListModel.categoryIconName(section) 0277 width: Kirigami.Units.iconSizes.smallMedium 0278 height: Kirigami.Units.iconSizes.smallMedium 0279 } 0280 0281 onClicked: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section)) 0282 0283 QQC2.ToolTip.text: roomListModel.categoryName(section) 0284 QQC2.ToolTip.visible: hovered 0285 QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay 0286 } 0287 } 0288 } 0289 0290 reuseItems: true 0291 currentIndex: -1 // we don't want any room highlighted by default 0292 0293 delegate: root.collapsed ? collapsedModeListComponent : normalModeListComponent 0294 0295 Component { 0296 id: collapsedModeListComponent 0297 0298 CollapsedRoomDelegate { 0299 filterText: sortFilterRoomListModel.filterText 0300 } 0301 } 0302 0303 Component { 0304 id: normalModeListComponent 0305 0306 RoomDelegate { 0307 filterText: sortFilterRoomListModel.filterText 0308 0309 connection: root.connection 0310 0311 height: visible ? implicitHeight : 0 0312 0313 visible: categoryVisible || filterText.length > 0 0314 } 0315 } 0316 0317 footer: Delegates.RoundedItemDelegate { 0318 visible: listView.view.count > 0 && spaceDrawer.showDirectChats 0319 text: i18n("Find your friends") 0320 icon.name: "list-add-user" 0321 icon.width: Kirigami.Units.gridUnit * 2 0322 icon.height: Kirigami.Units.gridUnit * 2 0323 0324 onClicked: pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/UserSearchPage.qml", { 0325 connection: root.connection 0326 }, { 0327 title: i18nc("@title", "Find your friends") 0328 }) 0329 } 0330 } 0331 } 0332 } 0333 0334 footer: Loader { 0335 width: parent.width 0336 sourceComponent: Kirigami.Settings.isMobile ? exploreComponentMobile : userInfoDesktop 0337 } 0338 0339 MouseArea { 0340 anchors.top: parent.top 0341 anchors.bottom: parent.bottom 0342 parent: applicationWindow().overlay.parent 0343 0344 x: root.currentWidth - width / 2 0345 width: Kirigami.Units.smallSpacing * 2 0346 z: root.z + 1 0347 enabled: RoomManager.hasOpenRoom && applicationWindow().width >= Kirigami.Units.gridUnit * 35 0348 visible: enabled 0349 cursorShape: Qt.SplitHCursor 0350 0351 property int _lastX 0352 0353 onPressed: mouse => { 0354 _lastX = mouse.x; 0355 } 0356 onPositionChanged: mouse => { 0357 if (_lastX == -1) { 0358 return; 0359 } 0360 if (mouse.x > _lastX) { 0361 // we moved to the right 0362 if (_private.currentWidth < _private.collapseWidth && _private.currentWidth + (mouse.x - _lastX) >= _private.collapseWidth) { 0363 // Here we get back directly to a more wide mode. 0364 _private.currentWidth = _private.defaultWidth; 0365 Config.collapsed = false; 0366 } else if (_private.currentWidth >= _private.collapseWidth) { 0367 // Increase page width 0368 _private.currentWidth = Math.min(_private.defaultWidth, _private.currentWidth + (mouse.x - _lastX)); 0369 } 0370 } else if (mouse.x < _lastX) { 0371 const tmpWidth = _private.currentWidth - (_lastX - mouse.x); 0372 if (tmpWidth < _private.collapseWidth) { 0373 _private.currentWidth = Qt.binding(() => _private.collapsedSize); 0374 Config.collapsed = true; 0375 } else { 0376 _private.currentWidth = tmpWidth; 0377 } 0378 } 0379 } 0380 } 0381 0382 Component { 0383 id: userInfo 0384 UserInfo { 0385 visible: !root.collapsed 0386 bottomEdge: false 0387 connection: root.connection 0388 } 0389 } 0390 0391 Component { 0392 id: userInfoDesktop 0393 UserInfoDesktop { 0394 visible: !root.collapsed 0395 connection: root.connection 0396 } 0397 } 0398 0399 Component { 0400 id: exploreComponent 0401 ExploreComponent { 0402 desiredWidth: root.width - Kirigami.Units.largeSpacing 0403 collapsed: root.collapsed 0404 connection: root.connection 0405 } 0406 } 0407 0408 Component { 0409 id: exploreComponentMobile 0410 ExploreComponentMobile { 0411 connection: root.connection 0412 0413 onTextChanged: newText => { 0414 sortFilterRoomListModel.filterText = newText; 0415 } 0416 } 0417 } 0418 0419 /* 0420 * Hold the modifiable currentWidth in a private object so that only internal 0421 * members can modify it. 0422 */ 0423 QtObject { 0424 id: _private 0425 property int currentWidth: Config.collapsed ? collapsedSize : defaultWidth 0426 readonly property int defaultWidth: Kirigami.Units.gridUnit * 17 0427 readonly property int collapseWidth: Kirigami.Units.gridUnit * 10 0428 readonly property int collapsedSize: Kirigami.Units.gridUnit * 3 - Kirigami.Units.smallSpacing * 3 + (scrollView.QQC2.ScrollBar.vertical.visible ? scrollView.QQC2.ScrollBar.vertical.width : 0) 0429 } 0430 }