Ensure that user choice is given precedent for opening/closing the room drawer. This makes it so that the previous state is restored after one of the menu pages is closed or if the window is dragged thin then wide again. Fixes network/neochat#196
481 lines
16 KiB
QML
481 lines
16 KiB
QML
// SPDX-FileCopyrightText: 2018-2020 Black Hat <bhat@encom.eu.org>
|
|
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15 as QQC2
|
|
import QtQuick.Layouts 1.15
|
|
|
|
import org.kde.kirigami 2.15 as Kirigami
|
|
|
|
import org.kde.neochat 1.0
|
|
import NeoChat.Component 1.0
|
|
import NeoChat.Dialog 1.0
|
|
import NeoChat.Page 1.0
|
|
import NeoChat.Panel 1.0
|
|
import NeoChat.Dialog.KeyVerification 1.0
|
|
|
|
Kirigami.ApplicationWindow {
|
|
id: root
|
|
|
|
property int columnWidth: Kirigami.Units.gridUnit * 13
|
|
|
|
minimumWidth: Kirigami.Units.gridUnit * 15
|
|
minimumHeight: Kirigami.Units.gridUnit * 15
|
|
|
|
visible: false // Will be overridden in Component.onCompleted
|
|
wideScreen: width > columnWidth * 5
|
|
|
|
pageStack.initialPage: LoadingPage {}
|
|
pageStack.globalToolBar.canContainHandles: true
|
|
|
|
property bool roomListLoaded: false
|
|
|
|
property RoomPage roomPage
|
|
|
|
Connections {
|
|
target: root.quitAction
|
|
function onTriggered() {
|
|
Qt.quit()
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
active: Kirigami.Settings.hasPlatformMenuBar && !Kirigami.Settings.isMobile
|
|
source: Qt.resolvedUrl("qrc:/imports/NeoChat/Menu/GlobalMenu.qml")
|
|
}
|
|
|
|
// This timer allows to batch update the window size change to reduce
|
|
// the io load and also work around the fact that x/y/width/height are
|
|
// changed when loading the page and overwrite the saved geometry from
|
|
// the previous session.
|
|
Timer {
|
|
id: saveWindowGeometryTimer
|
|
interval: 1000
|
|
onTriggered: Controller.saveWindowGeometry()
|
|
}
|
|
|
|
Connections {
|
|
id: saveWindowGeometryConnections
|
|
enabled: false // Disable on startup to avoid writing wrong values if the window is hidden
|
|
target: root
|
|
|
|
function onClosing() { Controller.saveWindowGeometry(); }
|
|
function onWidthChanged() { saveWindowGeometryTimer.restart(); }
|
|
function onHeightChanged() { saveWindowGeometryTimer.restart(); }
|
|
function onXChanged() { saveWindowGeometryTimer.restart(); }
|
|
function onYChanged() { saveWindowGeometryTimer.restart(); }
|
|
}
|
|
|
|
|
|
Loader {
|
|
id: quickView
|
|
active: !Kirigami.Settings.isMobile
|
|
sourceComponent: QuickSwitcher { }
|
|
}
|
|
|
|
Connections {
|
|
target: RoomManager
|
|
|
|
function onPushRoom(room, event) {
|
|
root.roomPage = pageStack.push("qrc:/imports/NeoChat/Page/RoomPage.qml");
|
|
root.roomPage.forceActiveFocus();
|
|
if (event.length > 0) {
|
|
roomPage.goToEvent(event);
|
|
}
|
|
}
|
|
|
|
function onReplaceRoom(room, event) {
|
|
const roomItem = pageStack.get(pageStack.depth - 1);
|
|
pageStack.currentIndex = pageStack.depth - 1;
|
|
root.roomPage.forceActiveFocus();
|
|
if (event.length > 0) {
|
|
roomItem.goToEvent(event);
|
|
}
|
|
}
|
|
|
|
function goToEvent(event) {
|
|
if (event.length > 0) {
|
|
roomItem.goToEvent(event);
|
|
}
|
|
roomItem.forceActiveFocus();
|
|
}
|
|
|
|
function onPushWelcomePage() {
|
|
// TODO
|
|
}
|
|
|
|
function onOpenRoomInNewWindow(room) {
|
|
const secondayWindow = roomWindow.createObject(applicationWindow(), {currentRoom: room});
|
|
secondayWiroomWindowndow.width = root.width - pageStack.get(0).width;
|
|
secondayWindow.show();
|
|
}
|
|
|
|
function onShowUserDetail(user) {
|
|
const roomItem = pageStack.get(pageStack.depth - 1);
|
|
roomItem.showUserDetail(user);
|
|
}
|
|
|
|
function onAskDirectChatConfirmation(user) {
|
|
askDirectChatConfirmationComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
|
user: user,
|
|
}).open();
|
|
}
|
|
|
|
function onWarning(title, message) {
|
|
if (RoomManager.currentRoom) {
|
|
const roomItem = pageStack.get(pageStack.depth - 1);
|
|
roomItem.warning(title, message);
|
|
} else {
|
|
showPassiveNotification(i18n("Warning: %1", message));
|
|
}
|
|
}
|
|
}
|
|
|
|
function pushReplaceLayer(page, args) {
|
|
if (pageStack.layers.depth === 2) {
|
|
pageStack.layers.replace(page, args);
|
|
} else {
|
|
pageStack.layers.push(page, args);
|
|
}
|
|
}
|
|
|
|
contextDrawer: RoomDrawer {
|
|
id: contextDrawer
|
|
|
|
// This is a memory for all user initiated actions on the drawer, i.e. clicking the button
|
|
// It is used to ensure that user choice is remembered when changing pages and expanding and contracting the window width
|
|
property bool drawerUserState: Config.autoRoomInfoDrawer
|
|
|
|
// Connect to the onClicked function of the RoomDrawer handle button
|
|
Connections {
|
|
target: contextDrawer.handle.children[0]
|
|
function onClicked() {
|
|
contextDrawer.drawerUserState = contextDrawer.drawerOpen
|
|
}
|
|
}
|
|
|
|
modal: !root.wideScreen || !enabled
|
|
onEnabledChanged: drawerOpen = enabled && !modal
|
|
onModalChanged: {
|
|
if (Config.autoRoomInfoDrawer) {
|
|
drawerOpen = !modal && drawerUserState
|
|
dim = false
|
|
}
|
|
}
|
|
enabled: RoomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3
|
|
handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3 && (root.wideScreen || pageStack.currentIndex > 0)
|
|
}
|
|
|
|
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: RoomManager.hasOpenRoom && (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.globalToolBar.style: Kirigami.ApplicationHeaderStyle.ToolBar
|
|
pageStack.globalToolBar.showNavigationButtons: pageStack.currentIndex > 0 ? Kirigami.ApplicationHeaderStyle.ShowBackButton : 0
|
|
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: 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
|
|
contentItem.implicitWidth: columnWidth
|
|
isMenu: true
|
|
actions: [
|
|
Kirigami.Action {
|
|
text: i18n("Explore rooms")
|
|
icon.name: "compass"
|
|
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {connection: Controller.activeConnection})
|
|
enabled: pageStack.layers.currentItem.title !== i18n("Explore Rooms") && Controller.accountCount > 0
|
|
},
|
|
Kirigami.Action {
|
|
text: i18n("Start a Chat")
|
|
icon.name: "irc-join-channel"
|
|
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {connection: Controller.activeConnection})
|
|
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
|
},
|
|
Kirigami.Action {
|
|
text: i18n("Create a Room")
|
|
icon.name: "irc-join-channel"
|
|
onTriggered: {
|
|
let dialog = createRoomDialog.createObject(root.overlay);
|
|
dialog.open();
|
|
}
|
|
shortcut: StandardKey.New
|
|
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
|
},
|
|
Kirigami.Action {
|
|
text: i18n("Configure NeoChat...")
|
|
icon.name: "settings-configure"
|
|
onTriggered: pageStack.pushDialogLayer("qrc:/imports/NeoChat/Settings/SettingsPage.qml", {}, {
|
|
title: i18n("Configure")
|
|
})
|
|
enabled: pageStack.layers.currentItem.title !== i18n("Configure NeoChat...")
|
|
shortcut: StandardKey.Preferences
|
|
},
|
|
Kirigami.Action {
|
|
text: i18n("Logout")
|
|
icon.name: "list-remove-user"
|
|
enabled: Controller.accountCount > 0
|
|
onTriggered: Controller.logout(Controller.activeConnection, true)
|
|
},
|
|
Kirigami.Action {
|
|
text: i18n("Quit")
|
|
icon.name: "gtk-quit"
|
|
shortcut: StandardKey.Quit
|
|
onTriggered: Qt.quit()
|
|
}
|
|
]
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
Controller.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
|
if (Config.minimizeToSystemTrayOnStartup && !Kirigami.Settings.isMobile && Controller.supportSystemTray && Config.systemTray) {
|
|
restoreWindowGeometryConnections.enabled = true; // To restore window size and position
|
|
} else {
|
|
visible = true;
|
|
saveWindowGeometryConnections.enabled = true;
|
|
}
|
|
}
|
|
Connections {
|
|
target: Config
|
|
function onBlurChanged() {
|
|
Controller.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
|
}
|
|
function onCompactLayoutChanged() {
|
|
Controller.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
|
}
|
|
}
|
|
|
|
// blur effect
|
|
color: Config.blur && !Config.compactLayout ? "transparent" : Kirigami.Theme.backgroundColor
|
|
|
|
// we need to apply the translucency effect separately on top of the color
|
|
background: Rectangle {
|
|
color: Config.blur && !Config.compactLayout ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 1 - Config.transparency) : "transparent"
|
|
}
|
|
|
|
Component {
|
|
id: roomListComponent
|
|
RoomListPage {
|
|
id: roomList
|
|
|
|
Connections {
|
|
target: root.roomPage
|
|
function onSwitchRoomUp() {
|
|
roomList.goToPreviousRoom();
|
|
}
|
|
function onSwitchRoomDown() {
|
|
roomList.goToNextRoom();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: AccountRegistry
|
|
function onRowsRemoved() {
|
|
if (AccountRegistry.rowCount() === 0) {
|
|
RoomManager.reset();
|
|
pageStack.clear();
|
|
roomListLoaded = false;
|
|
pageStack.push("qrc:/imports/NeoChat/Page/WelcomePage.qml");
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: Controller
|
|
|
|
function onInitiated() {
|
|
if (Controller.accountCount === 0) {
|
|
pageStack.replace("qrc:/imports/NeoChat/Page/WelcomePage.qml", {});
|
|
} else if (!roomListLoaded) {
|
|
pageStack.replace(roomListComponent, {
|
|
activeConnection: Controller.activeConnection
|
|
});
|
|
roomListLoaded = true;
|
|
RoomManager.loadInitialRoom();
|
|
}
|
|
}
|
|
|
|
function onGlobalErrorOccured(error, detail) {
|
|
showPassiveNotification(i18n("%1: %2", error, detail));
|
|
}
|
|
|
|
function onUserConsentRequired(url) {
|
|
consentSheet.url = url
|
|
consentSheet.open()
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
id: restoreWindowGeometryConnections
|
|
enabled: false
|
|
target: root
|
|
|
|
function onVisibleChanged() {
|
|
if (!visible) {
|
|
return;
|
|
}
|
|
Controller.restoreWindowGeometry(root);
|
|
restoreWindowGeometryConnections.enabled = false; // Only restore window geometry for the first time
|
|
saveWindowGeometryConnections.enabled = true;
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: keyVerificationDialogComponent
|
|
KeyVerificationDialog { }
|
|
}
|
|
|
|
Connections {
|
|
target: Controller.activeConnection
|
|
function onDirectChatAvailable(directChat) {
|
|
RoomManager.enterRoom(Controller.activeConnection.room(directChat.id));
|
|
}
|
|
function onNewKeyVerificationSession(session) {
|
|
applicationWindow().pageStack.pushDialogLayer(keyVerificationDialogComponent, {
|
|
session: session,
|
|
}, {
|
|
title: i18nc("@title:window", "Session Verification")
|
|
});
|
|
}
|
|
}
|
|
|
|
Kirigami.OverlaySheet {
|
|
id: consentSheet
|
|
|
|
property string url: ""
|
|
|
|
title: i18n("User consent")
|
|
|
|
QQC2.Label {
|
|
id: label
|
|
|
|
text: i18n("Your homeserver requires you to agree to its terms and conditions before being able to use it. Please click the button below to read them.")
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width
|
|
}
|
|
footer: QQC2.Button {
|
|
text: i18n("Open")
|
|
onClicked: UrlHelper.openUrl(consentSheet.url)
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: createRoomDialog
|
|
|
|
CreateRoomDialog {}
|
|
}
|
|
|
|
Component {
|
|
id: roomWindow
|
|
RoomWindow {}
|
|
}
|
|
|
|
Component {
|
|
id: userDialog
|
|
UserDetailDialog {}
|
|
}
|
|
|
|
Component {
|
|
id: askDirectChatConfirmationComponent
|
|
|
|
Kirigami.OverlaySheet {
|
|
id: askDirectChatConfirmation
|
|
required property var user;
|
|
|
|
parent: QQC2.ApplicationWindow.overlay
|
|
title: i18n("Start a chat")
|
|
contentItem: QQC2.Label {
|
|
text: i18n("Do you want to start a chat with %1?", user.displayName)
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
footer: QQC2.DialogButtonBox {
|
|
standardButtons: QQC2.DialogButtonBox.Ok | QQC2.DialogButtonBox.Cancel
|
|
onAccepted: {
|
|
user.requestDirectChat();
|
|
askDirectChatConfirmation.close();
|
|
}
|
|
onRejected: askDirectChatConfirmation.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
property Item hoverLinkIndicator: QQC2.Control {
|
|
parent: overlay.parent
|
|
property string text
|
|
opacity: linkText.text.length > 0 ? 1 : 0
|
|
|
|
z: 20
|
|
x: 0
|
|
y: parent.height - implicitHeight
|
|
contentItem: QQC2.Label {
|
|
id: linkText
|
|
text: parent.text.startsWith("https://matrix.to/") ? "" : parent.text
|
|
}
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
|
background: Rectangle {
|
|
color: Kirigami.Theme.backgroundColor
|
|
}
|
|
}
|
|
}
|