nextcloud-desktop/src/gui/tray/Window.qml
Dominique Fuchs 3287ec2387 Typo fixes, minor additions
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2020-09-28 12:38:21 +00:00

710 lines
32 KiB
QML

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: "Nextcloud client tray window"
// Close tray window when focus is lost (e.g. click somewhere else on the screen)
onActiveChanged: {
if(!active) {
trayWindow.hide();
Systray.setClosed();
}
}
onVisibleChanged: {
currentAccountAvatar.source = ""
currentAccountAvatar.source = "image://avatars/currentUser"
currentAccountStateIndicator.source = ""
currentAccountStateIndicator.source = UserModel.isUserConnected(UserModel.currentUserId()) ? "qrc:///client/theme/colored/astate-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: {
currentAccountAvatar.source = ""
currentAccountAvatar.source = "image://avatars/currentUser"
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: "Nextcloud client tray window"
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: "Current account"
Accessible.description: "Shows the currently selected account and opens a dropdown list for all accounts when clicked"
Accessible.onPressAction: {
syncPauseButton.text = Systray.syncIsPaused() ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
accountMenu.open()
}
MouseArea {
id: accountBtnMouseArea
anchors.fill: parent
hoverEnabled: Style.hoverEffectsEnabled
// HACK: Imitate Qt hover effect brightness (which is not accessible as property)
// so that indicator background also flicks when hovered
onContainsMouseChanged: {
currentAccountStateIndicatorBackground.color = (containsMouse ? Style.ncBlueHover : Style.ncBlue)
}
// 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 menu")
Accessible.description: qsTr("Popup menu showing a list of accounts and general client actions")
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.description: qsTr("Opens setup window to add a new account connection")
Accessible.onPressAction: {
Systray.pauseResumeSync()
}
}
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.description: qsTr("Button that pauses or resumes sync activity")
Accessible.onPressAction: {
Systray.pauseResumeSync()
}
}
MenuItem {
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.description: qsTr("Button that opens the client settings window")
Accessible.onPressAction: {
Systray.openSettings()
}
}
MenuItem {
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.description: qsTr("Quits the nextcloud client application")
Accessible.onPressAction: {
Systray.shutdown()
}
}
}
}
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: "image://avatars/currentUser"
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
}
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")
Accessible.description: qsTr("Icon that indicates connection state for the currently selected account")
}
}
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: "white"
font.pixelSize: Style.topLinePixelSize
font.bold: true
}
Label {
id: currentAccountServer
width: Style.currentAccountLabelWidth
text: UserModel.currentUser.server
elide: Text.ElideRight
color: "white"
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("Local folder button")
Accessible.description: qsTr("Opens the local folder corresponding to the currently selected account")
Accessible.onPressAction: {
UserModel.openCurrentAccountLocalFolder()
}
}
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")
Accessible.description: qsTr("Opens Nextcloud Talk corresponding the currently sdelected users server in a new browser window")
Accessible.onPressAction: {
UserModel.openCurrentAccountTalk()
}
}
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("Show other apps")
Accessible.description: qsTr("Opens a popup menu with all other supported Nextcloud apps from the currently selected users server")
Accessible.onPressAction: {
appsMenu.open();
UserModel.openCurrentAccountServer();
}
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: appName
Accessible.description: qsTr("Opens the corresponding Nextcloud app in a new browser window")
Accessible.onPressAction: {
UserAppsModel.openAppUrl(appUrl)
}
}
}
}
}
}
} // 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")
Accessible.description: qsTr("A list of activities for the currently selected user account")
model: activityModel
delegate: RowLayout {
id: activityItem
width: parent.width
height: Style.trayWindowHeaderHeight
spacing: 0
Accessible.role: Accessible.ListItem
Accessible.name: activityTextColumn.text
Accessible.description: qsTr("Activity item - if applicable opens local folder containing the corresponding sync object when clicked")
Accessible.onPressAction: {
if (path !== "") {
Qt.openUrlExternally(path)
} else {
Qt.openUrlExternally(link)
}
}
MouseArea {
id: activityMouseArea
enabled: (path !== "" || link !== "")
anchors.left: activityItem.left
anchors.right: ((shareButton.visible) ? shareButton.left : activityItem.right)
height: parent.height
anchors.margins: 2
hoverEnabled: true
onClicked: {
if (path !== "") {
Qt.openUrlExternally(path)
} else {
Qt.openUrlExternally(link)
}
}
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 button")
Accessible.description: qsTr("Opens share window for the corresponding sync object")
Accessible.onPressAction: {
Systray.openShareDialog(displayPath,absolutePath)
}
}
}
/*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
}