nextcloud-desktop/src/gui/tray/Window.qml

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

694 lines
31 KiB
QML
Raw Normal View History

import QtQml 2.1
import QtQml.Models 2.1
import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtGraphicalEffects 1.0
// Custom qml modules are in /theme (and included by resources.qrc)
import Style 1.0
import com.nextcloud.desktopclient 1.0
Window {
id: trayWindow
width: Style.trayWindowWidth
height: Style.trayWindowHeight
color: "transparent"
flags: Qt.Dialog | Qt.FramelessWindowHint
readonly property int maxMenuHeight: Style.trayWindowHeight - Style.trayWindowHeaderHeight - 2 * Style.trayWindowBorderWidth
Accessible.role: Accessible.Application
Accessible.name: qsTr("Nextcloud desktop main dialog")
// Close tray window when focus is lost (e.g. click somewhere else on the screen)
onActiveChanged: {
if(!active) {
trayWindow.hide();
Systray.setClosed();
}
}
onVisibleChanged: {
currentAccountStateIndicator.source = ""
currentAccountStateIndicator.source = UserModel.isUserConnected(UserModel.currentUserId) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg"
// HACK: reload account Instantiator immediately by restting it - could be done better I guess
// see also id:accountMenu below
userLineInstantiator.active = false;
userLineInstantiator.active = true;
}
Connections {
target: UserModel
onRefreshCurrentUserGui: {
currentAccountStateIndicator.source = ""
currentAccountStateIndicator.source = UserModel.isUserConnected(UserModel.currentUserId) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg"
}
onNewUserSelected: {
accountMenu.close();
}
}
Connections {
target: Systray
onShowWindow: {
accountMenu.close();
appsMenu.close();
Systray.positionWindow(trayWindow);
trayWindow.show();
trayWindow.raise();
trayWindow.requestActivate();
Systray.setOpened();
UserModel.fetchCurrentActivityModel();
}
onHideWindow: {
trayWindow.hide();
Systray.setClosed();
}
}
OpacityMask {
anchors.fill: parent
source: ShaderEffectSource {
sourceItem: trayWindowBackground
hideSource: true
}
maskSource: Rectangle {
width: trayWindowBackground.width
height: trayWindowBackground.height
radius: trayWindowBackground.radius
}
}
Rectangle {
id: trayWindowBackground
anchors.fill: parent
radius: Style.trayWindowRadius
border.width: Style.trayWindowBorderWidth
border.color: Style.menuBorder
Accessible.role: Accessible.Grouping
Accessible.name: qsTr("Nextcloud desktop main dialog")
Rectangle {
id: trayWindowHeaderBackground
anchors.left: trayWindowBackground.left
anchors.top: trayWindowBackground.top
height: Style.trayWindowHeaderHeight
width: Style.trayWindowWidth
color: Style.ncBlue
// The overlay rectangle below eliminates the rounded corners from the bottom of the header
// as Qt only allows setting the radius for all corners right now, not specific ones
Rectangle {
id: trayWindowHeaderButtomHalfBackground
anchors.left: trayWindowHeaderBackground.left
anchors.bottom: trayWindowHeaderBackground.bottom
height: Style.trayWindowHeaderHeight / 2
width: Style.trayWindowWidth
color: Style.ncBlue
}
RowLayout {
id: trayWindowHeaderLayout
spacing: 0
anchors.fill: parent
Button {
id: currentAccountButton
Layout.preferredWidth: Style.currentAccountButtonWidth
Layout.preferredHeight: Style.trayWindowHeaderHeight
display: AbstractButton.IconOnly
flat: true
Accessible.role: Accessible.ButtonMenu
Accessible.name: qsTr("Current account")
Accessible.onPressAction: currentAccountButton.clicked()
MouseArea {
id: accountBtnMouseArea
anchors.fill: parent
hoverEnabled: Style.hoverEffectsEnabled
// We call open() instead of popup() because we want to position it
// exactly below the dropdown button, not the mouse
onClicked: {
syncPauseButton.text = Systray.syncIsPaused() ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
if (accountMenu.visible) {
accountMenu.close()
} else {
accountMenu.open()
}
}
Menu {
id: accountMenu
// x coordinate grows towards the right
// y coordinate grows towards the bottom
x: (currentAccountButton.x + 2)
y: (currentAccountButton.y + Style.trayWindowHeaderHeight + 2)
width: (Style.currentAccountButtonWidth - 2)
height: Math.min(implicitHeight, maxMenuHeight)
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
background: Rectangle {
border.color: Style.menuBorder
radius: Style.currentAccountButtonRadius
}
Accessible.role: PopupMenu
Accessible.name: qsTr("Account switcher and settings menu")
onClosed: {
// HACK: reload account Instantiator immediately by restting it - could be done better I guess
// see also onVisibleChanged above
userLineInstantiator.active = false;
userLineInstantiator.active = true;
}
Instantiator {
id: userLineInstantiator
model: UserModel
delegate: UserLine {}
onObjectAdded: accountMenu.insertItem(index, object)
onObjectRemoved: accountMenu.removeItem(object)
}
MenuItem {
id: addAccountButton
height: Style.addAccountButtonHeight
hoverEnabled: true
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
}
}
RowLayout {
anchors.fill: parent
spacing: 0
Image {
Layout.leftMargin: 12
verticalAlignment: Qt.AlignCenter
source: "qrc:///client/theme/black/add.svg"
sourceSize.width: Style.headerButtonIconSize
sourceSize.height: Style.headerButtonIconSize
}
Label {
Layout.leftMargin: 14
text: qsTr("Add account")
color: "black"
font.pixelSize: Style.topLinePixelSize
}
// Filler on the right
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
onClicked: UserModel.addAccount()
Accessible.role: Accessible.MenuItem
Accessible.name: qsTr("Add new account")
Accessible.onPressAction: addAccountButton.clicked()
}
MenuSeparator {
contentItem: Rectangle {
implicitHeight: 1
color: Style.menuBorder
}
}
MenuItem {
id: syncPauseButton
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.pauseResumeSync()
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
}
}
Accessible.role: Accessible.MenuItem
Accessible.name: Systray.syncIsPaused() ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
Accessible.onPressAction: syncPauseButton.clicked()
}
MenuItem {
id: settingsButton
text: qsTr("Settings")
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.openSettings()
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
}
}
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: settingsButton.clicked()
}
MenuItem {
id: exitButton
text: qsTr("Exit");
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.shutdown()
background: Item {
height: parent.height
width: parent.menu.width
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: parent.parent.hovered ? Style.lightHover : "transparent"
}
}
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: exitButton.clicked()
}
}
}
background: Rectangle {
color: accountBtnMouseArea.containsMouse ? "white" : "transparent"
opacity: 0.2
}
RowLayout {
id: accountControlRowLayout
height: Style.trayWindowHeaderHeight
width: Style.currentAccountButtonWidth
spacing: 0
Image {
id: currentAccountAvatar
Layout.leftMargin: 8
verticalAlignment: Qt.AlignCenter
cache: false
source: UserModel.currentUser.avatar != "" ? UserModel.currentUser.avatar : "image://avatars/fallbackWhite"
Layout.preferredHeight: Style.accountAvatarSize
Layout.preferredWidth: Style.accountAvatarSize
Accessible.role: Accessible.Graphic
Accessible.name: qsTr("Current user avatar")
Rectangle {
id: currentAccountStateIndicatorBackground
width: Style.accountAvatarStateIndicatorSize + 2
height: width
anchors.bottom: currentAccountAvatar.bottom
anchors.right: currentAccountAvatar.right
color: Style.ncBlue
radius: width*0.5
}
Rectangle {
width: Style.accountAvatarStateIndicatorSize + 2
height: width
anchors.bottom: currentAccountAvatar.bottom
anchors.right: currentAccountAvatar.right
color: accountBtnMouseArea.containsMouse ? "white" : "transparent"
opacity: 0.2
radius: width*0.5
}
Image {
id: currentAccountStateIndicator
source: UserModel.isUserConnected(UserModel.currentUserId) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg"
cache: false
x: currentAccountStateIndicatorBackground.x + 1
y: currentAccountStateIndicatorBackground.y + 1
sourceSize.width: Style.accountAvatarStateIndicatorSize
sourceSize.height: Style.accountAvatarStateIndicatorSize
Accessible.role: Accessible.Indicator
Accessible.name: UserModel.isUserConnected(UserModel.currentUserId()) ? qsTr("Connected") : qsTr("Disconnected")
}
}
Column {
id: accountLabels
spacing: 4
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 6
Label {
id: currentAccountUser
width: Style.currentAccountLabelWidth
text: UserModel.currentUser.name
elide: Text.ElideRight
color: Style.ncTextColor
font.pixelSize: Style.topLinePixelSize
font.bold: true
}
Label {
id: currentAccountServer
width: Style.currentAccountLabelWidth
text: UserModel.currentUser.server
elide: Text.ElideRight
color: Style.ncTextColor
font.pixelSize: Style.subLinePixelSize
}
}
Image {
Layout.alignment: Qt.AlignRight
verticalAlignment: Qt.AlignCenter
Layout.margins: Style.accountDropDownCaretMargin
source: "qrc:///client/theme/white/caret-down.svg"
sourceSize.width: Style.accountDropDownCaretSize
sourceSize.height: Style.accountDropDownCaretSize
}
}
}
// Filler between account dropdown and header app buttons
Item {
id: trayWindowHeaderSpacer
Layout.fillWidth: true
}
HeaderButton {
id: openLocalFolderButton
visible: UserModel.currentUser.hasLocalFolder
icon.source: "qrc:///client/theme/white/folder.svg"
onClicked: UserModel.openCurrentAccountLocalFolder()
Accessible.role: Accessible.Button
Accessible.name: qsTr("Open local folder of current account")
Accessible.onPressAction: openLocalFolderButton.clicked()
}
HeaderButton {
id: trayWindowTalkButton
visible: UserModel.currentUser.serverHasTalk
icon.source: "qrc:///client/theme/white/talk-app.svg"
onClicked: UserModel.openCurrentAccountTalk()
Accessible.role: Accessible.Button
Accessible.name: qsTr("Open Nextcloud Talk in browser")
Accessible.onPressAction: trayWindowTalkButton.clicked()
}
HeaderButton {
id: trayWindowAppsButton
icon.source: "qrc:///client/theme/white/more-apps.svg"
onClicked: {
if(appsMenu.count <= 0) {
UserModel.openCurrentAccountServer()
} else if (appsMenu.visible) {
appsMenu.close()
} else {
appsMenu.open()
}
}
Accessible.role: Accessible.ButtonMenu
Accessible.name: qsTr("More apps")
Accessible.onPressAction: trayWindowAppsButton.clicked()
Menu {
id: appsMenu
y: (trayWindowAppsButton.y + trayWindowAppsButton.height + 2)
readonly property Item listContentItem: contentItem.contentItem
width: Math.min(listContentItem.childrenRect.width + 4, Style.trayWindowWidth / 2)
height: Math.min(implicitHeight, maxMenuHeight)
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
background: Rectangle {
border.color: Style.menuBorder
radius: 2
}
Accessible.role: Accessible.PopupMenu
Accessible.name: qsTr("Apps menu")
Instantiator {
id: appsMenuInstantiator
model: UserAppsModel
onObjectAdded: appsMenu.insertItem(index, object)
onObjectRemoved: appsMenu.removeItem(object)
delegate: MenuItem {
id: appEntry
text: appName
font.pixelSize: Style.topLinePixelSize
icon.source: appIconUrl
width: contentItem.implicitWidth + leftPadding + rightPadding
onTriggered: UserAppsModel.openAppUrl(appUrl)
hoverEnabled: true
background: Item {
width: appsMenu.width
height: parent.height
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: appEntry.hovered ? Style.lightHover : "transparent"
}
}
Accessible.role: Accessible.MenuItem
Accessible.name: qsTr("Open %1 in browser").arg(appName)
Accessible.onPressAction: appEntry.triggered()
}
}
}
}
}
} // Rectangle trayWindowHeaderBackground
ListView {
id: activityListView
anchors.top: trayWindowHeaderBackground.bottom
anchors.horizontalCenter: trayWindowBackground.horizontalCenter
width: Style.trayWindowWidth - Style.trayWindowBorderWidth
height: Style.trayWindowHeight - Style.trayWindowHeaderHeight
clip: true
ScrollBar.vertical: ScrollBar {
id: listViewScrollbar
}
keyNavigationEnabled: true
Accessible.role: Accessible.List
Accessible.name: qsTr("Activity list")
model: activityModel
delegate: RowLayout {
id: activityItem
width: parent.width
height: Style.trayWindowHeaderHeight
spacing: 0
Accessible.role: Accessible.ListItem
Accessible.name: path !== "" ? qsTr("Open %1 locally").arg(displayPath)
: message
Accessible.onPressAction: activityMouseArea.clicked()
MouseArea {
id: activityMouseArea
enabled: (path !== "" || link !== "")
anchors.left: activityItem.left
anchors.right: (shareButton.visible) ? shareButton.left
: (replyButton.visible) ? replyButton.left
: activityItem.right
height: parent.height
anchors.margins: 2
hoverEnabled: true
onClicked: activityModel.triggerActionAtIndex(model.index)
Rectangle {
anchors.fill: parent
color: (parent.containsMouse ? Style.lightHover : "transparent")
}
}
Image {
id: activityIcon
anchors.left: activityItem.left
anchors.leftMargin: 8
anchors.rightMargin: 8
Layout.preferredWidth: shareButton.icon.width
Layout.preferredHeight: shareButton.icon.height
verticalAlignment: Qt.AlignCenter
cache: true
source: icon
sourceSize.height: 64
sourceSize.width: 64
}
Column {
id: activityTextColumn
anchors.left: activityIcon.right
anchors.leftMargin: 8
spacing: 4
Layout.alignment: Qt.AlignLeft
Text {
id: activityTextTitle
text: (type === "Activity" || type === "Notification") ? subject : message
width: Style.activityLabelBaseWidth + ((path === "") ? activityItem.height : 0) + ((link === "") ? activityItem.height : 0) - 8
elide: Text.ElideRight
font.pixelSize: Style.topLinePixelSize
color: activityTextTitleColor
}
Text {
id: activityTextInfo
text: (type === "Sync") ? displayPath
: (type === "File") ? subject
: (type === "Notification") ? message
: ""
height: (text === "") ? 0 : activityTextTitle.height
width: Style.activityLabelBaseWidth + ((path === "") ? activityItem.height : 0) + ((link === "") ? activityItem.height : 0) - 8
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
}
Text {
id: activityTextDateTime
text: dateTime
height: (text === "") ? 0 : activityTextTitle.height
width: Style.activityLabelBaseWidth + ((path === "") ? activityItem.height : 0) + ((link === "") ? activityItem.height : 0) - 8
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
color: "#808080"
}
ToolTip {
id: toolTip
visible: activityMouseArea.containsMouse
text: activityTextTitle.text + ((activityTextInfo.text !== "") ? "\n\n" + activityTextInfo.text : "")
delay: 250
timeout: 10000
// Can be dropped on more recent Qt, but on 5.12 it doesn't wrap...
contentItem: Text {
text: toolTip.text
font: toolTip.font
wrapMode: Text.Wrap
color: toolTip.palette.toolTipText
}
}
}
Button {
id: shareButton
anchors.right: activityItem.right
Layout.preferredWidth: (path === "") ? 0 : parent.height
Layout.preferredHeight: parent.height
Layout.alignment: Qt.AlignRight
flat: true
hoverEnabled: true
visible: (path === "") ? false : true
display: AbstractButton.IconOnly
icon.source: "qrc:///client/theme/share.svg"
icon.color: "transparent"
background: Rectangle {
color: parent.hovered ? Style.lightHover : "transparent"
}
ToolTip.visible: hovered
ToolTip.delay: 1000
ToolTip.text: qsTr("Open share dialog")
onClicked: Systray.openShareDialog(displayPath,absolutePath)
Accessible.role: Accessible.Button
Accessible.name: qsTr("Share %1").arg(displayPath)
Accessible.onPressAction: shareButton.clicked()
}
Button {
id: replyButton
anchors.right: activityItem.right
Layout.preferredWidth: (objectType == "chat" || objectType == "call") ? parent.height : 0
Layout.preferredHeight: parent.height
Layout.alignment: Qt.AlignRight
flat: true
hoverEnabled: true
visible: (objectType == "chat" || objectType == "call") ? true : false
display: AbstractButton.IconOnly
icon.source: "qrc:///client/theme/reply.svg"
icon.color: "transparent"
background: Rectangle {
color: parent.hovered ? Style.lightHover : "transparent"
}
ToolTip.visible: hovered
ToolTip.delay: 1000
ToolTip.text: qsTr("Open Talk")
onClicked: Qt.openUrlExternally(link)
Accessible.role: Accessible.Button
Accessible.name: qsTr("Open Talk %1").arg(link)
Accessible.onPressAction: replyButton.clicked()
}
}
/*add: Transition {
NumberAnimation { properties: "y"; from: -60; duration: 100; easing.type: Easing.Linear }
}
remove: Transition {
NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 100 }
}
removeDisplaced: Transition {
SequentialAnimation {
PauseAnimation { duration: 100}
NumberAnimation { properties: "y"; duration: 100; easing.type: Easing.Linear }
}
}
displaced: Transition {
NumberAnimation { properties: "y"; duration: 100; easing.type: Easing.Linear }
}*/
}
} // Rectangle trayWindowBackground
}