Make sidebar collapsible

This commit is contained in:
Carl Schwan
2021-06-14 12:50:09 +00:00
parent 5c6a540807
commit a086964769
5 changed files with 260 additions and 78 deletions

View File

@@ -18,6 +18,24 @@ Kirigami.ScrollablePage {
id: page
property var enteredRoom
property bool collapsedMode: Config.roomListPageWidth === applicationWindow().collapsedPageWidth && applicationWindow().shouldUseSidebars
onCollapsedModeChanged: if (collapsedMode) {
sortFilterRoomListModel.filterText = "";
if (page.contentItem && page.contentItem.flickableItem && page.contentItem.flickableItem.QQC2.ScrollBar.vertical) {
page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = false;
}
} else {
page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = true;
}
// HACK: the scrollbar is created with a 0 timer, so we need to set the visible flag
// after it has been created
Timer {
running: true
interval: 200
onTriggered: page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = !collapsedMode;
}
function goToNextRoom() {
do {
@@ -33,17 +51,51 @@ Kirigami.ScrollablePage {
listView.currentItem.action.trigger();
}
title: i18n("Rooms")
titleDelegate: collapsedMode ? empty : searchField
titleDelegate: Kirigami.SearchField {
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.fillHeight: true
Layout.fillWidth: true
onTextChanged: sortFilterRoomListModel.filterText = text
KeyNavigation.tab: listView
Component {
id: empty
Item {}
}
Component {
id: searchField
Kirigami.SearchField {
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.fillHeight: true
Layout.fillWidth: true
onTextChanged: sortFilterRoomListModel.filterText = text
KeyNavigation.tab: listView
}
}
header: QQC2.ItemDelegate {
visible: page.collapsedMode
action: Kirigami.Action {
id: enterRoomAction
onTriggered: quickView.item.open();
}
topPadding: Kirigami.Units.largeSpacing
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
width: visible ? page.width : 0
height: visible ? Kirigami.Units.gridUnit * 2 : 0
Kirigami.Icon {
anchors.centerIn: parent
width: 22 * Kirigami.Units.devicePixelRatio
height: 22 * Kirigami.Units.devicePixelRatio
source: "search"
}
Kirigami.Separator {
width: parent.width
anchors.bottom: parent.bottom
}
}
ListView {
id: listView
@@ -64,6 +116,7 @@ Kirigami.ScrollablePage {
}
}
ItemSelectionModel {
id: itemSelection
model: roomListModel
@@ -97,11 +150,14 @@ Kirigami.ScrollablePage {
level: 3
text: roomListModel.categoryName(section)
Layout.fillWidth: true
elide: Text.ElideRight
visible: !page.collapsedMode
}
Kirigami.Icon {
source: roomListModel.categoryVisible(section) ? "go-up" : "go-down"
source: page.collapsedMode ? roomListModel.categoryIconName(section) : (roomListModel.categoryVisible(section) ? "go-up" : "go-down")
implicitHeight: Kirigami.Units.iconSizes.small
implicitWidth: Kirigami.Units.iconSizes.small
Layout.alignment: Qt.AlignHCenter
}
}
}
@@ -109,87 +165,128 @@ Kirigami.ScrollablePage {
reuseItems: true
currentIndex: -1 // we don't want any room highlighted by default
delegate: Kirigami.BasicListItem {
id: roomListItem
visible: model.categoryVisible || sortFilterRoomListModel.filterText.length > 0 || Config.mergeRoomList
topPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
highlighted: listView.currentIndex === index
focus: true
icon: undefined
action: Kirigami.Action {
id: enterRoomAction
onTriggered: {
RoomManager.enterRoom(currentRoom);
itemSelection.setCurrentIndex(sortFilterRoomListModel.mapToSource(
sortFilterRoomListModel.index(index, 0)), ItemSelectionModel.SelectCurrent)
}
}
Keys.onEnterPressed: enterRoomAction.trigger()
Keys.onReturnPressed: enterRoomAction.trigger()
bold: unreadCount > 0
label: name ?? ""
subtitle: {
let txt = (lastEvent.length === 0 ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm, " ")
if (txt.length) {
return txt
}
return " "
}
delegate: page.collapsedMode ? collapsedModeListComponent : normalModeListComponent
leading: Kirigami.Avatar {
source: avatar ? "image://mxc/" + avatar : ""
name: model.name || i18n("No Name")
implicitWidth: visible ? height : 0
visible: Config.showAvatarInTimeline
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
}
Component {
id: collapsedModeListComponent
trailing: RowLayout {
QQC2.Label {
text: notificationCount
visible: notificationCount > 0
padding: Kirigami.Units.smallSpacing
color: highlightCount > 0 ? "white" : Kirigami.Theme.textColor
Layout.minimumWidth: height
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.Button
color: highlightCount > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
radius: height / 2
QQC2.ItemDelegate {
action: Kirigami.Action {
id: enterRoomAction
onTriggered: {
RoomManager.enterRoom(currentRoom);
itemSelection.setCurrentIndex(sortFilterRoomListModel.mapToSource(
sortFilterRoomListModel.index(index, 0)), ItemSelectionModel.SelectCurrent)
}
}
QQC2.Button {
id: configButton
visible: roomListItem.hovered || Kirigami.Settings.isMobile
Accessible.name: i18n("Configure room")
Keys.onEnterPressed: enterRoomAction.trigger()
Keys.onReturnPressed: enterRoomAction.trigger()
topPadding: Kirigami.Units.largeSpacing
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
width: ListView.view.width
height: ListView.view.width
action: Kirigami.Action {
id: optionAction
icon.name: "configure"
onTriggered: {
const menu = roomListContextMenu.createObject(page, {"room": currentRoom})
configButton.visible = true
configButton.down = true
menu.closed.connect(function() {
configButton.down = undefined
configButton.visible = Qt.binding(function() { return roomListItem.hovered || Kirigami.Settings.isMobile })
})
menu.popup()
contentItem: Kirigami.Avatar {
source: avatar ? "image://mxc/" + avatar : ""
name: model.name || i18n("No Name")
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
}
QQC2.ToolTip {
enabled: text.length !== 0
text: name ?? ""
}
}
}
Component {
id: roomListContextMenu
RoomListContextMenu {}
}
Component {
id: normalModeListComponent
Kirigami.BasicListItem {
id: roomListItem
visible: model.categoryVisible || sortFilterRoomListModel.filterText.length > 0 || Config.mergeRoomList
topPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
highlighted: listView.currentIndex === index
focus: true
icon: undefined
action: Kirigami.Action {
id: enterRoomAction
onTriggered: {
RoomManager.enterRoom(currentRoom);
itemSelection.setCurrentIndex(sortFilterRoomListModel.mapToSource(
sortFilterRoomListModel.index(index, 0)), ItemSelectionModel.SelectCurrent)
}
}
Keys.onEnterPressed: enterRoomAction.trigger()
Keys.onReturnPressed: enterRoomAction.trigger()
bold: unreadCount > 0
label: name ?? ""
subtitle: {
let txt = (lastEvent.length === 0 ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm, " ")
if (txt.length) {
return txt
}
return " "
}
leading: Kirigami.Avatar {
source: avatar ? "image://mxc/" + avatar : ""
name: model.name || i18n("No Name")
implicitWidth: visible ? height : 0
visible: Config.showAvatarInTimeline
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
}
trailing: RowLayout {
QQC2.Label {
text: notificationCount
visible: notificationCount > 0
padding: Kirigami.Units.smallSpacing
color: highlightCount > 0 ? "white" : Kirigami.Theme.textColor
Layout.minimumWidth: height
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.Button
color: highlightCount > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
radius: height / 2
}
}
QQC2.Button {
id: configButton
visible: roomListItem.hovered || Kirigami.Settings.isMobile
Accessible.name: i18n("Configure room")
action: Kirigami.Action {
id: optionAction
icon.name: "configure"
onTriggered: {
const menu = roomListContextMenu.createObject(page, {"room": currentRoom})
configButton.visible = true
configButton.down = true
menu.closed.connect(function() {
configButton.down = undefined
configButton.visible = Qt.binding(function() { return roomListItem.hovered || Kirigami.Settings.isMobile })
})
menu.popup()
}
}
}
}
}
}
Component {
id: roomListContextMenu
RoomListContextMenu {}
}
}
footer: QQC2.ToolBar {
visible: accountList.count > 1
visible: accountList.count > 1 && !collapsedMode
height: visible ? implicitHeight : 0
leftPadding: 0
rightPadding: 0

View File

@@ -166,7 +166,70 @@ Kirigami.ApplicationWindow {
handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3
}
pageStack.columnView.columnWidth: Kirigami.Units.gridUnit * 17
readonly property int defaultPageWidth: Kirigami.Units.gridUnit * 17
readonly property int minPageWidth: Kirigami.Units.gridUnit * 10
readonly property int collapsedPageWidth: Kirigami.Units.gridUnit * 3 - Kirigami.Units.smallSpacing * 3
readonly property bool shouldUseSidebars: (Config.roomListPageWidth > minPageWidth ? root.width >= Kirigami.Units.gridUnit * 35 : root.width > Kirigami.Units.gridUnit * 27) && roomListLoaded
readonly property int pageWidth: {
if (Config.roomListPageWidth === -1) {
return defaultPageWidth;
} else if (Config.roomListPageWidth < minPageWidth) {
return collapsedPageWidth;
} else {
return Config.roomListPageWidth;
}
}
pageStack.defaultColumnWidth: pageWidth
pageStack.columnView.columnResizeMode: shouldUseSidebars ? Kirigami.ColumnView.FixedColumns : Kirigami.ColumnView.SingleColumn
MouseArea {
visible: root.pageStack.wideMode
z: 500
anchors.top: parent.top
anchors.bottom: parent.bottom
x: root.pageStack.defaultColumnWidth - (width / 2)
width: Kirigami.Units.devicePixelRatio * 2
property int _lastX: -1
enabled: !Kirigami.Settings.isMobile
cursorShape: !Kirigami.Settings.isMobile ? Qt.SplitHCursor : undefined
onPressed: _lastX = mouseX
onReleased: Config.save();
onPositionChanged: {
if (_lastX == -1) {
return;
}
if (mouse.x > _lastX) {
// we moved to the right
if (Config.roomListPageWidth === root.collapsedPageWidth && root.pageWidth + (mouse.x - _lastX) >= root.minPageWidth) {
// Here we get back directly to a more wide mode.
Config.roomListPageWidth = root.minPageWidth;
if (root.width < Kirigami.Units.gridUnit * 35) {
root.width = Kirigami.Units.gridUnit * 35;
}
} else if (Config.roomListPageWidth !== root.collapsedPageWidth) {
// Increase page width
Config.roomListPageWidth = Math.min(root.defaultPageWidth, root.pageWidth + (mouse.x - _lastX));
}
} else if (mouse.x < _lastX) {
const tmpWidth = root.pageWidth - (_lastX - mouse.x);
if (tmpWidth < root.minPageWidth) {
Config.roomListPageWidth = root.collapsedPageWidth;
} else {
Config.roomListPageWidth = tmpWidth;
}
}
}
}
globalDrawer: Kirigami.GlobalDrawer {
property bool hasLayer

View File

@@ -41,6 +41,9 @@
<label>"Show your messages on the right</label>
<default>true</default>
</entry>
<entry name="RoomListPageWidth" type="int">
<default>-1</default>
</entry>
</group>
<group name="Timeline">
<entry name="ShowAvatarInTimeline" type="bool">

View File

@@ -432,6 +432,24 @@ QString RoomListModel::categoryName(int section)
}
}
QString RoomListModel::categoryIconName(int section)
{
switch (section) {
case 1:
return QStringLiteral("user-invisible");
case 2:
return QStringLiteral("favorite");
case 3:
return QStringLiteral("dialog-messages");
case 4:
return QStringLiteral("group");
case 5:
return QStringLiteral("object-order-lower");
default:
return QStringLiteral("tools-report-bug");
}
}
void RoomListModel::setCategoryVisible(int category, bool visible)
{
beginResetModel();

View File

@@ -69,6 +69,7 @@ public:
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE [[nodiscard]] static QString categoryName(int section);
Q_INVOKABLE [[nodiscard]] static QString categoryIconName(int section);
Q_INVOKABLE void setCategoryVisible(int category, bool visible);
Q_INVOKABLE [[nodiscard]] bool categoryVisible(int category) const;