Make sidebar collapsible
This commit is contained in:
@@ -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
|
||||
|
||||
65
qml/main.qml
65
qml/main.qml
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user